Merge remote-tracking branch 'origin/main' into randomizer

# Conflicts:
#	src/f_op/f_op_actor_mng.cpp
This commit is contained in:
CraftyBoss
2026-05-24 03:40:08 -07:00
25 changed files with 167 additions and 81 deletions
+16 -12
View File
@@ -7,6 +7,10 @@ on:
- '*LICENSE'
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('run-{0}', github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
@@ -22,13 +26,13 @@ jobs:
matrix:
include:
- name: GCC x86_64
runner: ubuntu-latest
runner: ubuntu-24.04
preset: gcc
artifact_arch: x86_64
# - name: GCC aarch64
# runner: ubuntu-24.04-arm
# preset: gcc
# artifact_arch: aarch64
- name: GCC aarch64
runner: ubuntu-24.04-arm
preset: gcc
artifact_arch: aarch64
# - name: Clang x86_64
# runner: ubuntu-latest
# preset: clang
@@ -221,12 +225,12 @@ jobs:
msvc_arch: amd64
vcpkg_arch: x64
artifact_arch: x86_64
# - name: MSVC arm64
# runner: windows-11-arm
# preset: arm64-msvc
# msvc_arch: arm64
# vcpkg_arch: arm64
# artifact_arch: arm64
- name: MSVC arm64
runner: windows-latest
preset: arm64-msvc
msvc_arch: amd64_arm64
vcpkg_arch: arm64
artifact_arch: arm64
# - name: Clang x86_64
# runner: windows-latest
# preset: clang
@@ -255,7 +259,7 @@ jobs:
- name: Install dependencies
run: |
choco install ninja
vcpkg install freetype:${{matrix.vcpkg_arch}}-windows-static zstd:${{matrix.vcpkg_arch}}-windows-static
vcpkg install freetype:${{matrix.vcpkg_arch}}-windows zstd:${{matrix.vcpkg_arch}}-windows
- name: Configure CMake
run: cmake --preset x-windows-ci-${{matrix.preset}}
+3
View File
@@ -100,6 +100,8 @@ project(dusklight LANGUAGES C CXX VERSION ${DUSK_VERSION_STRING})
if (APPLE)
enable_language(OBJC OBJCXX)
endif ()
# Adjust CMAKE_SYSTEM_PROCESSOR on Windows to match compiler target
include(cmake/WindowsTargetProcessor.cmake)
if (APPLE AND NOT TVOS AND CMAKE_SYSTEM_NAME STREQUAL tvOS)
# ios.toolchain.cmake hack for SDL
set(TVOS ON)
@@ -168,6 +170,7 @@ if (DUSK_MOVIE_SUPPORT)
endif ()
set(_jpeg_cmake_args
-DCMAKE_INSTALL_PREFIX=${_jpeg_install_dir}
-DCMAKE_PROJECT_INCLUDE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/WindowsTargetProcessor.cmake
-DENABLE_SHARED=OFF
-DWITH_TURBOJPEG=ON
-DWITH_JAVA=OFF
+9 -3
View File
@@ -33,7 +33,11 @@
"value": true
},
"DUSK_SENTRY_DSN": "$env{SENTRY_DSN}",
"DUSK_SENTRY_ENVIRONMENT": "production"
"DUSK_SENTRY_ENVIRONMENT": "production",
"Rust_RUSTUP_INSTALL_MISSING_TARGET": {
"type": "BOOL",
"value": true
}
}
},
{
@@ -430,7 +434,6 @@
"x-macos-ci"
],
"cacheVariables": {
"AURORA_DAWN_PROVIDER": "vendor",
"CMAKE_OSX_ARCHITECTURES": "x86_64",
"Rust_CARGO_TARGET": "x86_64-apple-darwin"
}
@@ -490,7 +493,10 @@
"inherits": [
"x-windows-ci",
"windows-arm64-msvc"
]
],
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "arm64-windows"
}
}
],
"buildPresets": [
+3 -2
View File
@@ -6,6 +6,7 @@ fi
build_dir="$PWD/build"
linuxdeploy="$build_dir/linuxdeploy-$(uname -m).AppImage"
lib_dir="/usr/lib/$(uname -m)-linux-gnu"
# Get linuxdeploy
mkdir -p "$build_dir"
@@ -19,8 +20,8 @@ for install_path in build/install/*; do
cp -r "$install_path" build/appdir/usr/bin
done
cp -r platforms/freedesktop/{16x16,32x32,48x48,64x64,128x128,256x256,512x512,1024x1024} build/appdir/usr/share/icons/hicolor
cp platforms/freedesktop/dusklight.desktop build/appdir/usr/share/applications
cp platforms/freedesktop/dev.twilitrealm.dusk.desktop build/appdir/usr/share/applications
cd build/install
VERSION="$DUSK_VERSION" NO_STRIP=1 "$linuxdeploy" \
-l /usr/lib/x86_64-linux-gnu/libusb-1.0.so --appdir "$build_dir/appdir" --output appimage
-l "$lib_dir/libusb-1.0.so" --appdir "$build_dir/appdir" --output appimage
+9
View File
@@ -0,0 +1,9 @@
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
if (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64" OR CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64EC")
set(CMAKE_SYSTEM_PROCESSOR "ARM64")
elseif (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "x64")
set(CMAKE_SYSTEM_PROCESSOR "AMD64")
elseif (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "X86")
set(CMAKE_SYSTEM_PROCESSOR "X86")
endif ()
endif ()
+1 -1
+4 -4
View File
@@ -248,11 +248,11 @@
runHook preInstall
install -Dm755 dusklight "$out/bin/dusklight"
cp -r "$src/res" "$out/bin/res"
install -Dm644 "$src/platforms/freedesktop/dusklight.desktop" \
"$out/share/applications/dusklight.desktop"
install -Dm644 "$src/platforms/freedesktop/dev.twilitrealm.dusk.desktop" \
"$out/share/applications/dev.twilitrealm.dusk.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"
install -Dm644 "$src/platforms/freedesktop/''${size}x''${size}/apps/dev.twilitrealm.dusk.png" \
"$out/share/icons/hicolor/''${size}x''${size}/apps/dev.twilitrealm.dusk.png"
done
runHook postInstall
'';
@@ -1575,6 +1575,12 @@ struct J3DAlphaComp {
u8 getRef1() const { return mRef1; }
void load() const {
#ifdef AVOID_UB
if (mID > 255) {
J3DGDSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0);
return;
}
#endif
J3DGDSetAlphaCompare((GXCompare)getComp0(), mRef0, (GXAlphaOp)getOp(), (GXCompare)getComp1(), mRef1);
}
+14 -1
View File
@@ -13,20 +13,33 @@ inline denormal_state denormals_enable()
}
inline void denormals_restore(denormal_state saved) { _mm_setcsr(saved); }
#elif defined(__aarch64__) || defined(_M_ARM64)
#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
#include <cstdint>
#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC))
#include <intrin.h>
#endif
using denormal_state = uint64_t;
inline denormal_state denormals_enable()
{
#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC))
denormal_state saved = static_cast<denormal_state>(_ReadStatusReg(ARM64_FPCR));
_WriteStatusReg(ARM64_FPCR, static_cast<__int64>(saved | (1ULL << 24))); // FZ
return saved;
#else
denormal_state saved;
asm volatile("mrs %0, fpcr" : "=r"(saved));
asm volatile("msr fpcr, %0" :: "r"(saved | (1ULL << 24))); // FZ
return saved;
#endif
}
inline void denormals_restore(denormal_state saved)
{
#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC))
_WriteStatusReg(ARM64_FPCR, static_cast<__int64>(saved));
#else
asm volatile("msr fpcr, %0" :: "r"(saved));
#endif
}
#elif defined(__arm__) || defined(_M_ARM)
@@ -11,6 +11,7 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
@@ -12,6 +12,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.Window;
@@ -27,10 +28,12 @@ import java.util.List;
public class DuskActivity extends SDLActivity {
private static final String TAG = "DuskActivity";
private static final int FOLDER_DIALOG_REQUEST_CODE = 0x4455;
private static final int MANAGE_STORAGE_REQUEST_CODE = 0x4456;
private static final String EXTERNAL_STORAGE_AUTHORITY =
"com.android.externalstorage.documents";
private long folderDialogUserdata = 0;
private boolean awaitingManageStoragePermission = false;
private static native void nativeFolderDialogResult(long userdata, String path, String error);
@@ -89,6 +92,9 @@ public class DuskActivity extends SDLActivity {
protected void onResume() {
super.onResume();
hideSystemBars();
if (awaitingManageStoragePermission) {
resumeFolderDialogAfterPermissionGrant();
}
}
@Override
@@ -171,6 +177,19 @@ public class DuskActivity extends SDLActivity {
}
folderDialogUserdata = userdata;
if (requiresManageStoragePermission() && !hasManageStoragePermission()) {
if (!requestManageStoragePermission()) {
finishFolderDialogWithError("Unable to request Android file access permission");
return false;
}
return true;
}
openFolderDialog();
return true;
}
private void openFolderDialog() {
runOnUiThread(() -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
@@ -185,9 +204,71 @@ public class DuskActivity extends SDLActivity {
finishFolderDialog(Activity.RESULT_CANCELED, null);
}
});
}
private boolean requiresManageStoragePermission() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
}
private boolean hasManageStoragePermission() {
return !requiresManageStoragePermission() || Environment.isExternalStorageManager();
}
private boolean requestManageStoragePermission() {
if (!requiresManageStoragePermission()) {
return true;
}
awaitingManageStoragePermission = true;
runOnUiThread(() -> {
if (tryStartManageStorageIntent(
new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
.setData(Uri.parse("package:" + getPackageName()))) ||
tryStartManageStorageIntent(
new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)))
{
return;
}
finishFolderDialogWithError("Unable to request Android file access permission");
});
return true;
}
private boolean tryStartManageStorageIntent(Intent intent) {
try {
startActivityForResult(intent, MANAGE_STORAGE_REQUEST_CODE);
return true;
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Unable to open all-files access settings.", e);
return false;
}
}
private void resumeFolderDialogAfterPermissionGrant() {
awaitingManageStoragePermission = false;
if (folderDialogUserdata == 0) {
return;
}
if (hasManageStoragePermission()) {
openFolderDialog();
return;
}
finishFolderDialogWithError(
"Allow \"All files access\" for Dusklight before choosing a custom data folder");
}
private void finishFolderDialogWithError(String error) {
long userdata = folderDialogUserdata;
folderDialogUserdata = 0;
awaitingManageStoragePermission = false;
if (userdata != 0) {
nativeFolderDialogResult(userdata, null, error);
}
}
private void finishFolderDialog(int resultCode, Intent data) {
long userdata = folderDialogUserdata;
folderDialogUserdata = 0;

Before

Width:  |  Height:  |  Size: 928 KiB

After

Width:  |  Height:  |  Size: 928 KiB

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before

Width:  |  Height:  |  Size: 1014 B

After

Width:  |  Height:  |  Size: 1014 B

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 279 KiB

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

@@ -3,7 +3,7 @@ Name=Dusklight
GenericName=Dusklight
Comment=PC port of a classic adventure game
Exec=dusklight
Icon=dusklight
Icon=dev.twilitrealm.dusk
Terminal=false
Type=Application
Categories=Game;
+6
View File
@@ -4005,7 +4005,13 @@ int daAlink_c::procWolfHowlDemoInit() {
} else if (name == fpcNm_Tag_WaraHowl_e) {
mZ2WolfHowlMgr.setCorrectCurve(static_cast<daTagWrHowl_c*>(field_0x27f4)->getTuneId());
} else {
#if TARGET_PC
if (mZ2WolfHowlMgr.getCorrectCurveID() != 9) {
mZ2WolfHowlMgr.setCorrectCurve(-1);
}
#else
mZ2WolfHowlMgr.setCorrectCurve(-1);
#endif
}
} else {
#if TARGET_PC
+5
View File
@@ -324,6 +324,11 @@ int daItem_c::_daItem_create() {
m_itemNo = daItem_prm::getItemNo(this);
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(m_itemNo)) {
return cPhs_ERROR_e;
}
#endif
BOOL flag = dItem_data::chkFlag(m_itemNo, 2);
#if DEBUG
+7
View File
@@ -525,7 +525,14 @@ bool validate_writable_data_path(const std::filesystem::path& path, std::string*
try {
io::FileStream::WriteAllText(probePath, "dusk");
} catch (const std::exception& e) {
#if defined(__ANDROID__)
set_error(errorOut,
fmt::format("{} could not write to the selected folder. On Android, allow "
"\"All files access\" for Dusklight and try again.",
AppName));
#else
set_error(errorOut, fmt::format("{} could not write to the selected folder.", AppName));
#endif
Log.warn("Failed write probe for custom data folder '{}': {}", io::fs_path_to_string(path),
e.what());
return false;
+1 -1
View File
@@ -15,7 +15,7 @@
#define DUSK_CAN_OPEN_DATA_FOLDER 0
#endif
#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || defined(__ANDROID__)
#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST)
#define DUSK_CAN_CHANGE_DATA_FOLDER 0
#else
#define DUSK_CAN_CHANGE_DATA_FOLDER 1
-56
View File
@@ -1402,12 +1402,6 @@ fpc_ProcID fopAcM_createItemForPresentDemo(cXyz const* i_pos, int i_itemNo, u8 p
JUT_ASSERT(3214, 0 <= i_itemNo && i_itemNo < 256);
dComIfGp_event_setGtItm(i_itemNo);
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return fpcM_ERROR_PROCESS_ID_e;
}
#endif
if (i_itemNo == dItemNo_NONE_e) {
OS_REPORT("プレゼントデモ用なのに「ハズレ」です![%d]\n", i_itemNo); // Even though it is for a Present Demo, it is a 'Miss'!
return fpcM_ERROR_PROCESS_ID_e;
@@ -1426,12 +1420,6 @@ fpc_ProcID fopAcM_createItemForTrBoxDemo(cXyz const* i_pos, int i_itemNo, int i_
JUT_ASSERT(3259, 0 <= i_itemNo && i_itemNo < 256);
dComIfGp_event_setGtItm(i_itemNo);
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return fpcM_ERROR_PROCESS_ID_e;
}
#endif
if (i_itemNo == dItemNo_NONE_e) {
OS_REPORT("ゲットデモ用なのに「ハズレ」です![%d]\n", i_itemNo); // Even though it is for a Get Demo, it is a 'Miss'!
return fpcM_ERROR_PROCESS_ID_e;
@@ -1557,12 +1545,6 @@ fpc_ProcID fopAcM_createItemFromTable(cXyz const* i_pos, int i_itemNo, int i_ite
JUT_ASSERT(3655, 0 <= i_itemNo && i_itemNo <= 255 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255);
// clang-format on
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return fpcM_ERROR_PROCESS_ID_e;
}
#endif
u8 tableNum;
ItemTableList* tableList;
tableList = (ItemTableList*)dComIfGp_getItemTable();
@@ -1606,12 +1588,6 @@ fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitN
JUT_ASSERT(3824, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255);
// clang-format on
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return fpcM_ERROR_PROCESS_ID_e;
}
#endif
if (i_itemNo == dItemNo_NONE_e) {
return fpcM_ERROR_PROCESS_ID_e;
}
@@ -1624,10 +1600,6 @@ fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomN
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF,
f32 i_speedY, int param_8) {
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return fpcM_ERROR_PROCESS_ID_e;
}
if (randomizer_IsActive()) {
if (i_itemNo == dItemNo_Randomizer_UTAWA_HEART_e)
{
@@ -1657,10 +1629,6 @@ fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_ro
const csXyz* i_angle, const cXyz* i_scale, int param_6,
int param_7) {
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return fpcM_ERROR_PROCESS_ID_e;
}
// If we are fighting Ook in randomizer, we want to handle the boomerang check a different way.
if (randomizer_IsActive()) {
if (daAlink_c::checkStageName("D_MN05B")) {
@@ -1681,12 +1649,6 @@ fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_ro
fopAc_ac_c* fopAcM_createItemForDirectGet(const cXyz* i_pos, int i_itemNo, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF,
f32 i_speedY) {
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return NULL;
}
#endif
fopAc_ac_c* item = fopAcM_fastCreateItem(i_pos, i_itemNo, i_roomNo, i_angle, i_scale, &i_speedF, &i_speedY, -1,
0x7, NULL);
fopAc_ac_c* ret = item;
@@ -1696,12 +1658,6 @@ fopAc_ac_c* fopAcM_createItemForDirectGet(const cXyz* i_pos, int i_itemNo, int i
fopAc_ac_c* fopAcM_createItemForSimpleDemo(const cXyz* i_pos, int i_itemNo, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF,
f32 i_speedY) {
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return NULL;
}
#endif
fopAc_ac_c* item = fopAcM_fastCreateItem(i_pos, i_itemNo, i_roomNo, i_angle, i_scale, &i_speedF, &i_speedY, -1,
0x4, NULL);
fopAc_ac_c* ret = item;
@@ -1714,12 +1670,6 @@ fpc_ProcID fopAcM_createItem(const cXyz* i_pos, int i_itemNo, int i_itemBitNo, i
JUT_ASSERT(4067, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255);
// clang-format on
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return fpcM_ERROR_PROCESS_ID_e;
}
#endif
if (i_itemNo == dItemNo_NONE_e) {
return fpcM_ERROR_PROCESS_ID_e;
}
@@ -1784,12 +1734,6 @@ fopAc_ac_c* fopAcM_fastCreateItem2(const cXyz* i_pos, int i_itemNo, int i_itemBi
JUT_ASSERT(4202, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255);
// clang-format on
#if TARGET_PC
if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) {
return NULL;
}
#endif
csXyz item_angle(csXyz::Zero);
if (i_itemNo == dItemNo_NONE_e) {