Compare commits

..

62 Commits

Author SHA1 Message Date
madeline 99ea28e9f7 maybe fix ook crash 2026-05-20 14:33:48 -07:00
Giorgio Mendieta 31dd069879 docs: Improve building.md file (#1530)
* docs: Improve building.md file

- Fix broken XCode link
- Add MacOS running instructions
- Alphabetize linux packages

* fix: collapse packages sections for readability

* fix: collapse packages sections for readability

* fix: add --dvd flag to specify iso path

* fix: Remove incorrect info about game.iso
2026-05-17 07:51:38 -06:00
SrBananaMan a499877c37 Add an enable texture replacements option in the video tab (#1517)
Co-authored-by: Luke Street <luke@street.dev>
2026-05-17 07:51:15 -06:00
Olivia!! b91a58feab Adds option to unbind using controller only (#1212)
* Adds option to unbind using controller only

* Adds a new cheat to let you transform from the start of the game, without Midna or the Shadow Crystal

* Revert "Adds a new cheat to let you transform from the start of the game, without Midna or the Shadow Crystal"

This reverts commit 51ed736729.

* Do not allow unbinding the A or B buttons to prevent softlocks when no other input sources are available (e.g. playing on a TV)

* change 'and' to '&&' in accordance with code standards
2026-05-17 07:45:08 -06:00
doop c710ea5b38 Use 10% steps for bloom brightness setting (#1423) 2026-05-17 07:44:29 -06:00
Olivia!! cd4b29f8a1 Update controller bindings menu. (#1461)
* Add a button to reset to default controls, and hides the digital L and R binds except on advanced menu.

* Rename Controller mentions to Device + Extra menu sounds

* Shouldn't add a column here

* Changes mentions of controller to device in accordance with #1479

---------

Co-authored-by: MelonSpeedruns <melonspeedruns@stratobox.net>
2026-05-17 07:38:24 -06:00
Loïs 6faa4a3330 Fix mButtonText buffer overflow on long localized action labels (#1491) 2026-05-17 07:34:18 -06:00
Luke Street eed81e9ecc Update aurora 2026-05-17 09:33:26 -04:00
Loïs a773e5489e Fix unresponsive R trigger in dusklight menu (#1424) 2026-05-17 07:27:27 -06:00
Krutonium db6a1835d2 Flake: Update GitHub revision and hash for aurora-src (#1237)
* Flake: Update GitHub revision and hash for aurora-src

Fixes Build Error due to Aurora being out of date in the flake.

* No Required Aurora Hash
2026-05-17 07:26:50 -06:00
David Kanevskyy e10e1f74d5 Feature - remember window position (#1238)
* added .zed/ to gitignore (editor configs)

* remember window position upon closing

* Save window location on SDL_EVENT_WINDOW_MOVED or SDL_EVENT_WINDOW_RESIZED

* Fix code format mistakes

* Also persist window width/height

* Undo change to input::handle_event

* Undo aurora submodule change

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-05-17 07:25:12 -06:00
TakaRikka ff3eb9aa48 Merge pull request #1511 from SailorSnoW/fix/ook-consistency
Fix Ook first jump not matching console behavior
2026-05-16 17:55:41 -07:00
TakaRikka 513fdd2312 Merge pull request #1502 from SailorSnoW/fix/autostart-run
Fix LiveSplit not starting when TV settings screen is skipped
2026-05-16 17:52:25 -07:00
TakaRikka f33c3bdf72 Merge pull request #1380 from TwilitRealm/fast-boots-backwalk-anim-fix
Fix Backwalk Animation Speed with Fast Iron Boots
2026-05-16 17:48:50 -07:00
TakaRikka e08b427650 Merge branch 'main' into fast-boots-backwalk-anim-fix 2026-05-16 17:43:31 -07:00
SailorSnoW 9f0fa12c6e Fix Ook first jump not matching console behavior 2026-05-17 02:12:14 +02:00
SailorSnoW ad4f75bbb6 Fix LiveSplit not starting when TV settings screen is skipped 2026-05-16 19:13:07 +02:00
Loïs 5d2811955c Fix item wheel background capturing interpolated frame instead of current sim (#1447) 2026-05-16 08:19:57 -04:00
Pieter-Jan Briers 3b30c09e79 Fix PAL name keyboard (#1467)
Fixes #1460
2026-05-16 08:15:27 -04:00
MelonSpeedruns 8adc40708e Infinite Rupee now set to max Rupees (#1417)
Co-authored-by: MelonSpeedruns <melonspeedruns@stratobox.net>
2026-05-16 10:28:45 +02:00
SuperDude88 2541c3f293 Update Description (#1439)
Mention that it applies to other cases besides just boots
2026-05-16 10:28:21 +02:00
Loïs 30b003c9d9 fix rein interpolation jitter by rolling snapshot once per sim tick (#1421) 2026-05-15 17:16:36 -04:00
SuperDude88 c77ed40208 Fix Backwalk Animation Speed
Also make one of the other similar checks more concise
2026-05-15 01:52:53 -04:00
Luke Street af8300a77c Update aurora 2026-05-14 23:22:08 -06:00
SuperDude88 1ce1003e1a Bold Back Button Name in Popup (#1376)
* Bold Back Button Name

Makes it stand out more, especially when the back button is called something else that's just a common word

* Just b

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-05-14 23:21:01 -06:00
Loïs 10e2181692 Fix file select softlock when resetting during save (#1293) 2026-05-14 23:17:17 -06:00
Miguel 424d1d7b71 Fixed Debug target not compiling (#1157)
* Update f_op_actor_mng.h

* Update f_op_actor_mng.h
2026-05-14 23:10:09 -06:00
matthewdavidrichardanderson 26be454999 soft reset hotkey on release (#1358)
Co-authored-by: matthewdavidrichardanderson <matthewdavidrichardanderson@mfb.com>
2026-05-14 23:05:08 -06:00
Pieter-Jan Briers cf89da811a Fix fast tears going out of bounds (#1337)
Incorrect decomp from TPHD making the tears think you're always in the Zora River ride

Fixes https://github.com/TwilitRealm/dusklight/issues/1047
2026-05-14 23:04:36 -06:00
tomlube f02c7bd1ac modify gx init fail text (#1332) 2026-05-14 23:04:13 -06:00
Pieter-Jan Briers 53abb8b96d Use safe string copies in d_save.cpp (#1232)
* Use safe string copies in d_save.cpp

Seeing sentry crashes here that I suspect involve buffer overflows here.
2026-05-14 23:03:42 -06:00
Loïs 6a27fa23c8 Fix game mode on MacOS <26 (#1362) 2026-05-14 22:56:28 -06:00
Luke Street 025a425884 Use -fsigned-char on ARM 2026-05-14 22:54:55 -06:00
Irastris 5064385c8c Add styling for <b> tags (#1349) 2026-05-15 03:12:36 +02:00
Luke Street e52d9e9b71 Fix mDoLib_project with safe area 2026-05-14 17:06:58 -06:00
qwertyquerty 3520bb746c better completionist achievement requirements for skybook (#1258) 2026-05-14 22:56:15 +02:00
Irastris 1e10d95936 Disable scrolling the name cursor (#1243) 2026-05-14 22:53:34 +02:00
Loïs a923aa24e1 Fix Rollgoal completion reward to scale with Bigger Wallets (#1296) 2026-05-14 22:45:27 +02:00
tomlube 9c4f61678e update instructions (#1329)
better OS interoperability with iloader and imo much easier to set up too
2026-05-14 22:43:46 +02:00
MelonSpeedruns 7967ee1f66 Fix Autosaves resetting memory card during minigames (#1318)
* Fix autosaves resetting memory card during minigames

* Update description

* remove unused include

---------

Co-authored-by: MelonSpeedruns <melonspeedruns@stratobox.net>
2026-05-14 22:42:39 +02:00
Loïs 2bdc7b6c09 use bit-stable lerp form in frame_interpolation (#1326) 2026-05-14 15:05:54 -04:00
tomlube 232f73365c rebrand fix (#1290) 2026-05-14 16:31:37 +02:00
TakaRikka a913f1699f Merge pull request #1252 from TwilitRealm/erd
clear e_rd static boss ptr on delete
2026-05-13 19:31:35 -07:00
TakaRikka 01b4eaa2fa clear e_rd static boss ptr on delete 2026-05-13 19:27:39 -07:00
SuperDude88 9d2ba3eb49 Rename Settings Preset (#1233)
To match the bloom preset, since that name was updated in #1211
2026-05-13 20:26:30 -04:00
JaxonWasTaken 6026b4bb9b Apply rebrand to bloom setting value name (#1211)
The display name for BloomSetting::Dusk was unchanged in the rebranding
process, and still shows up as "Dusk" in the settings menu. Rename it to
"Dusklight" to bring it in line with the rebrand.
2026-05-13 16:34:14 -04:00
Luke Street 7a77d48954 Use legacy path if migration fails 2026-05-13 09:08:41 -06:00
qwertyquerty 4ee0d8ed4b 1.1.1 fixes (#1168)
* fix keyboard npe

* fix autosave NPE

* hintTalkEvCamera UB

* fix UB in f_pc_base logging

* fix NPE in karg carry logic

* fix link model dangling pointers

* exponential audio slider and better audio default

* fix speedrun mode defaullt layer restore issue
2026-05-13 08:56:16 -06:00
qwertyquerty ce554a107d speedrun mode hotfixes (#1160) 2026-05-13 01:10:25 -06:00
Luke Street 9ce1ab7d5a Migrate only user files 2026-05-13 00:47:29 -06:00
Luke Street 8830760d34 BUILD_SHARED_LIBS=OFF for Android 2026-05-12 23:04:13 -06:00
SuperDude88 9abe89f47f Oops (#1156)
Forgot one
2026-05-12 22:57:42 -06:00
Loïs 3e62c1e96e Add Invincible Enemies cheat (#1123) 2026-05-12 22:52:35 -06:00
Irastris d9bbea300d Add map loader to RmlUi (#1147)
* Add Warp to RmlUi

* Remove ImGui map loader
2026-05-12 22:52:02 -06:00
Luke Street 1a951511be Merge pull request #1153 from TwilitRealm/slingshot-ammo-cheat
Infinite Slingshot Seeds
2026-05-12 22:51:23 -06:00
Luke Street ab0efb7a3b CI fixes and enable mobile THP support 2026-05-12 22:33:12 -06:00
Luke Street 5fdc3c7a54 Merge pull request #875 from Krutonium/flake-desktop-icon 2026-05-12 22:10:53 -06:00
SuperDude88 2978ae145d Infinite Slingshot Seeds 2026-05-13 00:04:58 -04:00
Krutonium e93773757f Edit CMakeLists to add Nix Version 2026-05-12 23:35:54 -04:00
Krutonium 6be742b15f Merge branch 'main' into flake-desktop-icon 2026-05-12 23:34:10 -04:00
Krutonium d6cbc9b6d5 Set Version String to be NixOS-<short git hash> OR dirty 2026-05-10 14:21:03 -04:00
Krutonium b10211c4c2 Add Desktop Icon 2026-05-10 13:25:43 -04:00
70 changed files with 1353 additions and 726 deletions
+1
View File
@@ -1,6 +1,7 @@
# IDE folders
.idea/
.vs/
.zed/
# Caches
__pycache__
+23 -7
View File
@@ -5,8 +5,19 @@ if (NOT CMAKE_BUILD_TYPE)
"Build type options: Debug Release RelWithDebInfo MinSizeRel" FORCE)
endif ()
# obtain revision info from git
find_package(Git)
set(DUSK_VERSION_OVERRIDE "" CACHE STRING "Override version string (skips git detection and format validation)")
if (DUSK_VERSION_OVERRIDE)
set(DUSK_WC_DESCRIBE "${DUSK_VERSION_OVERRIDE}")
set(DUSK_VERSION_STRING "0.0.0.0")
set(DUSK_SHORT_VERSION_STRING "0.0.0")
set(DUSK_WC_REVISION "")
set(DUSK_WC_BRANCH "")
set(DUSK_WC_DATE "")
message(STATUS "Dusklight version overridden to ${DUSK_WC_DESCRIBE}")
else ()
# obtain revision info from git
find_package(Git)
if (GIT_FOUND)
# make sure version information gets re-run when the current Git HEAD changes
execute_process(WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --git-path HEAD
@@ -63,6 +74,8 @@ else ()
set(DUSK_SHORT_VERSION_STRING "0.0.0")
endif ()
endif ()
# Add version information to CI environment variables
if(DEFINED ENV{GITHUB_ENV})
file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION=${DUSK_WC_DESCRIBE}\n")
@@ -112,11 +125,6 @@ option(DUSK_BUILD_WARNINGS "Enable compiler warnings (off by default)")
option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.")
option(DUSK_MOVIE_SUPPORT "If on, compile against libjpeg-turbo to enable THP file decoding" ON)
option(DUSK_ENABLE_UPDATE_CHECKER "Enable update checking support" ON)
if(ANDROID)
set(DUSK_MOVIE_SUPPORT OFF)
endif ()
option(DUSK_ENABLE_SENTRY_NATIVE "Enable sentry-native crash reporting support" OFF)
set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
set(DUSK_SENTRY_ENVIRONMENT "development" CACHE STRING "Sentry environment")
@@ -155,6 +163,8 @@ if (DUSK_MOVIE_SUPPORT)
list(APPEND _jpeg_cmake_args -DCMAKE_TOOLCHAIN_FILE=${_jpeg_toolchain_file})
endif ()
set(_jpeg_passthrough_vars
ANDROID_ABI
ANDROID_PLATFORM
CMAKE_BUILD_TYPE
CMAKE_C_COMPILER
CMAKE_C_COMPILER_LAUNCHER
@@ -266,6 +276,12 @@ if (DUSK_ENABLE_SENTRY_NATIVE)
endif ()
endif ()
# Use signed char on ARM to match the original game (and x86)
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _arch)
if(_arch MATCHES "^(arm|aarch64)" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
add_compile_options(-fsigned-char)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
set(PLATFORM_NAME win32)
elseif (CMAKE_SYSTEM_NAME STREQUAL Darwin)
+10 -25
View File
@@ -249,22 +249,11 @@
"type": "BOOL",
"value": false
},
"CMAKE_DISABLE_FIND_PACKAGE_BZip2": {
"CMAKE_DISABLE_FIND_PACKAGE_PkgConfig": {
"type": "BOOL",
"value": true
},
"CMAKE_DISABLE_FIND_PACKAGE_LibLZMA": {
"type": "BOOL",
"value": true
},
"CMAKE_DISABLE_FIND_PACKAGE_zstd": {
"type": "BOOL",
"value": true
},
"CMAKE_DISABLE_FIND_PACKAGE_Freetype": {
"type": "BOOL",
"value": true
}
"CMAKE_IGNORE_PREFIX_PATH": "/opt/homebrew"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
@@ -329,7 +318,11 @@
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install",
"CMAKE_TOOLCHAIN_FILE": "$env{ANDROID_HOME}/ndk/$env{ANDROID_NDK_VERSION}/build/cmake/android.toolchain.cmake",
"ANDROID_PLATFORM": "android-28"
"ANDROID_PLATFORM": "android-28",
"BUILD_SHARED_LIBS": {
"type": "BOOL",
"value": false
}
}
},
{
@@ -416,7 +409,7 @@
},
"CMAKE_OSX_DEPLOYMENT_TARGET": "11.0",
"CMAKE_IGNORE_PREFIX_PATH": "/opt/homebrew",
"DUSK_MOVIE_SUPPORT": {
"BUILD_SHARED_LIBS": {
"type": "BOOL",
"value": false
}
@@ -449,11 +442,7 @@
],
"cacheVariables": {
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache",
"DUSK_MOVIE_SUPPORT": {
"type": "BOOL",
"value": false
}
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache"
}
},
{
@@ -463,11 +452,7 @@
],
"cacheVariables": {
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache",
"DUSK_MOVIE_SUPPORT": {
"type": "BOOL",
"value": false
}
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache"
}
},
{
+183 -53
View File
@@ -1,50 +1,164 @@
### Building
#### Prerequisites
# Building Dusklight
## Dependencies
The following dependencies are required:
* [CMake 3.25+](https://cmake.org)
* Windows: Install `CMake Tools` in Visual Studio
* macOS: `brew install cmake`
* [Python 3+](https://python.org)
* Windows: [Microsoft Store](https://go.microsoft.com/fwlink?linkID=2082640)
* Verify it's added to `%PATH%` by typing `python` in `cmd`.
* macOS: `brew install python@3`
* **[Windows]** [Visual Studio 2026 Community](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx)
* Select `C++ Development` and verify the following packages are included:
* `Windows 11 SDK`
* `CMake Tools`
* `C++ Clang Compiler`
* `C++ Clang-cl`
* **[macOS]** [Xcode 16.4+](https://developer.apple.com/xcode/download/)
* **[Linux]** Actively tested on Ubuntu 24.04, Arch Linux & derivatives.
* Ubuntu 24.04+ packages
```
build-essential curl git ninja-build clang lld zlib1g-dev libcurl4-openssl-dev \
libglu1-mesa-dev libdbus-1-dev libvulkan-dev libxi-dev libxrandr-dev libasound2-dev libpulse-dev \
libudev-dev libpng-dev libncurses5-dev cmake libx11-xcb-dev python3 python-is-python3 \
libclang-dev libfreetype-dev libxinerama-dev libxcursor-dev python3-markupsafe libgtk-3-dev \
libxss-dev libxtst-dev
```
* Arch Linux packages
```
base-devel cmake ninja llvm vulkan-headers python python-markupsafe clang lld alsa-lib libpulse libxrandr freetype2
```
* Fedora packages
```
cmake vulkan-headers ninja-build clang-devel llvm-devel libpng-devel
```
* It's also important that you install the developer tools and libraries
```
sudo dnf groupinstall "Development Tools" "Development Libraries"
```
#### Setup
Clone and initialize the Dusklight repository
### Windows
* Install [CMake 3.25+](https://cmake.org) by searching `CMake Tools` in Visual Studio
* Install Python 3 from the [Microsoft Store](https://go.microsoft.com/fwlink?linkID=2082640) and verify it's added to `%PATH%` by typing `python` in `cmd`.
Recommended IDEs:
* [Visual Studio 2026 Community](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx). During installation:
* Select `C++ Development` and verify the following packages are included:
* `Windows 11 SDK`
* `CMake Tools`
* `C++ Clang Compiler`
* `C++ Clang-cl`
### macOS
* Make sure [Homebrew](https://brew.sh) is installed
* Install [CMake 3.25+](https://cmake.org)
```sh
git clone --recursive https://github.com/TwilitRealm/dusklight.git
cd dusklight
git pull
git submodule update --init --recursive
brew install cmake
```
#### Building
* Install Python 3
```sh
brew install python@3
```
Recommended IDEs:
* [Xcode 16.4 or later](https://developer.apple.com/xcode/)
* [Visual Studio Code](https://code.visualstudio.com/download/)
* [CLion](https://www.jetbrains.com/clion/)
### Linux
Actively tested on Ubuntu 24.04, Arch Linux & derivatives.
**Ubuntu 24.04+ packages**
<details>
<summary>Click to expand</summary>
* Run the following command to install the required dependencies:
```sh
sudo apt update && sudo apt install -y \
build-essential \
clang \
cmake \
curl \
git \
libasound2-dev \
libclang-dev \
libcurl4-openssl-dev \
libdbus-1-dev \
libfreetype-dev \
libglu1-mesa-dev \
libgtk-3-dev \
libncurses5-dev \
libpng-dev \
libpulse-dev \
libudev-dev \
libvulkan-dev \
libx11-xcb-dev \
libxcursor-dev \
libxi-dev \
libxinerama-dev \
libxrandr-dev \
libxss-dev \
libxtst-dev \
lld \
ninja-build \
python-is-python3 \
python3 \
python3-markupsafe \
zlib1g-dev
```
</details>
<br>
**Arch Linux packages**
<details>
<summary>Click to expand</summary>
* Run the following command to install the required dependencies:
```sh
sudo pacman -S --needed \
alsa-lib \
base-devel \
clang \
cmake \
freetype2 \
libpulse \
libxrandr \
lld \
llvm \
ninja \
python \
python-markupsafe \
vulkan-headers
```
</details>
<br>
**Fedora packages**
<details>
<summary>Click to expand</summary>
* Run the following command to install the required dependencies:
```sh
sudo dnf install -y \
clang-devel \
cmake \
libpng-devel \
llvm-devel \
ninja-build \
vulkan-headers
```
* It's also important that you install the developer tools and libraries
```sh
sudo dnf groupinstall \
"Development Libraries" "Development Tools"
```
</details>
<br>
Recommended IDEs:
* [CLion](https://www.jetbrains.com/clion/)
* [Visual Studio Code](https://code.visualstudio.com/download/)
## Building
* Clone and initialize the Dusklight repository:
```sh
git clone --recursive https://github.com/TwilitRealm/dusklight.git
git pull
cd dusklight
git submodule update --init --recursive
```
**CLion (Windows / macOS / Linux)**
@@ -64,7 +178,8 @@ cmake --build --preset macos-default-relwithdebinfo
```
Alternate presets available:
- `macos-default-debug`: Clang, Debug
* `macos-default-debug`: Clang, Debug
**ninja (Linux)**
@@ -74,9 +189,10 @@ cmake --build --preset linux-default-relwithdebinfo
```
Alternate presets available:
- `linux-default-debug`: GCC, Debug
- `linux-clang-relwithdebinfo`: Clang, RelWithDebInfo
- `linux-clang-debug`: Clang, Debug
* `linux-default-debug`: GCC, Debug
* `linux-clang-relwithdebinfo`: Clang, RelWithDebInfo
* `linux-clang-debug`: Clang, Debug
**ninja (Windows)**
@@ -86,13 +202,27 @@ cmake --build --preset windows-msvc-relwithdebinfo
```
Alternate presets available:
- `windows-msvc-debug`: MSVC, Debug
- `windows-clang-relwithdebinfo`: Clang-cl, RelWithDebInfo
- `windows-clang-debug`: Clang-cl, Debug
#### Running
Pass the disc image as a positional argument. Supported formats: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ
* `windows-msvc-debug`: MSVC, Debug
* `windows-clang-relwithdebinfo`: Clang-cl, RelWithDebInfo
* `windows-clang-debug`: Clang-cl, Debug
## Running
**Windows / Linux**
* Pass the disc image as a positional argument using the `--dvd` flag. Supported formats are: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ
```sh
build/{preset}/dusklight/path/to/game.rvz
build/{preset}/dusklight --dvd /path/to/game.iso
```
**macOS**
macOS builds an `.app` bundle which contains the executable and all necessary resources.
* Pass the disc image as a positional argument using the `--dvd` flag. Supported formats are: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ
```sh
build/{preset}/Dusklight.app/Contents/MacOS/Dusklight --dvd /path/to/game.iso
```
If no path is specified, Dusklight defaults to `game.iso` in the current working directory.
+23 -21
View File
@@ -1,33 +1,40 @@
# Installing Dusklight on iOS via AltStore
# Installing Dusklight on iOS via iloader
## Prerequisites
- Mac with Homebrew installed
- iPhone connected via USB
- A Windows, Linux, or macOS device
- iOS device connected to computer via USB
- Dusklight IPA file (download the latest `Dusklight-vX.X.X-ios-arm64.ipa` from the [releases page](https://github.com/TwilitRealm/dusk/releases))
- Game disc - `GZ2E01` (Gamecube USA) or `GZ2PE01` (Gamecube PAL)
## 1. Install AltServer
```sh
brew install altserver
open -a AltServer
```
AltServer will appear in your menu bar.
- Executable bundles can be installed from [iloader's main page](https://iloader.app/) or [their GitHub](https://github.com/nab138/iloader) for Windows, Linux, and macOS.
- Windows WILL require iTunes to be installed
- Linux WILL require usbmuxd to be installed, this is installed by default in most distros though
## 2. Enable Developer Mode (iOS 16+)
- On your iPhone, go to **Settings > Privacy & Security > Developer Mode**
- Toggle it on and restart when prompted
## 3. Install AltStore on Your iPhone
## 3. Install Dusklight on Your iPhone
- Click AltServer in the menu bar
- Click **Install AltStore > [Your iPhone]**
- Enter your Apple ID credentials when prompted
- On your iPhone, go to **Settings > General > VPN & Device Management**
- Tap your Apple ID under "Developer App" and tap **Trust**
1. Sign into your Apple ID (this is required for registering app IDs, it is sent securely and not stored by iloader)
* You may be prompted to put in a code from your iOS device, do so
2. Plug in your iOS device via USB into your PC. If you're missing a dependency, an error pop-up will tell you to install it
* You will need to hit `Refresh` after plugging it in at this stage so that it can be detected, it does not automatically refresh
3. Leave settings unchanged (the Anisette server should stay Sidestore (.io))
3.(a) Installing SideStore directly is not required, but provides you a way to install Dusklight on your phone without being plugged into a computer later
4. Press `Import IPA` and choose your downloaded `Dusk-v.X.X.X-ios-arm64.ipa`, it will begin installing on your device
**NOTE:** *At various stages, you may be prompted to trust your device, do so*
## 3. Getting Dusk trusted
When installing sideloaded iOS applications, at first you will need to manually trust the app due to Apple's security policies
* Go to **Settings > General > VPN & Device Management**
* Tap the Apple ID you signed into iloader with under "Developer App" and tap **Trust**
* Tap **Allow** on the pop-up
## 4. Copy Files to Your iPhone
@@ -38,9 +45,4 @@ Transfer the IPA and game disc to your iPhone so they're accessible in the Files
- **USB transfer** - Connect your iPhone and drag files via Finder's sidebar
- **Cloud storage** - Upload to Google Drive, Dropbox, etc. and download on your iPhone
## 5. Install via AltStore
- Open **AltStore** on your iPhone
- Go to the **My Apps** tab
- Tap the **+** button (top left)
- Open the **Files** app and select the `.ipa` file
You may now use Dusklight on iOS, iPadOS!
+1 -1
+2 -3
View File
@@ -1450,8 +1450,6 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiEngine.hpp
src/dusk/imgui/ImGuiBloomWindow.cpp
src/dusk/imgui/ImGuiBloomWindow.hpp
src/dusk/imgui/ImGuiEnhancedLightingWindow.cpp
src/dusk/imgui/ImGuiEnhancedLightingWindow.hpp
src/dusk/imgui/ImGuiMenuTools.cpp
src/dusk/imgui/ImGuiMenuTools.hpp
src/dusk/imgui/ImGuiActorSpawner.cpp
@@ -1460,7 +1458,6 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiHeapOverlay.cpp
src/dusk/imgui/ImGuiControllerOverlay.cpp
src/dusk/imgui/ImGuiStubLog.cpp
src/dusk/imgui/ImGuiMapLoader.cpp
src/dusk/imgui/ImGuiSaveEditor.cpp
src/dusk/imgui/ImGuiStateShare.hpp
src/dusk/imgui/ImGuiStateShare.cpp
@@ -1511,6 +1508,8 @@ set(DUSK_FILES
src/dusk/ui/tab_bar.hpp
src/dusk/ui/ui.cpp
src/dusk/ui/ui.hpp
src/dusk/ui/warp.cpp
src/dusk/ui/warp.hpp
src/dusk/ui/window.cpp
src/dusk/ui/window.hpp
src/dusk/achievements.cpp
+14 -10
View File
@@ -15,12 +15,6 @@
# Dependencies that are not packaged in nixpkgs (used by the Linux package build):
buildSources = pkgs: {
aurora-src = pkgs.fetchFromGitHub {
owner = "encounter";
repo = "aurora";
rev = "63606a43265a3bc18dafd500ab4d7a2108f109e6";
hash = "sha256-xBvnAwGwNzav67Ac6oUz7RqDUwqgL2bsME3OOMn8Tqw=";
};
dawn-src = pkgs.fetchzip {
url = "https://github.com/encounter/dawn-build/releases/download/v20260423.175430/dawn-linux-x86_64.tar.gz";
hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs=";
@@ -50,18 +44,20 @@
# Dusklight Actual (Linux x86_64 only — relies on prebuilt dawn/nod binaries)
mkDusklight = pkgs:
let srcs = buildSources pkgs; in
let srcs = buildSources pkgs;
versionSuffix = if self ? shortRev && self.shortRev != null
then "nix-${self.shortRev}"
else "nix-dirty";
in
pkgs.stdenv.mkDerivation {
name = "dusklight";
src = ./.;
postUnpack = ''
mkdir -p $sourceRoot/extern/aurora
cp -r ${srcs.aurora-src}/. $sourceRoot/extern/aurora/
chmod -R u+w $sourceRoot/extern/aurora
sed -i '/add_subdirectory(tests)/d' $sourceRoot/extern/aurora/CMakeLists.txt
'';
# Remove last line to re-enable tests
cmakeFlags = [
"-DDUSK_VERSION_OVERRIDE=${versionSuffix}"
"-DFETCHCONTENT_FULLY_DISCONNECTED=ON"
"-DFETCHCONTENT_SOURCE_DIR_CXXOPTS=${pkgs.cxxopts.src}"
"-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}"
@@ -83,6 +79,14 @@
mkdir -p $out/bin
cp dusklight $out/bin/dusklight
cp -r ./res $out/bin/res
mkdir -p $out/share/applications
cp $src/platforms/freedesktop/dusklight.desktop $out/share/applications/dusklight.desktop
for size in 16 32 48 64 128 256 512 1024; do
install -Dm644 $src/platforms/freedesktop/''${size}x''${size}/apps/dusklight.png \
$out/share/icons/hicolor/''${size}x''${size}/apps/dusklight.png
done
'';
nativeBuildInputs = [
pkgs.cmake
+11
View File
@@ -1,8 +1,19 @@
#pragma once
#include <cmath>
#include <dolphin/types.h>
namespace dusk::audio {
// Converts a 0-1 volume to a linear amplitude multiplier.
// The curve is -4 dB per 10% step: 100% = 0 dB, 90% = -4 dB, ..., 0% = -inf dB
inline f32 MasterVolumeToLinear(f32 v) {
if (v <= 0.0f) {
return 0.0f;
}
return std::pow(10.0f, (v - 1.0f) * 2.0f);
}
/**
* Initialize the audio system and start playing audio.
*/
+1
View File
@@ -5,6 +5,7 @@
#include <m_Do/m_Do_MemCardRWmng.h>
#include <m_Do/m_Do_MemCard.h>
#include <d/actor/d_a_alink.h>
void noAutoSave();
void triggerAutoSave();
+14 -1
View File
@@ -175,6 +175,7 @@ class ConfigVar : public ConfigVarBase {
T defaultValue;
T value;
T overrideValue;
ConfigVarLayer priorLayer = ConfigVarLayer::Default;
public:
/**
@@ -265,6 +266,7 @@ public:
void setSpeedrunValue(T newValue) {
checkRegistered();
if (layer != ConfigVarLayer::Override) {
priorLayer = layer;
overrideValue = std::move(newValue);
layer = ConfigVarLayer::Speedrun;
}
@@ -282,9 +284,20 @@ public:
checkRegistered();
if (layer == ConfigVarLayer::Speedrun) {
overrideValue = {};
layer = ConfigVarLayer::Value;
layer = priorLayer;
}
}
/**
* \brief Get the user-persisted value, ignoring any temporary overrides.
*
* Used by Save() to write the correct value even when a speedrun override is active.
*/
[[nodiscard]] constexpr const T& getValueForSave() const noexcept {
checkRegistered();
const ConfigVarLayer effectiveLayer = (layer == ConfigVarLayer::Speedrun) ? priorLayer : layer;
return effectiveLayer == ConfigVarLayer::Default ? defaultValue : value;
}
};
using ActionBindConfigVar = ConfigVar<int>;
+1
View File
@@ -17,6 +17,7 @@ void ensure_initialized();
void begin_record();
void end_record();
void begin_sim_tick();
uint64_t sim_tick_seq();
void begin_frame(bool enabled, bool is_sim_frame, float step);
void interpolate();
float get_interpolation_step();
-1
View File
@@ -14,7 +14,6 @@ constexpr const char* SHOW_DEBUG_OVERLAY = "F3";
constexpr const char* SHOW_HEAP_VIEWER = "F4";
constexpr const char* SHOW_PLAYER_INFO = "F5";
constexpr const char* SHOW_SAVE_EDITOR = "F6";
constexpr const char* SHOW_MAP_LOADER = "F7";
constexpr const char* SHOW_STATE_SHARE = "F8";
constexpr const char* SHOW_DEBUG_CAMERA = "F9";
constexpr const char* SHOW_AUDIO_DEBUG = "F10";
+1
View File
@@ -10,6 +10,7 @@ extern bool IsShuttingDown;
extern bool IsGameLaunched;
extern bool RestartRequested;
extern std::filesystem::path ConfigPath;
extern std::filesystem::path CachePath;
#if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) || \
(defined(TARGET_OS_TV) && TARGET_OS_TV)
+7 -7
View File
@@ -72,6 +72,10 @@ struct UserSettings {
ConfigVar<bool> lockAspectRatio;
ConfigVar<bool> enableFpsOverlay;
ConfigVar<int> fpsOverlayCorner;
ConfigVar<int> windowPositionX;
ConfigVar<int> windowPositionY;
ConfigVar<int> windowWidth;
ConfigVar<int> windowHeight;
} video;
struct {
@@ -120,16 +124,10 @@ struct UserSettings {
ConfigVar<bool> enableDiscordPresence;
// Graphics
ConfigVar<bool> enhancedLighting;
ConfigVar<bool> enableSpecularLighting;
ConfigVar<bool> enableRimLighting;
ConfigVar<float> specularIntensity;
ConfigVar<float> rimIntensity;
ConfigVar<float> ambientLightMultiplier;
ConfigVar<float> diffuseLightMultiplier;
ConfigVar<BloomMode> bloomMode;
ConfigVar<float> bloomMultiplier;
ConfigVar<bool> disableWaterRefraction;
ConfigVar<bool> enableTextureReplacements;
ConfigVar<bool> enableFrameInterpolation;
ConfigVar<int> internalResolutionScale;
ConfigVar<int> shadowResolutionMultiplier;
@@ -165,6 +163,7 @@ struct UserSettings {
// Cheats
ConfigVar<bool> infiniteHearts;
ConfigVar<bool> infiniteArrows;
ConfigVar<bool> infiniteSeeds;
ConfigVar<bool> infiniteBombs;
ConfigVar<bool> infiniteOil;
ConfigVar<bool> infiniteOxygen;
@@ -178,6 +177,7 @@ struct UserSettings {
ConfigVar<bool> fastRoll;
ConfigVar<bool> fastSpinner;
ConfigVar<bool> freeMagicArmor;
ConfigVar<bool> invincibleEnemies;
// Technical
ConfigVar<bool> restoreWiiGlitches;
+1 -1
View File
@@ -108,7 +108,7 @@ struct fopAcM_search_prm {
struct fOpAcm_HIO_entry_c : public mDoHIO_entry_c {
virtual ~fOpAcm_HIO_entry_c() {}
#if DEBUG
#if DEBUG && !TARGET_PC
void removeHIO(const fopAc_ac_c* i_this) { removeHIO(*i_this); }
void removeHIO(const fopAc_ac_c& i_this) { removeHIO(i_this.base); }
void removeHIO(const fopEn_enemy_c& i_this) { removeHIO(i_this.base); }
+3 -1
View File
@@ -28,7 +28,9 @@
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.adventure-games</string>
<key>LSSupportsGameMode</key>
<true/>
<true/>
</dict>
</plist>
+4
View File
@@ -58,6 +58,10 @@ toast:active {
background-color: rgba(45, 43, 26, 80%);
}*/
b {
font-weight: bold;
}
toast heading {
display: flex;
gap: 18dp;
+4
View File
@@ -14,6 +14,10 @@ body {
color: #E0DBC8;
}
b {
font-weight: bold;
}
window {
display: flex;
flex-flow: column;
+2 -7
View File
@@ -7562,12 +7562,7 @@ void daAlink_c::setBlendMoveAnime(f32 i_morf) {
f32 sp2C;
f32 sp28 = mpHIO->mMove.m.mFootPositionRatio;
BOOL sp24 = checkEventRun();
BOOL sp20 = checkBootsMoveAnime(1);
#if TARGET_PC
if (dusk::getSettings().game.enableFastIronBoots) {
sp20 = FALSE;
}
#endif
BOOL sp20 = checkBootsMoveAnime(1) IF_DUSK(&& !dusk::getSettings().game.enableFastIronBoots);
f32 var_f29;
@@ -8080,7 +8075,7 @@ void daAlink_c::setBlendAtnBackMoveAnime(f32 i_morf) {
daAlink_ANM var_r27;
daAlink_ANM var_r29;
if (checkBootsMoveAnime(1)) {
if (checkBootsMoveAnime(1) IF_DUSK(&& !dusk::getSettings().game.enableFastIronBoots)) {
mMaxSpeed = mpHIO->mAtnMove.m.mMaxBackwardsSpeed;
var_f27 = mpHIO->mAtnMove.m.mMinBackWalkFrame;
var_f31 = mpHIO->mAtnMove.m.mBackWalkChangeRate;
+6 -1
View File
@@ -77,7 +77,12 @@ int daAlink_c::loadModelDVD() {
mpWlMidnaHairModel = NULL;
if (!checkNoResetFlg2(FLG2_UNK_280000)) {
dComIfG_resDelete(&mPhaseReq, mArcName);
if (!dComIfG_resDelete(&mPhaseReq, mArcName)) {
#if TARGET_PC
// resDelete no-ops if load was in-progress; force-unregister before freeAll
dComIfG_deleteObjectResMain(mArcName);
#endif
}
cPhs_Reset(&mPhaseReq);
mpArcHeap->freeAll();
+6
View File
@@ -8723,6 +8723,12 @@ int daAlink_c::procWolfCargoCarry() {
return checkNextActionWolf(0);
}
#if TARGET_PC
if (field_0x280c.getActor() == NULL) {
return checkNextActionWolf(0);
}
#endif
mDoMtx_stack_c::copy(((e_yc_class*)field_0x280c.getActor())->getLegR3Mtx());
mDoMtx_stack_c::transM(-9.0f, -7.0f, -30.0f);
mDoMtx_stack_c::multVecZero(&current.pos);
+18
View File
@@ -157,6 +157,21 @@ static void* s_h_sub(void* i_actor, void* i_data) {
return NULL;
}
#if TARGET_PC
static void sort_target_info_by_id() {
for (int i = 1; i < target_info_count; i++) {
void* key = target_info[i];
fpc_ProcID key_id = fopAcM_GetID(key);
int j = i - 1;
while (j >= 0 && fopAcM_GetID(target_info[j]) > key_id) {
target_info[j + 1] = target_info[j];
j--;
}
target_info[j + 1] = key;
}
}
#endif
static daPillar_c* search_hasira(e_mk_class* i_this) {
fopEn_enemy_c* actor = (fopEn_enemy_c*)&i_this->actor;
daPillar_c* pillar_p;
@@ -170,6 +185,9 @@ static daPillar_c* search_hasira(e_mk_class* i_this) {
if (i_this->firstHasiraFlag == 0) {
i_this->firstHasiraFlag++;
#if TARGET_PC
sort_target_info_by_id();
#endif
return (daPillar_c*)target_info[TREG_S(7) + 5];
}
+6
View File
@@ -7053,6 +7053,12 @@ static int daE_RD_IsDelete(e_rd_class*) {
}
static int daE_RD_Delete(e_rd_class* i_this) {
#if TARGET_PC
if (boss == i_this) {
boss = NULL;
}
#endif
fopEn_enemy_c* enemy = (fopEn_enemy_c*)&i_this->enemy;
fopAcM_RegisterDeleteID(i_this, "E_RD");
+10 -4
View File
@@ -22,6 +22,7 @@
#if TARGET_PC
#include "dusk/dusk.h"
#include "dusk/frame_interpolation.h"
namespace {
// FRAME INTERP NOTE: Sim tick control point snapshots for interpolation
@@ -32,6 +33,7 @@ int s_horseReinSimNumPrev;
int s_horseReinSimNumCurr;
bool s_horseReinSimPrevValid;
bool s_horseReinSimCurrValid;
uint64_t s_horseReinSimRolledSeq;
} // namespace
#endif
@@ -3033,10 +3035,14 @@ void daHorse_c::copyReinPos() {
}
#if TARGET_PC
if (field_0x1204 > 0) {
if (s_horseReinSimCurrValid && s_horseReinSimNumCurr > 0) {
memcpy(s_horseReinSimPrev, s_horseReinSimCurr, s_horseReinSimNumCurr * sizeof(cXyz));
s_horseReinSimNumPrev = s_horseReinSimNumCurr;
s_horseReinSimPrevValid = true;
const uint64_t simSeq = dusk::frame_interp::sim_tick_seq();
if (simSeq != s_horseReinSimRolledSeq) {
s_horseReinSimRolledSeq = simSeq;
if (s_horseReinSimCurrValid && s_horseReinSimNumCurr > 0) {
memcpy(s_horseReinSimPrev, s_horseReinSimCurr, s_horseReinSimNumCurr * sizeof(cXyz));
s_horseReinSimNumPrev = s_horseReinSimNumCurr;
s_horseReinSimPrevValid = true;
}
}
memcpy(s_horseReinSimCurr, m_reinLine.getPos(0), field_0x1204 * sizeof(cXyz));
s_horseReinSimNumCurr = field_0x1204;
+4
View File
@@ -1967,7 +1967,11 @@ static void demo_camera_shop(npc_henna_class* i_this) {
i_this->mMsgFlow.init(actor, 0x365, 0, NULL);
/* dSv_event_flag_c::KORO2_ALLCLEAR - Fishing - After all stages (8-8) of roll goal game cleared */
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[0x335]);
#if TARGET_PC
dComIfGp_setItemRupeeCount(dComIfGs_getRupeeMax());
#else
dComIfGp_setItemRupeeCount(1000);
#endif
} else if ((lbl_82_bss_91 & 0x38) == 0) {
i_this->mMsgFlow.init(actor, 0x34f, 0, NULL);
/* dSv_event_flag_c::F_0469 - Fishing Pond - Reserved for fishing */
+2 -1
View File
@@ -299,7 +299,8 @@ int daObjDrop_c::modeParentWait() {
#if TARGET_PC
static inline BOOL checkGetCargoRide() {
if ((daPy_getPlayerActorClass()->checkCargoCarry() && strcmp(dComIfGp_getStartStageName(), "F_SP112") == 0) ||
if (daPy_getPlayerActorClass()->checkCargoCarry() &&
strcmp(dComIfGp_getStartStageName(), "F_SP112") == 0 &&
dComIfGs_isLightDropGetFlag(dComIfGp_getStartStageDarkArea()))
{
return true;
+1 -2
View File
@@ -143,13 +143,12 @@ void dBrightCheck_c::modeMove() {
if (mDoCPd_c::getTrigA(PAD_1) || mDoCPd_c::getTrigStart(PAD_1)) {
mDoAud_seStart(Z2SE_ENTER_GAME, NULL, 0, 0);
#ifdef TARGET_PC
dusk::speedrun::start();
if (dusk::getSettings().game.speedrunMode && !dusk::getSettings().game.hideTvSettingsScreen) {
// start a new run if a run isn't already in progress
if (!dusk::m_speedrunInfo.m_isRunStarted) {
dusk::resetForSpeedrunMode();
dusk::m_speedrunInfo.startRun();
dusk::speedrun::start();
}
}
+8
View File
@@ -15,6 +15,7 @@
#include "f_op/f_op_actor_mng.h"
#if TARGET_PC
#include "dusk/achievements.h"
#include "dusk/settings.h"
#endif
static int plCutLRC[58] = {
@@ -429,6 +430,13 @@ fopAc_ac_c* cc_at_check(fopAc_ac_c* i_enemy, dCcU_AtInfo* i_AtInfo) {
}
}
#if TARGET_PC
if (dusk::getSettings().game.invincibleEnemies &&
fopAcM_GetGroup(i_enemy) == fopAc_ENEMY_e) {
i_AtInfo->mAttackPower = 0;
}
#endif
if (i_AtInfo->mAttackPower != 0) {
i_enemy->health -= i_AtInfo->mAttackPower;
}
+8
View File
@@ -19,6 +19,7 @@
#include "d/d_timer.h"
#include "f_op/f_op_msg_mng.h"
#include "f_op/f_op_scene_mng.h"
#include "m_Do/m_Do_MemCard.h"
#include "m_Do/m_Do_Reset.h"
#include "m_Do/m_Do_controller_pad.h"
#include "m_Do/m_Do_graphic.h"
@@ -1238,6 +1239,13 @@ BOOL dComIfG_resetToOpening(scene_class* i_scene) {
}
#endif
#ifdef TARGET_PC
if (!mDoMemCd_isCardCommNone()) {
return 0;
}
g_mDoMemCd_control.SaveSync();
#endif
dComIfG_changeOpeningScene(i_scene, fpcNm_OPENING_SCENE_e);
mDoAud_bgmStop(30);
mDoAud_resetProcess();
+4
View File
@@ -3882,7 +3882,11 @@ bool dCamera_c::hintTalkEvCamera() {
cSAngle acStack_1fc(20.0f);
for (i = 0; i < 2; i++) {
#if AVOID_UB
for (j = 0; j < 10; j++) {
#else
for (j = 0; j < 12; j++) {
#endif
cSAngle acStack_200(local_b0[j] * fVar22);
hintTalk->mDirection.U(acStack_1f8 + acStack_200);
hintTalk->mDirection.V(((hintTalk->field_0x28.V() * acStack_200.Cos()) * 0.2f) + acStack_1fc);
+8
View File
@@ -26,6 +26,10 @@
#include "f_op/f_op_overlap_mng.h"
#include "m_Do/m_Do_controller_pad.h"
#ifdef TARGET_PC
#include "dusk/frame_interpolation.h"
#endif
class dDlst_MENU_CAPTURE_c : public dDlst_base_c {
public:
virtual void draw() {
@@ -1088,6 +1092,10 @@ void dMw_c::dMw_ring_create(u8 i_origin) {
}
mpCapture->setCaptureFlag();
#ifdef TARGET_PC
dusk::frame_interp::request_presentation_sync();
#endif
}
bool dMw_c::dMw_ring_delete() {
+13
View File
@@ -18,6 +18,9 @@
#include "d/d_pane_class.h"
#include "dusk/frame_interpolation.h"
#include <cstring>
#if TARGET_PC
#include "dusk/string.hpp"
#endif
#if VERSION == VERSION_GCN_JPN
#define STR_BUF_LEN 528
@@ -2927,6 +2930,12 @@ bool dMeterButton_c::isClose() {
}
void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param_3) {
#if TARGET_PC
char* i_string_full = i_string;
char i_string_buf[sizeof(mButtonText[0])];
dusk::SafeStringCopyTruncate(i_string_buf, i_string);
i_string = i_string_buf;
#endif
if (strcmp(mButtonText[param_2], i_string) != 0 || field_0x4be[param_2] != i_button) {
if (param_2 == 0 && strcmp(mButtonText[1], i_string) == 0 &&
((i_button == BUTTON_A_e && field_0x4be[1] == BUTTON_A_e) ||
@@ -3022,6 +3031,10 @@ void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param
strcpy(mButtonText[param_2], i_string);
#if TARGET_PC
i_string = i_string_full;
#endif
if (param_2 == 0) {
if (param_3 != 0) {
field_0x4d9 = param_2;
+10 -7
View File
@@ -77,16 +77,16 @@ static const char* l_mojiEisu[65] = {
// That can't work on a modern platform, so instead I've filled them out ahead of time.
static const char* l_mojiEisuPal_1[65] = {
"A", "N", "\xC0", "\xCF", "1", "B", "O", "\xC1", "\xD0", "2", "C", "P", "\xC2", "\xD1", "3", "D", "Q",
"\xC3", "\xD2", "4", "E", "R", "\xC4", "\xD3", "5", "F", "S", "\xC5", "\xD4", "6", "G", "T", "\xC6", "\xD5",
"7", "H", "U", "\xC7", "\xD6", "8", "I", "V", "\xC8", "\xD7", "9", "J", "W", "\xC9", "\xD8", "0", "K",
"X", "\xCA", "\xD9", ",", "L", "Y", "\xCB", "\xDA", ".", "M", "Z", "\xCC", "\xDB", " ",
"\xC4", "\xD2", "4", "E", "R", "\xC6", "\xD3", "5", "F", "S", "\xC7", "\xD4", "6", "G", "T", "\xC8", "\xD6",
"7", "H", "U", "\xC9", "\x8C", "8", "I", "V", "\xCA", "\xD9", "9", "J", "W", "\xCB", "\xDA", "0", "K",
"X", "\xCC", "\xDB", ",", "L", "Y", "\xCD", "\xDC", ".", "M", "Z", "\xCE", "\x2D", " ",
};
static const char* l_mojiEisuPal_2[65] = {
"a", "n", "\xE0", "\xEF", "1", "b", "o", "\xE1", "\xF0", "2", "c", "p", "\xE2", "\xF1", "3", "d", "q",
"\xE3", "\xF2", "4", "e", "r", "\xE4", "\xF3", "5", "f", "s", "\xE5", "\xF4", "6", "g", "t", "\xE6",
"\xF5", "7", "h", "u", "\xE7", "\xF6", "8", "i", "v", "\xE8", "\xF7", "9", "j", "w", "\xE9", "\xF8", "0",
"k", "x", "\xEA", "\xF9", ",", "l", "y", "\xEB", "\xFA", ".", "m", "z", "\xEC", "\xFB", " ",
"\xE4", "\xF2", "4", "e", "r", "\xE6", "\xF3", "5", "f", "s", "\xE7", "\xF4", "6", "g", "t", "\xE8",
"\xF6", "7", "h", "u", "\xE9", "\x9C", "8", "i", "v", "\xEA", "\xF9", "9", "j", "w", "\xEB", "\xFA", "0",
"k", "x", "\xEC", "\xFB", ",", "l", "y", "\xED", "\xFC", ".", "m", "z", "\xEE", "\xDF", " ",
};
#elif REGION_PAL
static const char* l_mojiEisuPal_1[65] = {
@@ -295,6 +295,7 @@ void dName_c::_move() {
}
} else {
#endif
#if !TARGET_PC
if (mDoCPd_c::getTrigRight(PAD_1)) {
// BUG: this check only fails if the cursor is at exactly 7
// setMoji allows the cursor to reach 8, which is out of bounds here
@@ -311,7 +312,9 @@ void dName_c::_move() {
mCurPos--;
nameCursorMove();
}
} else if (mDoCPd_c::getTrigB(PAD_1)) {
} else
#endif
if (mDoCPd_c::getTrigB(PAD_1)) {
if (mCurPos == 0) {
mDoAud_seStart(Z2SE_SY_MENU_BACK, 0, 0, 0);
field_0x2ac = mSelProc;
+2
View File
@@ -10,6 +10,7 @@
#include "d/d_meter2_info.h"
#include "d/d_s_name.h"
#include "dusk/imgui/ImGuiConsole.hpp"
#include "dusk/livesplit.h"
#include "dusk/memory.h"
#include "dusk/speedrun.h"
#include "dusk/settings.h"
@@ -422,6 +423,7 @@ void dScnName_c::changeGameScene() {
if (!dusk::m_speedrunInfo.m_isRunStarted) {
dusk::resetForSpeedrunMode();
dusk::m_speedrunInfo.startRun();
dusk::speedrun::start();
}
}
+3 -5
View File
@@ -30,7 +30,9 @@
#if TARGET_PC
#include "dusk/settings.h"
#include <f_ap/f_ap_game.h>
#include <dusk/autosave.h>
#include "dusk/string.hpp"
#define strcpy dusk::SafeStringCopy
#endif
static u8 dSv_item_rename(u8 i_itemNo) {
@@ -349,10 +351,6 @@ void dSv_player_item_c::setItem(int i_slotNo, u8 i_itemNo) {
dComIfGp_setSelectItem(i);
}
}
#if TARGET_PC
triggerAutoSave();
#endif
}
u8 dSv_player_item_c::getItem(int i_slotNo, bool i_checkCombo) const {
+6 -3
View File
@@ -191,13 +191,17 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
}
bool hasJewelRod = false;
for (int slot = 0; slot < 24 && !hasJewelRod; ++slot) {
bool hasAncientDoc = false;
for (int slot = 0; slot < 24; ++slot) {
const u8 item = dComIfGs_getItem(slot, false);
if (item == dItemNo_JEWEL_ROD_e || item == dItemNo_JEWEL_BEE_ROD_e || item == dItemNo_JEWEL_WORM_ROD_e) {
hasJewelRod = true;
}
if (item == dItemNo_ANCIENT_DOCUMENT_e || item == dItemNo_ANCIENT_DOCUMENT2_e || item == dItemNo_AIR_LETTER_e) {
hasAncientDoc = true;
}
}
if (!hasJewelRod) {
if (!hasJewelRod || !hasAncientDoc) {
return;
}
@@ -212,7 +216,6 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
dItemNo_KANTERA_e,
dItemNo_PACHINKO_e,
dItemNo_HAWK_EYE_e,
dItemNo_ANCIENT_DOCUMENT_e,
dItemNo_HORSE_FLUTE_e,
};
for (u8 required : requiredWheelItems) {
+26 -6
View File
@@ -14,9 +14,23 @@ static AutoSaveFuncs AutoSaveFuncsProc[] = {
void noAutoSave() {}
bool canAutoSave() {
daAlink_c* player = (daAlink_c*)daAlink_getAlinkActorClass();
if (player == nullptr) {
return false;
}
if (player->checkCargoCarry() || player->checkCanoeRide()) {
return false;
}
return dusk::getSettings().game.autoSave && shouldAutoSave && mAutoSaveProc == 0 &&
strcmp(dComIfGp_getStartStageName(), "F_SP102") != 0 &&
strcmp(dComIfGp_getStartStageName(), "F_SP112") != 0;
}
void triggerAutoSave() {
if (dusk::getSettings().game.autoSave && shouldAutoSave && mAutoSaveProc == 0 &&
strcmp(dComIfGp_getStartStageName(), "F_SP102") != 0)
if (canAutoSave())
{
mAutoSaveProc = 1;
}
@@ -26,8 +40,12 @@ void updateAutoSave() {
(AutoSaveFuncsProc[mAutoSaveProc])();
}
void writeAutoSave() {
int stageNo = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo());
bool writeAutoSave() {
stage_stag_info_class* stagInfo = dComIfGp_getStageStagInfo();
if (stagInfo == nullptr) {
return false;
}
int stageNo = dStage_stagInfo_GetSaveTbl(stagInfo);
dComIfGs_putSave(stageNo);
dComIfGs_setMemoryToCard(mSaveBuffer, dComIfGs_getDataNum());
@@ -40,6 +58,7 @@ void writeAutoSave() {
}
g_mDoMemCd_control.save(mSaveBuffer, sizeof(mSaveBuffer), 0);
return true;
}
void autoSaving() {
@@ -48,8 +67,9 @@ void autoSaving() {
if (cardState == 2) {
mAutoSaveProc = 1;
} else if (cardState == 1) {
writeAutoSave();
mAutoSaveProc = 3;
if (writeAutoSave()) {
mAutoSaveProc = 3;
}
}
}
}
+3 -2
View File
@@ -61,7 +61,7 @@ void ConfigImpl<T>::loadFromJson(ConfigVar<T>& cVar, const json& jsonValue) {
template<ConfigValue T>
nlohmann::json ConfigImpl<T>::dumpToJson(const ConfigVar<T>& cVar) {
return cVar.getValue();
return cVar.getValueForSave();
}
template<ConfigValue T> requires std::is_integral_v<T> && std::is_signed_v<T>
@@ -249,7 +249,8 @@ void dusk::config::Save() {
json j;
for (const auto& pair : RegisteredConfigVars) {
if (pair.second->getLayer() == ConfigVarLayer::Value) {
const auto layer = pair.second->getLayer();
if (layer == ConfigVarLayer::Value || layer == ConfigVarLayer::Speedrun) {
j[pair.first] = pair.second->getImpl()->dumpToJson(*pair.second);
}
}
+1 -1
View File
@@ -61,7 +61,7 @@ std::string release_name() {
}
std::filesystem::path sentry_database_path() {
return dusk::ConfigPath / "sentry";
return dusk::CachePath / "sentry";
}
std::filesystem::path log_attachment_path() {
+222 -93
View File
@@ -6,6 +6,7 @@
#include "dusk/main.h"
#include <array>
#include <chrono>
#include <filesystem>
#include <optional>
#include <ranges>
@@ -30,6 +31,21 @@ constexpr auto kLocationDescriptorName = "data_location.json";
constexpr auto kPipelineCacheName = "pipeline_cache.db";
constexpr auto kInitialPipelineCacheName = "initial_pipeline_cache.db";
constexpr std::array<std::string_view, 4> kUserDataDirectories = {
"texture_replacements",
"USA",
"EUR",
"JAP",
};
constexpr std::array<std::string_view, 6> kUserDataFiles = {
"achievements.json",
"config.json",
"controller_ports.dat",
"imgui.ini",
"keyboard_bindings.dat",
"states.json",
};
enum class LocationMode {
Default,
Portable,
@@ -62,6 +78,7 @@ struct MigrationStats {
std::optional<std::filesystem::path> sConfiguredDataPath;
std::optional<std::filesystem::path> sActiveDescriptorPath;
std::optional<std::filesystem::path> sActivePrefPath;
std::filesystem::path path_from_utf8(std::string_view value) {
return std::filesystem::path{
@@ -70,19 +87,22 @@ std::filesystem::path path_from_utf8(std::string_view value) {
};
}
std::filesystem::path get_legacy_path() {
if (std::string_view{LegacyAppName}.empty()) {
std::filesystem::path legacy_path_for_pref_path(const std::filesystem::path& prefPath) {
if (std::string_view{LegacyAppName}.empty() || prefPath.empty()) {
return {};
}
char* prefPath = SDL_GetPrefPath(OrgName, LegacyAppName);
if (!prefPath) {
Log.fatal("Unable to get PrefPath: {}", SDL_GetError());
auto normalizedPrefPath = prefPath;
if (normalizedPrefPath.filename().empty()) {
normalizedPrefPath = normalizedPrefPath.parent_path();
}
std::filesystem::path result{reinterpret_cast<const char8_t*>(prefPath)};
SDL_free(prefPath);
return result;
const auto parentPath = normalizedPrefPath.parent_path();
if (parentPath.empty()) {
return {};
}
return parentPath / LegacyAppName;
}
std::filesystem::path get_pref_path() {
@@ -96,6 +116,13 @@ std::filesystem::path get_pref_path() {
return result;
}
std::filesystem::path active_pref_path() {
if (sActivePrefPath) {
return *sActivePrefPath;
}
return get_pref_path();
}
std::filesystem::path base_path_relative(const std::filesystem::path& path) {
const auto* basePath = SDL_GetBasePath();
if (!basePath) {
@@ -249,6 +276,69 @@ std::filesystem::path absolute_path(const std::filesystem::path& path) {
return absolute.lexically_normal();
}
std::filesystem::path rename_legacy_pref_path(
const std::filesystem::path& legacyPath, const std::filesystem::path& prefPath) {
if (legacyPath.empty() || prefPath.empty() ||
normalized_path(legacyPath) == normalized_path(prefPath))
{
return prefPath;
}
std::error_code ec;
if (!std::filesystem::exists(legacyPath, ec)) {
if (ec) {
Log.warn("Failed to inspect legacy data directory '{}': {}",
io::fs_path_to_string(legacyPath), ec.message());
}
return prefPath;
}
const bool prefExists = std::filesystem::exists(prefPath, ec);
if (ec) {
Log.warn("Failed to inspect data directory '{}': {}", io::fs_path_to_string(prefPath),
ec.message());
return prefPath;
}
if (prefExists) {
if (!std::filesystem::is_directory(prefPath, ec) ||
!std::filesystem::is_empty(prefPath, ec))
{
if (ec) {
Log.warn("Failed to inspect data directory '{}': {}",
io::fs_path_to_string(prefPath), ec.message());
} else {
Log.info("Skipping legacy data directory rename because '{}' is not empty",
io::fs_path_to_string(prefPath));
}
return prefPath;
}
std::filesystem::remove(prefPath, ec);
if (ec) {
Log.warn("Failed to remove empty data directory '{}' before legacy rename: {}",
io::fs_path_to_string(prefPath), ec.message());
return prefPath;
}
}
std::filesystem::rename(legacyPath, prefPath, ec);
if (ec) {
Log.warn("Failed to rename legacy data directory '{}' to '{}': {}",
io::fs_path_to_string(legacyPath), io::fs_path_to_string(prefPath), ec.message());
ec.clear();
if (!std::filesystem::exists(prefPath, ec) && !ec) {
Log.info("Using legacy data directory '{}' because the new data directory is absent",
io::fs_path_to_string(legacyPath));
return legacyPath;
}
return prefPath;
}
Log.info("Renamed legacy data directory '{}' to '{}'", io::fs_path_to_string(legacyPath),
io::fs_path_to_string(prefPath));
return prefPath;
}
bool is_same_or_inside(const std::filesystem::path& root, const std::filesystem::path& path) {
const auto normalizedRoot = normalized_path(root);
const auto normalizedPath = normalized_path(path);
@@ -283,85 +373,46 @@ bool should_skip_migration_path(const std::filesystem::path& path,
return false;
}
bool has_location_descriptor(const std::filesystem::path& path) {
std::error_code ec;
return std::filesystem::exists(path / kLocationDescriptorName, ec);
bool matches_name(std::string_view name, const auto& names) {
return std::ranges::find(names, name) != names.end();
}
bool remove_empty_destination_for_rename(const std::filesystem::path& path) {
std::error_code ec;
const bool exists = std::filesystem::exists(path, ec);
if (ec) {
Log.debug("Could not inspect migration destination '{}': {}", io::fs_path_to_string(path),
ec.message());
bool should_migrate_user_data_path(
const std::filesystem::path& sourcePath, const std::filesystem::path& from) {
const auto relativePath = sourcePath.lexically_relative(from);
if (relativePath.empty() || relativePath.is_absolute()) {
return false;
}
if (!exists) {
auto it = relativePath.begin();
if (it == relativePath.end() || *it == "..") {
return false;
}
const auto first = io::fs_path_to_string(*it);
if (matches_name(first, kUserDataDirectories)) {
return true;
}
const bool canRemove = std::filesystem::is_directory(path, ec) &&
std::filesystem::is_empty(path, ec) && !has_location_descriptor(path);
if (ec || !canRemove) {
if (ec) {
Log.debug("Could not inspect migration destination '{}': {}",
io::fs_path_to_string(path), ec.message());
}
++it;
if (it != relativePath.end()) {
return false;
}
std::filesystem::remove(path, ec);
if (ec) {
Log.debug("Could not remove empty migration destination '{}': {}",
io::fs_path_to_string(path), ec.message());
return false;
const auto filename = io::fs_path_to_string(relativePath.filename());
if (matches_name(filename, kUserDataFiles)) {
return true;
}
return true;
}
bool try_rename_directory_migration(
const std::filesystem::path& from, const std::filesystem::path& to) {
std::error_code ec;
if (!std::filesystem::is_directory(from, ec)) {
return false;
}
if (ec) {
Log.debug("Could not inspect migration source '{}': {}", io::fs_path_to_string(from),
ec.message());
return false;
}
if (has_location_descriptor(from)) {
return false;
}
if (!remove_empty_destination_for_rename(to)) {
return false;
}
std::filesystem::create_directories(to.parent_path(), ec);
if (ec) {
Log.debug("Could not create migration destination parent '{}': {}",
io::fs_path_to_string(to.parent_path()), ec.message());
return false;
}
std::filesystem::rename(from, to, ec);
if (ec) {
Log.debug("Could not rename data directory '{}' to '{}': {}", io::fs_path_to_string(from),
io::fs_path_to_string(to), ec.message());
return false;
}
Log.info("Renamed data directory '{}' to '{}'", io::fs_path_to_string(from),
io::fs_path_to_string(to));
return true;
return relativePath.extension() == ".controller" || relativePath.extension() == ".gci" ||
(filename.starts_with("MemoryCard") && filename.ends_with(".raw"));
}
std::filesystem::path current_data_path() {
if (!ConfigPath.empty()) {
return ConfigPath;
}
const auto prefPath = get_pref_path();
const auto prefPath = active_pref_path();
const auto descriptor = read_location_descriptor(prefPath);
if (descriptor) {
sActiveDescriptorPath = descriptor->path;
@@ -427,7 +478,7 @@ bool write_location_descriptor(LocationMode mode, const std::filesystem::path& t
json["previousPath"] = io::fs_path_to_string(descriptor.previousPath);
}
const auto prefPath = get_pref_path();
const auto prefPath = active_pref_path();
for (const auto& path : descriptor_write_paths(prefPath)) {
if (write_descriptor_json(path, json)) {
sActiveDescriptorPath = path;
@@ -439,6 +490,61 @@ bool write_location_descriptor(LocationMode mode, const std::filesystem::path& t
return false;
}
void set_error(std::string* errorOut, std::string error) {
if (errorOut != nullptr) {
*errorOut = std::move(error);
}
}
bool validate_writable_data_path(const std::filesystem::path& path, std::string* errorOut) {
if (path.empty()) {
set_error(errorOut, "Choose a folder.");
return false;
}
std::error_code ec;
std::filesystem::create_directories(path, ec);
if (ec) {
set_error(errorOut, fmt::format("{} could not create the selected folder.", AppName));
Log.warn("Failed to create custom data folder '{}': {}", io::fs_path_to_string(path),
ec.message());
return false;
}
if (!std::filesystem::is_directory(path, ec)) {
set_error(errorOut, "The selected path is not a folder.");
if (ec) {
Log.warn("Failed to inspect custom data folder '{}': {}", io::fs_path_to_string(path),
ec.message());
}
return false;
}
const auto probePath = path / fmt::format(".write-probe-{}.tmp",
std::chrono::steady_clock::now().time_since_epoch().count());
try {
io::FileStream::WriteAllText(probePath, "dusk");
} catch (const std::exception& e) {
set_error(errorOut, fmt::format("{} could not write to the selected folder.", AppName));
Log.warn("Failed write probe for custom data folder '{}': {}", io::fs_path_to_string(path),
e.what());
return false;
}
std::filesystem::remove(probePath, ec);
if (ec) {
set_error(
errorOut, fmt::format("{} could write to the selected folder, but could not remove "
"the test file it created.",
AppName));
Log.warn("Failed to remove custom data folder write probe '{}': {}",
io::fs_path_to_string(probePath), ec.message());
return false;
}
return true;
}
std::uintmax_t remove_empty_directories(const std::filesystem::path& root, bool includeRoot) {
std::error_code ec;
std::vector<std::filesystem::path> directories;
@@ -647,14 +753,6 @@ void migrate_directory(const std::filesystem::path& from, const std::filesystem:
return;
}
if (try_rename_directory_migration(from, to)) {
return;
}
if (try_rename_directory_migration(from, to)) {
return;
}
std::filesystem::create_directories(to, ec);
if (ec) {
++stats.failures;
@@ -700,6 +798,16 @@ void migrate_directory(const std::filesystem::path& from, const std::filesystem:
continue;
}
if (!should_migrate_user_data_path(sourcePath, from)) {
++stats.skippedUnsupportedEntries;
if (std::filesystem::is_directory(status)) {
it.disable_recursion_pending();
}
ec.clear();
it.increment(ec);
continue;
}
const auto relativePath = sourcePath.lexically_relative(from);
if (relativePath.empty() || relativePath.is_absolute()) {
++stats.failures;
@@ -761,8 +869,6 @@ void migrate_data(const std::filesystem::path& prefPath, const std::filesystem::
const LocationDescriptor* descriptor) {
if (descriptor && !descriptor->previousPath.empty()) {
migrate_directory(descriptor->previousPath, dataPath, prefPath);
} else if (const auto legacyPath = get_legacy_path(); !legacyPath.empty()) {
migrate_directory(legacyPath, dataPath, prefPath);
}
}
@@ -899,17 +1005,25 @@ bool open_data_path() {
#endif
}
bool set_custom_data_path(const std::filesystem::path& path) {
if (path.empty()) {
Log.warn("Ignoring empty custom data path");
bool set_custom_data_path(const std::filesystem::path& path, std::string* errorOut) {
if (!validate_writable_data_path(path, errorOut)) {
return false;
}
return write_location_descriptor(LocationMode::Custom, path);
if (!write_location_descriptor(LocationMode::Custom, path)) {
set_error(errorOut, fmt::format("{} could not save the data folder setting.", AppName));
return false;
}
return true;
}
bool set_custom_data_path(const char* path) {
return set_custom_data_path(path_from_utf8(path));
bool set_custom_data_path(const char* path, std::string* errorOut) {
if (path == nullptr) {
set_error(errorOut, "Choose a folder.");
return false;
}
return set_custom_data_path(path_from_utf8(path), errorOut);
}
bool set_portable_data_path() {
@@ -917,12 +1031,12 @@ bool set_portable_data_path() {
}
bool reset_data_path() {
const auto prefPath = get_pref_path();
const auto prefPath = active_pref_path();
return write_location_descriptor(LocationMode::Default, default_data_path(prefPath));
}
bool is_default_data_path() {
const auto prefPath = get_pref_path();
const auto prefPath = active_pref_path();
return normalized_path(configured_data_path()) == normalized_path(default_data_path(prefPath));
}
@@ -931,7 +1045,7 @@ std::filesystem::path configured_data_path() {
return *sConfiguredDataPath;
}
const auto prefPath = get_pref_path();
const auto prefPath = active_pref_path();
const auto descriptor = read_location_descriptor(prefPath);
if (descriptor) {
sActiveDescriptorPath = descriptor->path;
@@ -941,6 +1055,13 @@ std::filesystem::path configured_data_path() {
return *sConfiguredDataPath;
}
std::filesystem::path cache_path() {
if (!CachePath.empty()) {
return CachePath;
}
return active_pref_path();
}
bool is_data_path_restart_pending() {
if (ConfigPath.empty()) {
return false;
@@ -949,8 +1070,12 @@ bool is_data_path_restart_pending() {
return normalized_path(ConfigPath) != normalized_path(configured_data_path());
}
std::filesystem::path initialize_data() {
const auto prefPath = get_pref_path();
Paths initialize_data() {
const auto preferredPrefPath = get_pref_path();
const auto prefPath =
rename_legacy_pref_path(legacy_path_for_pref_path(preferredPrefPath), preferredPrefPath);
sActivePrefPath = prefPath;
const auto descriptor = read_location_descriptor(prefPath);
if (descriptor) {
sActiveDescriptorPath = descriptor->path;
@@ -963,9 +1088,13 @@ std::filesystem::path initialize_data() {
migrate_data(prefPath, dataPath, descriptor ? &descriptor->descriptor : nullptr);
ensure_data_directory(dataPath);
ensure_initial_pipeline_cache(dataPath);
ensure_data_directory(prefPath);
ensure_initial_pipeline_cache(prefPath);
return dataPath;
return Paths{
.userPath = dataPath,
.cachePath = prefPath,
};
}
} // namespace dusk::data
+11 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include <filesystem>
#include <string>
#if defined(__APPLE__)
#include <TargetConditionals.h>
@@ -14,7 +15,7 @@
#define DUSK_CAN_OPEN_DATA_FOLDER 0
#endif
#if defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST
#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || defined(__ANDROID__)
#define DUSK_CAN_CHANGE_DATA_FOLDER 0
#else
#define DUSK_CAN_CHANGE_DATA_FOLDER 1
@@ -22,11 +23,17 @@
namespace dusk::data {
std::filesystem::path initialize_data();
struct Paths {
std::filesystem::path userPath;
std::filesystem::path cachePath;
};
Paths initialize_data();
std::filesystem::path configured_data_path();
std::filesystem::path cache_path();
bool open_data_path();
bool set_custom_data_path(const char* path);
bool set_custom_data_path(const std::filesystem::path& path);
bool set_custom_data_path(const char* path, std::string* errorOut);
bool set_custom_data_path(const std::filesystem::path& path, std::string* errorOut);
bool set_portable_data_path();
bool reset_data_path();
bool is_default_data_path();
+11 -6
View File
@@ -21,6 +21,7 @@ bool g_sync_presentation = false;
float g_step = 0.0f;
bool g_is_sim_frame = false;
bool g_ui_tick_pending = false;
uint64_t g_sim_tick_seq = 0;
Recording g_current_recording;
Recording g_previous_recording;
@@ -66,19 +67,18 @@ void copy_view_to_snap(CameraSnapshot* dst, const view_class& v) {
}
inline void lerp_matrix(Mtx out, const Mtx lhs, const Mtx rhs, float step) {
const float old_weight = 1.0f - step;
for (size_t row = 0; row < 3; ++row) {
for (size_t col = 0; col < 4; ++col) {
out[row][col] = lhs[row][col] * old_weight + rhs[row][col] * step;
const float l = lhs[row][col];
out[row][col] = l + (rhs[row][col] - l) * step;
}
}
}
inline void lerp_xyz(cXyz* out, const cXyz& lhs, const cXyz& rhs, float step) {
const float old_weight = 1.0f - step;
out->x = lhs.x * old_weight + rhs.x * step;
out->y = lhs.y * old_weight + rhs.y * step;
out->z = lhs.z * old_weight + rhs.z * step;
out->x = lhs.x + (rhs.x - lhs.x) * step;
out->y = lhs.y + (rhs.y - lhs.y) * step;
out->z = lhs.z + (rhs.z - lhs.z) * step;
}
static s16 lerp_bank(s16 a, s16 b, f32 t) {
@@ -135,6 +135,11 @@ void begin_sim_tick() {
s_interpolationCallBackWork.clear();
s_cam_prev = std::move(s_cam_curr);
++g_sim_tick_seq;
}
uint64_t sim_tick_seq() {
return g_sim_tick_seq;
}
void begin_frame(bool enabled, bool is_sim_frame, float step) {
+3 -5
View File
@@ -263,7 +263,7 @@ namespace dusk {
}
if (getSettings().game.enableResetKeybind && ImGui::GetIO().KeyCtrl &&
ImGui::IsKeyPressed(ImGuiKey_R) && !fpcM_SearchByName(fpcNm_LOGO_SCENE_e))
ImGui::IsKeyReleased(ImGuiKey_R) && !fpcM_SearchByName(fpcNm_LOGO_SCENE_e))
{
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
}
@@ -322,8 +322,8 @@ namespace dusk {
}
ImGui::PushFont(ImGuiEngine::fontLarge);
ImGuiTextCenter("Failed to initialize any graphics backend.");
ImGuiTextCenter("\nYour system may be misconfigured, or your hardware may not support the required versions of any of the available backends.");
ImGuiTextCenter("\nA clean reinstall of Dusklight may help. For further assistance, please visit #tech-support on the Twilit Realm Discord server.");
ImGuiTextCenter("\nDusklight requires Vulkan 1.1+, or Direct X 12.0.");
ImGuiTextCenter("\nTry updating your Operating System and GPU drivers.");
const auto& style = ImGui::GetStyle();
const auto retrySize = ImGui::CalcTextSize("Retry (Auto backend)");
const auto quitSize = ImGui::CalcTextSize("Quit");
@@ -368,9 +368,7 @@ namespace dusk {
m_menuTools.ShowProcessManager();
m_menuTools.ShowHeapOverlay();
m_menuTools.ShowStubLog();
m_menuTools.ShowMapLoader();
m_menuTools.ShowBloomWindow();
m_menuTools.ShowEnhancedLightingWindow();
m_menuTools.ShowPlayerInfo();
m_menuTools.ShowAudioDebug();
m_menuTools.ShowSaveEditor();
@@ -1,103 +0,0 @@
#include "imgui.h"
#include "ImGuiEnhancedLightingWindow.hpp"
#include "ImGuiMenuTools.hpp"
#include "dusk/config.hpp"
#include "dusk/settings.h"
#include <aurora/gfx.h>
namespace dusk {
static void ApplyFromSettings() {
const auto& g = getSettings().game;
aurora_set_enhanced_lighting_state({
g.enhancedLighting.getValue(),
g.enableSpecularLighting.getValue(),
g.enableRimLighting.getValue(),
g.specularIntensity.getValue(),
g.rimIntensity.getValue(),
g.ambientLightMultiplier.getValue(),
g.diffuseLightMultiplier.getValue(),
});
}
void DrawEnhancedLightingWindow(bool& open) {
if (!open) {
return;
}
if (!ImGui::Begin("Enhanced Lighting", &open)) {
ImGui::End();
return;
}
auto& g = getSettings().game;
bool changed = false;
bool enabled = g.enhancedLighting.getValue();
if (ImGui::Checkbox("Enhanced Lighting", &enabled)) {
g.enhancedLighting.setValue(enabled);
changed = true;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Enables per-pixel lighting with Blinn-Phong shading.");
}
if (!enabled) {
ImGui::BeginDisabled();
}
bool enableSpecular = g.enableSpecularLighting.getValue();
if (ImGui::Checkbox("Specular Highlights", &enableSpecular)) {
g.enableSpecularLighting.setValue(enableSpecular);
changed = true;
}
bool enableRim = g.enableRimLighting.getValue();
if (ImGui::Checkbox("Rim Lighting", &enableRim)) {
g.enableRimLighting.setValue(enableRim);
changed = true;
}
float specularIntensity = g.specularIntensity.getValue();
if (ImGui::SliderFloat("Specular Intensity", &specularIntensity, 0.0f, 1.0f, "%.2f")) {
g.specularIntensity.setValue(specularIntensity);
changed = true;
}
float rimIntensity = g.rimIntensity.getValue();
if (ImGui::SliderFloat("Rim Intensity", &rimIntensity, 0.0f, 0.5f, "%.3f")) {
g.rimIntensity.setValue(rimIntensity);
changed = true;
}
float ambientMultiplier = g.ambientLightMultiplier.getValue();
if (ImGui::SliderFloat("Ambient Multiplier", &ambientMultiplier, 0.0f, 3.0f, "%.2f")) {
g.ambientLightMultiplier.setValue(ambientMultiplier);
changed = true;
}
float diffuseMultiplier = g.diffuseLightMultiplier.getValue();
if (ImGui::SliderFloat("Diffuse Multiplier", &diffuseMultiplier, 0.0f, 3.0f, "%.2f")) {
g.diffuseLightMultiplier.setValue(diffuseMultiplier);
changed = true;
}
if (!enabled) {
ImGui::EndDisabled();
}
if (changed) {
ApplyFromSettings();
config::Save();
}
ImGui::End();
}
void ImGuiMenuTools::ShowEnhancedLightingWindow() {
DrawEnhancedLightingWindow(m_showEnhancedLightingWindow);
}
} // namespace dusk
@@ -1,5 +0,0 @@
#pragma once
namespace dusk {
void DrawEnhancedLightingWindow(bool& open);
} // namespace dusk
-149
View File
@@ -1,149 +0,0 @@
#include "d/d_com_inf_game.h"
#include "imgui.h"
#include <imgui_internal.h>
#include "ImGuiConsole.hpp"
#include "ImGuiMenuTools.hpp"
#include "dusk/map_loader_definitions.h"
#include "fmt/format.h"
namespace dusk {
void ImGuiMenuTools::ShowMapLoader() {
if (!getSettings().backend.enableAdvancedSettings ||
!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F7, m_showMapLoader))
{
return;
}
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
// ImGui::SetNextWindowBgAlpha(0.65f);
if (!ImGui::Begin("Map Loader", &m_showMapLoader, windowFlags)) {
ImGui::End();
return;
}
ImGui::Checkbox("Show Internal Names", &m_mapLoaderInfo.showInternalNames);
const char* previewRegion = "None";
if (m_mapLoaderInfo.regionIdx != -1) {
previewRegion = gameRegions[m_mapLoaderInfo.regionIdx].regionName;
}
if (ImGui::BeginCombo("Select Region", previewRegion)) {
int idx = 0;
for (const auto& region : gameRegions) {
if (ImGui::Selectable(region.regionName)) {
if (m_mapLoaderInfo.regionIdx != idx) {
m_mapLoaderInfo.mapIdx = 0;
m_mapLoaderInfo.roomNoIdx = 0;
m_mapLoaderInfo.pointNoIdx = 0;
}
m_mapLoaderInfo.regionIdx = idx;
}
idx++;
}
ImGui::EndCombo();
}
if (m_mapLoaderInfo.regionIdx != -1) {
const auto& region = gameRegions[m_mapLoaderInfo.regionIdx];
std::string previewMap = "None";
if (m_mapLoaderInfo.mapIdx != -1) {
const auto& map = region.maps[m_mapLoaderInfo.mapIdx];
previewMap = m_mapLoaderInfo.showInternalNames ? fmt::format("{} ({})", map.mapName, map.mapFile) : map.mapName;
}
if (ImGui::BeginCombo("Select Map", previewMap.data())) {
int prevMapIdx = m_mapLoaderInfo.mapIdx;
for (int i = 0; i < region.maps.size(); ++i) {
const auto& map = region.maps[i];
std::string label = m_mapLoaderInfo.showInternalNames ? fmt::format("{} ({})", map.mapName, map.mapFile) : map.mapName;
if (ImGui::Selectable(label.data())) {
m_mapLoaderInfo.mapIdx = i;
}
}
ImGui::EndCombo();
if (m_mapLoaderInfo.mapIdx != prevMapIdx) {
m_mapLoaderInfo.roomNoIdx = 0;
m_mapLoaderInfo.pointNoIdx = 0;
}
}
} else {
ImGui::Text("No region selected.");
}
if (m_mapLoaderInfo.regionIdx != -1 && m_mapLoaderInfo.mapIdx != -1) {
const auto& region = gameRegions[m_mapLoaderInfo.regionIdx];
const auto& map = region.maps[m_mapLoaderInfo.mapIdx];
const auto& room = map.mapRooms[m_mapLoaderInfo.roomNoIdx];
if (map.mapRooms.size() > 1) {
ImGui::Text("Selected Room: %2d", room.roomNo);
ImGui::SameLine();
if (ImGui::Button("-###RoomNoIdxDec")) {
m_mapLoaderInfo.roomNoIdx--;
if (m_mapLoaderInfo.roomNoIdx < 0) {
m_mapLoaderInfo.roomNoIdx = map.mapRooms.size() - 1;
}
m_mapLoaderInfo.pointNoIdx = 0;
}
ImGui::SameLine();
if (ImGui::Button("+###RoomNoIdxInc")) {
m_mapLoaderInfo.roomNoIdx++;
if (m_mapLoaderInfo.roomNoIdx >= map.mapRooms.size()) {
m_mapLoaderInfo.roomNoIdx = 0;
}
m_mapLoaderInfo.pointNoIdx = 0;
}
}
constexpr int MAX_LAYER = 14;
ImGui::Text("Selected Layer: %3d", m_mapLoaderInfo.layer);
ImGui::SameLine();
if (ImGui::Button("-###layerDec")) {
m_mapLoaderInfo.layer--;
if (m_mapLoaderInfo.layer < -1) {
m_mapLoaderInfo.layer = MAX_LAYER;
}
}
ImGui::SameLine();
if (ImGui::Button("+###layerInc")) {
m_mapLoaderInfo.layer++;
if (m_mapLoaderInfo.layer > MAX_LAYER) {
m_mapLoaderInfo.layer = -1;
}
}
if (room.roomPoints.size() > 1) {
ImGui::Text("Selected Point: %3d", room.roomPoints[m_mapLoaderInfo.pointNoIdx]);
ImGui::SameLine();
if (ImGui::Button("-###PointNoIdxDec")) {
m_mapLoaderInfo.pointNoIdx--;
if (m_mapLoaderInfo.pointNoIdx < 0) {
m_mapLoaderInfo.pointNoIdx = room.roomPoints.size() - 1;
}
}
ImGui::SameLine();
if (ImGui::Button("+###PointNoIdxInc")) {
m_mapLoaderInfo.pointNoIdx++;
if (m_mapLoaderInfo.pointNoIdx >= room.roomPoints.size()) {
m_mapLoaderInfo.pointNoIdx = 0;
}
}
}
if (ImGui::Button("Warp")) {
dComIfGp_setNextStage(map.mapFile, room.roomPoints[m_mapLoaderInfo.pointNoIdx], room.roomNo, m_mapLoaderInfo.layer);
}
}
ImGui::End();
}
} // namespace dusk
+1 -5
View File
@@ -1,13 +1,11 @@
#include "fmt/format.h"
#include "imgui.h"
#include <aurora/gfx.h>
#include "aurora/gfx.h"
#include "ImGuiConfig.hpp"
#include "dusk/hotkeys.h"
#include "dusk/settings.h"
#include "ImGuiBloomWindow.hpp"
#include "ImGuiConsole.hpp"
#include "ImGuiEnhancedLightingWindow.hpp"
#include "ImGuiMenuTools.hpp"
#include "ImGuiEngine.hpp"
@@ -42,7 +40,6 @@ namespace dusk {
ImGui::BeginDisabled(getSettings().game.speedrunMode);
ImGui::MenuItem("Save Editor", hotkeys::SHOW_SAVE_EDITOR, &m_showSaveEditor);
ImGui::MenuItem("Map Loader", hotkeys::SHOW_MAP_LOADER, &m_showMapLoader);
ImGui::MenuItem("State Share", hotkeys::SHOW_STATE_SHARE, &m_showStateShare);
ImGui::EndDisabled();
@@ -106,7 +103,6 @@ namespace dusk {
ImGui::MenuItem("Debug Camera", hotkeys::SHOW_DEBUG_CAMERA, &m_showCameraOverlay);
ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug);
ImGui::MenuItem("Bloom", nullptr, &m_showBloomWindow);
ImGui::MenuItem("Enhanced Lighting", nullptr, &m_showEnhancedLightingWindow);
ImGui::MenuItem("Stub Log", nullptr, &m_showStubLog);
ImGui::MenuItem("Actor Spawner", nullptr, &m_showActorSpawner);
-16
View File
@@ -21,9 +21,7 @@ namespace dusk {
void ShowProcessManager();
void ShowHeapOverlay();
void ShowStubLog();
void ShowMapLoader();
void ShowBloomWindow();
void ShowEnhancedLightingWindow();
void ShowPlayerInfo();
void ShowAudioDebug();
void ShowSaveEditor();
@@ -44,23 +42,9 @@ namespace dusk {
bool m_showStubLog = false;
bool m_showMapLoader = false;
bool m_showBloomWindow = false;
bool m_showEnhancedLightingWindow = false;
bool m_showAudioDebug = false;
struct {
int mapIdx = -1;
int regionIdx = -1;
int roomNoIdx = 0;
int pointNoIdx = 0;
int roomNo = -1;
int pointNo = -1;
int spawnId = 0;
int layer = -1;
bool showInternalNames = false;
} m_mapLoaderInfo;
bool m_showPlayerInfo = false;
int m_playerInfoOverlayCorner = 1; // top-right
+18 -19
View File
@@ -1,5 +1,8 @@
#include "dusk/settings.h"
#include "dusk/config.hpp"
#include "dusk/dusk.h"
#include <SDL3/SDL_video.h>
namespace dusk {
@@ -10,10 +13,14 @@ UserSettings g_userSettings = {
.lockAspectRatio {"video.lockAspectRatio", false},
.enableFpsOverlay {"game.enableFpsOverlay", false},
.fpsOverlayCorner {"game.fpsOverlayCorner", 0},
.windowPositionX {"video.windowPositionX", SDL_WINDOWPOS_UNDEFINED},
.windowPositionY {"video.windowPositionY", SDL_WINDOWPOS_UNDEFINED},
.windowWidth {"video.windowWidth", defaultWindowWidth * 2},
.windowHeight {"video.windowHeight", defaultWindowHeight * 2},
},
.audio = {
.masterVolume {"audio.masterVolume", 80},
.masterVolume {"audio.masterVolume", 60},
.mainMusicVolume {"audio.mainMusicVolume", 100},
.subMusicVolume {"audio.subMusicVolume", 100},
.soundEffectsVolume {"audio.soundEffectsVolume", 100},
@@ -55,16 +62,10 @@ UserSettings g_userSettings = {
.enableDiscordPresence {"game.enableDiscordPresence", true},
// Graphics
.enhancedLighting {"game.enhancedLighting", false},
.enableSpecularLighting {"game.enableSpecularLighting", true},
.enableRimLighting {"game.enableRimLighting", true},
.specularIntensity {"game.specularIntensity", 0.2f},
.rimIntensity {"game.rimIntensity", 0.08f},
.ambientLightMultiplier {"game.ambientLightMultiplier", 1.0f},
.diffuseLightMultiplier {"game.diffuseLightMultiplier", 1.0f},
.bloomMode {"game.bloomMode", BloomMode::Dusk},
.bloomMultiplier {"game.bloomMultiplier", 1.0f},
.disableWaterRefraction {"game.disableWaterRefraction", false},
.enableTextureReplacements {"game.enableTextureReplacements", true},
.enableFrameInterpolation {"game.enableFrameInterpolation", false},
.internalResolutionScale {"game.internalResolutionScale", 0},
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
@@ -100,6 +101,7 @@ UserSettings g_userSettings = {
// Cheats
.infiniteHearts {"game.infiniteHearts", false},
.infiniteArrows {"game.infiniteArrows", false},
.infiniteSeeds {"game.infiniteSeeds", false},
.infiniteBombs {"game.infiniteBombs", false},
.infiniteOil {"game.infiniteOil", false},
.infiniteOxygen {"game.infiniteOxygen", false},
@@ -113,6 +115,7 @@ UserSettings g_userSettings = {
.fastRoll {"game.fastRoll", false},
.fastSpinner {"game.fastSpinner", false},
.freeMagicArmor {"game.freeMagicArmor", false},
.invincibleEnemies {"game.invincibleEnemies", false},
// Technical
.restoreWiiGlitches {"game.restoreWiiGlitches", false},
@@ -182,6 +185,10 @@ void registerSettings() {
Register(g_userSettings.video.lockAspectRatio);
Register(g_userSettings.video.enableFpsOverlay);
Register(g_userSettings.video.fpsOverlayCorner);
Register(g_userSettings.video.windowPositionX);
Register(g_userSettings.video.windowPositionY);
Register(g_userSettings.video.windowWidth);
Register(g_userSettings.video.windowHeight);
// Audio
Register(g_userSettings.audio.masterVolume);
@@ -220,20 +227,10 @@ void registerSettings() {
Register(g_userSettings.game.minimalHUD);
Register(g_userSettings.game.pauseOnFocusLost);
Register(g_userSettings.game.enableDiscordPresence);
// Enhanced lighting
Register(g_userSettings.game.enhancedLighting);
Register(g_userSettings.game.enableSpecularLighting);
Register(g_userSettings.game.enableRimLighting);
Register(g_userSettings.game.specularIntensity);
Register(g_userSettings.game.rimIntensity);
Register(g_userSettings.game.ambientLightMultiplier);
Register(g_userSettings.game.diffuseLightMultiplier);
Register(g_userSettings.game.bloomMode);
Register(g_userSettings.game.bloomMultiplier);
Register(g_userSettings.game.disableWaterRefraction);
Register(g_userSettings.game.enableTextureReplacements);
Register(g_userSettings.game.internalResolutionScale);
Register(g_userSettings.game.shadowResolutionMultiplier);
Register(g_userSettings.game.enableDepthOfField);
@@ -261,6 +258,7 @@ void registerSettings() {
Register(g_userSettings.game.fastSpinner);
Register(g_userSettings.game.infiniteHearts);
Register(g_userSettings.game.infiniteArrows);
Register(g_userSettings.game.infiniteSeeds);
Register(g_userSettings.game.infiniteBombs);
Register(g_userSettings.game.infiniteOil);
Register(g_userSettings.game.infiniteOxygen);
@@ -269,6 +267,7 @@ void registerSettings() {
Register(g_userSettings.game.moonJump);
Register(g_userSettings.game.superClawshot);
Register(g_userSettings.game.alwaysGreatspin);
Register(g_userSettings.game.invincibleEnemies);
Register(g_userSettings.game.enableFrameInterpolation);
Register(g_userSettings.game.gyroMode);
Register(g_userSettings.game.enableGyroAim);
+1
View File
@@ -20,6 +20,7 @@ void resetForSpeedrunMode() {
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);
+83 -48
View File
@@ -37,7 +37,7 @@ Rml::String current_controller_name(int port) {
Rml::String controller_index_name(u32 index) {
const char* name = PADGetNameForControllerIndex(index);
if (name == nullptr) {
return fmt::format("Controller {}", index + 1);
return fmt::format("Device {}", index + 1);
}
return name;
}
@@ -124,7 +124,7 @@ Rml::String native_axis_name(const PADAxisMapping& mapping, SDL_Gamepad* gamepad
return native_button_name(gamepad, static_cast<u32>(mapping.nativeButton));
}
return "Not bound";
return "Not Bound";
}
bool is_dpad_button(PADButton button) {
@@ -162,7 +162,7 @@ bool keyboard_escape_pressed() {
Rml::String keyboard_key_name(s32 scancode) {
if (scancode == PAD_KEY_INVALID) {
return "Not bound";
return "Not Bound";
}
switch (scancode) {
case PAD_KEY_MOUSE_LEFT:
@@ -303,7 +303,7 @@ void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) {
});
};
addPageButton(Page::Controller, "Controller", [port] { return current_controller_name(port); }, [] { return false; });
addPageButton(Page::Controller, "Device", [port] { return current_controller_name(port); }, [] { return false; });
addPageButton(Page::Buttons, "Buttons", [] { return Rml::String(">"); }, [] { return false; });
addPageButton(Page::Triggers, "Triggers", [] { return Rml::String(">"); }, [] { return false; });
addPageButton(Page::Sticks, "Sticks", [] { return Rml::String(">"); }, [] { return false; });
@@ -349,7 +349,14 @@ void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) {
rightPane, [](Pane& pane) {
pane.add_text("Treat analog trigger movement as digital L and R button input.");
});
leftPane.register_control(leftPane.add_button("Restore Default Controls").on_pressed([this, port] {
mDoAud_seStartMenu(kSoundClick);
PADRestoreDefaultMapping(port);
}),
rightPane, [](Pane& pane) {
pane.clear();
pane.add_text("Restores all binding configurations for the currently selected device to their defaults.");
});
render_page(rightPane, port, mPage);
}
@@ -365,7 +372,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
[port] { return PADGetIndexForPort(port) < 0 && !keyboard_active(port); },
})
.on_pressed([this, port] {
mDoAud_seStartMenu(kSoundItemChange);
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
PADClearPort(port);
PADSetKeyboardActive(static_cast<u32>(port), FALSE);
@@ -378,7 +385,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
.isSelected = [port] { return keyboard_active(port); },
})
.on_pressed([this, port] {
mDoAud_seStartMenu(kSoundItemChange);
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
PADClearPort(port);
PADSetKeyboardActive(static_cast<u32>(port), TRUE);
@@ -388,7 +395,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
const u32 controllerCount = PADCount();
if (controllerCount == 0) {
pane.add_text("No controllers detected");
pane.add_text("No Device Detected");
break;
}
@@ -400,7 +407,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
[port, i] { return PADGetIndexForPort(port) == static_cast<s32>(i); },
})
.on_pressed([this, port, i] {
mDoAud_seStartMenu(kSoundItemChange);
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
PADSetKeyboardActive(static_cast<u32>(port), FALSE);
PADSetPortForIndex(i, port);
@@ -425,17 +432,18 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
PADKeyButtonBinding* bindings =
PADGetKeyButtonBindings(static_cast<u32>(port), &count);
if (bindings == nullptr) {
return Rml::String("Not bound");
return Rml::String("Not Bound");
}
for (u32 i = 0; i < PAD_BUTTON_COUNT; ++i) {
if (bindings[i].padButton == button) {
return keyboard_key_name(bindings[i].scancode);
}
}
return Rml::String("Not bound");
return Rml::String("Not Bound");
},
})
.on_pressed([this, port, button] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -462,7 +470,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
u32 buttonCount = 0;
PADButtonMapping* mappings = PADGetButtonMappings(port, &buttonCount);
if (mappings == nullptr) {
pane.add_text("No controller selected");
pane.add_text("No Device Selected");
break;
}
@@ -486,6 +494,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
},
})
.on_pressed([this, port, &mapping] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -512,6 +521,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
},
})
.on_pressed([this, port, &mapping] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -535,17 +545,18 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
PADKeyButtonBinding* bindings =
PADGetKeyButtonBindings(static_cast<u32>(port), &count);
if (bindings == nullptr) {
return Rml::String("Not bound");
return Rml::String("Not Bound");
}
for (u32 i = 0; i < PAD_BUTTON_COUNT; ++i) {
if (bindings[i].padButton == button) {
return keyboard_key_name(bindings[i].scancode);
}
}
return Rml::String("Not bound");
return Rml::String("Not Bound");
},
})
.on_pressed([this, port, button] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -566,17 +577,18 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
PADKeyAxisBinding* bindings =
PADGetKeyAxisBindings(static_cast<u32>(port), &count);
if (bindings == nullptr) {
return Rml::String("Not bound");
return Rml::String("Not Bound");
}
for (u32 i = 0; i < PAD_AXIS_COUNT; ++i) {
if (bindings[i].padAxis == axis) {
return keyboard_key_name(bindings[i].scancode);
}
}
return Rml::String("Not bound");
return Rml::String("Not Bound");
},
})
.on_pressed([this, port, axis] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -599,7 +611,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
u32 buttonCount = 0;
PADButtonMapping* buttons = PADGetButtonMappings(port, &buttonCount);
if (axes == nullptr && buttons == nullptr) {
pane.add_text("No controller selected");
pane.add_text("No Device Selected");
break;
}
@@ -623,6 +635,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
},
})
.on_pressed([this, port, &mapping] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -631,30 +644,33 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
}
}
pane.add_section("Digital");
if (buttons != nullptr) {
for (u32 i = 0; i < buttonCount; ++i) {
PADButtonMapping& mapping = buttons[i];
if (mapping.padButton != PAD_TRIGGER_L && mapping.padButton != PAD_TRIGGER_R) {
continue;
if (getSettings().backend.enableAdvancedSettings) {
pane.add_section("Digital");
if (buttons != nullptr) {
for (u32 i = 0; i < buttonCount; ++i) {
PADButtonMapping& mapping = buttons[i];
if (mapping.padButton != PAD_TRIGGER_L && mapping.padButton != PAD_TRIGGER_R) {
continue;
}
pane.add_select_button({
.key = PADGetButtonName(mapping.padButton),
.getValue =
[this, &mapping, gamepad] {
if (mPendingButtonMapping == &mapping) {
return pending_button_label();
}
return native_button_name(
gamepad, mapping.nativeButton);
},
})
.on_pressed([this, port, &mapping] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
mPendingButtonMapping = &mapping;
});
}
pane.add_select_button({
.key = PADGetButtonName(mapping.padButton),
.getValue =
[this, &mapping, gamepad] {
if (mPendingButtonMapping == &mapping) {
return pending_button_label();
}
return native_button_name(
gamepad, mapping.nativeButton);
},
})
.on_pressed([this, port, &mapping] {
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
mPendingButtonMapping = &mapping;
});
}
}
@@ -706,17 +722,18 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
PADKeyAxisBinding* bindings =
PADGetKeyAxisBindings(static_cast<u32>(port), &count);
if (bindings == nullptr) {
return Rml::String("Not bound");
return Rml::String("Not Bound");
}
for (u32 i = 0; i < PAD_AXIS_COUNT; ++i) {
if (bindings[i].padAxis == axis) {
return keyboard_key_name(bindings[i].scancode);
}
}
return Rml::String("Not bound");
return Rml::String("Not Bound");
},
})
.on_pressed([this, port, axis] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -741,7 +758,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
u32 axisCount = 0;
PADAxisMapping* axes = PADGetAxisMappings(port, &axisCount);
if (axes == nullptr) {
pane.add_text("No controller selected");
pane.add_text("No Device Selected");
break;
}
@@ -762,6 +779,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
},
})
.on_pressed([this, port, &mapping] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -907,6 +925,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
},
})
.on_pressed([this, port, actionBind] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -926,7 +945,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
u32 buttonCount = 0;
PADButtonMapping* mappings = PADGetButtonMappings(port, &buttonCount);
if (mappings == nullptr) {
pane.add_text("No controller selected");
pane.add_text("No Device Selected");
break;
}
@@ -950,6 +969,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
},
})
.on_pressed([this, port, actionBind] {
mDoAud_seStartMenu(kSoundClick);
cancel_pending_binding();
mPendingPort = port;
mPendingBindingArmed = false;
@@ -1013,6 +1033,12 @@ void ControllerConfigWindow::poll_pending_binding() {
const s32 nativeButton = PADGetNativeButtonPressed(mPendingPort);
if (nativeButton != -1) {
const int completedPort = mPendingPort;
if (mPendingButtonMapping->nativeButton == static_cast<u32>(nativeButton) &&
(mPendingButtonMapping->padButton != PAD_BUTTON_A &&
mPendingButtonMapping->padButton != PAD_BUTTON_B)) {
unmap_pending_binding();
return;
}
mPendingButtonMapping->nativeButton = static_cast<u32>(nativeButton);
finish_pending_binding(completedPort);
}
@@ -1023,6 +1049,10 @@ void ControllerConfigWindow::poll_pending_binding() {
const PADSignedNativeAxis nativeAxis = PADGetNativeAxisPulled(mPendingPort);
if (nativeAxis.nativeAxis != -1) {
const int completedPort = mPendingPort;
if (mPendingAxisMapping->nativeAxis.nativeAxis == nativeAxis.nativeAxis) {
unmap_pending_binding();
return;
}
mPendingAxisMapping->nativeAxis = nativeAxis;
mPendingAxisMapping->nativeButton = -1;
finish_pending_binding(completedPort);
@@ -1049,6 +1079,10 @@ void ControllerConfigWindow::poll_pending_binding() {
if (button != -1) {
const int completedPort = mPendingPort;
if (mPendingActionBinding->getValue() == button) {
unmap_pending_binding();
return;
}
mPendingActionBinding->setValue(button);
config::Save();
finish_pending_binding(completedPort);
@@ -1058,6 +1092,7 @@ void ControllerConfigWindow::poll_pending_binding() {
}
void ControllerConfigWindow::finish_pending_binding(int completedPort) {
mDoAud_seStartMenu(kSoundBindingChanged);
mPendingButtonMapping = nullptr;
mPendingAxisMapping = nullptr;
mPendingActionBinding = nullptr;
@@ -1110,11 +1145,11 @@ bool ControllerConfigWindow::pending_input_neutral() const {
}
Rml::String ControllerConfigWindow::pending_button_label() const {
return mPendingBindingArmed ? "Press a button..." : "Waiting...";
return mPendingBindingArmed ? "Press a Key or Button..." : "Waiting...";
}
Rml::String ControllerConfigWindow::pending_axis_label() const {
return mPendingBindingArmed ? "Move axis or press a button..." : "Waiting...";
return mPendingBindingArmed ? "Move Axis or press a Key or Button..." : "Waiting...";
}
void ControllerConfigWindow::cancel_pending_binding() {
@@ -1143,7 +1178,7 @@ void ControllerConfigWindow::finish_pending_key_binding() {
}
Rml::String ControllerConfigWindow::pending_key_label() const {
return mPendingBindingArmed ? "Press a key or mouse button..." : "Waiting...";
return mPendingBindingArmed ? "Press a Key or Mouse Button..." : "Waiting...";
}
void ControllerConfigWindow::stop_rumble_test() {
@@ -1159,7 +1194,7 @@ void ControllerConfigWindow::stop_rumble_test() {
Rml::String native_button_name(SDL_Gamepad* gamepad, u32 buttonUntyped) {
if (buttonUntyped == PAD_NATIVE_BUTTON_INVALID) {
return "Not bound";
return "Not Bound";
}
auto button = static_cast<SDL_GamepadButton>(buttonUntyped);
+37 -38
View File
@@ -26,6 +26,43 @@
#include <vector>
namespace dusk::ui {
Rml::String stage_option_label(const MapEntry& map, bool showInternalNames) {
return showInternalNames ? fmt::format("{} ({})", map.mapName, map.mapFile) : map.mapName;
}
Rml::String stage_label_for_file(const Rml::String& stageFile, bool showInternalNames) {
for (const auto& region : gameRegions) {
for (const auto& map : region.maps) {
if (stageFile == map.mapFile) {
return stage_option_label(map, showInternalNames);
}
}
}
return stageFile;
}
void populate_stage_picker(Pane& pane, std::function<Rml::String()> getStageFile,
std::function<void(const char*)> setStageFile, bool showInternalNames) {
pane.clear();
for (const auto& region : gameRegions) {
pane.add_section(region.regionName);
for (const auto& map : region.maps) {
pane.add_button({
.text = stage_option_label(map, showInternalNames),
.isSelected =
[getStageFile, stageFile = map.mapFile] {
return getStageFile() == stageFile;
},
})
.on_pressed([setStageFile, stageFile = map.mapFile] {
mDoAud_seStartMenu(kSoundItemChange);
setStageFile(stageFile);
});
}
}
}
namespace {
bool has_save_data() {
@@ -155,44 +192,6 @@ bool parse_vec3(const Rml::String& value, float& x, float& y, float& z) {
return *cursor == '\0';
}
Rml::String stage_option_label(const MapEntry& map) {
// TODO: option to show internal name?
// return fmt::format("{} ({})", map.mapName, map.mapFile);
return map.mapName;
}
Rml::String stage_label_for_file(const Rml::String& stageFile) {
for (const auto& region : gameRegions) {
for (const auto& map : region.maps) {
if (stageFile == map.mapFile) {
return stage_option_label(map);
}
}
}
return stageFile;
}
void populate_stage_picker(Pane& pane, std::function<Rml::String()> getStageFile,
std::function<void(const char*)> setStageFile) {
pane.clear();
for (const auto& region : gameRegions) {
pane.add_section(region.regionName);
for (const auto& map : region.maps) {
pane.add_button({
.text = stage_option_label(map),
.isSelected =
[getStageFile, stageFile = map.mapFile] {
return getStageFile() == stageFile;
},
})
.on_pressed([setStageFile, stageFile = map.mapFile] {
mDoAud_seStartMenu(kSoundItemChange);
setStageFile(stageFile);
});
}
}
}
Rml::String get_player_name() {
if (!has_save_data()) {
return "";
+11
View File
@@ -1,8 +1,19 @@
#pragma once
#include "window.hpp"
struct MapEntry;
namespace dusk::ui {
class Pane;
Rml::String stage_option_label(const MapEntry& map, bool showInternalNames = false);
Rml::String stage_label_for_file(const Rml::String& stageFile, bool showInternalNames = false);
void populate_stage_picker(Pane& pane, std::function<Rml::String()> getStageFile,
std::function<void(const char*)> setStageFile,
bool showInternalNames = false);
class EditorWindow : public Window {
public:
EditorWindow();
+2 -2
View File
@@ -184,7 +184,7 @@ Rml::String format_graphics_setting_value(GraphicsOption option, int value) {
case BloomMode::Classic:
return "Classic";
case BloomMode::Dusk:
return "Dusk";
return "Dusklight";
}
break;
case GraphicsOption::BloomMultiplier:
@@ -211,7 +211,7 @@ GraphicsTuner::GraphicsTuner(GraphicsTunerProps props, bool prelaunch)
SteppedCarousel::Props{
.min = mValueMin,
.max = mValueMax,
.step = 1,
.step = props.step,
.getValue = [this] { return get_value(mOption); },
.onChange = [this](int value) { set_value(mOption, value); },
.formatValue =
+1
View File
@@ -55,6 +55,7 @@ struct GraphicsTunerProps {
int valueMin = 0;
int valueMax = 0;
int defaultValue = 0;
int step = 1;
};
class GraphicsTuner : public Document {
+20 -11
View File
@@ -454,10 +454,18 @@ bool touch_moved_too_far(
return delta.SquaredMagnitude() > threshold * threshold;
}
void dispatch_menu_key(Rml::Context& context) noexcept {
void emit_key_press(Rml::Context& context, Rml::Input::KeyIdentifier key) noexcept {
context.ProcessMouseLeave();
context.ProcessKeyDown(Rml::Input::KI_F1, 0);
context.ProcessKeyUp(Rml::Input::KI_F1, 0);
context.ProcessKeyDown(key, 0);
}
void emit_key_tap(Rml::Context& context, Rml::Input::KeyIdentifier key) noexcept {
emit_key_press(context, key);
context.ProcessKeyUp(key, 0);
}
void dispatch_menu_key(Rml::Context& context) noexcept {
emit_key_tap(context, Rml::Input::KI_F1);
}
bool handle_touch_menu_tap(Rml::Context& context, const SDL_Event& event) noexcept {
@@ -627,7 +635,9 @@ void process_axis_direction(
if (repeat->held) {
if (released) {
if (!repeat->pending) {
if (repeat->pending) {
emit_key_tap(context, repeat->key);
} else {
context.ProcessKeyUp(repeat->key, 0);
}
set_pad_button_held(port, heldPadButton, false);
@@ -658,8 +668,7 @@ void process_axis_direction(
}
begin_gamepad_key(*repeat, key);
context.ProcessMouseLeave();
context.ProcessKeyDown(key, 0);
emit_key_press(context, key);
}
} // namespace
@@ -747,8 +756,7 @@ void handle_event(const SDL_Event& event) noexcept {
}
}
if (!deferred) {
context->ProcessMouseLeave();
context->ProcessKeyDown(key, 0);
emit_key_press(*context, key);
}
}
} else {
@@ -760,7 +768,9 @@ void handle_event(const SDL_Event& event) noexcept {
if (repeat != nullptr) {
*repeat = {};
}
if (!wasPending) {
if (wasPending) {
emit_key_tap(*context, key);
} else {
context->ProcessKeyUp(key, 0);
}
}
@@ -787,8 +797,7 @@ void update_input() noexcept {
repeat.pressedAt = now;
repeat.nextRepeatAt =
repeat.repeatable ? now + kGamepadRepeatInitialDelay : 0.0;
context->ProcessMouseLeave();
context->ProcessKeyDown(repeat.key, 0);
emit_key_press(*context, repeat.key);
continue;
}
+2 -3
View File
@@ -18,6 +18,7 @@
#include "modal.hpp"
#include "settings.hpp"
#include "ui.hpp"
#include "warp.hpp"
#include "window.hpp"
#include <chrono>
@@ -51,11 +52,9 @@ MenuBar::MenuBar() : Document(kDocumentSource), mRoot(mDocument->GetElementById(
.autoSelect = false,
});
mTabBar->add_tab("Settings", [this] { push(std::make_unique<SettingsWindow>()); });
// mTabBar->add_tab("Warp", [] {
// // TODO
// });
if (getSettings().backend.enableAdvancedSettings) {
mTabBar->add_tab("Warp", [this] { push(std::make_unique<WarpWindow>()); });
mTabBar->add_tab("Editor", [this] { push(std::make_unique<EditorWindow>()); });
}
+6 -5
View File
@@ -103,13 +103,13 @@ Rml::Element* create_controller_warning(Rml::Element* parent) {
auto* heading = append(elem, "heading");
auto* title = append(heading, "span");
title->SetInnerRML("No controller assigned");
title->SetInnerRML("No Device Assigned");
auto* icon = append(heading, "icon");
icon->SetClass("warning", true);
auto* message = append(elem, "message");
auto* content = append(message, "span");
content->SetInnerRML("Configure controller port 1 in Settings.");
content->SetInnerRML("Configure <b>Port 1</b> in Settings.");
return elem;
}
@@ -147,7 +147,7 @@ Rml::String back_button_name() {
#if defined(TARGET_ANDROID) || (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST)
constexpr auto kMenuNotificationPrefix = "3-finger tap or";
#else
constexpr auto kMenuNotificationPrefix = "Press F1 or";
constexpr auto kMenuNotificationPrefix = "Press <b>F1</b> or";
#endif
Rml::Element* create_menu_notification(Rml::Element* parent) {
@@ -169,7 +169,7 @@ Rml::Element* create_menu_notification(Rml::Element* parent) {
append(row, "span")->SetInnerRML(kMenuNotificationPrefix);
auto* icon = append(row, "icon");
icon->SetClass("controller", true);
append(row, "span")->SetInnerRML(escape(padButton));
append(row, "span")->SetInnerRML("<b>" + escape(padButton) + "</b>");
append(row, "span")->SetInnerRML("to open menu");
return elem;
@@ -354,8 +354,9 @@ void Overlay::update() {
}
}
u32 count = 0;
const bool showControllerWarning = PADGetIndexForPort(PAD_CHAN0) < 0 &&
PADGetKeyButtonBindings(PAD_CHAN0, nullptr) == nullptr &&
PADGetKeyButtonBindings(PAD_CHAN0, &count) == nullptr &&
dynamic_cast<Window*>(top_document()) == nullptr &&
dynamic_cast<WindowSmall*>(top_document()) == nullptr;
if (showControllerWarning && mControllerWarning == nullptr) {
+1 -1
View File
@@ -83,7 +83,7 @@ PresetWindow::PresetWindow() : WindowSmall("modal", "modal-dialog") {
"Enhancements disabled to match the GameCube version. "
"Good for speedrunning or simple nostalgia!",
applyPresetClassic},
{"Dusk",
{"Dusklight",
"Graphics & quality of life tweaks, including some from the Wii U version. "
"Our recommended way to play!",
applyPresetDusk},
+64 -14
View File
@@ -3,6 +3,7 @@
#include "aurora/gfx.h"
#include "bool_button.hpp"
#include "controller_config.hpp"
#include "dusk/app_info.hpp"
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/audio/DuskDsp.hpp"
#include "dusk/config.hpp"
@@ -17,6 +18,7 @@
#include "graphics_tuner.hpp"
#include "m_Do/m_Do_main.h"
#include "menu_bar.hpp"
#include "modal.hpp"
#include "number_button.hpp"
#include "menu_bar.hpp"
#include "pane.hpp"
@@ -25,6 +27,7 @@
#include <aurora/lib/window.hpp>
#include <SDL3/SDL_filesystem.h>
#include <fmt/format.h>
#if DUSK_ENABLE_SENTRY_NATIVE
#include "dusk/crash_reporting.h"
@@ -184,6 +187,7 @@ void reset_for_speedrun_mode() {
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);
@@ -197,6 +201,7 @@ void reset_for_speedrun_mode() {
getSettings().game.fastRoll.setSpeedrunValue(false);
getSettings().game.fastSpinner.setSpeedrunValue(false);
getSettings().game.freeMagicArmor.setSpeedrunValue(false);
getSettings().game.invincibleEnemies.setSpeedrunValue(false);
getSettings().game.pauseOnFocusLost.setSpeedrunValue(false);
aurora_set_pause_on_focus_lost(false);
@@ -295,13 +300,49 @@ private:
Rml::String mCurrentRml;
};
void show_data_folder_error_modal(std::string_view message) {
auto dismiss = [](Modal& modal) {
mDoAud_seStartMenu(kSoundWindowClose);
modal.pop();
};
push_document(std::make_unique<Modal>(Modal::Props{
.title = "Data Folder Not Changed",
.bodyRml = escape(message),
.actions =
{
ModalAction{
.label = "OK",
.onPressed = dismiss,
},
},
.onDismiss = dismiss,
.icon = "warning",
}));
if (auto* doc = top_document()) {
doc->focus();
}
}
void data_folder_dialog_callback(void*, const char* path, const char* error) {
if (error != nullptr || path == nullptr) {
if (error != nullptr) {
show_data_folder_error_modal(error);
return;
}
if (data::set_custom_data_path(path)) {
mDoAud_seStartMenu(kSoundItemChange);
if (path == nullptr) {
return;
}
std::string dataPathError;
if (data::set_custom_data_path(path, &dataPathError)) {
mDoAud_seStartMenu(kSoundItemChange);
return;
}
if (dataPathError.empty()) {
dataPathError =
fmt::format("{} could not use the selected folder as its data folder.", AppName);
}
show_data_folder_error_modal(dataPathError);
}
const Rml::String kInternalResolutionHelpText =
@@ -752,9 +793,16 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.valueMin = 0,
.valueMax = 100,
.defaultValue = 100,
.step = 10,
}, mPrelaunch);
leftPane.add_section("Rendering");
config_bool_select(leftPane, rightPane, getSettings().game.enableTextureReplacements,
{
.key = "Use Texture Pack",
.helpText = "Enable installed texture replacements.",
.onChange = [](bool value) { aurora_set_texture_replacements_enabled(value); },
});
config_bool_select(leftPane, rightPane, getSettings().game.enableFrameInterpolation,
{
.key = "Unlock Framerate",
@@ -788,18 +836,18 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
});
};
leftPane.add_section("Controller");
leftPane.register_control(leftPane.add_button("Configure Controller").on_pressed([this] {
leftPane.add_section("Inputs");
leftPane.register_control(leftPane.add_button("Configure Inputs").on_pressed([this] {
push(std::make_unique<ControllerConfigWindow>(mPrelaunch));
}),
rightPane, [](Pane& pane) {
pane.clear();
pane.add_text("Open controller binding configuration.");
pane.add_text("Open input binding configuration.");
});
config_bool_select(leftPane, rightPane, getSettings().game.allowBackgroundInput,
{
.key = "Allow Background Input",
.helpText = "Allow controller input even when the game window is not focused.",
.key = "Allow Background Inputs",
.helpText = "Allow inputs even when the game window is not focused.",
.onChange = [](bool value) { aurora_set_background_input(value); },
});
@@ -912,7 +960,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
[](int value) {
getSettings().audio.masterVolume.setValue(value);
config::Save();
audio::SetMasterVolume(value / 100.f);
audio::SetMasterVolume(audio::MasterVolumeToLinear(value / 100.0f));
},
.isModified =
[] {
@@ -1027,8 +1075,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
addOption("Faster Tears of Light", getSettings().game.fastTears,
"Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
addSpeedrunDisabledOption("Autosave", getSettings().game.autoSave,
"Autosaves the game when going to a new area, opening a dungeon door, "
"or getting a new item.");
"Autosaves the game when going to a new area or opening a dungeon door.");
addOption("Instant Saves", getSettings().game.instantSaves,
"Skips the delay when writing to the Memory Card.");
addOption("Hold B for Instant Text", getSettings().game.instantText,
@@ -1107,6 +1154,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
addCheat("Infinite Hearts", getSettings().game.infiniteHearts, "Keeps your health full.");
addCheat(
"Infinite Arrows", getSettings().game.infiniteArrows, "Keeps your arrow count full.");
addCheat("Infinite Seeds", getSettings().game.infiniteSeeds, "Keeps your slingshot pellets (seeds) full.");
addCheat("Infinite Bombs", getSettings().game.infiniteBombs, "Keeps all bomb bags full.");
addCheat("Infinite Oil", getSettings().game.infiniteOil, "Keeps your lantern oil full.");
addCheat("Infinite Oxygen", getSettings().game.infiniteOxygen,
@@ -1124,7 +1172,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
addCheat("Always Greatspin", getSettings().game.alwaysGreatspin,
"Allows the Great Spin attack without requiring full health.");
addCheat("Fast Iron Boots", getSettings().game.enableFastIronBoots,
"Speeds up movement while wearing the Iron Boots.");
"Speeds up movement while heavy, including wearing the Iron Boots, holding the Ball and Chain, wearing Magic Armor without rupees, etc.");
addCheat("Can Transform Anywhere", getSettings().game.canTransformAnywhere,
"Allows transforming even if NPCs are looking.");
addCheat("Fast Roll", getSettings().game.fastRoll,
@@ -1133,6 +1181,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
"Speeds up Spinner movement while holding R.");
addCheat("Free Magic Armor", getSettings().game.freeMagicArmor,
"Lets the magic armor work without consuming rupees.");
addCheat("Invincible Enemies", getSettings().game.invincibleEnemies,
"Prevents enemies from taking damage.");
});
add_tab("Interface", [this](Rml::Element* content) {
@@ -1204,7 +1254,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
});
pane.add_button(
{
.text = "Controller",
.text = "Missing Device",
.isSelected =
[] { return getSettings().game.enableControllerToasts.getValue(); },
})
@@ -1257,7 +1307,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
config_bool_select(leftPane, rightPane, getSettings().game.enableDiscordPresence,
{
.key = "Enable Discord Rich Presence",
.helpText = "Enable Dusk to integrate with Discord Rich Presence. This allows Discord to show your status in-game.",
.helpText = "Enable Dusklight to integrate with Discord Rich Presence. This allows Discord to show your status in-game.",
.onChange = [](bool enabled) {
if (enabled) {
dusk::discord::initialize();
+16 -2
View File
@@ -11,7 +11,9 @@
#include <ranges>
#include "aurora/lib/window.hpp"
#include "dusk/dusk.h"
#include "dusk/io.hpp"
#include "dusk/config.hpp"
#include "input.hpp"
#include "prelaunch.hpp"
#include "window.hpp"
@@ -130,7 +132,7 @@ void handle_event(const SDL_Event& event) noexcept {
if (getSettings().game.enableControllerToasts) {
const char* name = SDL_GetGamepadName(gamepad);
Rml::String content = fmt::format("<span>{}</span>", name ? name : "[Unknown]");
Rml::String title = "Controller connected";
Rml::String title = "Device Connected";
if (const char* icon = connection_state_icon(SDL_GetGamepadConnectionState(gamepad))) {
title = fmt::format(
"<row><span>{}</span> <icon class=\"connection\">&#x{};</icon></row>", title,
@@ -163,12 +165,24 @@ void handle_event(const SDL_Event& event) noexcept {
const char* name = SDL_GetGamepadNameForID(event.gdevice.which);
push_toast({
.type = "controller",
.title = "Controller disconnected",
.title = "Device Disconnected",
.content = name ? name : "[Unknown]",
.duration = std::chrono::seconds(4),
});
}
sConnectedGamepads.erase(event.gdevice.which);
} else if (event.type == SDL_EVENT_WINDOW_MOVED || event.type == SDL_EVENT_WINDOW_RESIZED) {
int x, y;
if (SDL_GetWindowPosition(aurora::window::get_sdl_window(), &x, &y)) {
getSettings().video.windowPositionX.setValue(x);
getSettings().video.windowPositionY.setValue(y);
}
int width, height;
if (SDL_GetWindowSize(aurora::window::get_sdl_window(), &width, &height)) {
getSettings().video.windowWidth.setValue(width);
getSettings().video.windowHeight.setValue(height);
}
config::Save();
}
input::handle_event(event);
}
+4
View File
@@ -26,6 +26,8 @@ struct Toast {
constexpr u32 kSoundClick = Z2SE_SY_CURSOR_OK;
// "Play" button clicked/pressed
constexpr u32 kSoundPlay = Z2SE_SY_ITEM_COMBINE_ON;
// Input binding changed
constexpr u32 kSoundBindingChanged = Z2SE_SY_ITEM_SET_X;
// Menu button pressed (open/close menu bar or hide/show the active window)
constexpr u32 kSoundMenuOpen = Z2SE_SY_MENU_SUB_IN;
@@ -49,6 +51,8 @@ constexpr u32 kSoundItemDisable = Z2SE_SUBJ_VIEW_OUT;
// Achievement unlocked
constexpr u32 kSoundAchievementUnlock = Z2SE_NAVI_FLY;
// Warning prompt
constexpr u32 kSoundWarning = Z2SE_SY_COW_GET_IN;
struct Insets {
float top = 0.0f;
+334
View File
@@ -0,0 +1,334 @@
#include "warp.hpp"
#include "editor.hpp"
#include "pane.hpp"
#include "dusk/map_loader_definitions.h"
#include "fmt/format.h"
namespace dusk::ui {
namespace {
constexpr int kMinLayer = -1;
constexpr int kMaxLayer = 14;
struct WarpSelectionState {
int regionIdx = 0;
int mapIdx = 0;
int roomIdx = 0;
int pointIdx = 0;
int layer = -1;
bool showInternalNames = false;
};
WarpSelectionState& selection_state() {
static WarpSelectionState state;
return state;
}
const RegionEntry* selected_region(const WarpSelectionState& state) {
if (state.regionIdx < 0 || state.regionIdx >= static_cast<int>(gameRegions.size())) {
return nullptr;
}
return &gameRegions[state.regionIdx];
}
const MapEntry* selected_map(const WarpSelectionState& state) {
const auto* region = selected_region(state);
if (region == nullptr || state.mapIdx < 0 || state.mapIdx >= static_cast<int>(region->maps.size())) {
return nullptr;
}
return &region->maps[state.mapIdx];
}
const RoomEntry* selected_room(const WarpSelectionState& state) {
const auto* map = selected_map(state);
if (map == nullptr || state.roomIdx < 0 || state.roomIdx >= static_cast<int>(map->mapRooms.size())) {
return nullptr;
}
return &map->mapRooms[state.roomIdx];
}
const s16* selected_point(const WarpSelectionState& state) {
const auto* room = selected_room(state);
if (room == nullptr || state.pointIdx < 0 ||
state.pointIdx >= static_cast<int>(room->roomPoints.size()))
{
return nullptr;
}
return &room->roomPoints[state.pointIdx];
}
void clamp_indices(WarpSelectionState& state) {
if (gameRegions.empty()) {
state.regionIdx = -1;
state.mapIdx = -1;
state.roomIdx = -1;
state.pointIdx = -1;
state.layer = std::clamp(state.layer, kMinLayer, kMaxLayer);
return;
}
state.regionIdx = std::clamp(state.regionIdx, 0, static_cast<int>(gameRegions.size()) - 1);
const auto& region = gameRegions[state.regionIdx];
if (region.maps.empty()) {
state.mapIdx = -1;
state.roomIdx = -1;
state.pointIdx = -1;
state.layer = std::clamp(state.layer, kMinLayer, kMaxLayer);
return;
}
state.mapIdx = std::clamp(state.mapIdx, 0, static_cast<int>(region.maps.size()) - 1);
const auto& map = region.maps[state.mapIdx];
if (map.mapRooms.empty()) {
state.roomIdx = -1;
state.pointIdx = -1;
state.layer = std::clamp(state.layer, kMinLayer, kMaxLayer);
return;
}
state.roomIdx = std::clamp(state.roomIdx, 0, static_cast<int>(map.mapRooms.size()) - 1);
const auto& room = map.mapRooms[state.roomIdx];
if (room.roomPoints.empty()) {
state.pointIdx = -1;
} else {
state.pointIdx = std::clamp(state.pointIdx, 0, static_cast<int>(room.roomPoints.size()) - 1);
}
state.layer = std::clamp(state.layer, kMinLayer, kMaxLayer);
}
bool can_warp(const WarpSelectionState& state) {
return selected_point(state) != nullptr;
}
void reset_selection(WarpSelectionState& state) {
state.roomIdx = 0;
state.pointIdx = 0;
state.layer = kMinLayer;
clamp_indices(state);
}
void populate_map_picker(Pane& pane, WarpSelectionState& state) {
pane.clear();
clamp_indices(state);
if (state.regionIdx < 0 || state.regionIdx >= static_cast<int>(gameRegions.size())) {
return;
}
pane.add_button({
.text = "Show Internal Names",
.isSelected = [&state] { return state.showInternalNames; },
})
.on_pressed([&pane, &state] {
mDoAud_seStartMenu(kSoundItemChange);
state.showInternalNames = !state.showInternalNames;
populate_map_picker(pane, state);
});
pane.add_section("Maps");
const auto& region = gameRegions[state.regionIdx];
for (int i = 0; i < static_cast<int>(region.maps.size()); ++i) {
pane.add_button({
.text = stage_option_label(region.maps[i], state.showInternalNames),
.isSelected = [i, &state] { return state.mapIdx == i; },
})
.on_pressed([i, &state] {
mDoAud_seStartMenu(kSoundItemChange);
if (state.mapIdx != i) {
state.mapIdx = i;
reset_selection(state);
}
});
}
}
} // namespace
WarpWindow::WarpWindow() {
add_tab("Warp", [this](Rml::Element* content) {
auto& leftPane = add_child<Pane>(content, Pane::Type::Controlled);
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
auto& state = selection_state();
clamp_indices(state);
leftPane.add_section("Destination");
leftPane.register_control(
leftPane.add_select_button({
.key = "Region",
.getValue =
[&state] {
clamp_indices(state);
const auto* region = selected_region(state);
return region == nullptr ? Rml::String{"None"} :
Rml::String{region->regionName};
},
}),
rightPane, [&state](Pane& pane) {
pane.clear();
for (int i = 0; i < static_cast<int>(gameRegions.size()); ++i) {
pane.add_button({
.text = gameRegions[i].regionName,
.isSelected = [i, &state] { return state.regionIdx == i; },
})
.on_pressed([i, &state] {
mDoAud_seStartMenu(kSoundItemChange);
if (state.regionIdx != i) {
state.regionIdx = i;
state.mapIdx = 0;
reset_selection(state);
}
});
}
});
leftPane.register_control(
leftPane.add_select_button({
.key = "Map",
.getValue =
[&state] {
clamp_indices(state);
const auto* map = selected_map(state);
return map == nullptr ? Rml::String{"None"} :
stage_option_label(*map, state.showInternalNames);
},
}),
rightPane, [&state](Pane& pane) { populate_map_picker(pane, state); });
leftPane.register_control(
leftPane.add_select_button({
.key = "Room",
.getValue = [&state] {
clamp_indices(state);
const auto* room = selected_room(state);
return room == nullptr ? Rml::String{"None"} :
fmt::format("{}", room->roomNo);
},
.isDisabled = [&state] {
clamp_indices(state);
const auto* map = selected_map(state);
return map == nullptr || map->mapRooms.size() <= 1;
},
}),
rightPane, [&state](Pane& pane) {
pane.clear();
clamp_indices(state);
if (state.regionIdx < 0 || state.regionIdx >= static_cast<int>(gameRegions.size())) {
return;
}
const auto& region = gameRegions[state.regionIdx];
if (state.mapIdx < 0 || state.mapIdx >= static_cast<int>(region.maps.size())) {
return;
}
const auto& map = region.maps[state.mapIdx];
for (int i = 0; i < static_cast<int>(map.mapRooms.size()); ++i) {
pane.add_button({
.text = fmt::format("{}", map.mapRooms[i].roomNo),
.isSelected = [i, &state] { return state.roomIdx == i; },
})
.on_pressed([i, &state] {
mDoAud_seStartMenu(kSoundItemChange);
if (state.roomIdx != i) {
state.roomIdx = i;
state.pointIdx = 0;
clamp_indices(state);
}
});
}
});
leftPane.register_control(
leftPane.add_select_button({
.key = "Point",
.getValue = [&state] {
clamp_indices(state);
const auto* point = selected_point(state);
return point == nullptr ? Rml::String{"None"} : fmt::format("{}", *point);
},
.isDisabled = [&state] {
clamp_indices(state);
const auto* room = selected_room(state);
return room == nullptr || room->roomPoints.size() <= 1;
},
}),
rightPane, [&state](Pane& pane) {
pane.clear();
clamp_indices(state);
if (state.regionIdx < 0 || state.regionIdx >= static_cast<int>(gameRegions.size())) {
return;
}
const auto& region = gameRegions[state.regionIdx];
if (state.mapIdx < 0 || state.mapIdx >= static_cast<int>(region.maps.size())) {
return;
}
const auto& map = region.maps[state.mapIdx];
if (state.roomIdx < 0 || state.roomIdx >= static_cast<int>(map.mapRooms.size())) {
return;
}
const auto& room = map.mapRooms[state.roomIdx];
for (int i = 0; i < static_cast<int>(room.roomPoints.size()); ++i) {
pane.add_button({
.text = fmt::format("{}", room.roomPoints[i]),
.isSelected = [i, &state] { return state.pointIdx == i; },
})
.on_pressed([i, &state] {
if (state.pointIdx != i) {
mDoAud_seStartMenu(kSoundItemChange);
state.pointIdx = i;
clamp_indices(state);
}
});
}
});
leftPane.register_control(
leftPane.add_select_button({
.key = "Layer",
.getValue = [&state] { return fmt::format("{}", state.layer); },
}),
rightPane, [&state](Pane& pane) {
pane.clear();
for (int layer = kMinLayer; layer <= kMaxLayer; ++layer) {
pane.add_button({
.text = fmt::format("{}", layer),
.isSelected = [layer, &state] { return state.layer == layer; },
})
.on_pressed([layer, &state] {
if (state.layer != layer) {
mDoAud_seStartMenu(kSoundItemChange);
state.layer = layer;
}
});
}
});
leftPane.add_section("Action");
leftPane.register_control(
leftPane.add_button({
.text = "Warp",
.isDisabled = [&state] {
clamp_indices(state);
return !can_warp(state);
},
})
.on_pressed([&state] {
clamp_indices(state);
if (!can_warp(state)) {
return;
}
mDoAud_seStartMenu(kSoundClick);
const auto& region = gameRegions[state.regionIdx];
const auto& map = region.maps[state.mapIdx];
const auto& room = map.mapRooms[state.roomIdx];
dComIfGp_setNextStage( map.mapFile, room.roomPoints[state.pointIdx], room.roomNo, state.layer);
}),
rightPane, [](Pane& pane) {
pane.clear();
pane.add_text("Warp to the selected destination.");
});
});
}
} // namespace dusk::ui
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "window.hpp"
namespace dusk::ui {
class WarpWindow : public Window {
public:
WarpWindow();
};
} // namespace dusk::ui
+5 -1
View File
@@ -792,6 +792,10 @@ static void duskExecute() {
dComIfGs_setArrowNum(dComIfGs_getArrowMax());
}
if (dusk::getSettings().game.infiniteSeeds) {
dComIfGs_setPachinkoNum(dComIfGs_getPachinkoMax());
}
if (dusk::getSettings().game.infiniteBombs) {
dComIfGs_setBombNum(0, 99);
dComIfGs_setBombNum(1, 99);
@@ -803,7 +807,7 @@ static void duskExecute() {
}
if (dusk::getSettings().game.infiniteRupees) {
dComIfGs_setRupee(9999);
dComIfGs_setRupee(dComIfGs_getRupeeMax());
}
if (dusk::getSettings().game.infiniteOxygen) {
+10 -1
View File
@@ -136,8 +136,17 @@ base_process_class* fpcBs_Create(s16 i_profname, fpc_ProcID i_procID, void* i_ap
u32 size;
pprofile = (process_profile_definition*)fpcPf_Get(i_profname);
if (pprofile == NULL) {
#if TARGET_PC
DuskLog.debug("fpcBs_Create: profile not found for profname={}", i_profname);
#endif
return NULL;
}
#if TARGET_PC
const char* procName = getProcName(i_profname);
DuskLog.debug("fpcBs_Create: pid={} profname={} ({}) profile={} procSize={} unkSize={}",
i_procID, getProcName(i_profname), i_profname, (void*)pprofile, pprofile->process_size, pprofile->unk_size);
i_procID, procName ? procName : "(unknown)", i_profname, (void*)pprofile, pprofile->process_size, pprofile->unk_size);
#endif
size = pprofile->process_size + pprofile->unk_size;
pprocess = (base_process_class*)cMl::memalignB(-4, size);
+6
View File
@@ -12,6 +12,12 @@
static int fpcLnIt_MethodCall(create_tag_class* i_createTag, method_filter* i_filter) {
#ifdef TARGET_PC
// on init_state==3 fpcEx_ExecuteQTo already ran (layer_tag.layer is NULL)
if (static_cast<base_process_class*>(i_createTag->mpTagData)->state.init_state == 3) {
return 0;
}
#endif
layer_class* layer = static_cast<base_process_class*>(i_createTag->mpTagData)->layer_tag.layer;
layer_class* save_layer = fpcLy_CurrentLayer();
int ret;
+2 -2
View File
@@ -96,8 +96,8 @@ void mDoLib_project(Vec* src, Vec* dst) {
xSize = FB_WIDTH;
} else {
#if TARGET_PC
xOffset = mDoGph_gInf_c::getSafeMinXF();
xSize = viewPort->width * mDoGph_gInf_c::hudAspectScaleUp;
xOffset = mDoGph_gInf_c::getMinXF();
xSize = mDoGph_gInf_c::getWidthF();
#else
xOffset = viewPort->x_orig;
xSize = viewPort->width;
+19 -23
View File
@@ -67,7 +67,6 @@
#include <aurora/aurora.h>
#include <aurora/event.h>
#include <aurora/gfx.h>
#include <aurora/main.h>
#include <aurora/dvd.h>
#include <dolphin/dvd.h>
@@ -81,6 +80,7 @@
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/audio/DuskDsp.hpp"
#include "dusk/config.hpp"
#include "dusk/speedrun.h"
#include "dusk/settings.h"
#include "dusk/io.hpp"
#include "dusk/version.hpp"
@@ -120,6 +120,7 @@ bool dusk::IsShuttingDown = false;
bool dusk::IsGameLaunched = false;
bool dusk::RestartRequested = false;
std::filesystem::path dusk::ConfigPath;
std::filesystem::path dusk::CachePath;
#endif
void dusk::RequestRestart() noexcept {
@@ -514,12 +515,17 @@ int game_main(int argc, char* argv[]) {
const auto startupLogLevel =
static_cast<AuroraLogLevel>(parsed_arg_options["log-level"].as<uint8_t>());
dusk::ConfigPath = dusk::data::initialize_data();
dusk::InitializeFileLogging(dusk::ConfigPath, startupLogLevel);
const auto dataPaths = dusk::data::initialize_data();
dusk::ConfigPath = dataPaths.userPath;
dusk::CachePath = dataPaths.cachePath;
dusk::InitializeFileLogging(dusk::CachePath, startupLogLevel);
log_build_info();
dusk::config::LoadFromUserPreferences();
if (dusk::getSettings().game.speedrunMode) {
dusk::resetForSpeedrunMode();
}
ApplyCVarOverrides(parsed_arg_options["cvar"]);
dusk::crash_reporting::initialize();
// TODO: How to handle this?
@@ -537,16 +543,18 @@ int game_main(int argc, char* argv[]) {
SDL_SetAppMetadata("Dusklight", DUSK_VERSION_STRING, "dev.twilitrealm.dusk");
{
const auto configPathString = dusk::ConfigPath.u8string();
const auto userPathString = dusk::ConfigPath.u8string();
const auto cachePathString = dusk::CachePath.u8string();
AuroraConfig config{};
config.appName = dusk::AppName;
config.configPath = reinterpret_cast<const char*>(configPathString.c_str());
config.userPath = reinterpret_cast<const char*>(userPathString.c_str());
config.cachePath = reinterpret_cast<const char*>(cachePathString.c_str());
config.vsync = dusk::getSettings().video.enableVsync;
config.startFullscreen = dusk::getSettings().video.enableFullscreen;
config.windowPosX = -1;
config.windowPosY = -1;
config.windowWidth = defaultWindowWidth * 2;
config.windowHeight = defaultWindowHeight * 2;
config.windowPosX = dusk::getSettings().video.windowPositionX;
config.windowPosY = dusk::getSettings().video.windowPositionY;
config.windowWidth = dusk::getSettings().video.windowWidth;
config.windowHeight = dusk::getSettings().video.windowHeight;
config.desiredBackend = ResolveDesiredBackend(parsed_arg_options);
config.logCallback = &aurora_log_callback;
config.logLevel = startupLogLevel;
@@ -555,21 +563,9 @@ int game_main(int argc, char* argv[]) {
config.allowJoystickBackgroundEvents = dusk::getSettings().game.allowBackgroundInput;
config.pauseOnFocusLost = dusk::getSettings().game.pauseOnFocusLost;
config.imGuiInitCallback = &aurora_imgui_init_callback;
config.allowTextureReplacements = true;
config.allowTextureReplacements = dusk::getSettings().game.enableTextureReplacements;
config.allowTextureDumps = false;
auroraInfo = aurora_initialize(argc, argv, &config);
{
const auto& g = dusk::getSettings().game;
aurora_set_enhanced_lighting_state({
g.enhancedLighting.getValue(),
g.enableSpecularLighting.getValue(),
g.enableRimLighting.getValue(),
g.specularIntensity.getValue(),
g.rimIntensity.getValue(),
g.ambientLightMultiplier.getValue(),
g.diffuseLightMultiplier.getValue(),
});
}
}
#ifdef DUSK_DISCORD
@@ -589,7 +585,7 @@ int game_main(int argc, char* argv[]) {
}
VISetFrameBufferScale(dusk::getSettings().game.internalResolutionScale.getValue());
dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f);
dusk::audio::SetMasterVolume(dusk::audio::MasterVolumeToLinear(dusk::getSettings().audio.masterVolume / 100.0f));
dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb);
dusk::audio::EnableHrtf = dusk::getSettings().audio.enableHrtf;