mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-23 06:34:15 -04:00
Merge branch 'main' into improvement/toggle_owl_statue_markers
This commit is contained in:
@@ -41,6 +41,10 @@ compile_commands.json
|
||||
# MacOS
|
||||
.DS_Store
|
||||
|
||||
# direnv / nix
|
||||
.direnv/
|
||||
.envrc
|
||||
|
||||
# ISOs
|
||||
*.iso
|
||||
|
||||
|
||||
+23
-7
@@ -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
@@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ It aims to be as accurate as possible to the original while also providing new o
|
||||
> Dusklight does *not* provide any copyrighted assets. You must provide your own copy of the original game.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelyhood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them.
|
||||
> At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelihood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them.
|
||||
|
||||
### 1. Verify your dump
|
||||
|
||||
|
||||
+183
-53
@@ -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.
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
# Installing Dusklight on iOS via AltStore
|
||||
# Installing Dusklight on iOS via iloader
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Mac with Homebrew installed
|
||||
- iPhone connected 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)
|
||||
- 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/dusklight/releases))
|
||||
- Legally acquired game disc - `GZ2E01` (Gamecube USA) or `GZ2PE01` (Gamecube PAL)
|
||||
|
||||
## 1. Install AltServer
|
||||
## 1. Install iloader
|
||||
|
||||
```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
|
||||
- Toggle it on, put in your device passcode, 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 directly to Apple and not stored by iloader)
|
||||
* You may be prompted to put in a code from your iOS device if you have 2FA enabled, 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 `Dusklight-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 Dusklight 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
|
||||
|
||||
Transfer the IPA and game disc to your iPhone so they're accessible in the Files app. A few ways to do this:
|
||||
Transfer the game disc (and optionally, the Dusklight IPA) to your iPhone so they are accessible in the Files app. A few ways to do this:
|
||||
|
||||
- **AirDrop** - Right-click the files on your Mac and choose Share > AirDrop
|
||||
- **iCloud Drive** - Place files in iCloud Drive on your Mac and they'll sync to Files on your iPhone
|
||||
- **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 and iPadOS!
|
||||
Vendored
+1
-1
Submodule extern/aurora updated: ff9ca7170c...10006618ee
+4
-1
@@ -1411,6 +1411,7 @@ set(DOLPHIN_FILES
|
||||
)
|
||||
|
||||
set(DUSK_FILES
|
||||
include/dusk/action_bindings.h
|
||||
include/dusk/endian_gx.hpp
|
||||
include/dusk/config.hpp
|
||||
include/dusk/dvd_asset.hpp
|
||||
@@ -1457,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
|
||||
@@ -1508,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
|
||||
@@ -1522,6 +1524,7 @@ set(DUSK_FILES
|
||||
src/dusk/discord.hpp
|
||||
src/dusk/discord_presence.cpp
|
||||
src/dusk/version.cpp
|
||||
src/dusk/action_bindings.cpp
|
||||
)
|
||||
|
||||
set(DUSK_HTTP_BACKEND_FILES
|
||||
|
||||
@@ -4,101 +4,216 @@
|
||||
};
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
|
||||
# Dependencies that are not packaged in nixpkgs:
|
||||
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=";
|
||||
stripRoot = false;
|
||||
};
|
||||
nod-src = pkgs.fetchzip {
|
||||
url = "https://github.com/encounter/nod/releases/download/v2.0.0-alpha.8/libnod-linux-x86_64.tar.gz";
|
||||
hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE=";
|
||||
stripRoot = false;
|
||||
};
|
||||
# The version of imgui on nixpkgs does not map cleanly.
|
||||
imgui-src = pkgs.fetchFromGitHub {
|
||||
owner = "ocornut";
|
||||
repo = "imgui";
|
||||
rev = "v1.91.9b-docking";
|
||||
hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ=";
|
||||
};
|
||||
sqlite-src = pkgs.fetchzip {
|
||||
url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip";
|
||||
hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM=";
|
||||
};
|
||||
rmlui-src = pkgs.fetchzip {
|
||||
url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz";
|
||||
hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg=";
|
||||
};
|
||||
# Dusklight Actual
|
||||
dusklight = pkgs.stdenv.mkDerivation {
|
||||
name = "dusklight";
|
||||
src = ./.;
|
||||
postUnpack = ''
|
||||
mkdir -p $sourceRoot/extern/aurora
|
||||
cp -r ${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 = [
|
||||
"-DFETCHCONTENT_FULLY_DISCONNECTED=ON"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_CXXOPTS=${pkgs.cxxopts.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_DAWN_PREBUILT=${dawn-src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_XXHASH=${pkgs.xxHash.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_FMT=${pkgs.fmt.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_TRACY=${pkgs.tracy.src}"
|
||||
"-DAURORA_SDL3_PROVIDER=system"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_NOD_PREBUILT=${nod-src}"
|
||||
"-DAURORA_NOD_PROVIDER=package"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_FREETYPE=${pkgs.freetype.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_SQLITE3=${sqlite-src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_IMGUI=${imgui-src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_RMLUI=${rmlui-src}"
|
||||
"-DCMAKE_CROSSCOMPILING=ON" # Tests are not working as I didn't want to work through getting google's test suite working as well. This is the only guard I could find to disable it.
|
||||
];
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp dusklight $out/bin/dusklight
|
||||
cp -r ./res $out/bin/res
|
||||
'';
|
||||
nativeBuildInputs = [
|
||||
pkgs.cmake
|
||||
pkgs.pkg-config
|
||||
pkgs.wayland
|
||||
];
|
||||
buildInputs = [
|
||||
pkgs.libGL
|
||||
pkgs.libX11
|
||||
pkgs.libXcursor
|
||||
pkgs.libxi
|
||||
pkgs.libxcb
|
||||
pkgs.libxrandr
|
||||
pkgs.libxscrnsaver
|
||||
pkgs.libxtst
|
||||
pkgs.libjpeg8
|
||||
pkgs.libxkbcommon
|
||||
pkgs.libglvnd
|
||||
pkgs.cxxopts
|
||||
pkgs.abseil-cpp
|
||||
pkgs.sdl3
|
||||
pkgs.fmt
|
||||
pkgs.tracy
|
||||
pkgs.freetype
|
||||
pkgs.zstd
|
||||
];
|
||||
supportedSystems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
pkgsFor = system: import nixpkgs { inherit system; };
|
||||
|
||||
# Dependencies that are not packaged in nixpkgs (used by the Linux package build):
|
||||
buildSources = pkgs: {
|
||||
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=";
|
||||
stripRoot = false;
|
||||
};
|
||||
nod-src = pkgs.fetchzip {
|
||||
url = "https://github.com/encounter/nod/releases/download/v2.0.0-alpha.8/libnod-linux-x86_64.tar.gz";
|
||||
hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE=";
|
||||
stripRoot = false;
|
||||
};
|
||||
# The version of imgui on nixpkgs does not map cleanly.
|
||||
imgui-src = pkgs.fetchFromGitHub {
|
||||
owner = "ocornut";
|
||||
repo = "imgui";
|
||||
rev = "v1.91.9b-docking";
|
||||
hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ=";
|
||||
};
|
||||
sqlite-src = pkgs.fetchzip {
|
||||
url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip";
|
||||
hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM=";
|
||||
};
|
||||
rmlui-src = pkgs.fetchzip {
|
||||
url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz";
|
||||
hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg=";
|
||||
};
|
||||
};
|
||||
|
||||
# Dusklight Actual (Linux x86_64 only — relies on prebuilt dawn/nod binaries)
|
||||
mkDusklight = pkgs:
|
||||
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 = ''
|
||||
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}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_DAWN_PREBUILT=${srcs.dawn-src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_XXHASH=${pkgs.xxHash.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_FMT=${pkgs.fmt.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_TRACY=${pkgs.tracy.src}"
|
||||
"-DAURORA_SDL3_PROVIDER=system"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_NOD_PREBUILT=${srcs.nod-src}"
|
||||
"-DAURORA_NOD_PROVIDER=package"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_FREETYPE=${pkgs.freetype.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_SQLITE3=${srcs.sqlite-src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_IMGUI=${srcs.imgui-src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_RMLUI=${srcs.rmlui-src}"
|
||||
"-DCMAKE_CROSSCOMPILING=ON" # Tests are not working as I didn't want to work through getting google's test suite working as well. This is the only guard I could find to disable it.
|
||||
];
|
||||
installPhase = ''
|
||||
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
|
||||
pkgs.pkg-config
|
||||
pkgs.wayland
|
||||
];
|
||||
buildInputs = [
|
||||
pkgs.libGL
|
||||
pkgs.libX11
|
||||
pkgs.libXcursor
|
||||
pkgs.libxi
|
||||
pkgs.libxcb
|
||||
pkgs.libxrandr
|
||||
pkgs.libxscrnsaver
|
||||
pkgs.libxtst
|
||||
pkgs.libjpeg8
|
||||
pkgs.libxkbcommon
|
||||
pkgs.libglvnd
|
||||
pkgs.cxxopts
|
||||
pkgs.abseil-cpp
|
||||
pkgs.sdl3
|
||||
pkgs.fmt
|
||||
pkgs.tracy
|
||||
pkgs.freetype
|
||||
pkgs.zstd
|
||||
];
|
||||
};
|
||||
|
||||
# Tooling common to every supported host (Linux and macOS).
|
||||
commonDevTools = pkgs: [
|
||||
pkgs.cmake
|
||||
pkgs.ninja
|
||||
pkgs.pkg-config
|
||||
pkgs.git
|
||||
pkgs.python3
|
||||
pkgs.python3Packages.markupsafe
|
||||
pkgs.rustc
|
||||
pkgs.cargo
|
||||
pkgs.sccache
|
||||
];
|
||||
|
||||
# Linux-only system libraries — mirrors the apt deps from .github/workflows/build.yml
|
||||
# so the cmake presets resolve the same set of headers as CI.
|
||||
linuxDevDeps = pkgs: [
|
||||
# Compilers / linkers
|
||||
pkgs.clang
|
||||
pkgs.lld
|
||||
# C/C++ utilities
|
||||
pkgs.curl
|
||||
pkgs.openssl
|
||||
pkgs.zlib
|
||||
pkgs.libpng
|
||||
pkgs.libjpeg_turbo
|
||||
pkgs.freetype
|
||||
pkgs.zstd
|
||||
pkgs.fmt
|
||||
pkgs.tracy
|
||||
pkgs.cxxopts
|
||||
pkgs.abseil-cpp
|
||||
pkgs.sdl3
|
||||
pkgs.ncurses
|
||||
pkgs.libunwind
|
||||
pkgs.libusb1
|
||||
pkgs.fuse
|
||||
# Wayland / display server
|
||||
pkgs.wayland
|
||||
pkgs.wayland-protocols
|
||||
pkgs.libxkbcommon
|
||||
pkgs.libdecor
|
||||
# OpenGL / Vulkan
|
||||
pkgs.libGL
|
||||
pkgs.libGLU
|
||||
pkgs.libglvnd
|
||||
pkgs.vulkan-headers
|
||||
pkgs.vulkan-loader
|
||||
# X11
|
||||
pkgs.libX11
|
||||
pkgs.libxcb
|
||||
pkgs.libXcursor
|
||||
pkgs.libxi
|
||||
pkgs.libxrandr
|
||||
pkgs.libxscrnsaver
|
||||
pkgs.libxtst
|
||||
pkgs.libxinerama
|
||||
# Audio
|
||||
pkgs.alsa-lib
|
||||
pkgs.libpulseaudio
|
||||
pkgs.pipewire
|
||||
# System integration
|
||||
pkgs.dbus
|
||||
pkgs.udev
|
||||
pkgs.gtk3
|
||||
];
|
||||
|
||||
# On macOS we deliberately avoid pulling Nix's cc-wrapper so CMake picks up
|
||||
# Apple Clang and the Xcode SDK directly, matching the macOS CI workflow.
|
||||
mkDarwinShell = pkgs:
|
||||
pkgs.mkShellNoCC {
|
||||
packages = commonDevTools pkgs;
|
||||
shellHook = ''
|
||||
echo "Dusklight dev shell (macOS)"
|
||||
echo "Requires Xcode Command Line Tools for Apple Clang and the macOS SDK."
|
||||
echo "Configure: cmake --preset macos-default-relwithdebinfo"
|
||||
echo "Build: cmake --build --preset macos-default-relwithdebinfo"
|
||||
'';
|
||||
};
|
||||
|
||||
mkLinuxShell = pkgs:
|
||||
pkgs.mkShell {
|
||||
packages = (commonDevTools pkgs) ++ (linuxDevDeps pkgs);
|
||||
shellHook = ''
|
||||
echo "Dusklight dev shell (Linux)"
|
||||
echo "Configure: cmake --preset linux-default-relwithdebinfo"
|
||||
echo " cmake --preset linux-clang-relwithdebinfo"
|
||||
echo "Build: cmake --build --preset <preset>"
|
||||
'';
|
||||
};
|
||||
|
||||
mkDevShell = pkgs:
|
||||
if pkgs.stdenv.isDarwin
|
||||
then mkDarwinShell pkgs
|
||||
else mkLinuxShell pkgs;
|
||||
in {
|
||||
packages.x86_64-linux.default = dusklight;
|
||||
packages.x86_64-linux.default = mkDusklight (pkgsFor "x86_64-linux");
|
||||
|
||||
devShells = forAllSystems (system: {
|
||||
default = mkDevShell (pkgsFor system);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef D_METER_D_METER_BUTTON_H
|
||||
#define D_METER_D_METER_BUTTON_H
|
||||
|
||||
#include "global.h"
|
||||
#include "JSystem/J2DGraph/J2DScreen.h"
|
||||
#include "JSystem/J2DGraph/J2DTextBox.h"
|
||||
#include "d/d_drawlist.h"
|
||||
@@ -194,7 +195,7 @@ public:
|
||||
/* 0x0FC */ CPaneMgr* field_0x0fc[4];
|
||||
/* 0x10C */ JKRHeap* mpHeap;
|
||||
/* 0x110 */ void* mpFishingTex;
|
||||
/* 0x114 */ char mButtonText[2][15];
|
||||
/* 0x114 */ char mButtonText[2][DUSK_IF_ELSE(32, 15)];
|
||||
/* 0x132 */ u8 field_0x132[0x134 - 0x132];
|
||||
/* 0x134 */ f32 field_0x134;
|
||||
/* 0x138 */ f32 field_0x138;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "dusk/config_var.hpp"
|
||||
|
||||
namespace dusk {
|
||||
|
||||
enum class ActionBinds {
|
||||
FIRST_PERSON_CAMERA,
|
||||
CALL_MIDNA,
|
||||
OPEN_DUSKLIGHT_MENU,
|
||||
TURBO_SPEED_BUTTON,
|
||||
COUNT,
|
||||
};
|
||||
|
||||
struct ActionBindData {
|
||||
std::array<config::ActionBindConfigVar, 4>* configVars{};
|
||||
std::string actionName{};
|
||||
};
|
||||
|
||||
struct ActionBindPressData {
|
||||
bool pressedCurFrame{false};
|
||||
bool pressedPrevFrame{false};
|
||||
};
|
||||
|
||||
using ActionBindsMap = std::unordered_map<ActionBinds, ActionBindData>;
|
||||
|
||||
ActionBindsMap& getActionBinds();
|
||||
|
||||
bool isActionBound(ActionBinds action, u32 port);
|
||||
|
||||
void updateActionBindings();
|
||||
|
||||
bool getActionBindTrig(ActionBinds action, u32 port);
|
||||
|
||||
bool getActionBindHold(ActionBinds action, u32 port);
|
||||
|
||||
bool getActionBindHoldAnyPort(ActionBinds action);
|
||||
|
||||
int getActionBindButton(ActionBinds action, u32 port);
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
@@ -13,5 +14,6 @@ void enterAutoSave();
|
||||
void autoSaving();
|
||||
void waitingForWrite();
|
||||
void endAutoSave();
|
||||
void toggleAutoSave(bool enabled);
|
||||
|
||||
#endif
|
||||
@@ -112,6 +112,13 @@ void Save();
|
||||
*/
|
||||
ConfigVarBase* GetConfigVar(std::string_view name);
|
||||
|
||||
/**
|
||||
* \brief Resets all custom action bindings for a specific port to nothing
|
||||
*
|
||||
* @param port The port to be cleared of action bindings
|
||||
*/
|
||||
void ClearAllActionBindings(int port);
|
||||
|
||||
/**
|
||||
* \brief Call a function on every registered CVar.
|
||||
*/
|
||||
|
||||
@@ -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,11 +284,24 @@ 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>;
|
||||
|
||||
}
|
||||
|
||||
#endif // DUSK_CONFIG_VAR_HPP
|
||||
|
||||
@@ -227,6 +227,28 @@ struct BE<Mtx> {
|
||||
}
|
||||
};
|
||||
|
||||
typedef f32 Mtx23[2][3];
|
||||
template <>
|
||||
struct BE<Mtx23> {
|
||||
BE<f32> contents[2][3];
|
||||
|
||||
auto& operator[](int x) {
|
||||
return contents[x];
|
||||
}
|
||||
|
||||
auto& operator[](int x) const {
|
||||
return contents[x];
|
||||
}
|
||||
|
||||
void to_host(Mtx23& mtx) const {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
mtx[i][j] = contents[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void be_swap(T& val) {
|
||||
val = BE<T>::swap(val);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "settings.h"
|
||||
|
||||
class camera_process_class;
|
||||
class view_class;
|
||||
@@ -17,7 +18,8 @@ void ensure_initialized();
|
||||
void begin_record();
|
||||
void end_record();
|
||||
void begin_sim_tick();
|
||||
void begin_frame(bool enabled, bool is_sim_frame, float step);
|
||||
uint64_t sim_tick_seq();
|
||||
void begin_frame(FrameInterpMode mode, bool is_sim_frame, float step);
|
||||
void interpolate();
|
||||
float get_interpolation_step();
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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)
|
||||
|
||||
+44
-2
@@ -1,6 +1,8 @@
|
||||
#ifndef DUSK_CONFIG_H
|
||||
#define DUSK_CONFIG_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "dusk/config_var.hpp"
|
||||
|
||||
namespace dusk {
|
||||
@@ -13,6 +15,11 @@ enum class BloomMode : int {
|
||||
Dusk = 2,
|
||||
};
|
||||
|
||||
enum class Resampler : int {
|
||||
Bilinear = 0,
|
||||
Area = 1,
|
||||
};
|
||||
|
||||
enum class GameLanguage : u8 {
|
||||
English = OS_LANGUAGE_ENGLISH,
|
||||
German = OS_LANGUAGE_GERMAN,
|
||||
@@ -32,6 +39,12 @@ enum class GyroMode : u8 {
|
||||
Mouse = 1,
|
||||
};
|
||||
|
||||
enum class FrameInterpMode : u8 {
|
||||
Off = 0,
|
||||
Capped = 1,
|
||||
Unlimited = 2,
|
||||
};
|
||||
|
||||
namespace config {
|
||||
template <>
|
||||
struct ConfigEnumRange<BloomMode> {
|
||||
@@ -39,6 +52,12 @@ struct ConfigEnumRange<BloomMode> {
|
||||
static constexpr auto max = BloomMode::Dusk;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ConfigEnumRange<Resampler> {
|
||||
static constexpr auto min = Resampler::Bilinear;
|
||||
static constexpr auto max = Resampler::Area;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ConfigEnumRange<GameLanguage> {
|
||||
static constexpr auto min = GameLanguage::English;
|
||||
@@ -56,7 +75,13 @@ struct ConfigEnumRange<GyroMode> {
|
||||
static constexpr auto min = GyroMode::Sensor;
|
||||
static constexpr auto max = GyroMode::Mouse;
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct ConfigEnumRange<FrameInterpMode> {
|
||||
static constexpr auto min = FrameInterpMode::Off;
|
||||
static constexpr auto max = FrameInterpMode::Unlimited;
|
||||
};
|
||||
} // namespace config
|
||||
|
||||
// Persistent user settings
|
||||
|
||||
@@ -70,6 +95,7 @@ struct UserSettings {
|
||||
ConfigVar<bool> lockAspectRatio;
|
||||
ConfigVar<bool> enableFpsOverlay;
|
||||
ConfigVar<int> fpsOverlayCorner;
|
||||
ConfigVar<int> maxFrameRate;
|
||||
} video;
|
||||
|
||||
struct {
|
||||
@@ -115,14 +141,17 @@ struct UserSettings {
|
||||
ConfigVar<bool> enableLinkDollRotation;
|
||||
ConfigVar<bool> enableAchievementToasts;
|
||||
ConfigVar<bool> enableControllerToasts;
|
||||
ConfigVar<bool> enableDiscordPresence;
|
||||
|
||||
// Graphics
|
||||
ConfigVar<BloomMode> bloomMode;
|
||||
ConfigVar<float> bloomMultiplier;
|
||||
ConfigVar<bool> disableWaterRefraction;
|
||||
ConfigVar<bool> enableFrameInterpolation;
|
||||
ConfigVar<bool> enableTextureReplacements;
|
||||
ConfigVar<FrameInterpMode> enableFrameInterpolation;
|
||||
ConfigVar<int> internalResolutionScale;
|
||||
ConfigVar<int> shadowResolutionMultiplier;
|
||||
ConfigVar<Resampler> resampler;
|
||||
ConfigVar<bool> enableDepthOfField;
|
||||
ConfigVar<bool> enableMapBackground;
|
||||
ConfigVar<bool> disableCutscenePillarboxing;
|
||||
@@ -155,6 +184,7 @@ struct UserSettings {
|
||||
// Cheats
|
||||
ConfigVar<bool> infiniteHearts;
|
||||
ConfigVar<bool> infiniteArrows;
|
||||
ConfigVar<bool> infiniteSeeds;
|
||||
ConfigVar<bool> infiniteBombs;
|
||||
ConfigVar<bool> infiniteOil;
|
||||
ConfigVar<bool> infiniteOxygen;
|
||||
@@ -168,12 +198,14 @@ struct UserSettings {
|
||||
ConfigVar<bool> fastRoll;
|
||||
ConfigVar<bool> fastSpinner;
|
||||
ConfigVar<bool> freeMagicArmor;
|
||||
ConfigVar<bool> invincibleEnemies;
|
||||
|
||||
// Technical
|
||||
ConfigVar<bool> restoreWiiGlitches;
|
||||
|
||||
// Controls
|
||||
ConfigVar<bool> enableTurboKeybind;
|
||||
ConfigVar<bool> enableResetKeybind;
|
||||
|
||||
// Tools
|
||||
ConfigVar<bool> speedrunMode;
|
||||
@@ -181,6 +213,8 @@ struct UserSettings {
|
||||
ConfigVar<bool> showSpeedrunRTATimer;
|
||||
ConfigVar<bool> recordingMode;
|
||||
ConfigVar<bool> removeQuestMapMarkers;
|
||||
ConfigVar<bool> showInputViewer;
|
||||
ConfigVar<bool> showInputViewerGyro;
|
||||
} game;
|
||||
|
||||
struct {
|
||||
@@ -194,6 +228,14 @@ struct UserSettings {
|
||||
ConfigVar<int> cardFileType;
|
||||
ConfigVar<bool> enableAdvancedSettings;
|
||||
} backend;
|
||||
|
||||
// Arrays of size 4 for 4 ports
|
||||
struct {
|
||||
std::array<ActionBindConfigVar, 4> firstPersonCamera;
|
||||
std::array<ActionBindConfigVar, 4> callMidna;
|
||||
std::array<ActionBindConfigVar, 4> openDusklightMenu;
|
||||
std::array<ActionBindConfigVar, 4> turboSpeedButton;
|
||||
} actionBindings;
|
||||
};
|
||||
|
||||
UserSettings& getSettings();
|
||||
|
||||
+40
-4
@@ -17,16 +17,24 @@
|
||||
#include <shellapi.h>
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach_time.h>
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class Limiter {
|
||||
public:
|
||||
using duration_t = Uint64;
|
||||
|
||||
void Reset() { m_oldTime = SDL_GetTicksNS(); }
|
||||
void Reset() {
|
||||
m_oldTime = SDL_GetTicksNS();
|
||||
}
|
||||
|
||||
void Sleep(duration_t targetFrameTime) {
|
||||
duration_t Sleep(duration_t targetFrameTime) {
|
||||
if (targetFrameTime == 0) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Uint64 start = SDL_GetTicksNS();
|
||||
@@ -41,6 +49,8 @@ public:
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
|
||||
return adjustedSleepTime;
|
||||
}
|
||||
|
||||
duration_t SleepTime(duration_t targetFrameTime) {
|
||||
@@ -74,7 +84,6 @@ private:
|
||||
if (!initialized || numSleeps++ % 1000 == 0) {
|
||||
LARGE_INTEGER freq;
|
||||
if (QueryPerformanceFrequency(&freq) == 0) {
|
||||
DuskLog.warn("QueryPerformanceFrequency failed: {}", GetLastError());
|
||||
return;
|
||||
}
|
||||
countPerNs = static_cast<double>(freq.QuadPart) / 1e9;
|
||||
@@ -98,6 +107,33 @@ private:
|
||||
#endif
|
||||
} while (current.QuadPart - start.QuadPart < ticksToWait);
|
||||
}
|
||||
#elif defined (__APPLE__)
|
||||
void NanoSleep(const duration_t duration) {
|
||||
// Hybrid approach using Apple Mach
|
||||
uint64_t start_mach = mach_absolute_time();
|
||||
|
||||
mach_timebase_info_data_t timebase_info;
|
||||
mach_timebase_info(&timebase_info);
|
||||
|
||||
uint64_t total_mach_ticks = (duration * timebase_info.denom) / timebase_info.numer;
|
||||
uint64_t target_mach = start_mach + total_mach_ticks;
|
||||
|
||||
uint64_t buffer_ns = 2'000'000ULL;
|
||||
uint64_t buffer_mach_ticks = (buffer_ns * timebase_info.denom) / timebase_info.numer;
|
||||
|
||||
if (total_mach_ticks > buffer_mach_ticks) {
|
||||
uint64_t sleep_until_mach = target_mach - buffer_mach_ticks;
|
||||
mach_wait_until(sleep_until_mach);
|
||||
}
|
||||
|
||||
while (mach_absolute_time() < target_mach) {
|
||||
#if defined(__aarch64__) || defined(__arm__)
|
||||
asm volatile("yield" ::: "memory"); // Hardware hint, not a scheduler hint.
|
||||
#else
|
||||
_mm_pause();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
void NanoSleep(const duration_t duration) { SDL_DelayPrecise(duration); }
|
||||
#endif
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -116,7 +116,7 @@ private:
|
||||
*
|
||||
*/
|
||||
struct J2DIndTexMtxInfo {
|
||||
/* 0x00 */ Mtx23 mMtx;
|
||||
/* 0x00 */ BE(Mtx23) mMtx;
|
||||
/* 0x18 */ s8 mScaleExp;
|
||||
|
||||
J2DIndTexMtxInfo& operator=(const J2DIndTexMtxInfo& other) {
|
||||
|
||||
@@ -287,28 +287,28 @@ template <typename T>
|
||||
class JASPoolAllocObject {
|
||||
public:
|
||||
#if TARGET_PC
|
||||
static void* operator new(size_t n, JKRHeapToken) {
|
||||
static void* operator new(size_t n, JKRHeapToken) IF_DUSK(noexcept) {
|
||||
return operator new(n);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void* operator new(size_t n) {
|
||||
static void* operator new(size_t n) IF_DUSK(noexcept) {
|
||||
#if PLATFORM_GCN
|
||||
JASMemPool<T>& memPool_ = getMemPool_();
|
||||
#endif
|
||||
return memPool_.alloc(n);
|
||||
}
|
||||
static void* operator new(size_t n, void* ptr) {
|
||||
static void* operator new(size_t n, void* ptr) IF_DUSK(noexcept) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
static void operator delete(void* ptr, size_t n, JKRHeapToken) {
|
||||
static void operator delete(void* ptr, size_t n, JKRHeapToken) IF_DUSK(noexcept) {
|
||||
operator delete(ptr, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void operator delete(void* ptr, size_t n) {
|
||||
static void operator delete(void* ptr, size_t n) IF_DUSK(noexcept) {
|
||||
#if PLATFORM_GCN
|
||||
JASMemPool<T>& memPool_ = getMemPool_();
|
||||
#endif
|
||||
@@ -402,28 +402,28 @@ template <typename T>
|
||||
class JASPoolAllocObject_MultiThreaded {
|
||||
public:
|
||||
#if TARGET_PC
|
||||
static void* operator new(size_t n, JKRHeapToken) {
|
||||
static void* operator new(size_t n, JKRHeapToken) IF_DUSK(noexcept) {
|
||||
return operator new(n);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void* operator new(size_t n) {
|
||||
static void* operator new(size_t n) IF_DUSK(noexcept) {
|
||||
#if PLATFORM_GCN
|
||||
JASMemPool_MultiThreaded<T>& memPool_ = getMemPool();
|
||||
#endif
|
||||
return memPool_.alloc(n);
|
||||
}
|
||||
static void* operator new(size_t n, void* ptr) {
|
||||
static void* operator new(size_t n, void* ptr) IF_DUSK(noexcept) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
static void operator delete(void* ptr, size_t n, JKRHeapToken) {
|
||||
static void operator delete(void* ptr, size_t n, JKRHeapToken) IF_DUSK(noexcept) {
|
||||
return operator delete(ptr, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void operator delete(void* ptr, size_t n) {
|
||||
static void operator delete(void* ptr, size_t n) IF_DUSK(noexcept) {
|
||||
#if PLATFORM_GCN
|
||||
JASMemPool_MultiThreaded<T>& memPool_ = getMemPool();
|
||||
#endif
|
||||
|
||||
@@ -237,11 +237,11 @@ enum class JKRHeapToken {
|
||||
Dummy
|
||||
};
|
||||
|
||||
inline void* operator new(size_t, JKRHeapToken, void* where) {
|
||||
inline void* operator new(size_t, JKRHeapToken, void* where) noexcept {
|
||||
return where;
|
||||
}
|
||||
|
||||
inline void* operator new[](size_t, JKRHeapToken, void* where) {
|
||||
inline void* operator new[](size_t, JKRHeapToken, void* where) noexcept {
|
||||
return where;
|
||||
}
|
||||
|
||||
@@ -264,21 +264,21 @@ inline void* operator new[](size_t, JKRHeapToken, void* where) {
|
||||
#define JKR_HEAP_TOKEN_PARAM
|
||||
#endif
|
||||
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM);
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment);
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment);
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept);
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) IF_DUSK(noexcept);
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) IF_DUSK(noexcept);
|
||||
|
||||
// On PC, these new[] overloads are only used to catch usages of JKR_NEW with [].
|
||||
void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM);
|
||||
void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM, int alignment);
|
||||
void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment);
|
||||
void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept);
|
||||
void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM, int alignment) IF_DUSK(noexcept);
|
||||
void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) IF_DUSK(noexcept);
|
||||
|
||||
void operator delete(void* ptr JKR_HEAP_TOKEN_PARAM);
|
||||
void operator delete[](void* ptr JKR_HEAP_TOKEN_PARAM);
|
||||
void operator delete(void* ptr JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept);
|
||||
void operator delete[](void* ptr JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept);
|
||||
|
||||
#if TARGET_PC
|
||||
template<typename T>
|
||||
void jkrDelete(T* ptr) {
|
||||
void jkrDelete(T* ptr) IF_DUSK(noexcept) {
|
||||
if (ptr == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -298,7 +298,7 @@ void jkrDelete(T* ptr) {
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void jkrDelete(void* ptr) {
|
||||
inline void jkrDelete(void* ptr) IF_DUSK(noexcept) {
|
||||
if (ptr == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -322,7 +322,7 @@ constexpr bool newArgsHasCustomAlignment() {
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T* jkrNewArray(size_t count, std::in_place_type_t<T>, Args&&... args) {
|
||||
T* jkrNewArray(size_t count, std::in_place_type_t<T>, Args&&... args) IF_DUSK(noexcept) {
|
||||
size_t allocSize = count * sizeof(T);
|
||||
if constexpr (!std::is_trivially_destructible<T>()) {
|
||||
static_assert(
|
||||
@@ -333,6 +333,10 @@ T* jkrNewArray(size_t count, std::in_place_type_t<T>, Args&&... args) {
|
||||
}
|
||||
|
||||
void* ptr = operator new(allocSize, JKRHeapToken::Dummy, args...);
|
||||
if (!ptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
T* dataPtr;
|
||||
if constexpr (!std::is_trivially_destructible<T>()) {
|
||||
auto length = static_cast<size_t*>(ptr);
|
||||
@@ -352,7 +356,7 @@ T* jkrNewArray(size_t count, std::in_place_type_t<T>, Args&&... args) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void jkrDeleteArray(T* pointer) {
|
||||
void jkrDeleteArray(T* pointer) IF_DUSK(noexcept) {
|
||||
if (pointer == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -372,7 +376,7 @@ void jkrDeleteArray(T* pointer) {
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void jkrDeleteArray(void* pointer) {
|
||||
inline void jkrDeleteArray(void* pointer) IF_DUSK(noexcept) {
|
||||
if (pointer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,8 +68,14 @@ void J2DIndTevStage::load(u8 tevStage) {
|
||||
}
|
||||
|
||||
void J2DIndTexMtx::load(u8 indTexMtx) {
|
||||
#ifdef TARGET_PC
|
||||
Mtx23 mtx;
|
||||
mIndTexMtxInfo.mMtx.to_host(mtx);
|
||||
GXSetIndTexMtx((GXIndTexMtxID)(GX_ITM_0 + indTexMtx), mtx, mIndTexMtxInfo.mScaleExp);
|
||||
#else
|
||||
GXSetIndTexMtx((GXIndTexMtxID)(GX_ITM_0 + indTexMtx), mIndTexMtxInfo.mMtx,
|
||||
mIndTexMtxInfo.mScaleExp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void J2DIndTexCoordScale::load(u8 indTexStage) {
|
||||
|
||||
@@ -370,28 +370,28 @@ constexpr auto FRAME_PERIOD = std::chrono::duration_cast<std::chrono::nanosecond
|
||||
constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2;
|
||||
|
||||
static void waitPrecise(Limiter& limiter, Limiter::duration_t targetNs) {
|
||||
const auto sleepTime = limiter.SleepTime(targetNs);
|
||||
const auto sleepTime = limiter.Sleep(targetNs);
|
||||
dusk::frameUsagePct =
|
||||
100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(targetNs));
|
||||
limiter.Sleep(targetNs);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void waitForTick(u32 p1, u16 p2) {
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit) {
|
||||
dusk::frameUsagePct = 0.f;
|
||||
return;
|
||||
static Limiter limiter;
|
||||
|
||||
if (dusk::frame_interp::is_enabled() && !dusk::getTransientSettings().skipFrameRateLimit) {
|
||||
dusk::frameUsagePct = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dusk::getTransientSettings().skipFrameRateLimit) {
|
||||
p1 = OS_TIMER_CLOCK / 120;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
if (fopOvlpM_IsPeek() && dusk::getTransientSettings().stateShareLoadActive) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ZoneScopedC(tracy::Color::DimGray);
|
||||
#endif
|
||||
|
||||
@@ -559,7 +559,7 @@ void* operator new(size_t size) {
|
||||
return JKRHeap::alloc(size, 4, NULL);
|
||||
}
|
||||
#else
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM) {
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM) noexcept {
|
||||
if (sCurrentHeap == NULL) {
|
||||
return fallback_alloc(size, 0, false);
|
||||
}
|
||||
@@ -576,7 +576,7 @@ void* operator new(size_t size, int alignment) {
|
||||
return JKRHeap::alloc(size, alignment, NULL);
|
||||
}
|
||||
#else
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) {
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) noexcept {
|
||||
void* mem = JKRHeap::alloc(size, alignment, nullptr);
|
||||
if (mem == nullptr) {
|
||||
return fallback_alloc(size, abs(alignment), true);
|
||||
@@ -585,7 +585,7 @@ void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) {
|
||||
void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) IF_DUSK(noexcept) {
|
||||
void* mem = JKRHeap::alloc(size, alignment, heap);
|
||||
#if TARGET_PC
|
||||
if (mem == nullptr) {
|
||||
@@ -600,7 +600,7 @@ void* operator new[](size_t size) {
|
||||
return JKRHeap::alloc(size, 4, NULL);
|
||||
}
|
||||
#else
|
||||
void* operator new[](size_t JKR_HEAP_TOKEN_PARAM) {
|
||||
void* operator new[](size_t JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept) {
|
||||
OSPanic(__FILE__, __LINE__, "Allocation should go through JKR_NEW_ARRAY instead");
|
||||
}
|
||||
#endif
|
||||
@@ -610,12 +610,12 @@ void* operator new[](size_t size, int alignment) {
|
||||
return JKRHeap::alloc(size, alignment, NULL);
|
||||
}
|
||||
#else
|
||||
void* operator new[](size_t JKR_HEAP_TOKEN_PARAM, int) {
|
||||
void* operator new[](size_t JKR_HEAP_TOKEN_PARAM, int) IF_DUSK(noexcept) {
|
||||
OSPanic(__FILE__, __LINE__, "Allocation should go through JKR_NEW_ARRAY instead");
|
||||
}
|
||||
#endif
|
||||
|
||||
void* operator new[](size_t JKR_HEAP_TOKEN_PARAM, JKRHeap*, int) {
|
||||
void* operator new[](size_t JKR_HEAP_TOKEN_PARAM, JKRHeap*, int) IF_DUSK(noexcept) {
|
||||
OSPanic(__FILE__, __LINE__, "Allocation should go through JKR_NEW_ARRAY instead");
|
||||
}
|
||||
|
||||
@@ -624,7 +624,7 @@ void operator delete(void* ptr) {
|
||||
JKRHeap::free(ptr, NULL);
|
||||
}
|
||||
#else
|
||||
void operator delete(void* ptr JKR_HEAP_TOKEN_PARAM) {
|
||||
void operator delete(void* ptr JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept) {
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
JKRHeap* heap = JKRHeap::findFromRoot(ptr);
|
||||
@@ -645,7 +645,7 @@ void operator delete[](void* ptr) {
|
||||
JKRHeap::free(ptr, NULL);
|
||||
}
|
||||
#else
|
||||
void operator delete[](void* ptr JKR_HEAP_TOKEN_PARAM) {
|
||||
void operator delete[](void* ptr JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept) {
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
JKRHeap* heap = JKRHeap::findFromRoot(ptr);
|
||||
|
||||
@@ -655,7 +655,7 @@ value_or_fun:
|
||||
|
||||
value:
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation && u <= 5 &&
|
||||
if (dusk::frame_interp::is_enabled() && u <= 5 &&
|
||||
(operation == data::UNK_0x2 || operation == data::UNK_0x3 || operation == data::UNK_0x12))
|
||||
{
|
||||
dusk::frame_interp::request_presentation_sync();
|
||||
@@ -666,7 +666,7 @@ value:
|
||||
|
||||
value_n:
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation &&
|
||||
if (dusk::frame_interp::is_enabled() &&
|
||||
(pN == TAdaptor_camera::sauVariableValue_3_POSITION_XYZ || pN == TAdaptor_camera::sauVariableValue_3_TARGET_POSITION_XYZ) &&
|
||||
(operation == data::UNK_0x2 || operation == data::UNK_0x3 || operation == data::UNK_0x12))
|
||||
{
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include <cmath>
|
||||
#include "os_report.h"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/action_bindings.h"
|
||||
#endif
|
||||
|
||||
u32 JUTGamePad::CRumble::sChannelMask[4] = {
|
||||
PAD_CHAN0_BIT,
|
||||
PAD_CHAN1_BIT,
|
||||
@@ -85,6 +89,9 @@ u32 JUTGamePad::sRumbleSupported;
|
||||
|
||||
u32 JUTGamePad::read() {
|
||||
sRumbleSupported = PADRead(mPadStatus);
|
||||
#if TARGET_PC
|
||||
dusk::updateActionBindings();
|
||||
#endif
|
||||
|
||||
switch (sClampMode) {
|
||||
case EClampStick:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -58,6 +58,10 @@ toast:active {
|
||||
background-color: rgba(45, 43, 26, 80%);
|
||||
}*/
|
||||
|
||||
b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
toast heading {
|
||||
display: flex;
|
||||
gap: 18dp;
|
||||
|
||||
@@ -14,6 +14,10 @@ body {
|
||||
color: #E0DBC8;
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
window {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
@@ -51,10 +51,13 @@
|
||||
#include "d/actor/d_a_ni.h"
|
||||
#include "d/d_s_play.h"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/action_bindings.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "res/Object/Alink.h"
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
static int daAlink_Create(fopAc_ac_c* i_this);
|
||||
static int daAlink_Delete(daAlink_c* i_this);
|
||||
@@ -5987,7 +5990,7 @@ void daAlink_c::setItemMatrix(int param_0) {
|
||||
|
||||
mDoMtx_stack_c::XrotS(-0x8000);
|
||||
#ifdef TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
Mtx boot_mtx;
|
||||
mDoMtx_concat(mpLinkModel->getAnmMtx(0x18), mDoMtx_stack_c::get(), boot_mtx);
|
||||
mpLinkBootModels[1]->setAnmMtx(1, boot_mtx);
|
||||
@@ -7559,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;
|
||||
|
||||
@@ -8077,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;
|
||||
@@ -9363,6 +9361,12 @@ BOOL daAlink_c::spActionTrigger() {
|
||||
}
|
||||
|
||||
BOOL daAlink_c::midnaTalkTrigger() const {
|
||||
#if TARGET_PC
|
||||
// If we have a custom bind for Midna, check that instead
|
||||
if (dusk::isActionBound(dusk::ActionBinds::CALL_MIDNA, 0)) {
|
||||
return dusk::getActionBindTrig(dusk::ActionBinds::CALL_MIDNA, 0);
|
||||
}
|
||||
#endif
|
||||
return mItemTrigger & BTN_Z;
|
||||
}
|
||||
|
||||
@@ -19763,7 +19767,7 @@ int daAlink_c::draw() {
|
||||
dComIfGd_getOpaListDark()->entryImm(mpHookChain, 0);
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation &&
|
||||
if (dusk::frame_interp::is_enabled() &&
|
||||
mEquipItem == dItemNo_IRONBALL_e &&
|
||||
mIronBallChainPos != NULL && mIronBallChainAngle != NULL)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/gyro.h"
|
||||
#include "dusk/action_bindings.h"
|
||||
#endif
|
||||
|
||||
bool daAlink_c::checkNoSubjectModeCamera() {
|
||||
@@ -144,8 +145,8 @@ BOOL daAlink_c::setBodyAngleToCamera() {
|
||||
f32 gy_pitch = 0.f;
|
||||
dusk::gyro::getAimDeltas(gy_yaw, gy_pitch);
|
||||
|
||||
shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale * (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f));
|
||||
sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale * (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f));
|
||||
shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale);
|
||||
sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale);
|
||||
|
||||
if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) {
|
||||
sp8 = mBodyAngle.x;
|
||||
@@ -192,7 +193,9 @@ BOOL daAlink_c::subjectCancelTrigger() {
|
||||
BOOL daAlink_c::checkSubjectEnd(BOOL i_isPlaySe) {
|
||||
setDoStatus(BUTTON_STATUS_BACK);
|
||||
|
||||
if (checkEventRun() || checkEquipAnime() || doTrigger() || checkSetItemTrigger(dItemNo_HAWK_EYE_e) || subjectCancelTrigger() || checkEndResetFlg0(ERFLG0_FORCE_SUBJECT_CANCEL) || dComIfGp_checkCameraAttentionStatus(field_0x317c, 0x2000)) {
|
||||
// Allow pressing the first person binding to also leave first person
|
||||
if (IF_DUSK(dusk::getActionBindTrig(dusk::ActionBinds::FIRST_PERSON_CAMERA, 0)) ||
|
||||
checkEventRun() || checkEquipAnime() || doTrigger() || checkSetItemTrigger(dItemNo_HAWK_EYE_e) || subjectCancelTrigger() || checkEndResetFlg0(ERFLG0_FORCE_SUBJECT_CANCEL) || dComIfGp_checkCameraAttentionStatus(field_0x317c, 0x2000)) {
|
||||
if (i_isPlaySe) {
|
||||
seStartSystem(Z2SE_SUBJ_VIEW_OUT);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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(¤t.pos);
|
||||
|
||||
@@ -397,7 +397,7 @@ static int daB_GND_Draw(b_gnd_class* i_this) {
|
||||
i_this->field_0x21e8.update(2, l_color, &a_this->tevStr);
|
||||
dComIfGd_set3DlineMat(&i_this->field_0x21e8);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->mReinsInterpCurrValid) {
|
||||
memcpy(i_this->mReinsInterpPrev, i_this->mReinsInterpCurr, sizeof(i_this->mReinsInterpCurr));
|
||||
memcpy(i_this->mReinsTexInterpPrev, i_this->mReinsTexInterpCurr, sizeof(i_this->mReinsTexInterpCurr));
|
||||
|
||||
@@ -674,7 +674,29 @@ static int daE_ARROW_Create(fopAc_ac_c* i_this) {
|
||||
}
|
||||
|
||||
int phase_state = dComIfG_resLoad(&a_this->mPhase, a_this->mResName);
|
||||
#if TARGET_PC
|
||||
static int s_create_frames = 0;
|
||||
static bool s_first_arrow = true;
|
||||
static fpc_ProcID s_last_scene_id = 0;
|
||||
|
||||
s_create_frames++;
|
||||
fpc_ProcID cur_scene_id = dStage_roomControl_c::getProcID();
|
||||
|
||||
if (cur_scene_id != s_last_scene_id) {
|
||||
s_first_arrow = true;
|
||||
s_last_scene_id = cur_scene_id;
|
||||
}
|
||||
|
||||
if (phase_state == cPhs_COMPLEATE_e && s_first_arrow && s_create_frames < 4) {
|
||||
return cPhs_INIT_e;
|
||||
}
|
||||
#endif
|
||||
if (phase_state == cPhs_COMPLEATE_e) {
|
||||
#if TARGET_PC
|
||||
s_create_frames = 0;
|
||||
s_first_arrow = false;
|
||||
#endif
|
||||
|
||||
a_this->mArrowType = fopAcM_GetParam(a_this) & 0xF;
|
||||
a_this->mFlags = fopAcM_GetParam(a_this) & 0xF0;
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ static int daE_DB_Draw(e_db_class* i_this) {
|
||||
i_this->stalkLine.update(12, l_color, &actor->tevStr);
|
||||
dComIfGd_set3DlineMat(&i_this->stalkLine);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->mStalkLineInterpCurrValid) {
|
||||
memcpy(i_this->mStalkLineInterpPrev, i_this->mStalkLineInterpCurr, sizeof(i_this->mStalkLineInterpCurr));
|
||||
i_this->mStalkLineInterpPrevValid = true;
|
||||
|
||||
@@ -103,7 +103,7 @@ static int daE_HB_Draw(e_hb_class* i_this) {
|
||||
i_this->stalkLine.update(12, l_color, &actor->tevStr);
|
||||
dComIfGd_set3DlineMat(&i_this->stalkLine);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->mStalkLineInterpCurrValid) {
|
||||
memcpy(i_this->mStalkLineInterpPrev, i_this->mStalkLineInterpCurr, sizeof(i_this->mStalkLineInterpCurr));
|
||||
i_this->mStalkLineInterpPrevValid = true;
|
||||
|
||||
@@ -105,7 +105,7 @@ static int daE_MB_Draw(e_mb_class* i_this) {
|
||||
i_this->mRopeMat.update(16, l_color, &a_this->tevStr);
|
||||
dComIfGd_set3DlineMat(&i_this->mRopeMat);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->mRopeInterpCurrValid) {
|
||||
memcpy(i_this->mRopeInterpPrev, i_this->mRopeInterpCurr, sizeof(i_this->mRopeInterpCurr));
|
||||
i_this->mRopeInterpPrevValid = true;
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -117,6 +117,13 @@ static void daE_S1_interp_callback(bool isSimFrame, void* pUserWork) {
|
||||
dst[i] = p0 + (p1 - p0) * alpha;
|
||||
}
|
||||
}
|
||||
GXColor line_color;
|
||||
line_color.r = JREG_S(0) + 5;
|
||||
line_color.g = JREG_S(1) + 10;
|
||||
line_color.b = JREG_S(2) + 10;
|
||||
line_color.a = 0xFF;
|
||||
|
||||
i_this->mLineMat.update(16, line_color, &i_this->tevStr);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -154,7 +161,7 @@ static int daE_S1_Draw(e_s1_class* i_this) {
|
||||
dComIfGd_set3DlineMatDark(&i_this->mLineMat);
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->mHairInterpCurrValid) {
|
||||
memcpy(i_this->mHairInterpPrev, i_this->mHairInterpCurr, sizeof(i_this->mHairInterpCurr));
|
||||
i_this->mHairInterpPrevValid = true;
|
||||
|
||||
@@ -535,7 +535,7 @@ static int daE_WB_Draw(e_wb_class* i_this) {
|
||||
i_this->himo_tex.update(2, l_color, &actor->tevStr);
|
||||
dComIfGd_set3DlineMat(&i_this->himo_tex);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->himo_interp_curr_valid) {
|
||||
memcpy(i_this->himo_mat_interp_prev, i_this->himo_mat_interp_curr, sizeof(i_this->himo_mat_interp_curr));
|
||||
memcpy(i_this->himo_tex_interp_prev, i_this->himo_tex_interp_curr, sizeof(i_this->himo_tex_interp_curr));
|
||||
|
||||
@@ -107,7 +107,7 @@ static s32 daE_YD_Draw(e_yd_class* i_this) {
|
||||
i_this->mLineMat.update(12, l_color, &i_this->actor.tevStr);
|
||||
dComIfGd_set3DlineMat(&i_this->mLineMat);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->mLineMatInterpCurrValid) {
|
||||
memcpy(i_this->mLineMatInterpPrev, i_this->mLineMatInterpCurr, sizeof(i_this->mLineMatInterpCurr));
|
||||
i_this->mLineMatInterpPrevValid = true;
|
||||
|
||||
@@ -139,6 +139,7 @@ static BOOL pl_check(e_yg_class* i_this, f32 i_dist) {
|
||||
#if TARGET_PC
|
||||
static void daE_YG_interp_callback(bool isSimFrame, void* pUserWork) {
|
||||
e_yg_class* i_this = (e_yg_class*)pUserWork;
|
||||
fopAc_ac_c* actor = (fopAc_ac_c*)&i_this->actor;
|
||||
if (!i_this->mTentacleInterpPrevValid || !i_this->mTentacleInterpCurrValid) {
|
||||
return;
|
||||
}
|
||||
@@ -152,6 +153,13 @@ static void daE_YG_interp_callback(bool isSimFrame, void* pUserWork) {
|
||||
dst[i] = p0 + (p1 - p0) * alpha;
|
||||
}
|
||||
}
|
||||
GXColor color;
|
||||
color.r = JREG_S(0) + 20;
|
||||
color.g = JREG_S(1) + 20;
|
||||
color.b = JREG_S(2) + 20;
|
||||
color.a = 0xFF;
|
||||
|
||||
i_this->mLineMat.update(10, color, &actor->tevStr);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -183,7 +191,7 @@ static int daE_YG_Draw(e_yg_class* i_this) {
|
||||
dComIfGd_set3DlineMatDark(&i_this->mLineMat);
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->mTentacleInterpCurrValid) {
|
||||
memcpy(i_this->mTentacleInterpPrev, i_this->mTentacleInterpCurr, sizeof(i_this->mTentacleInterpCurr));
|
||||
i_this->mTentacleInterpPrevValid = true;
|
||||
|
||||
@@ -135,7 +135,7 @@ static int daE_YH_Draw(e_yh_class* i_this) {
|
||||
i_this->mLine.update(12, l_color, &a_this->tevStr);
|
||||
dComIfGd_set3DlineMat(&i_this->mLine);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (i_this->mLineInterpCurrValid) {
|
||||
memcpy(i_this->mLineInterpPrev, i_this->mLineInterpCurr, sizeof(i_this->mLineInterpCurr));
|
||||
i_this->mLineInterpPrevValid = true;
|
||||
|
||||
@@ -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;
|
||||
@@ -3159,7 +3165,7 @@ void daHorse_c::setReinPosNormalSubstance() {
|
||||
#if TARGET_PC
|
||||
void daHorse_c::lerpControlPoints(f32 alpha) {
|
||||
// FRAME INTERP NOTE: Currently only lerping points for Epona's reins. Need a more global solution.
|
||||
if (!dusk::getSettings().game.enableFrameInterpolation || !s_horseReinSimPrevValid || !s_horseReinSimCurrValid) {
|
||||
if (!dusk::frame_interp::is_enabled() || !s_horseReinSimPrevValid || !s_horseReinSimCurrValid) {
|
||||
return;
|
||||
}
|
||||
const int nCurr = s_horseReinSimNumCurr;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -325,7 +325,7 @@ int daObjFchain_c::draw() {
|
||||
dComIfGd_getOpaListDark()->entryImm(&mShape, 0);
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (mChainInterpCurrValid) {
|
||||
memcpy(mChainInterpPrev, mChainInterpCurr, sizeof(mChainInterpCurr));
|
||||
mChainInterpPrevValid = true;
|
||||
|
||||
@@ -493,7 +493,7 @@ int daObjKLift00_c::Draw() {
|
||||
dComIfGd_setList();
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
if (mChainInterpCurrValid) {
|
||||
memcpy(mChainInterpPrev, mChainInterpCurr, mNumChains * sizeof(cXyz));
|
||||
mChainInterpPrevValid = true;
|
||||
|
||||
@@ -170,7 +170,7 @@ int daTitle_c::Execute() {
|
||||
}
|
||||
|
||||
#ifdef TARGET_PC
|
||||
if (!dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (!dusk::frame_interp::is_enabled()) {
|
||||
#endif
|
||||
dMenu_Collect3D_c::setViewPortOffsetY(0.0f);
|
||||
#ifdef TARGET_PC
|
||||
@@ -354,7 +354,7 @@ void daTitle_c::fastLogoDispInit() {
|
||||
mProcID = 5;
|
||||
|
||||
#ifdef TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
dusk::frame_interp::request_presentation_sync();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||
#include "dusk/speedrun.h"
|
||||
#include "m_Do/m_Do_controller_pad.h"
|
||||
#include <dusk/autosave.h>
|
||||
|
||||
dBrightCheck_c::dBrightCheck_c(JKRArchive* i_archive) {
|
||||
mArchive = i_archive;
|
||||
@@ -142,15 +143,16 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
toggleAutoSave(true);
|
||||
#endif
|
||||
mCompleteCheck = true;
|
||||
mMode = MODE_WAIT_e;
|
||||
|
||||
+17
-7
@@ -31,6 +31,7 @@
|
||||
#if TARGET_PC
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/action_bindings.h"
|
||||
#include "imgui.h"
|
||||
#endif
|
||||
|
||||
@@ -838,6 +839,12 @@ void dCamera_c::updatePad() {
|
||||
mTrigB = mDoCPd_c::getTrigB(mPadID) ? true : false;
|
||||
|
||||
#if TARGET_PC
|
||||
// If our custom action binding is triggered, and we're not already in first person, go into first person
|
||||
if (dusk::getActionBindTrig(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID) && mGear != -1) {
|
||||
setComStat(0x1000);
|
||||
mGear = 0;
|
||||
}
|
||||
|
||||
if (mCamParam.mManualMode) {
|
||||
return;
|
||||
}
|
||||
@@ -877,7 +884,8 @@ void dCamera_c::updatePad() {
|
||||
|
||||
if (mPadInfo.mCStick.mLastPosY < -mCamSetup.mCStick.SwTHH()) {
|
||||
if (mCStickYState != -1) {
|
||||
if (mGear == -1 && mCurMode == 4) {
|
||||
// Don't use regular first person trigger if custom mapping is set
|
||||
if (mGear == -1 && mCurMode == 4 IF_DUSK(&& !dusk::isActionBound(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID))) {
|
||||
mGear = 0;
|
||||
setComStat(0x2000);
|
||||
} else if (mGear == 0 && sp6C) {
|
||||
@@ -888,7 +896,8 @@ void dCamera_c::updatePad() {
|
||||
mCStickYState = -1;
|
||||
} else if (mPadInfo.mCStick.mLastPosY > mCamSetup.mCStick.SwTHH()) {
|
||||
if (mCStickYState != 1) {
|
||||
if (mGear == 0 && sp6B) {
|
||||
// Don't use regular first person trigger if custom mapping is set
|
||||
if (mGear == 0 && sp6B IF_DUSK(&& !dusk::isActionBound(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID))) {
|
||||
setComStat(0x1000);
|
||||
} else if (mGear == 1) {
|
||||
mGear = 0;
|
||||
@@ -7649,9 +7658,10 @@ bool dCamera_c::freeCamera() {
|
||||
f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY);
|
||||
|
||||
// If we aren't in manual cam mode, don't trigger it if the player tries to hit C-up
|
||||
// for first person
|
||||
if (mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY < 0 ||
|
||||
(mCamParam.mManualMode == 1 && mPadInfo.mCStick.mLastPosY != 0)) {
|
||||
// for first person unless they have first person bound to a custom binding
|
||||
if ((dusk::isActionBound(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID) && mPadInfo.mCStick.mLastPosY != 0) ||
|
||||
mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY < 0 || (mCamParam.mManualMode == 1 && mPadInfo.mCStick.mLastPosY != 0))
|
||||
{
|
||||
mCamParam.mManualMode = 1;
|
||||
camMovement = camMovement.normalize();
|
||||
camMovement.y *= dusk::getSettings().game.invertCameraYAxis ? 1.0f : -1.0f;
|
||||
@@ -10421,7 +10431,7 @@ bool dCamera_c::eventCamera(s32 param_0) {
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
switch (var_r29) {
|
||||
case 3:
|
||||
case 4:
|
||||
@@ -11312,7 +11322,7 @@ static int camera_execute(camera_process_class* i_this) {
|
||||
#ifdef TARGET_PC
|
||||
widezoom_correction(i_this, i_this->mCamera.TrimHeight());
|
||||
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
dusk::frame_interp::add_interpolation_callback([](bool _, void* pUserWork) {
|
||||
const auto i_this = static_cast<camera_process_class*>(pUserWork);
|
||||
const auto camera = &i_this->mCamera;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -991,7 +991,7 @@ void dMenu_DmapBg_c::draw() {
|
||||
-35.0f + (local_224.x - local_218.x),
|
||||
-35.0f + (local_224.y - local_218.y));
|
||||
#if TARGET_PC
|
||||
if (!dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (!dusk::frame_interp::is_enabled()) {
|
||||
field_0xdda = 0;
|
||||
}
|
||||
#else
|
||||
@@ -2624,7 +2624,7 @@ void dMenu_Dmap_c::zoomIn_proc() {
|
||||
|
||||
void dMenu_Dmap_c::zoomOut_init_proc() {
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
mpDrawBg->resetScrollArrowMask();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1146,7 +1146,7 @@ void dMenu_Fmap_c::zoom_spot_to_region_init() {
|
||||
field_0x1ec = 1.0f;
|
||||
#if TARGET_PC
|
||||
// Frame interp note: field_0x122d used to be set every draw, causing flickering. Do it here instead.
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (dusk::frame_interp::is_enabled()) {
|
||||
mpDraw2DBack->resetScrollArrowMask();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -437,7 +437,7 @@ void dMenu_Fmap2DBack_c::draw() {
|
||||
if (field_0x122d) {
|
||||
mpMeterHaihai->drawHaihai(field_0x122d);
|
||||
#if TARGET_PC
|
||||
if (!dusk::getSettings().game.enableFrameInterpolation) {
|
||||
if (!dusk::frame_interp::is_enabled()) {
|
||||
field_0x122d = 0;
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -188,6 +188,14 @@ void dMsgScrnTree_c::exec() {
|
||||
fukiAlpha(1.0f);
|
||||
}
|
||||
mpPmP_c->scale(g_MsgObject_HIO_c.mBoxWoodScaleX, g_MsgObject_HIO_c.mBoxWoodScaleY);
|
||||
|
||||
#if TARGET_PC
|
||||
const f32 hudScale = mDoGph_gInf_c::hudAspectScaleUp;
|
||||
if (hudScale > 1.0f) {
|
||||
field_0xc4->getPanePtr()->setBasePosition(J2DBasePosition_4);
|
||||
field_0xc4->getPanePtr()->scale(hudScale, 1.0f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void dMsgScrnTree_c::draw() {
|
||||
|
||||
+10
-7
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
@@ -20,6 +21,7 @@
|
||||
#include "m_Do/m_Do_machine.h"
|
||||
#include "m_Do/m_Do_main.h"
|
||||
#include "m_Do/m_Do_mtx.h"
|
||||
#include <dusk/autosave.h>
|
||||
|
||||
#if TARGET_PC
|
||||
#define SHOW_TV_SETTINGS_SCREEN (this->mShowTvSettingsScreen)
|
||||
@@ -421,8 +423,11 @@ void dScnName_c::changeGameScene() {
|
||||
if (!dusk::m_speedrunInfo.m_isRunStarted) {
|
||||
dusk::resetForSpeedrunMode();
|
||||
dusk::m_speedrunInfo.startRun();
|
||||
dusk::speedrun::start();
|
||||
}
|
||||
}
|
||||
|
||||
toggleAutoSave(true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,6 +1042,10 @@ static BOOL heapSizeCheck() {
|
||||
|
||||
bool dScnPly_c::resetGame() {
|
||||
if (fpcM_GetName(this) == fpcNm_OPENING_SCENE_e) {
|
||||
#if TARGET_PC
|
||||
toggleAutoSave(false);
|
||||
#endif
|
||||
|
||||
if (!dStage_roomControl_c::resetArchiveBank(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+3
-5
@@ -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 {
|
||||
|
||||
@@ -53,6 +53,11 @@ static std::string FormatToString(const char* msg, va_list list) {
|
||||
size *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
while (!str.empty() && str[str.size()-1] == '\n') {
|
||||
str.pop_back();
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -692,6 +695,12 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
return;
|
||||
}
|
||||
|
||||
// prevent stuff like https://github.com/TwilitRealm/dusklight/issues/949
|
||||
if (link->getDemoMode() != 0) {
|
||||
inJump = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inJump) {
|
||||
if (link->mProcID == daAlink_c::PROC_CUT_JUMP) {
|
||||
inJump = true;
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "dusk/action_bindings.h"
|
||||
|
||||
#include "aurora/lib/input.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/ui/ui.hpp"
|
||||
|
||||
namespace dusk {
|
||||
|
||||
static std::array<std::array<ActionBindPressData, static_cast<int>(ActionBinds::COUNT)>, PAD_CHANMAX> actionPressData{};
|
||||
|
||||
ActionBindsMap& getActionBinds() {
|
||||
static ActionBindsMap actionBinds = {
|
||||
{ActionBinds::FIRST_PERSON_CAMERA, {&getSettings().actionBindings.firstPersonCamera, "First Person Camera"}},
|
||||
{ActionBinds::CALL_MIDNA, {&getSettings().actionBindings.callMidna, "Call Midna"}},
|
||||
{ActionBinds::OPEN_DUSKLIGHT_MENU, {&getSettings().actionBindings.openDusklightMenu, "Open Dusklight Menu"}},
|
||||
{ActionBinds::TURBO_SPEED_BUTTON, {&getSettings().actionBindings.turboSpeedButton, "Turbo Speed Button"}},
|
||||
};
|
||||
return actionBinds;
|
||||
}
|
||||
|
||||
bool isActionBound(ActionBinds action, u32 port) {
|
||||
auto& actionBinds = getActionBinds();
|
||||
// Check to make sure action is properly bound
|
||||
if (!actionBinds.contains(action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getActionBindButton(action, port) != PAD_NATIVE_BUTTON_INVALID;
|
||||
}
|
||||
|
||||
void updateActionBindings() {
|
||||
for (u32 port = 0; port < PAD_CHANMAX; ++port) {
|
||||
// Move the current press to the previous frame
|
||||
for (auto& pressData : actionPressData[port]) {
|
||||
pressData.pressedPrevFrame = pressData.pressedCurFrame;
|
||||
pressData.pressedCurFrame = false;
|
||||
}
|
||||
|
||||
// Update current frame with whether action button is pressed
|
||||
for (auto& [action, boundAction] : getActionBinds()) {
|
||||
// If the action isn't bound, or if documents are visible and the action isn't
|
||||
// opening the dusklight menu, don't update. Otherwise, we may accidentally
|
||||
// perform actions while the dusklight menu is open.
|
||||
if (!isActionBound(action, port) ||
|
||||
(ui::any_document_visible() && action != ActionBinds::OPEN_DUSKLIGHT_MENU)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int button = boundAction.configVars->at(port);
|
||||
|
||||
// If keyboard is active for this port
|
||||
u32 count = 0;
|
||||
if (PADGetKeyButtonBindings(port, &count) != nullptr) {
|
||||
int numKeys = 0;
|
||||
const bool* kbState = SDL_GetKeyboardState(&numKeys);
|
||||
if (kbState[button]) {
|
||||
actionPressData[port][static_cast<int>(action)].pressedCurFrame = true;
|
||||
}
|
||||
} else {
|
||||
// If controller is active
|
||||
auto controller = aurora::input::get_controller_for_player(port);
|
||||
if (controller) {
|
||||
if (SDL_GetGamepadButton(controller->m_controller, static_cast<SDL_GamepadButton>(button))) {
|
||||
actionPressData[port][static_cast<int>(action)].pressedCurFrame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool getActionBindTrig(ActionBinds action, u32 port) {
|
||||
return isActionBound(action, port) &&
|
||||
actionPressData[port][static_cast<int>(action)].pressedCurFrame &&
|
||||
!actionPressData[port][static_cast<int>(action)].pressedPrevFrame;
|
||||
}
|
||||
|
||||
bool getActionBindHold(ActionBinds action, u32 port) {
|
||||
return isActionBound(action, port) &&
|
||||
actionPressData[port][static_cast<int>(action)].pressedCurFrame &&
|
||||
actionPressData[port][static_cast<int>(action)].pressedPrevFrame;
|
||||
}
|
||||
|
||||
bool getActionBindHoldAnyPort(ActionBinds action) {
|
||||
for (u32 port = 0; port < PAD_CHANMAX; ++port) {
|
||||
if (getActionBindHold(action, port)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int getActionBindButton(ActionBinds action, u32 port) {
|
||||
return (*getActionBinds()[action].configVars)[port];
|
||||
}
|
||||
}
|
||||
+31
-6
@@ -2,6 +2,7 @@
|
||||
#include "dusk/ui/ui.hpp"
|
||||
#include "imgui/ImGuiConsole.hpp"
|
||||
|
||||
bool shouldAutoSave = false;
|
||||
u8 mSaveBuffer[QUEST_LOG_SIZE * 3];
|
||||
u8 mAutoSaveProc = 0;
|
||||
int autoSaveWriteState = 0;
|
||||
@@ -13,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 && mAutoSaveProc == 0 &&
|
||||
strcmp(dComIfGp_getStartStageName(), "F_SP102") != 0)
|
||||
if (canAutoSave())
|
||||
{
|
||||
mAutoSaveProc = 1;
|
||||
}
|
||||
@@ -25,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());
|
||||
@@ -39,6 +58,7 @@ void writeAutoSave() {
|
||||
}
|
||||
|
||||
g_mDoMemCd_control.save(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void autoSaving() {
|
||||
@@ -47,8 +67,9 @@ void autoSaving() {
|
||||
if (cardState == 2) {
|
||||
mAutoSaveProc = 1;
|
||||
} else if (cardState == 1) {
|
||||
writeAutoSave();
|
||||
mAutoSaveProc = 3;
|
||||
if (writeAutoSave()) {
|
||||
mAutoSaveProc = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,4 +110,8 @@ void endAutoSave() {
|
||||
.duration = std::chrono::milliseconds(1500),
|
||||
});
|
||||
mAutoSaveProc = 0;
|
||||
}
|
||||
|
||||
void toggleAutoSave(bool enabled) {
|
||||
shouldAutoSave = enabled;
|
||||
}
|
||||
+30
-2
@@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/action_bindings.h"
|
||||
|
||||
using namespace dusk::config;
|
||||
|
||||
@@ -55,12 +56,29 @@ static T sanitizeEnumValue(const ConfigVar<T>& cVar, T value) {
|
||||
|
||||
template<ConfigValue T>
|
||||
void ConfigImpl<T>::loadFromJson(ConfigVar<T>& cVar, const json& jsonValue) {
|
||||
if constexpr (std::is_enum_v<T>) {
|
||||
if (jsonValue.is_boolean()) {
|
||||
using Underlying = std::underlying_type_t<T>;
|
||||
const bool b = jsonValue.get<bool>();
|
||||
|
||||
Underlying raw;
|
||||
if constexpr (std::is_same_v<T, dusk::FrameInterpMode>) {
|
||||
raw = b ? static_cast<Underlying>(2) : static_cast<Underlying>(0);
|
||||
} else {
|
||||
raw = b ? static_cast<Underlying>(1) : static_cast<Underlying>(0);
|
||||
}
|
||||
|
||||
cVar.setValue(sanitizeEnumValue(cVar, static_cast<T>(raw)), false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cVar.setValue(sanitizeEnumValue(cVar, jsonValue.get<T>()), false);
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -157,6 +175,8 @@ namespace dusk::config {
|
||||
template class ConfigImpl<dusk::DiscVerificationState>;
|
||||
template class ConfigImpl<dusk::GameLanguage>;
|
||||
template class ConfigImpl<dusk::GyroMode>;
|
||||
template class ConfigImpl<dusk::FrameInterpMode>;
|
||||
template class ConfigImpl<dusk::Resampler>;
|
||||
}
|
||||
|
||||
void dusk::config::Register(ConfigVarBase& configVar) {
|
||||
@@ -248,7 +268,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);
|
||||
}
|
||||
}
|
||||
@@ -256,6 +277,13 @@ void dusk::config::Save() {
|
||||
io::FileStream::WriteAllText(reinterpret_cast<const char*>(configJsonPath.c_str()), j.dump(4));
|
||||
}
|
||||
|
||||
void dusk::config::ClearAllActionBindings(int port) {
|
||||
for (auto& actionBinding : getActionBinds() | std::views::values) {
|
||||
actionBinding.configVars->at(port).setValue(PAD_NATIVE_BUTTON_INVALID);
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
||||
ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) {
|
||||
const auto configVar = RegisteredConfigVars.find(name);
|
||||
if (configVar != RegisteredConfigVars.end()) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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,10 +135,15 @@ void begin_sim_tick() {
|
||||
|
||||
s_interpolationCallBackWork.clear();
|
||||
s_cam_prev = std::move(s_cam_curr);
|
||||
++g_sim_tick_seq;
|
||||
}
|
||||
|
||||
void begin_frame(bool enabled, bool is_sim_frame, float step) {
|
||||
g_enabled = enabled;
|
||||
uint64_t sim_tick_seq() {
|
||||
return g_sim_tick_seq;
|
||||
}
|
||||
|
||||
void begin_frame(FrameInterpMode mode, bool is_sim_frame, float step) {
|
||||
g_enabled = mode != FrameInterpMode::Off;
|
||||
g_is_sim_frame = is_sim_frame;
|
||||
g_step = std::clamp(step, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
#include <dusk/frame_interpolation.h>
|
||||
|
||||
namespace dusk::game_clock {
|
||||
|
||||
@@ -45,7 +46,8 @@ MainLoopPacer advance_main_loop() {
|
||||
MainLoopPacer out{};
|
||||
out.presentation_dt_seconds = presentation_dt;
|
||||
|
||||
const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation &&
|
||||
const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation.getValue() !=
|
||||
dusk::FrameInterpMode::Off &&
|
||||
!dusk::getTransientSettings().skipFrameRateLimit;
|
||||
out.is_interpolating = should_interpolate;
|
||||
out.sim_pace = sim_pace();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "ImGuiEngine.hpp"
|
||||
#include "JSystem/JUtility/JUTGamePad.h"
|
||||
#include "SDL3/SDL_mouse.h"
|
||||
#include "dusk/action_bindings.h"
|
||||
#include "dusk/audio/DuskAudioSystem.h"
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/data.hpp"
|
||||
@@ -239,7 +240,8 @@ namespace dusk {
|
||||
}
|
||||
|
||||
void ImGuiConsole::UpdateSettings() {
|
||||
getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab);
|
||||
getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind &&
|
||||
(ImGui::IsKeyDown(ImGuiKey_Tab) || getActionBindHoldAnyPort(ActionBinds::TURBO_SPEED_BUTTON));
|
||||
|
||||
if (dusk::frame_interp::get_ui_tick_pending() && mDoMain::developmentMode == 1 && (mDoCPd_c::getHold(PAD_1) & (PAD_TRIGGER_R | PAD_TRIGGER_L)) == (PAD_TRIGGER_R | PAD_TRIGGER_L) && mDoCPd_c::getTrigY(PAD_1)) {
|
||||
getTransientSettings().moveLinkActive = !getTransientSettings().moveLinkActive;
|
||||
@@ -260,6 +262,12 @@ namespace dusk {
|
||||
config::Save();
|
||||
}
|
||||
|
||||
if (getSettings().game.enableResetKeybind && ImGui::GetIO().KeyCtrl &&
|
||||
ImGui::IsKeyReleased(ImGuiKey_R) && !fpcM_SearchByName(fpcNm_LOGO_SCENE_e))
|
||||
{
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
}
|
||||
|
||||
if (ImGui::GetIO().KeyShift && ImGui::IsKeyPressed(ImGuiKey_F1)) {
|
||||
if (getSettings().backend.enableAdvancedSettings) {
|
||||
m_isHidden = !m_isHidden;
|
||||
@@ -314,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");
|
||||
@@ -360,7 +368,6 @@ namespace dusk {
|
||||
m_menuTools.ShowProcessManager();
|
||||
m_menuTools.ShowHeapOverlay();
|
||||
m_menuTools.ShowStubLog();
|
||||
m_menuTools.ShowMapLoader();
|
||||
m_menuTools.ShowBloomWindow();
|
||||
m_menuTools.ShowPlayerInfo();
|
||||
m_menuTools.ShowAudioDebug();
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
#include "imgui.h"
|
||||
#include <imgui_internal.h>
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "dusk/settings.h"
|
||||
|
||||
#include <dolphin/pad.h>
|
||||
|
||||
namespace dusk {
|
||||
void ImGuiMenuTools::ShowInputViewer() {
|
||||
if (!m_showInputViewer) {
|
||||
if (!getSettings().game.showInputViewer) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -259,10 +260,10 @@ namespace dusk {
|
||||
size.y = 130 * scale;
|
||||
ImGui::Dummy(size);
|
||||
|
||||
if (PADHasSensor(PAD_1, PAD_SENSOR_GYRO) == TRUE) {
|
||||
if (getSettings().game.showInputViewerGyro)
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox("Gyro Values", &m_showInputViewerGyro);
|
||||
if (m_showInputViewerGyro) {
|
||||
{
|
||||
ImGui::TextUnformatted("Gyro");
|
||||
|
||||
constexpr float kBarScale = 4.0f;
|
||||
|
||||
@@ -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
|
||||
@@ -40,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();
|
||||
@@ -49,9 +48,6 @@ namespace dusk {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox("Show Input Viewer", &m_showInputViewer);
|
||||
|
||||
#if DUSK_CAN_OPEN_DATA_FOLDER
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Open Data Folder")) {
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace dusk {
|
||||
void ShowProcessManager();
|
||||
void ShowHeapOverlay();
|
||||
void ShowStubLog();
|
||||
void ShowMapLoader();
|
||||
void ShowBloomWindow();
|
||||
void ShowPlayerInfo();
|
||||
void ShowAudioDebug();
|
||||
@@ -43,22 +42,9 @@ namespace dusk {
|
||||
|
||||
bool m_showStubLog = false;
|
||||
|
||||
bool m_showMapLoader = false;
|
||||
|
||||
bool m_showBloomWindow = 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
|
||||
@@ -69,8 +55,6 @@ namespace dusk {
|
||||
bool m_showStateShare = false;
|
||||
ImGuiStateShare m_stateShare;
|
||||
|
||||
bool m_showInputViewer = false;
|
||||
bool m_showInputViewerGyro = false;
|
||||
bool m_showActorSpawner = false;
|
||||
int m_inputOverlayCorner = 3;
|
||||
std::string m_controllerName;
|
||||
|
||||
@@ -713,7 +713,7 @@ namespace dusk {
|
||||
transformLevel++;
|
||||
}
|
||||
}
|
||||
if (ImGui::SliderInt("Transform Level", &transformLevel, 0, 3)) {
|
||||
if (ImGui::SliderInt("Transform Level", &transformLevel, 0, 4)) {
|
||||
u8 newFlags = 0;
|
||||
for (int i = 0; i < transformLevel; i++) {
|
||||
newFlags |= (1 << i);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <unordered_set>
|
||||
#include <zstd.h>
|
||||
#include <dusk/autosave.h>
|
||||
|
||||
namespace dusk {
|
||||
|
||||
@@ -135,6 +136,8 @@ bool ImGuiStateShare::applyEncodedState(const std::string& encoded, const std::s
|
||||
return false;
|
||||
}
|
||||
|
||||
toggleAutoSave(false);
|
||||
|
||||
StateSharePacket pkt;
|
||||
memcpy(&pkt, raw.data(), sizeof(pkt));
|
||||
pkt.stageName[7] = '\0';
|
||||
|
||||
+12
-10
@@ -17,6 +17,8 @@ using namespace dusk::io;
|
||||
#else
|
||||
#define MODE(val) val
|
||||
#endif
|
||||
#define _SH_DENYNO 0
|
||||
#define _SH_DENYWR 0
|
||||
#endif
|
||||
|
||||
static FILE* ThrowIfNotOpen(const FileStream& file) {
|
||||
@@ -31,19 +33,19 @@ static FILE* ThrowIfNotOpen(const FileStream& file) {
|
||||
throw std::system_error(std::make_error_code(static_cast<std::errc>(code)));
|
||||
}
|
||||
|
||||
static FILE* OpenCore(const std::filesystem::path& path, const MODE_TYPE* mode) {
|
||||
static FILE* OpenCore(const std::filesystem::path& path, const MODE_TYPE* mode, int shareFlag) {
|
||||
FILE* file;
|
||||
|
||||
int err;
|
||||
errno = 0;
|
||||
#if _WIN32
|
||||
static_assert(std::is_same_v<std::filesystem::path::value_type, wchar_t>);
|
||||
err = _wfopen_s(&file, path.c_str(), mode);
|
||||
file = _wfsopen(path.c_str(), mode, shareFlag);
|
||||
#else
|
||||
errno = 0;
|
||||
static_assert(std::is_same_v<std::filesystem::path::value_type, char>);
|
||||
file = fopen(path.c_str(), mode);
|
||||
err = errno;
|
||||
#endif
|
||||
err = errno;
|
||||
|
||||
if (!file) {
|
||||
ThrowForError(err);
|
||||
@@ -52,8 +54,8 @@ static FILE* OpenCore(const std::filesystem::path& path, const MODE_TYPE* mode)
|
||||
return file;
|
||||
}
|
||||
|
||||
static FILE* OpenCore(const char* path, const MODE_TYPE* mode) {
|
||||
return OpenCore(reinterpret_cast<const char8_t*>(path), mode);
|
||||
static FILE* OpenCore(const char* path, const MODE_TYPE* mode, int shareFlag) {
|
||||
return OpenCore(reinterpret_cast<const char8_t*>(path), mode, shareFlag);
|
||||
}
|
||||
|
||||
FileStream::FileStream() noexcept : file(nullptr) {
|
||||
@@ -76,19 +78,19 @@ FileStream::~FileStream() {
|
||||
}
|
||||
|
||||
FileStream FileStream::OpenRead(const char* utf8Path) {
|
||||
return FileStream(OpenCore(utf8Path, MODE("rb")));
|
||||
return FileStream(OpenCore(utf8Path, MODE("rb"), _SH_DENYWR));
|
||||
}
|
||||
|
||||
FileStream FileStream::OpenRead(const std::filesystem::path& path) {
|
||||
return FileStream(OpenCore(path, MODE("rb")));
|
||||
return FileStream(OpenCore(path, MODE("rb"), _SH_DENYWR));
|
||||
}
|
||||
|
||||
FileStream FileStream::Create(const char* utf8Path) {
|
||||
return FileStream(OpenCore(utf8Path, MODE("wb")));
|
||||
return FileStream(OpenCore(utf8Path, MODE("wb"), _SH_DENYWR));
|
||||
}
|
||||
|
||||
FileStream FileStream::Create(const std::filesystem::path& path) {
|
||||
return FileStream(OpenCore(path, MODE("wb")));
|
||||
return FileStream(OpenCore(path, MODE("wb"), _SH_DENYWR));
|
||||
}
|
||||
|
||||
std::vector<u8> FileStream::ReadFull() {
|
||||
|
||||
+65
-2
@@ -10,10 +10,11 @@ UserSettings g_userSettings = {
|
||||
.lockAspectRatio {"video.lockAspectRatio", false},
|
||||
.enableFpsOverlay {"game.enableFpsOverlay", false},
|
||||
.fpsOverlayCorner {"game.fpsOverlayCorner", 0},
|
||||
.maxFrameRate {"video.maxFrameRate", 240},
|
||||
},
|
||||
|
||||
.audio = {
|
||||
.masterVolume {"audio.masterVolume", 80},
|
||||
.masterVolume {"audio.masterVolume", 60},
|
||||
.mainMusicVolume {"audio.mainMusicVolume", 100},
|
||||
.subMusicVolume {"audio.subMusicVolume", 100},
|
||||
.soundEffectsVolume {"audio.soundEffectsVolume", 100},
|
||||
@@ -52,14 +53,17 @@ UserSettings g_userSettings = {
|
||||
.enableLinkDollRotation {"game.enableLinkDollRotation", false},
|
||||
.enableAchievementToasts {"game.enableAchievementToasts", true},
|
||||
.enableControllerToasts {"game.enableControllerToasts", true},
|
||||
.enableDiscordPresence {"game.enableDiscordPresence", true},
|
||||
|
||||
// Graphics
|
||||
.bloomMode {"game.bloomMode", BloomMode::Dusk},
|
||||
.bloomMultiplier {"game.bloomMultiplier", 1.0f},
|
||||
.disableWaterRefraction {"game.disableWaterRefraction", false},
|
||||
.enableFrameInterpolation {"game.enableFrameInterpolation", false},
|
||||
.enableTextureReplacements {"game.enableTextureReplacements", true},
|
||||
.enableFrameInterpolation {"game.enableFrameInterpolation", FrameInterpMode::Off},
|
||||
.internalResolutionScale {"game.internalResolutionScale", 0},
|
||||
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
|
||||
.resampler {"game.resampler", Resampler::Bilinear},
|
||||
.enableDepthOfField {"game.enableDepthOfField", true},
|
||||
.enableMapBackground {"game.enableMapBackground", true},
|
||||
.disableCutscenePillarboxing {"game.disableCutscenePillarboxing", false},
|
||||
@@ -92,6 +96,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},
|
||||
@@ -105,12 +110,14 @@ UserSettings g_userSettings = {
|
||||
.fastRoll {"game.fastRoll", false},
|
||||
.fastSpinner {"game.fastSpinner", false},
|
||||
.freeMagicArmor {"game.freeMagicArmor", false},
|
||||
.invincibleEnemies {"game.invincibleEnemies", false},
|
||||
|
||||
// Technical
|
||||
.restoreWiiGlitches {"game.restoreWiiGlitches", false},
|
||||
|
||||
// Controls
|
||||
.enableTurboKeybind {"game.enableTurboKeybind", false},
|
||||
.enableResetKeybind {"game.enableResetKeybind", false},
|
||||
|
||||
// Tools
|
||||
.speedrunMode {"game.speedrunMode", false},
|
||||
@@ -118,6 +125,8 @@ UserSettings g_userSettings = {
|
||||
.showSpeedrunRTATimer {"game.showSpeedrunRTATimer", true},
|
||||
.recordingMode {"game.recordingMode", false},
|
||||
.removeQuestMapMarkers {"game.removeQuestMapMarkers", false},
|
||||
.showInputViewer {"game.showInputViewer", false},
|
||||
.showInputViewerGyro {"game.showInputViewerGyro", false}
|
||||
},
|
||||
|
||||
.backend = {
|
||||
@@ -130,6 +139,34 @@ UserSettings g_userSettings = {
|
||||
.checkForUpdates {"backend.checkForUpdates", true},
|
||||
.cardFileType {"backend.cardFileType", static_cast<int>(CARD_GCIFOLDER)},
|
||||
.enableAdvancedSettings {"backend.enableAdvancedSettings", false},
|
||||
},
|
||||
|
||||
// Not sure if there's a better way to declare this
|
||||
.actionBindings = {
|
||||
.firstPersonCamera {
|
||||
ActionBindConfigVar{"actionBindings.firstPersonCamera_port0", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.firstPersonCamera_port1", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.firstPersonCamera_port2", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.firstPersonCamera_port3", PAD_NATIVE_BUTTON_INVALID},
|
||||
},
|
||||
.callMidna {
|
||||
ActionBindConfigVar{"actionBindings.callMidna_port0", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.callMidna_port1", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.callMidna_port2", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.callMidna_port3", PAD_NATIVE_BUTTON_INVALID},
|
||||
},
|
||||
.openDusklightMenu {
|
||||
ActionBindConfigVar{"actionBindings.openDusklightMenu_port0", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.openDusklightMenu_port1", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.openDusklightMenu_port2", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.openDusklightMenu_port3", PAD_NATIVE_BUTTON_INVALID},
|
||||
},
|
||||
.turboSpeedButton {
|
||||
ActionBindConfigVar{"actionBindings.turboButton_port0", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.turboButton_port1", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.turboButton_port2", PAD_NATIVE_BUTTON_INVALID},
|
||||
ActionBindConfigVar{"actionBindings.turboButton_port3", PAD_NATIVE_BUTTON_INVALID},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,6 +181,7 @@ void registerSettings() {
|
||||
Register(g_userSettings.video.lockAspectRatio);
|
||||
Register(g_userSettings.video.enableFpsOverlay);
|
||||
Register(g_userSettings.video.fpsOverlayCorner);
|
||||
Register(g_userSettings.video.maxFrameRate);
|
||||
|
||||
// Audio
|
||||
Register(g_userSettings.audio.masterVolume);
|
||||
@@ -181,10 +219,13 @@ void registerSettings() {
|
||||
Register(g_userSettings.game.freeCameraSensitivity);
|
||||
Register(g_userSettings.game.minimalHUD);
|
||||
Register(g_userSettings.game.pauseOnFocusLost);
|
||||
Register(g_userSettings.game.enableDiscordPresence);
|
||||
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.resampler);
|
||||
Register(g_userSettings.game.shadowResolutionMultiplier);
|
||||
Register(g_userSettings.game.enableDepthOfField);
|
||||
Register(g_userSettings.game.enableMapBackground);
|
||||
@@ -201,14 +242,18 @@ void registerSettings() {
|
||||
Register(g_userSettings.game.noLowHpSound);
|
||||
Register(g_userSettings.game.midnasLamentNonStop);
|
||||
Register(g_userSettings.game.enableTurboKeybind);
|
||||
Register(g_userSettings.game.enableResetKeybind);
|
||||
Register(g_userSettings.game.speedrunMode);
|
||||
Register(g_userSettings.game.liveSplitEnabled);
|
||||
Register(g_userSettings.game.showSpeedrunRTATimer);
|
||||
Register(g_userSettings.game.recordingMode);
|
||||
Register(g_userSettings.game.removeQuestMapMarkers);
|
||||
Register(g_userSettings.game.showInputViewer);
|
||||
Register(g_userSettings.game.showInputViewerGyro);
|
||||
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);
|
||||
@@ -217,6 +262,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);
|
||||
@@ -242,6 +288,23 @@ void registerSettings() {
|
||||
Register(g_userSettings.backend.checkForUpdates);
|
||||
Register(g_userSettings.backend.cardFileType);
|
||||
Register(g_userSettings.backend.enableAdvancedSettings);
|
||||
|
||||
Register(g_userSettings.actionBindings.firstPersonCamera[0]);
|
||||
Register(g_userSettings.actionBindings.firstPersonCamera[1]);
|
||||
Register(g_userSettings.actionBindings.firstPersonCamera[2]);
|
||||
Register(g_userSettings.actionBindings.firstPersonCamera[3]);
|
||||
Register(g_userSettings.actionBindings.callMidna[0]);
|
||||
Register(g_userSettings.actionBindings.callMidna[1]);
|
||||
Register(g_userSettings.actionBindings.callMidna[2]);
|
||||
Register(g_userSettings.actionBindings.callMidna[3]);
|
||||
Register(g_userSettings.actionBindings.openDusklightMenu[0]);
|
||||
Register(g_userSettings.actionBindings.openDusklightMenu[1]);
|
||||
Register(g_userSettings.actionBindings.openDusklightMenu[2]);
|
||||
Register(g_userSettings.actionBindings.openDusklightMenu[3]);
|
||||
Register(g_userSettings.actionBindings.turboSpeedButton[0]);
|
||||
Register(g_userSettings.actionBindings.turboSpeedButton[1]);
|
||||
Register(g_userSettings.actionBindings.turboSpeedButton[2]);
|
||||
Register(g_userSettings.actionBindings.turboSpeedButton[3]);
|
||||
}
|
||||
|
||||
// Transient settings
|
||||
|
||||
@@ -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);
|
||||
|
||||
+247
-111
@@ -15,6 +15,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "dusk/action_bindings.h"
|
||||
#include "dusk/config.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
@@ -34,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;
|
||||
}
|
||||
@@ -108,68 +111,6 @@ const std::vector<ButtonNames> kGamepadButtonNames = {
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
Rml::String native_button_name(SDL_Gamepad* gamepad, u32 buttonUntyped) {
|
||||
if (buttonUntyped == PAD_NATIVE_BUTTON_INVALID) {
|
||||
return "Not bound";
|
||||
}
|
||||
|
||||
auto button = static_cast<SDL_GamepadButton>(buttonUntyped);
|
||||
if (gamepad != nullptr) {
|
||||
switch (SDL_GetGamepadButtonLabel(gamepad, button)) {
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_A:
|
||||
return "A";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_B:
|
||||
return "B";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_X:
|
||||
return "X";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_Y:
|
||||
return "Y";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
|
||||
return "Cross";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
|
||||
return "Circle";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
|
||||
return "Triangle";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
|
||||
return "Square";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const SDL_GamepadType type =
|
||||
gamepad != nullptr ? SDL_GetGamepadType(gamepad) : SDL_GAMEPAD_TYPE_UNKNOWN;
|
||||
for (const auto& buttonNames : kGamepadButtonNames) {
|
||||
if (buttonNames.button != button) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& name : buttonNames.names) {
|
||||
if (name.type == type) {
|
||||
return name.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (button) {
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
return "D-pad left";
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
return "D-pad right";
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
return "D-pad up";
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
return "D-pad down";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (const char* name = PADGetNativeButtonName(buttonUntyped)) {
|
||||
return name;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
Rml::String native_axis_name(const PADAxisMapping& mapping, SDL_Gamepad* gamepad) {
|
||||
if (mapping.nativeAxis.nativeAxis != -1) {
|
||||
Rml::String value = PADGetNativeAxisName(mapping.nativeAxis);
|
||||
@@ -183,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) {
|
||||
@@ -221,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:
|
||||
@@ -362,11 +303,12 @@ 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; });
|
||||
addPageButton(Page::Rumble, "Rumble", [] { return Rml::String(">"); }, [port] { return !PADSupportsRumbleIntensity(static_cast<u32>(port)); });
|
||||
addPageButton(Page::Actions, "Custom Action Bindings", [] {return Rml::String(">"); }, [] { return false; });
|
||||
|
||||
leftPane.add_section("Options");
|
||||
leftPane.register_control(leftPane.add_child<BoolButton>(BoolButton::Props{
|
||||
@@ -407,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);
|
||||
}
|
||||
|
||||
@@ -423,11 +372,12 @@ 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);
|
||||
PADSerializeMappings();
|
||||
ClearAllActionBindings(port);
|
||||
});
|
||||
|
||||
pane.add_button({
|
||||
@@ -435,16 +385,17 @@ 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);
|
||||
PADSerializeMappings();
|
||||
ClearAllActionBindings(port);
|
||||
});
|
||||
|
||||
const u32 controllerCount = PADCount();
|
||||
if (controllerCount == 0) {
|
||||
pane.add_text("No controllers detected");
|
||||
pane.add_text("No Device Detected");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -456,11 +407,12 @@ 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);
|
||||
PADSerializeMappings();
|
||||
ClearAllActionBindings(port);
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -480,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;
|
||||
@@ -517,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;
|
||||
}
|
||||
|
||||
@@ -541,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;
|
||||
@@ -567,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;
|
||||
@@ -590,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;
|
||||
@@ -621,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;
|
||||
@@ -654,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;
|
||||
}
|
||||
|
||||
@@ -678,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;
|
||||
@@ -686,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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,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;
|
||||
@@ -796,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;
|
||||
}
|
||||
|
||||
@@ -817,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;
|
||||
@@ -946,6 +909,79 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
||||
pane.add_text("Configure your desired rumble intensities, then run a test to check how they feel.");
|
||||
break;
|
||||
}
|
||||
case Page::Actions: {
|
||||
if (keyboard_active(port)) {
|
||||
auto addActionBinding = [&](auto actionBind, const std::string& key) {
|
||||
pane.add_select_button(
|
||||
{
|
||||
.key = key,
|
||||
.getValue =
|
||||
[this, actionBind] {
|
||||
if (mPendingActionBinding == actionBind) {
|
||||
return pending_key_label();
|
||||
}
|
||||
|
||||
return keyboard_key_name(actionBind->getValue());
|
||||
},
|
||||
})
|
||||
.on_pressed([this, port, actionBind] {
|
||||
mDoAud_seStartMenu(kSoundClick);
|
||||
cancel_pending_binding();
|
||||
mPendingPort = port;
|
||||
mPendingBindingArmed = false;
|
||||
mPendingActionBinding = actionBind;
|
||||
});
|
||||
};
|
||||
|
||||
pane.add_section("Custom Action Bindings");
|
||||
pane.add_text("A key bound to any action here will REPLACE the default control for"
|
||||
" that action. Only bind buttons here that aren't used anywhere else.");
|
||||
for (auto& [configVars, actionName] : getActionBinds() | std::views::values) {
|
||||
addActionBinding(&configVars->at(port), actionName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
u32 buttonCount = 0;
|
||||
PADButtonMapping* mappings = PADGetButtonMappings(port, &buttonCount);
|
||||
if (mappings == nullptr) {
|
||||
pane.add_text("No Device Selected");
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_Gamepad* gamepad = gamepad_for_port(port);
|
||||
pane.add_section("Custom Action Bindings");
|
||||
pane.add_text("A button bound to any action here will REPLACE the default control for"
|
||||
" that action. Only bind buttons here that aren't used anywhere else. The glyphs"
|
||||
" shown for in game actions will not change. This is not recommended for "
|
||||
" regular Gamecube controllers.");
|
||||
auto addActionBinding = [&](auto actionBind, const std::string& key) {
|
||||
pane.add_select_button({
|
||||
.key = key,
|
||||
.getValue =
|
||||
[this, gamepad, actionBind] {
|
||||
if (mPendingActionBinding == actionBind) {
|
||||
return pending_button_label();
|
||||
}
|
||||
|
||||
return native_button_name(
|
||||
gamepad, actionBind->getValue());
|
||||
},
|
||||
})
|
||||
.on_pressed([this, port, actionBind] {
|
||||
mDoAud_seStartMenu(kSoundClick);
|
||||
cancel_pending_binding();
|
||||
mPendingPort = port;
|
||||
mPendingBindingArmed = false;
|
||||
mPendingActionBinding = actionBind;
|
||||
});
|
||||
};
|
||||
|
||||
for (auto& [configVars, actionName] : getActionBinds() | std::views::values) {
|
||||
addActionBinding(&configVars->at(port), actionName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -997,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);
|
||||
}
|
||||
@@ -1007,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);
|
||||
@@ -1020,12 +1066,36 @@ void ControllerConfigWindow::poll_pending_binding() {
|
||||
mPendingAxisMapping->nativeButton = nativeButton;
|
||||
finish_pending_binding(completedPort);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPendingActionBinding != nullptr) {
|
||||
int button{};
|
||||
if (keyboard_active(mPendingPort)) {
|
||||
button = keyboard_key_pressed();
|
||||
} else {
|
||||
button = PADGetNativeButtonPressed(mPendingPort);
|
||||
}
|
||||
|
||||
if (button != -1) {
|
||||
const int completedPort = mPendingPort;
|
||||
if (mPendingActionBinding->getValue() == button) {
|
||||
unmap_pending_binding();
|
||||
return;
|
||||
}
|
||||
mPendingActionBinding->setValue(button);
|
||||
config::Save();
|
||||
finish_pending_binding(completedPort);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerConfigWindow::finish_pending_binding(int completedPort) {
|
||||
mDoAud_seStartMenu(kSoundBindingChanged);
|
||||
mPendingButtonMapping = nullptr;
|
||||
mPendingAxisMapping = nullptr;
|
||||
mPendingActionBinding = nullptr;
|
||||
mPendingPort = -1;
|
||||
mPendingBindingArmed = false;
|
||||
mSuppressNavigationUntilNeutral = true;
|
||||
@@ -1035,7 +1105,7 @@ void ControllerConfigWindow::finish_pending_binding(int completedPort) {
|
||||
|
||||
void ControllerConfigWindow::unmap_pending_binding() {
|
||||
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr &&
|
||||
mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
||||
mPendingActionBinding == nullptr && mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1048,6 +1118,9 @@ void ControllerConfigWindow::unmap_pending_binding() {
|
||||
mPendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE};
|
||||
mPendingAxisMapping->nativeButton = -1;
|
||||
finish_pending_binding(completedPort);
|
||||
} else if (mPendingActionBinding != nullptr) {
|
||||
mPendingActionBinding->setValue(PAD_NATIVE_BUTTON_INVALID);
|
||||
finish_pending_binding(completedPort);
|
||||
} else if (mPendingKeyButton >= 0) {
|
||||
PADSetKeyButtonBinding(static_cast<u32>(completedPort),
|
||||
{PAD_KEY_INVALID, static_cast<PADButton>(mPendingKeyButton)});
|
||||
@@ -1061,7 +1134,7 @@ void ControllerConfigWindow::unmap_pending_binding() {
|
||||
|
||||
bool ControllerConfigWindow::capture_active() const {
|
||||
return mPendingButtonMapping != nullptr || mPendingAxisMapping != nullptr ||
|
||||
mPendingKeyButton >= 0 || mPendingKeyAxis >= 0;
|
||||
mPendingActionBinding != nullptr || mPendingKeyButton >= 0 || mPendingKeyAxis >= 0;
|
||||
}
|
||||
|
||||
bool ControllerConfigWindow::pending_input_neutral() const {
|
||||
@@ -1072,21 +1145,22 @@ 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() {
|
||||
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr &&
|
||||
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr && mPendingActionBinding == nullptr &&
|
||||
!mSuppressNavigationUntilNeutral && mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mPendingButtonMapping = nullptr;
|
||||
mPendingAxisMapping = nullptr;
|
||||
mPendingActionBinding = nullptr;
|
||||
mPendingKeyButton = -1;
|
||||
mPendingKeyAxis = -1;
|
||||
mPendingPort = -1;
|
||||
@@ -1104,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() {
|
||||
@@ -1118,4 +1192,66 @@ void ControllerConfigWindow::stop_rumble_test() {
|
||||
mRumbleTestPort = -1;
|
||||
}
|
||||
|
||||
Rml::String native_button_name(SDL_Gamepad* gamepad, u32 buttonUntyped) {
|
||||
if (buttonUntyped == PAD_NATIVE_BUTTON_INVALID) {
|
||||
return "Not Bound";
|
||||
}
|
||||
|
||||
auto button = static_cast<SDL_GamepadButton>(buttonUntyped);
|
||||
if (gamepad != nullptr) {
|
||||
switch (SDL_GetGamepadButtonLabel(gamepad, button)) {
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_A:
|
||||
return "A";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_B:
|
||||
return "B";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_X:
|
||||
return "X";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_Y:
|
||||
return "Y";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
|
||||
return "Cross";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
|
||||
return "Circle";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
|
||||
return "Triangle";
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
|
||||
return "Square";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const SDL_GamepadType type =
|
||||
gamepad != nullptr ? SDL_GetGamepadType(gamepad) : SDL_GAMEPAD_TYPE_UNKNOWN;
|
||||
for (const auto& buttonNames : kGamepadButtonNames) {
|
||||
if (buttonNames.button != button) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& name : buttonNames.names) {
|
||||
if (name.type == type) {
|
||||
return name.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (button) {
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
return "D-pad left";
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
return "D-pad right";
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
return "D-pad up";
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
return "D-pad down";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (const char* name = PADGetNativeButtonName(buttonUntyped)) {
|
||||
return name;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "window.hpp"
|
||||
#include "dusk/config_var.hpp"
|
||||
|
||||
#include <pad.h>
|
||||
|
||||
@@ -20,6 +21,7 @@ private:
|
||||
Triggers,
|
||||
Sticks,
|
||||
Rumble,
|
||||
Actions,
|
||||
};
|
||||
|
||||
void build_port_tab(Rml::Element* content, int port);
|
||||
@@ -50,6 +52,9 @@ private:
|
||||
int mPendingKeyAxis = -1;
|
||||
bool mRumbleTestActive = false;
|
||||
int mRumbleTestPort = -1;
|
||||
ActionBindConfigVar* mPendingActionBinding = nullptr;
|
||||
};
|
||||
|
||||
Rml::String native_button_name(SDL_Gamepad* gamepad, u32 buttonUntyped);
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
+39
-40
@@ -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 "";
|
||||
@@ -1502,14 +1501,14 @@ EditorWindow::EditorWindow() {
|
||||
.getValue =
|
||||
[] {
|
||||
return std::popcount(static_cast<unsigned>(
|
||||
get_player_status_b()->mTransformLevelFlag & 0x7));
|
||||
get_player_status_b()->mTransformLevelFlag & 0xF));
|
||||
},
|
||||
.setValue =
|
||||
[](int value) {
|
||||
get_player_status_b()->mTransformLevelFlag =
|
||||
static_cast<u8>((1u << value) - 1u);
|
||||
},
|
||||
.max = 3,
|
||||
.max = 4,
|
||||
}),
|
||||
rightPane, {});
|
||||
leftPane.register_control(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
#include <aurora/aurora.h>
|
||||
#include <dolphin/gx/GXAurora.h>
|
||||
#include <dolphin/vi.h>
|
||||
#include <fmt/format.h>
|
||||
@@ -43,6 +44,8 @@ int get_value(GraphicsOption option) {
|
||||
return getSettings().game.internalResolutionScale.getValue();
|
||||
case GraphicsOption::ShadowResolution:
|
||||
return getSettings().game.shadowResolutionMultiplier.getValue();
|
||||
case GraphicsOption::Resampler:
|
||||
return static_cast<int>(getSettings().game.resampler.getValue());
|
||||
case GraphicsOption::BloomMode:
|
||||
return static_cast<int>(getSettings().game.bloomMode.getValue());
|
||||
case GraphicsOption::BloomMultiplier:
|
||||
@@ -62,6 +65,22 @@ void set_value(GraphicsOption option, int value) {
|
||||
case GraphicsOption::ShadowResolution:
|
||||
getSettings().game.shadowResolutionMultiplier.setValue(value);
|
||||
break;
|
||||
case GraphicsOption::Resampler: {
|
||||
const auto sampler = static_cast<Resampler>(std::clamp(value,
|
||||
static_cast<int>(Resampler::Bilinear),
|
||||
static_cast<int>(Resampler::Area)));
|
||||
getSettings().game.resampler.setValue(sampler);
|
||||
switch (sampler) {
|
||||
case Resampler::Area:
|
||||
aurora_set_resampler(SAMPLER_AREA);
|
||||
break;
|
||||
case Resampler::Bilinear:
|
||||
default:
|
||||
aurora_set_resampler(SAMPLER_BILINEAR);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GraphicsOption::BloomMode:
|
||||
getSettings().game.bloomMode.setValue(static_cast<BloomMode>(std::clamp(
|
||||
value, static_cast<int>(BloomMode::Off), static_cast<int>(BloomMode::Dusk))));
|
||||
@@ -177,6 +196,14 @@ Rml::String format_graphics_setting_value(GraphicsOption option, int value) {
|
||||
}
|
||||
case GraphicsOption::ShadowResolution:
|
||||
return fmt::format("{}×", value);
|
||||
case GraphicsOption::Resampler:
|
||||
switch (static_cast<Resampler>(value)) {
|
||||
case Resampler::Bilinear:
|
||||
return "Bilinear";
|
||||
case Resampler::Area:
|
||||
return "Area";
|
||||
}
|
||||
break;
|
||||
case GraphicsOption::BloomMode:
|
||||
switch (static_cast<BloomMode>(value)) {
|
||||
case BloomMode::Off:
|
||||
@@ -184,7 +211,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 +238,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 =
|
||||
|
||||
@@ -42,6 +42,7 @@ private:
|
||||
enum class GraphicsOption {
|
||||
InternalResolution,
|
||||
ShadowResolution,
|
||||
Resampler,
|
||||
BloomMode,
|
||||
BloomMultiplier,
|
||||
};
|
||||
@@ -55,6 +56,7 @@ struct GraphicsTunerProps {
|
||||
int valueMin = 0;
|
||||
int valueMax = 0;
|
||||
int defaultValue = 0;
|
||||
int step = 1;
|
||||
};
|
||||
|
||||
class GraphicsTuner : public Document {
|
||||
|
||||
+41
-16
@@ -12,6 +12,8 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "dusk/action_bindings.h"
|
||||
|
||||
namespace dusk::ui::input {
|
||||
namespace {
|
||||
|
||||
@@ -203,6 +205,9 @@ Rml::Input::KeyIdentifier map_raw_gamepad_button(SDL_GamepadButton button) noexc
|
||||
case SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
return Rml::Input::KI_RETURN;
|
||||
case SDL_GAMEPAD_BUTTON_BACK:
|
||||
if (isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0)) {
|
||||
return Rml::Input::KI_UNKNOWN;
|
||||
}
|
||||
return Rml::Input::KI_F1;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
return Rml::Input::KI_NEXT;
|
||||
@@ -216,6 +221,9 @@ Rml::Input::KeyIdentifier map_raw_gamepad_button(SDL_GamepadButton button) noexc
|
||||
Rml::Input::KeyIdentifier map_raw_button_alias(SDL_GamepadButton button) noexcept {
|
||||
switch (button) {
|
||||
case SDL_GAMEPAD_BUTTON_BACK:
|
||||
if (isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0)) {
|
||||
return Rml::Input::KI_UNKNOWN;
|
||||
}
|
||||
return Rml::Input::KI_F1;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
return Rml::Input::KI_NEXT;
|
||||
@@ -318,12 +326,20 @@ bool find_event_pad_button(
|
||||
|
||||
Rml::Input::KeyIdentifier map_gamepad_button(const SDL_GamepadButtonEvent& event) noexcept {
|
||||
const auto nativeButton = static_cast<SDL_GamepadButton>(event.button);
|
||||
if (nativeButton == SDL_GAMEPAD_BUTTON_BACK) {
|
||||
u32 port = 0;
|
||||
bool foundEventPort = find_event_port(event.which, port);
|
||||
if (foundEventPort) {
|
||||
int openMenuButton = getActionBindButton(ActionBinds::OPEN_DUSKLIGHT_MENU, port);
|
||||
if (openMenuButton != PAD_NATIVE_BUTTON_INVALID && openMenuButton == nativeButton) {
|
||||
return Rml::Input::KI_F1;
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeButton == SDL_GAMEPAD_BUTTON_BACK && !isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, port)) {
|
||||
return Rml::Input::KI_F1;
|
||||
}
|
||||
|
||||
u32 port = 0;
|
||||
if (!find_event_port(event.which, port)) {
|
||||
if (!foundEventPort) {
|
||||
return map_raw_gamepad_button(nativeButton);
|
||||
}
|
||||
|
||||
@@ -438,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 {
|
||||
@@ -611,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);
|
||||
@@ -631,7 +657,7 @@ void process_axis_direction(
|
||||
if (chorded) {
|
||||
consume_menu_chord(port, context);
|
||||
}
|
||||
const auto key = chorded ? Rml::Input::KI_F1 : map_gamepad_axis(event, sign);
|
||||
const auto key = chorded && !isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, port) ? Rml::Input::KI_F1 : map_gamepad_axis(event, sign);
|
||||
if (key == Rml::Input::KI_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
@@ -642,8 +668,7 @@ void process_axis_direction(
|
||||
}
|
||||
|
||||
begin_gamepad_key(*repeat, key);
|
||||
context.ProcessMouseLeave();
|
||||
context.ProcessKeyDown(key, 0);
|
||||
emit_key_press(context, key);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -719,7 +744,7 @@ void handle_event(const SDL_Event& event) noexcept {
|
||||
if (chorded) {
|
||||
consume_menu_chord(port, *context);
|
||||
}
|
||||
const auto key = chorded ? Rml::Input::KI_F1 : map_gamepad_button(event.gbutton);
|
||||
const auto key = chorded && !isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, port) ? Rml::Input::KI_F1 : map_gamepad_button(event.gbutton);
|
||||
if (key != Rml::Input::KI_UNKNOWN) {
|
||||
bool deferred = false;
|
||||
if (repeat != nullptr) {
|
||||
@@ -731,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 {
|
||||
@@ -744,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);
|
||||
}
|
||||
}
|
||||
@@ -771,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>()); });
|
||||
}
|
||||
|
||||
|
||||
+18
-5
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "aurora/lib/logging.hpp"
|
||||
#include "dusk/achievements.h"
|
||||
#include "dusk/action_bindings.h"
|
||||
#include "controller_config.hpp"
|
||||
#include "dusk/livesplit.h"
|
||||
#include "dusk/speedrun.h"
|
||||
#include "fmt/format.h"
|
||||
@@ -101,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;
|
||||
}
|
||||
@@ -145,19 +147,29 @@ 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) {
|
||||
auto* elem = append(parent, "toast");
|
||||
elem->SetClass("menu-notification", true);
|
||||
|
||||
// Get name of button for action binding if the action is bound
|
||||
Rml::String padButton{};
|
||||
SDL_Gamepad* gamepad = gamepad_for_port(PAD_CHAN0);
|
||||
if (isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0) && gamepad != nullptr) {
|
||||
padButton = native_button_name(gamepad,
|
||||
getActionBindButton(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0));
|
||||
} else {
|
||||
padButton = back_button_name();
|
||||
}
|
||||
|
||||
auto* message = append(elem, "message");
|
||||
auto* row = append(message, "row");
|
||||
append(row, "span")->SetInnerRML(kMenuNotificationPrefix);
|
||||
auto* icon = append(row, "icon");
|
||||
icon->SetClass("controller", true);
|
||||
append(row, "span")->SetInnerRML(escape(back_button_name()));
|
||||
append(row, "span")->SetInnerRML("<b>" + escape(padButton) + "</b>");
|
||||
append(row, "span")->SetInnerRML("to open menu");
|
||||
|
||||
return elem;
|
||||
@@ -342,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) {
|
||||
|
||||
@@ -40,7 +40,7 @@ void applyPresetDusk() {
|
||||
s.game.enableQuickTransform.setValue(true);
|
||||
s.game.instantSaves.setValue(true);
|
||||
s.game.midnasLamentNonStop.setValue(true);
|
||||
s.game.enableFrameInterpolation.setValue(true);
|
||||
s.game.enableFrameInterpolation.setValue(FrameInterpMode::Unlimited);
|
||||
s.game.sunsSong.setValue(true);
|
||||
s.game.bloomMode.setValue(BloomMode::Dusk);
|
||||
s.game.internalResolutionScale.setValue(0);
|
||||
@@ -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},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user