mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-11 12:39:48 -04:00
Merge branch 'main' into hash-verif
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 85 KiB |
Vendored
+1
-1
Submodule extern/aurora updated: 552be91d68...69fcfbffc2
@@ -1450,7 +1450,6 @@ set(DUSK_FILES
|
||||
src/dusk/imgui/ImGuiProcessOverlay.cpp
|
||||
src/dusk/imgui/ImGuiCameraOverlay.cpp
|
||||
src/dusk/imgui/ImGuiHeapOverlay.cpp
|
||||
src/dusk/imgui/ImGuiDebugPad.cpp
|
||||
src/dusk/imgui/ImGuiControllerOverlay.cpp
|
||||
src/dusk/imgui/ImGuiStubLog.cpp
|
||||
src/dusk/imgui/ImGuiMapLoader.cpp
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
struct RoomEntry {
|
||||
u8 roomNo;
|
||||
std::vector<s16> roomPoints = {};
|
||||
|
||||
+1
-137
@@ -2,9 +2,6 @@
|
||||
#define _SRC_DUSK_MATH_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <bit>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846f
|
||||
@@ -19,139 +16,6 @@ inline float i_cosf(float x) { return cos(x); }
|
||||
inline float i_tanf(float x) { return tan(x); }
|
||||
inline float i_acosf(float x) { return acos(x); }
|
||||
|
||||
|
||||
// frsqrte matching courtesy of Geotale, with reference to https://achurch.org/cpu-tests/ppc750cl.s
|
||||
|
||||
struct BaseAndDec32 {
|
||||
uint32_t base;
|
||||
int32_t dec;
|
||||
};
|
||||
|
||||
struct BaseAndDec64 {
|
||||
uint64_t base;
|
||||
int64_t dec;
|
||||
};
|
||||
|
||||
union c32 {
|
||||
constexpr c32(const float p) {
|
||||
f = p;
|
||||
}
|
||||
|
||||
constexpr c32(const uint32_t p) {
|
||||
u = p;
|
||||
}
|
||||
|
||||
uint32_t u;
|
||||
float f;
|
||||
};
|
||||
|
||||
union c64 {
|
||||
constexpr c64(const double p) {
|
||||
f = p;
|
||||
}
|
||||
|
||||
constexpr c64(const uint64_t p) {
|
||||
u = p;
|
||||
}
|
||||
|
||||
uint64_t u;
|
||||
double f;
|
||||
};
|
||||
|
||||
static constexpr uint64_t EXPONENT_SHIFT_F64 = 52;
|
||||
static constexpr uint64_t MANTISSA_MASK_F64 = 0x000fffffffffffffULL;
|
||||
static constexpr uint64_t EXPONENT_MASK_F64 = 0x7ff0000000000000ULL;
|
||||
static constexpr uint64_t SIGN_MASK_F64 = 0x8000000000000000ULL;
|
||||
|
||||
static constexpr std::array<BaseAndDec64, 32> RSQRTE_TABLE = {{
|
||||
{0x69fa000000000ULL, -0x15a0000000LL},
|
||||
{0x5f2e000000000ULL, -0x13cc000000LL},
|
||||
{0x554a000000000ULL, -0x1234000000LL},
|
||||
{0x4c30000000000ULL, -0x10d4000000LL},
|
||||
{0x43c8000000000ULL, -0x0f9c000000LL},
|
||||
{0x3bfc000000000ULL, -0x0e88000000LL},
|
||||
{0x34b8000000000ULL, -0x0d94000000LL},
|
||||
{0x2df0000000000ULL, -0x0cb8000000LL},
|
||||
{0x2794000000000ULL, -0x0bf0000000LL},
|
||||
{0x219c000000000ULL, -0x0b40000000LL},
|
||||
{0x1bfc000000000ULL, -0x0aa0000000LL},
|
||||
{0x16ae000000000ULL, -0x0a0c000000LL},
|
||||
{0x11a8000000000ULL, -0x0984000000LL},
|
||||
{0x0ce6000000000ULL, -0x090c000000LL},
|
||||
{0x0862000000000ULL, -0x0898000000LL},
|
||||
{0x0416000000000ULL, -0x082c000000LL},
|
||||
{0xffe8000000000ULL, -0x1e90000000LL},
|
||||
{0xf0a4000000000ULL, -0x1c00000000LL},
|
||||
{0xe2a8000000000ULL, -0x19c0000000LL},
|
||||
{0xd5c8000000000ULL, -0x17c8000000LL},
|
||||
{0xc9e4000000000ULL, -0x1610000000LL},
|
||||
{0xbedc000000000ULL, -0x1490000000LL},
|
||||
{0xb498000000000ULL, -0x1330000000LL},
|
||||
{0xab00000000000ULL, -0x11f8000000LL},
|
||||
{0xa204000000000ULL, -0x10e8000000LL},
|
||||
{0x9994000000000ULL, -0x0fe8000000LL},
|
||||
{0x91a0000000000ULL, -0x0f08000000LL},
|
||||
{0x8a1c000000000ULL, -0x0e38000000LL},
|
||||
{0x8304000000000ULL, -0x0d78000000LL},
|
||||
{0x7c48000000000ULL, -0x0cc8000000LL},
|
||||
{0x75e4000000000ULL, -0x0c28000000LL},
|
||||
{0x6fd0000000000ULL, -0x0b98000000LL},
|
||||
}};
|
||||
|
||||
[[nodiscard]] static inline double frsqrte(const double val) {
|
||||
c64 bits(val);
|
||||
|
||||
uint64_t mantissa = bits.u & MANTISSA_MASK_F64;
|
||||
int64_t exponent = bits.u & EXPONENT_MASK_F64;
|
||||
bool sign = (bits.u & SIGN_MASK_F64) != 0;
|
||||
|
||||
// Handle 0 case
|
||||
if (mantissa == 0 && exponent == 0) {
|
||||
return std::copysign(std::numeric_limits<double>::infinity(), bits.f);
|
||||
}
|
||||
|
||||
// Handle NaN-like
|
||||
if (exponent == EXPONENT_MASK_F64) {
|
||||
if (mantissa == 0) {
|
||||
return sign ? std::numeric_limits<double>::quiet_NaN() : 0.0;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
// Handle negative inputs
|
||||
if (sign) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
if (exponent == 0) {
|
||||
// Shift so one bit goes to where the exponent would be,
|
||||
// then clear that bit to mimic a not-subnormal number!
|
||||
// Aka, if there are 12 leading zeroes, shift left once
|
||||
uint32_t shift = std::countl_zero(mantissa) - static_cast<uint32_t>(63 - EXPONENT_SHIFT_F64);
|
||||
|
||||
mantissa <<= shift;
|
||||
mantissa &= MANTISSA_MASK_F64;
|
||||
// The shift is subtracted by 1 because denormals by default
|
||||
// are offset by 1 (exponent 0 doesn't have implied 1 bit)
|
||||
exponent -= static_cast<int64_t>(shift - 1) << EXPONENT_SHIFT_F64;
|
||||
}
|
||||
|
||||
// In reality this doesn't get the full exponent -- Only the least significant bit
|
||||
// Only that's needed because square roots of higher exponent bits simply multiply the
|
||||
// result by 2!!
|
||||
uint32_t key = static_cast<uint32_t>((static_cast<uint64_t>(exponent) | mantissa) >> 37);
|
||||
uint64_t new_exp =
|
||||
(static_cast<uint64_t>((0xbfcLL << EXPONENT_SHIFT_F64) - exponent) >> 1) & EXPONENT_MASK_F64;
|
||||
|
||||
// Remove the bits relating to anything higher than the LSB of the exponent
|
||||
const auto &entry = RSQRTE_TABLE[0x1f & (key >> 11)];
|
||||
|
||||
// The result is given by an estimate then an adjustment based on the original
|
||||
// key that was computed
|
||||
uint64_t new_mantissa = static_cast<uint64_t>(entry.base + entry.dec * static_cast<int64_t>(key & 0x7ff));
|
||||
|
||||
return c64(new_exp | new_mantissa).f;
|
||||
}
|
||||
#include <dolphin/ppc_math.h>
|
||||
|
||||
#endif // _SRC_DUSK_MATH_H_
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef J3DSTRUCT_H
|
||||
#define J3DSTRUCT_H
|
||||
|
||||
#include <cstring>
|
||||
#include <gx.h>
|
||||
#include <mtx.h>
|
||||
#include <mtx.h>
|
||||
#include "global.h"
|
||||
#include "JSystem/JMath/JMath.h"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DLightInfo {
|
||||
bool operator==(J3DLightInfo& other) const;
|
||||
@@ -28,7 +28,7 @@ struct J3DLightInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTextureSRTInfo {
|
||||
// NOTE: Big endian when loaded from file!
|
||||
@@ -79,7 +79,7 @@ enum J3DTexMtxMode {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTexMtxInfo {
|
||||
bool operator==(J3DTexMtxInfo& other) const;
|
||||
@@ -97,7 +97,7 @@ struct J3DTexMtxInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DIndTexMtxInfo {
|
||||
J3DIndTexMtxInfo& operator=(J3DIndTexMtxInfo const&);
|
||||
@@ -107,7 +107,7 @@ struct J3DIndTexMtxInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DFogInfo {
|
||||
bool operator==(J3DFogInfo&) const;
|
||||
@@ -126,7 +126,7 @@ struct J3DFogInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DNBTScaleInfo {
|
||||
bool operator==(const J3DNBTScaleInfo& other) const;
|
||||
@@ -153,7 +153,7 @@ struct J3DIndTexOrderInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTevSwapModeInfo {
|
||||
/* 0x0 */ u8 mRasSel;
|
||||
@@ -164,7 +164,7 @@ struct J3DTevSwapModeInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTevSwapModeTableInfo {
|
||||
/* 0x0 */ u8 field_0x0;
|
||||
@@ -175,7 +175,7 @@ struct J3DTevSwapModeTableInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTevStageInfo {
|
||||
/* 0x0 */ u8 field_0x0;
|
||||
@@ -202,7 +202,7 @@ struct J3DTevStageInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DIndTevStageInfo {
|
||||
/* 0x0 */ u8 mIndStage;
|
||||
@@ -219,7 +219,7 @@ struct J3DIndTevStageInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTexCoordInfo {
|
||||
/* 0x0 */ u8 mTexGenType;
|
||||
@@ -265,7 +265,7 @@ struct J3DBlendInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTevOrderInfo {
|
||||
void operator=(const J3DTevOrderInfo& other) {
|
||||
|
||||
+49
-1
@@ -110,6 +110,47 @@ toast.achievement heading {
|
||||
color: #C2A42D;
|
||||
}
|
||||
|
||||
toast.controller-warning {
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: 40dp;
|
||||
left: 50%;
|
||||
width: 440dp;
|
||||
max-width: 90%;
|
||||
transform: translateX(-50%) scale(0.9);
|
||||
}
|
||||
|
||||
toast.controller-warning[open] {
|
||||
transform: translateX(-50%) scale(1);
|
||||
}
|
||||
|
||||
toast.controller-warning heading {
|
||||
color: #C2A42D;
|
||||
}
|
||||
|
||||
toast.menu-notification {
|
||||
top: 40dp;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
left: 50%;
|
||||
max-width: 90%;
|
||||
transform: translateX(-50%) scale(0.9);
|
||||
}
|
||||
|
||||
toast.menu-notification[open] {
|
||||
transform: translateX(-50%) scale(1);
|
||||
}
|
||||
|
||||
toast.menu-notification message {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
toast.menu-notification message row {
|
||||
align-items: center;
|
||||
gap: 6dp;
|
||||
}
|
||||
|
||||
icon {
|
||||
font-family: "Material Symbols Rounded";
|
||||
font-weight: normal;
|
||||
@@ -138,6 +179,13 @@ icon.controller {
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
icon.warning {
|
||||
width: 24dp;
|
||||
height: 24dp;
|
||||
font-size: 24dp;
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
logo {
|
||||
position: absolute;
|
||||
width: 100dp;
|
||||
@@ -186,4 +234,4 @@ logo img.outer {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ void daAlink_c::setOriginalHeap(JKRExpHeap** i_ppheap, u32 i_size) {
|
||||
u32 var_r28 = 0x10;
|
||||
u32 size = ROUND(i_size, 16);
|
||||
#if TARGET_PC
|
||||
size *= 2;
|
||||
size *= 20; // Increase Link's heap size to prevent mods from crashing with higher-quality models.
|
||||
#endif
|
||||
JKRHeap* parent = mDoExt_getGameHeap();
|
||||
|
||||
|
||||
@@ -254,7 +254,11 @@ BOOL daBdoor_c::checkArea() {
|
||||
if (fabsf(vec.z) > 100.0f) {
|
||||
return false;
|
||||
}
|
||||
#ifdef TARGET_PC
|
||||
return (s16)((s32)fabs(current.angle.y - 0x7fff - player->current.angle.y) & 0xffff) <= 0x4000 ? 1 : 0;
|
||||
#else
|
||||
return (s16)fabs((f64)(current.angle.y - 0x7fff - player->current.angle.y)) <= 0x4000 ? 1 : 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL daBdoor_c::checkFront() {
|
||||
|
||||
@@ -825,7 +825,11 @@ int daBdoorL1_c::checkArea() {
|
||||
if (fabsf(local_48.z) > 100.0f) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef TARGET_PC
|
||||
if ((s16)((s32)fabs(current.angle.y - 0x7fff - player->current.angle.y) & 0xffff) <= 0x4000) {
|
||||
#else
|
||||
if ((s16)fabs((f64)(current.angle.y - 0x7fff - player->current.angle.y)) <= 0x4000) {
|
||||
#endif
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
|
||||
@@ -348,7 +348,11 @@ int daBdoorL5_c::checkArea() {
|
||||
if (fabsf(local_48.z) > 100.0f) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef TARGET_PC
|
||||
if ((s16)((s32)fabs(current.angle.y - 0x7fff - player->current.angle.y) & 0xffff) <= 0x4000) {
|
||||
#else
|
||||
if ((s16)fabs((f64)(current.angle.y - 0x7fff - player->current.angle.y)) <= 0x4000) {
|
||||
#endif
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
|
||||
@@ -1317,8 +1317,12 @@ int daMBdoorL1_c::checkArea() {
|
||||
if (fabsf(local_48.z) > 110.0f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef TARGET_PC
|
||||
if ((s16)((s32)fabs(angle - 0x7fff - player->current.angle.y) & 0xffff) > 0x4000) {
|
||||
#else
|
||||
if ((s16)fabs((f64)(angle - 0x7fff - player->current.angle.y)) > 0x4000) {
|
||||
#endif
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
|
||||
+121
-31
@@ -15,13 +15,47 @@
|
||||
#include <cstring>
|
||||
|
||||
#ifdef TARGET_PC
|
||||
#include <span>
|
||||
#include <numbers>
|
||||
#include <array>
|
||||
|
||||
constexpr u16 kMapResolutionMultiplier = 4;
|
||||
constexpr u16 kMapCircleSize = 16 * kMapResolutionMultiplier;
|
||||
constexpr u16 kMapImageSide = 16 * kMapResolutionMultiplier;
|
||||
constexpr u32 kMapImageTotalPixels = kMapImageSide * kMapImageSide;
|
||||
|
||||
typedef std::function<u8(size_t, size_t)> PaintI8Fn;
|
||||
|
||||
void paint_i8(std::span<u8> dst, size_t width, PaintI8Fn paint) {
|
||||
const auto blocksAcross = width >> 3;
|
||||
|
||||
for (size_t i = 0; i < dst.size(); i++) {
|
||||
// 8x4 block swizzling for I8
|
||||
const auto blockIdx = i >> 5;
|
||||
const auto localIdx = i & 31;
|
||||
|
||||
const auto blockY = blockIdx / blocksAcross;
|
||||
const auto blockX = blockIdx % blocksAcross;
|
||||
|
||||
const auto localY = localIdx >> 3;
|
||||
const auto localX = localIdx & 7;
|
||||
|
||||
const auto x = (blockX << 3) + localX;
|
||||
const auto y = (blockY << 2) + localY;
|
||||
|
||||
dst[i] = paint(x, y);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void dMpath_n::dTexObjAggregate_c::create() {
|
||||
static int const data[7] = {
|
||||
79, 80, 77, 78, 76, 81, 82,
|
||||
79, // 0: im_map_icon_square_4i.bti
|
||||
80, // 1: im_map_icon_tresurebox_4i.bti
|
||||
77, // 2: im_map_icon_enter_4i.bti
|
||||
78, // 3: im_map_icon_nijumaru_4i.bti
|
||||
76, // 4: im_map_icon_circle_4i.bti
|
||||
81, // 5: im_map_icon_try_force_4i.bti
|
||||
82, // 6: map_icon_circle16x16_4i.bti
|
||||
};
|
||||
|
||||
for (int lp1 = 0; lp1 < 7; lp1++) {
|
||||
@@ -35,45 +69,101 @@ void dMpath_n::dTexObjAggregate_c::create() {
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
auto hqCircle = JKR_NEW TGXTexObj();
|
||||
static bool hqTexsDrawn = false;
|
||||
|
||||
static bool hqCircleDrawn = false;
|
||||
static u8 hqCircleData[kMapCircleSize * kMapCircleSize];
|
||||
static u8 hqCircleData[kMapImageTotalPixels];
|
||||
static u8 hqCircleAltData[kMapImageTotalPixels];
|
||||
static u8 hqNijumaruData[kMapImageTotalPixels];
|
||||
static u8 hqEnterData[kMapImageTotalPixels];
|
||||
static u8 hqTryForceData[kMapImageTotalPixels];
|
||||
|
||||
if (!hqCircleDrawn) {
|
||||
const auto center = kMapCircleSize / 2.0f;
|
||||
const auto radiusSq = center * center;
|
||||
const auto blocksAcross = kMapCircleSize >> 3;
|
||||
const auto totalPixels = sizeof(hqCircleData);
|
||||
if (!hqTexsDrawn) {
|
||||
constexpr auto center = kMapImageSide / 2.0f;
|
||||
constexpr auto radiusSq = center * center;
|
||||
|
||||
for (size_t i = 0; i < totalPixels; i++) {
|
||||
// 8x4 block swizzling for I8
|
||||
const auto blockIdx = i >> 5;
|
||||
const auto localIdx = i & 31;
|
||||
// 6: map_icon_circle16x16_4i.bti - simple circle
|
||||
paint_i8(std::span{hqCircleData}, kMapImageSide, [=](auto x, auto y) {
|
||||
const auto dx = (x + 0.5f) - center;
|
||||
const auto dy = (y + 0.5f) - center;
|
||||
return (dx * dx + dy * dy < radiusSq) ? 0x11 : 0;
|
||||
});
|
||||
|
||||
const auto blockY = blockIdx / blocksAcross;
|
||||
const auto blockX = blockIdx % blocksAcross;
|
||||
|
||||
const auto localY = localIdx >> 3;
|
||||
const auto localX = localIdx & 7;
|
||||
|
||||
const auto x = (blockX << 3) + localX;
|
||||
const auto y = (blockY << 2) + localY;
|
||||
// 4: im_map_icon_circle_4i.bti - outlined circle
|
||||
paint_i8(std::span{hqCircleAltData}, kMapImageSide, [=](auto x, auto y) {
|
||||
constexpr auto innerRadius = kMapImageSide * 3.0f / 8.0f;
|
||||
constexpr auto innerRadiusSq = innerRadius * innerRadius;
|
||||
|
||||
const auto dx = (x + 0.5f) - center;
|
||||
const auto dy = (y + 0.5f) - center;
|
||||
const auto dSq = dx * dx + dy * dy;
|
||||
|
||||
// the original texture is in I4 format and uses 1 to indicate if inside the circle
|
||||
// so we scale to I8 range: 255 / 15 = 17
|
||||
hqCircleData[i] = (dx * dx + dy * dy < radiusSq) ? 17 : 0;
|
||||
}
|
||||
hqCircleDrawn = true;
|
||||
return dSq < radiusSq ? (dSq < innerRadiusSq ? 0x22 : 0x11) : 0;
|
||||
});
|
||||
|
||||
// 3: im_map_icon_nijumaru_4i.bti - concentric rings
|
||||
paint_i8(std::span{hqNijumaruData}, kMapImageSide, [=](auto x, auto y) {
|
||||
constexpr u8 nijumaruRings[] = {0x11, 0x22, 0x11, 0x11, 0x22, 0x22};
|
||||
|
||||
const auto dx = (x + 0.5f) - center;
|
||||
const auto dy = (y + 0.5f) - center;
|
||||
const auto dSq = dx * dx + dy * dy;
|
||||
|
||||
if (dSq < radiusSq) {
|
||||
const auto ringIndex =
|
||||
static_cast<size_t>(std::trunc(std::sqrt(dSq) / kMapImageSide * 12));
|
||||
return nijumaruRings[ringIndex];
|
||||
}
|
||||
return u8{0};
|
||||
});
|
||||
|
||||
// 2: im_map_icon_enter_4i.bti - outlined octagram
|
||||
paint_i8(std::span{hqEnterData}, kMapImageSide, [=](auto x, auto y) {
|
||||
constexpr auto outlineWidth = kMapImageSide / 6.0f;
|
||||
|
||||
const auto adx = std::abs((x + 0.5f) - center);
|
||||
const auto ady = std::abs((y + 0.5f) - center);
|
||||
const auto dist =
|
||||
std::min(adx + ady, std::max(adx, ady) * std::numbers::sqrt2_v<float>) -
|
||||
kMapImageSide / 2.0f;
|
||||
|
||||
return dist > 0.0f ? 0 : (dist > -outlineWidth ? 0x22 : 0x33);
|
||||
});
|
||||
|
||||
// 5: im_map_icon_try_force_4i.bti - outlined circle with triangle
|
||||
paint_i8(std::span{hqTryForceData}, kMapImageSide, [=](auto x, auto y) {
|
||||
constexpr auto innerRadiusNorm = 5.0f / 12.0f;
|
||||
constexpr auto innerRadius = kMapImageSide * innerRadiusNorm;
|
||||
constexpr auto innerRadiusSq = innerRadius * innerRadius;
|
||||
constexpr auto triRadius = kMapImageSide * innerRadiusNorm / 2.0f;
|
||||
|
||||
const auto dx = (x + 0.5f) - center;
|
||||
const auto dy = (y + 0.5f) - center;
|
||||
const auto dSq = dx * dx + dy * dy;
|
||||
const auto triSideDist = (std::numbers::sqrt3_v<float> * std::abs(dx) - dy) * 0.5f;
|
||||
const auto insideTri = std::max(dy, triSideDist) < triRadius;
|
||||
|
||||
return insideTri ? 0x22 : (dSq < radiusSq ? (dSq < innerRadiusSq ? 0x33 : 0x22) : 0);
|
||||
});
|
||||
|
||||
hqTexsDrawn = true;
|
||||
}
|
||||
|
||||
GXInitTexObj(hqCircle, hqCircleData, kMapCircleSize, kMapCircleSize, GX_TF_I8, GX_CLAMP,
|
||||
GX_CLAMP, GX_FALSE);
|
||||
GXInitTexObjLOD(hqCircle, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
|
||||
mp_texObj[6] = hqCircle;
|
||||
constexpr auto replacements = std::to_array<std::pair<size_t, const u8*> >({
|
||||
{2, hqEnterData},
|
||||
{3, hqNijumaruData},
|
||||
{4, hqCircleAltData},
|
||||
{5, hqTryForceData},
|
||||
{6, hqCircleData},
|
||||
});
|
||||
|
||||
for (const auto& [idx, data] : replacements) {
|
||||
JKR_DELETE(mp_texObj[idx]);
|
||||
const auto texobj = JKR_NEW TGXTexObj();
|
||||
GXInitTexObj(
|
||||
texobj, data, kMapImageSide, kMapImageSide, GX_TF_I8, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
||||
GXInitTexObjLOD(texobj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
|
||||
mp_texObj[idx] = texobj;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1403,7 +1403,7 @@ void dMenu_Fmap2DBack_c::regionTextureDraw() {
|
||||
if (uVar10 != uVar9) {
|
||||
bool b = 0;
|
||||
f32 v = mTransX + (dVar14 + (mRegionMinMapX[uVar10] + field_0xf0c[uVar10]));
|
||||
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
b = true;
|
||||
|
||||
@@ -291,10 +291,6 @@ namespace dusk {
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
|
||||
AddToast(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ?
|
||||
"3-finger tap to toggle menu"s :
|
||||
"Press F1 to toggle menu"s,
|
||||
4.f);
|
||||
m_isLaunchInitialized = true;
|
||||
if (getSettings().game.liveSplitEnabled) {
|
||||
dusk::speedrun::connectLiveSplit();
|
||||
@@ -389,7 +385,6 @@ namespace dusk {
|
||||
m_menuTools.ShowSaveEditor();
|
||||
m_menuTools.ShowStateShare();
|
||||
}
|
||||
DuskDebugPad(); // temporary, remove later
|
||||
|
||||
// Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds.
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
@@ -73,8 +73,6 @@ bool ImGuiButtonCenter(std::string_view text);
|
||||
float ImGuiScale();
|
||||
} // namespace dusk
|
||||
|
||||
void DuskDebugPad();
|
||||
|
||||
#if defined(_WIN32) || \
|
||||
(defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_MACCATALYST) || \
|
||||
(defined(__linux__) && !defined(__ANDROID__))
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#include "m_Do/m_Do_controller_pad.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "ImGuiConsole.hpp"
|
||||
|
||||
void DuskDebugPad() {
|
||||
auto& pad = mDoCPd_c::getCpadInfo(PAD_1);
|
||||
auto KeyFlag = [&](ImGuiKey key, u32 padFlag) {
|
||||
if (ImGui::IsKeyDown(key))
|
||||
pad.mButtonFlags |= padFlag;
|
||||
if (ImGui::IsKeyPressed(key))
|
||||
pad.mPressedButtonFlags |= padFlag;
|
||||
|
||||
};
|
||||
|
||||
KeyFlag(ImGuiKey_K, PAD_BUTTON_A);
|
||||
KeyFlag(ImGuiKey_J, PAD_BUTTON_B);
|
||||
KeyFlag(ImGuiKey_L, PAD_BUTTON_X);
|
||||
KeyFlag(ImGuiKey_I, PAD_BUTTON_Y);
|
||||
KeyFlag(ImGuiKey_H, PAD_BUTTON_START);
|
||||
KeyFlag(ImGuiKey_O, PAD_TRIGGER_Z);
|
||||
KeyFlag(ImGuiKey_Keypad8, PAD_BUTTON_UP);
|
||||
KeyFlag(ImGuiKey_Keypad2, PAD_BUTTON_DOWN);
|
||||
KeyFlag(ImGuiKey_Keypad6, PAD_BUTTON_RIGHT);
|
||||
KeyFlag(ImGuiKey_Keypad4, PAD_BUTTON_LEFT);
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey_W)) {
|
||||
pad.mMainStickPosY = 1.0f;
|
||||
pad.mMainStickValue = 1.0f;
|
||||
pad.mMainStickAngle = 0x8000;
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey_S)) {
|
||||
pad.mMainStickPosY = -1.0f;
|
||||
pad.mMainStickValue = 1.0f;
|
||||
pad.mMainStickAngle = 0;
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey_D)) {
|
||||
pad.mMainStickPosX = 1.0f;
|
||||
pad.mMainStickValue = 1.0f;
|
||||
pad.mMainStickAngle = 0x4000;
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey_A)) {
|
||||
pad.mMainStickPosX = -1.0f;
|
||||
pad.mMainStickValue = 1.0f;
|
||||
pad.mMainStickAngle = -0x4000;
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey_Q)) {
|
||||
pad.mTriggerLeft = 1.0;
|
||||
pad.mTrigLockL = 1;
|
||||
pad.mHoldLockL = 1;
|
||||
}
|
||||
if (ImGui::IsKeyDown(ImGuiKey_E)) {
|
||||
pad.mTriggerRight = 1.0;
|
||||
pad.mTrigLockR = 1;
|
||||
pad.mHoldLockR = 1;
|
||||
}
|
||||
}
|
||||
+2317
-2190
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,6 @@
|
||||
#include "d/actor/d_a_player.h"
|
||||
|
||||
#include <map>
|
||||
#include <bit>
|
||||
|
||||
namespace dusk {
|
||||
enum ItemType {
|
||||
@@ -1354,38 +1353,30 @@ namespace dusk {
|
||||
// genCommonAreaFlags(membit);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
concept FlagIter = requires(T t) {
|
||||
++t;
|
||||
--t;
|
||||
t + 1;
|
||||
t < t;
|
||||
{ t->flagID } -> std::convertible_to<u16>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept FlagTester = requires(T t, u16 flagID) {
|
||||
{ t(flagID) } -> std::convertible_to<bool>;
|
||||
};
|
||||
|
||||
static void sortByFlags(FlagIter auto begin, FlagIter auto end, FlagTester auto&& flagTester) {
|
||||
template <typename FlagIter, typename FlagTester>
|
||||
requires requires(FlagIter a, FlagTester tester) {
|
||||
--a; ++a; a < a; *a;
|
||||
a + 1;
|
||||
{ tester(*a) } -> std::convertible_to<bool>;
|
||||
}
|
||||
static void sortByFlags(FlagIter begin, FlagIter end, FlagTester&& flagTester) {
|
||||
if (begin == end) return;
|
||||
|
||||
FlagIter auto fullEnd = end;
|
||||
auto fullEnd = end;
|
||||
|
||||
// We want to find the location of where we can swap our `On` flags to.
|
||||
// We're gonna put the `Off` bits first, and the `On` bits last. 0 < 1
|
||||
// We can achieve this by skipping all the `On` bits at the end.
|
||||
|
||||
// backtrack until we find a bit that is off
|
||||
while (begin < --end && flagTester(end->flagID)) {
|
||||
while (begin < --end && flagTester(*end)) {
|
||||
// move the end pointer back while we find on bits
|
||||
}
|
||||
|
||||
// end should now be pointing to a bit that is off
|
||||
while (begin < end) {
|
||||
// if there's a flag that's on
|
||||
if (flagTester(begin->flagID)) {
|
||||
if (flagTester(*begin)) {
|
||||
// move it to the end
|
||||
std::rotate(begin, begin + 1, fullEnd);
|
||||
// move back the end of where we're checking
|
||||
@@ -1401,122 +1392,82 @@ namespace dusk {
|
||||
|
||||
|
||||
static void genAreaFlagTable(uint8_t areaIndex, dSv_memBit_c& membit) {
|
||||
constexpr auto makeMask = [](uint8_t size) -> uint16_t { return (1 << size) - 1; };
|
||||
constexpr auto getByteIndexFromFlag = [](uint16_t f) -> uint8_t { return f >> 8; };
|
||||
constexpr auto getBitMaskFromFlag = [](uint16_t f) -> uint8_t { return f & 0xff; };
|
||||
constexpr auto getValueSize = [getBitMaskFromFlag](uint16_t f) -> uint8_t {
|
||||
return std::popcount(getBitMaskFromFlag(f));
|
||||
};
|
||||
|
||||
constexpr auto makeEventFlag = [](uint8_t byteIndex, uint8_t bitIndices) -> uint16_t {
|
||||
return (byteIndex << 8) | bitIndices;
|
||||
};
|
||||
|
||||
const auto eventFlagToAreaFlag = [&](uint16_t areaFlag) -> int {
|
||||
auto byteInd = getByteIndexFromFlag(areaFlag);
|
||||
constexpr size_t areaIndexSize = 5;
|
||||
// if we're looking at 0x580, that would be byte 5, and check if 0x80 is set on that byte
|
||||
// the event flags are structured differently than area flags
|
||||
// B is byte index, b is the flag mask to check
|
||||
// event flags are BBBBBBBB bbbbbbbb
|
||||
// for area flags, they check bitIndex, not mask, i is index
|
||||
// also area uses u32 index, not byte index
|
||||
// area flags are BBBiiiii
|
||||
// so we need to convert from bit mask to index
|
||||
// also our byte index has to become a u32 index
|
||||
|
||||
// dividing byte index by sizeof(u32) gets us the u32 index
|
||||
// but in big endian, the first byte is the highest order byte of the u32
|
||||
// so we skip 24 bytes for the first byte, 16 for the second, etc
|
||||
// essentially (3 - (x % 4)), reversing the modulus, 0=3, 1=2
|
||||
auto bitsToSkip = 8 * ((sizeof(u32) - 1) - (byteInd % sizeof(u32)));
|
||||
return ((byteInd / sizeof(u32)) << areaIndexSize) | ((std::countr_zero(areaFlag) + bitsToSkip) & makeMask(areaIndexSize));
|
||||
};
|
||||
|
||||
constexpr uint8_t validTbox = sizeof(membit.mTbox);
|
||||
constexpr uint8_t validSwitch = validTbox + sizeof(membit.mSwitch);
|
||||
constexpr uint8_t validItem = validSwitch + sizeof(membit.mItem);
|
||||
constexpr uint16_t tboxConvert = 0;
|
||||
constexpr uint16_t switchConvert = sizeof(membit.mTbox) << 8;
|
||||
constexpr uint16_t itemConvert = switchConvert + (sizeof(membit.mItem) << 8);
|
||||
|
||||
const auto LoadFlag = [&](uint16_t flag) -> bool {
|
||||
const auto byteIndex = getByteIndexFromFlag(flag);
|
||||
|
||||
if (byteIndex < validTbox) {
|
||||
return membit.isTbox(eventFlagToAreaFlag(flag - tboxConvert));
|
||||
} else if (byteIndex < validSwitch) {
|
||||
return membit.isSwitch(eventFlagToAreaFlag(flag - switchConvert));
|
||||
} else if (byteIndex < validItem) {
|
||||
return membit.isItem(eventFlagToAreaFlag(flag - itemConvert));
|
||||
const auto LoadFlag = [&](const EventAreaFlags& flag) -> bool {
|
||||
switch (flag.flag.type) {
|
||||
case AreaFlagType::Item: {
|
||||
return membit.isItem(flag.flag.flagID);
|
||||
} break;
|
||||
case AreaFlagType::Switch: {
|
||||
return membit.isSwitch(flag.flag.flagID);
|
||||
} break;
|
||||
case AreaFlagType::Tbox: {
|
||||
return membit.isTbox(flag.flag.flagID);
|
||||
} break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const auto SetFlag = [&](uint16_t flag, bool set) -> void {
|
||||
const auto byteIndex = getByteIndexFromFlag(flag);
|
||||
const auto SetFlag = [&](const AreaFlagInd& flag, bool set) -> void {
|
||||
if (set) {
|
||||
if (byteIndex < validTbox) {
|
||||
membit.onTbox(eventFlagToAreaFlag(flag - tboxConvert));
|
||||
} else if (byteIndex < validSwitch) {
|
||||
membit.onSwitch(eventFlagToAreaFlag(flag - switchConvert));
|
||||
} else if (byteIndex < validItem) {
|
||||
membit.onItem(eventFlagToAreaFlag(flag - itemConvert));
|
||||
switch (flag.type) {
|
||||
case AreaFlagType::Item: {
|
||||
membit.onItem(flag.flagID);
|
||||
} break;
|
||||
case AreaFlagType::Switch: {
|
||||
membit.onSwitch(flag.flagID);
|
||||
} break;
|
||||
case AreaFlagType::Tbox: {
|
||||
membit.onTbox(flag.flagID);
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
if (byteIndex < validTbox) {
|
||||
membit.offTbox(eventFlagToAreaFlag(flag - tboxConvert));
|
||||
} else if (byteIndex < validSwitch) {
|
||||
membit.offSwitch(eventFlagToAreaFlag(flag - switchConvert));
|
||||
} else if (byteIndex < validItem) {
|
||||
membit.offItem(eventFlagToAreaFlag(flag - itemConvert));
|
||||
switch (flag.type) {
|
||||
case AreaFlagType::Item: {
|
||||
membit.offItem(flag.flagID);
|
||||
} break;
|
||||
case AreaFlagType::Switch: {
|
||||
membit.offSwitch(flag.flagID);
|
||||
} break;
|
||||
case AreaFlagType::Tbox: {
|
||||
membit.offTbox(flag.flagID);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const auto LoadMultiByteFlag = [&](uint16_t flag) -> uint8_t {
|
||||
const auto bitInds = getBitMaskFromFlag(flag);
|
||||
const auto byteIndex = getByteIndexFromFlag(flag);
|
||||
|
||||
const uint16_t startingMask = std::bit_floor(bitInds);
|
||||
uint8_t val = 0;
|
||||
for (uint16_t bitIndexMask = startingMask; (bitInds & bitIndexMask) != 0;
|
||||
bitIndexMask >>= 1)
|
||||
{
|
||||
val <<= 1;
|
||||
if (LoadFlag(makeEventFlag(byteIndex, bitInds & bitIndexMask))) {
|
||||
val |= 1;
|
||||
}
|
||||
const auto LoadMultiByteFlag = [&](const AreaFlagMultibit& flag) -> uint8_t {
|
||||
BE(u32)* areaFlags = nullptr;
|
||||
switch (flag.type) {
|
||||
case AreaFlagType::Item: {
|
||||
areaFlags = membit.mItem;
|
||||
} break;
|
||||
case AreaFlagType::Switch: {
|
||||
areaFlags = membit.mSwitch;
|
||||
} break;
|
||||
case AreaFlagType::Tbox: {
|
||||
areaFlags = membit.mTbox;
|
||||
} break;
|
||||
}
|
||||
return val;
|
||||
assert(areaFlags != nullptr);
|
||||
return (areaFlags[flag.index] & flag.mask) >> flag.shift;
|
||||
};
|
||||
|
||||
const auto SetMultiByteFlag = [&](uint16_t flag, uint8_t val) -> void {
|
||||
const auto bitInds = getBitMaskFromFlag(flag);
|
||||
const auto byteIndex = getByteIndexFromFlag(flag);
|
||||
|
||||
const uint16_t startingMask = std::bit_floor(bitInds);
|
||||
uint16_t valueMask = 1 << (getValueSize(flag) - 1);
|
||||
|
||||
for (uint16_t bitIndexMask = startingMask; (bitInds & bitIndexMask) != 0;
|
||||
bitIndexMask >>= 1, valueMask >>= 1)
|
||||
{
|
||||
SetFlag(makeEventFlag(byteIndex, bitInds & bitIndexMask), (val & valueMask) != 0);
|
||||
const auto SetMultiByteFlag = [&](const AreaFlagMultibit& flag, uint8_t val) -> void {
|
||||
BE(u32)* areaFlags = nullptr;
|
||||
switch (flag.type) {
|
||||
case AreaFlagType::Item: {
|
||||
areaFlags = membit.mItem;
|
||||
} break;
|
||||
case AreaFlagType::Switch: {
|
||||
areaFlags = membit.mSwitch;
|
||||
} break;
|
||||
case AreaFlagType::Tbox: {
|
||||
areaFlags = membit.mTbox;
|
||||
} break;
|
||||
}
|
||||
};
|
||||
|
||||
const auto LoadSpreadMultiByte = [&](uint16_t high, uint16_t low) -> uint8_t {
|
||||
if (low == AREA_FLAG_NONE)
|
||||
return LoadMultiByteFlag(high);
|
||||
return (LoadMultiByteFlag(high) << getValueSize(low)) | LoadMultiByteFlag(low);
|
||||
};
|
||||
|
||||
const auto SetSpreadMultiByte = [&](uint16_t high, uint16_t low, uint8_t value) -> void {
|
||||
if (low == AREA_FLAG_NONE)
|
||||
return SetMultiByteFlag(high, value);
|
||||
const auto lowerSize = getValueSize(low);
|
||||
SetMultiByteFlag(high, value >> lowerSize);
|
||||
SetMultiByteFlag(low, value & makeMask(lowerSize));
|
||||
areaFlags[flag.index] &= ~flag.mask;
|
||||
areaFlags[flag.index] |= (val << flag.shift) & flag.mask;
|
||||
};
|
||||
|
||||
auto iter = imguiAreaFlagLookup.find(areaIndex);
|
||||
@@ -1568,7 +1519,7 @@ namespace dusk {
|
||||
case COLUMN_DESC:
|
||||
return l.description < r.description;
|
||||
case COLUMN_BIT:
|
||||
return l.flagID < r.flagID;
|
||||
return l.GetFlagID() < r.GetFlagID();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -1597,9 +1548,9 @@ namespace dusk {
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool flag = LoadFlag(e.flagID);
|
||||
if (ImGui::Checkbox(fmt::format("##_unused_area_flag_{}", e.flagID).c_str(), &flag)) {
|
||||
SetFlag(e.flagID, flag);
|
||||
bool flag = LoadFlag(e);
|
||||
if (ImGui::Checkbox(fmt::format("##_unused_area_flag_{}", e.flag.flagID).c_str(), &flag)) {
|
||||
SetFlag(e.flag, flag);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
@@ -1611,7 +1562,7 @@ namespace dusk {
|
||||
}
|
||||
|
||||
for (const auto& multiByteFlag : areaFlags.multibyteFlags) {
|
||||
auto flagValue = LoadSpreadMultiByte(multiByteFlag.highOrderflag, multiByteFlag.lowOrderflag);
|
||||
auto flagValue = LoadMultiByteFlag(multiByteFlag.flag);
|
||||
|
||||
const char* currentVal = "UNKNOWN";
|
||||
|
||||
@@ -1623,7 +1574,7 @@ namespace dusk {
|
||||
if (ImGui::BeginCombo(multiByteFlag.name, currentVal)) {
|
||||
for (const auto& [val, name] : multiByteFlag.enumValues) {
|
||||
if (ImGui::Selectable(name)) {
|
||||
SetSpreadMultiByte(multiByteFlag.highOrderflag, multiByteFlag.lowOrderflag, val);
|
||||
SetMultiByteFlag(multiByteFlag.flag, val);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
@@ -1756,7 +1707,9 @@ namespace dusk {
|
||||
// if we're sorting by flags, do special sort, regular sort is bad for sorting bools
|
||||
// it can swap values that are the same, and that causes constant reordering
|
||||
if (column == COLUMN_FLAG) {
|
||||
const auto testEventFunc = [&event](u16 flag) -> bool { return event.isEventBit(flag); };
|
||||
const auto testEventFunc = [&event](const duskImguiEventFlagEntry& flag) -> bool {
|
||||
return event.isEventBit(flag.flagID);
|
||||
};
|
||||
|
||||
if (direction == ImGuiSortDirection_Ascending) {
|
||||
sortByFlags(std::begin(duskImguiEventFlags),
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include <SDL3/SDL_keyboard.h>
|
||||
#include <SDL3/SDL_mouse.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <array>
|
||||
@@ -17,9 +18,17 @@
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
bool keyboard_active(int port) {
|
||||
u32 count = 0;
|
||||
return PADGetKeyButtonBindings(static_cast<u32>(port), &count) != nullptr;
|
||||
}
|
||||
|
||||
Rml::String current_controller_name(int port) {
|
||||
const char* name = PADGetName(port);
|
||||
return name == nullptr ? "None" : name;
|
||||
if (name != nullptr) {
|
||||
return name;
|
||||
}
|
||||
return keyboard_active(port) ? "Keyboard" : "None";
|
||||
}
|
||||
|
||||
Rml::String controller_index_name(u32 index) {
|
||||
@@ -202,6 +211,74 @@ bool keyboard_escape_pressed() {
|
||||
return keys != nullptr && SDL_SCANCODE_ESCAPE < keyCount && keys[SDL_SCANCODE_ESCAPE];
|
||||
}
|
||||
|
||||
Rml::String keyboard_key_name(s32 scancode) {
|
||||
if (scancode == PAD_KEY_INVALID) {
|
||||
return "Not bound";
|
||||
}
|
||||
switch (scancode) {
|
||||
case PAD_KEY_MOUSE_LEFT:
|
||||
return "Mouse Left";
|
||||
case PAD_KEY_MOUSE_MIDDLE:
|
||||
return "Mouse Middle";
|
||||
case PAD_KEY_MOUSE_RIGHT:
|
||||
return "Mouse Right";
|
||||
case PAD_KEY_MOUSE_X1:
|
||||
return "Mouse X1";
|
||||
case PAD_KEY_MOUSE_X2:
|
||||
return "Mouse X2";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (scancode < 0) {
|
||||
return "Unknown";
|
||||
}
|
||||
const char* name = SDL_GetScancodeName(static_cast<SDL_Scancode>(scancode));
|
||||
if (name == nullptr || name[0] == '\0') {
|
||||
return "Unknown";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
bool keyboard_neutral() {
|
||||
int keyCount = 0;
|
||||
const bool* keys = SDL_GetKeyboardState(&keyCount);
|
||||
if (keys != nullptr) {
|
||||
for (int i = 0; i < keyCount; ++i) {
|
||||
if (keys[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
float x, y;
|
||||
if (SDL_GetMouseState(&x, &y) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 keyboard_key_pressed() {
|
||||
int keyCount = 0;
|
||||
const bool* keys = SDL_GetKeyboardState(&keyCount);
|
||||
if (keys != nullptr) {
|
||||
for (int i = 1; i < keyCount; ++i) {
|
||||
if (i == SDL_SCANCODE_ESCAPE) {
|
||||
continue;
|
||||
}
|
||||
if (keys[i]) {
|
||||
return static_cast<s32>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
float x, y;
|
||||
const auto mouseButtons = SDL_GetMouseState(&x, &y);
|
||||
for (int btn = 1; btn <= 5; ++btn) {
|
||||
if (mouseButtons & (1u << (btn - 1))) {
|
||||
return -(btn + 1); // maps to PAD_KEY_MOUSE_LEFT (-2), etc.
|
||||
}
|
||||
}
|
||||
return PAD_KEY_INVALID;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ControllerConfigWindow::ControllerConfigWindow() {
|
||||
@@ -311,23 +388,38 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
||||
|
||||
switch (page) {
|
||||
case Page::Controller: {
|
||||
const u32 controllerCount = PADCount();
|
||||
if (controllerCount == 0) {
|
||||
pane.add_text("No controllers detected");
|
||||
break;
|
||||
}
|
||||
pane.add_button(
|
||||
{
|
||||
.text = "None",
|
||||
.isSelected =
|
||||
[port] { return PADGetIndexForPort(port) < 0 && !keyboard_active(port); },
|
||||
})
|
||||
.on_pressed([this, port] {
|
||||
mDoAud_seStartMenu(kSoundItemChange);
|
||||
cancel_pending_binding();
|
||||
PADClearPort(port);
|
||||
PADSetKeyboardActive(static_cast<u32>(port), FALSE);
|
||||
PADSerializeMappings();
|
||||
});
|
||||
|
||||
pane.add_button({
|
||||
.text = "None",
|
||||
.isSelected = [port] { return PADGetIndexForPort(port) < 0; },
|
||||
.text = "Keyboard",
|
||||
.isSelected = [port] { return keyboard_active(port); },
|
||||
})
|
||||
.on_pressed([this, port] {
|
||||
mDoAud_seStartMenu(kSoundItemChange);
|
||||
cancel_pending_binding();
|
||||
PADClearPort(port);
|
||||
PADSetKeyboardActive(static_cast<u32>(port), TRUE);
|
||||
PADSerializeMappings();
|
||||
});
|
||||
|
||||
const u32 controllerCount = PADCount();
|
||||
if (controllerCount == 0) {
|
||||
pane.add_text("No controllers detected");
|
||||
break;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < controllerCount; ++i) {
|
||||
pane.add_button(
|
||||
{
|
||||
@@ -338,6 +430,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
||||
.on_pressed([this, port, i] {
|
||||
mDoAud_seStartMenu(kSoundItemChange);
|
||||
cancel_pending_binding();
|
||||
PADSetKeyboardActive(static_cast<u32>(port), FALSE);
|
||||
PADSetPortForIndex(i, port);
|
||||
PADSerializeMappings();
|
||||
});
|
||||
@@ -345,6 +438,54 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
||||
break;
|
||||
}
|
||||
case Page::Buttons: {
|
||||
if (keyboard_active(port)) {
|
||||
auto addKeyButton = [&](PADButton button) {
|
||||
pane.add_select_button(
|
||||
{
|
||||
.key = PADGetButtonName(button),
|
||||
.getValue =
|
||||
[this, port, button] {
|
||||
if (mPendingKeyButton == static_cast<int>(button)) {
|
||||
return pending_key_label();
|
||||
}
|
||||
u32 count = 0;
|
||||
PADKeyButtonBinding* bindings =
|
||||
PADGetKeyButtonBindings(static_cast<u32>(port), &count);
|
||||
if (bindings == nullptr) {
|
||||
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");
|
||||
},
|
||||
})
|
||||
.on_pressed([this, port, button] {
|
||||
cancel_pending_binding();
|
||||
mPendingPort = port;
|
||||
mPendingBindingArmed = false;
|
||||
mPendingKeyButton = static_cast<int>(button);
|
||||
});
|
||||
};
|
||||
|
||||
pane.add_section("Buttons");
|
||||
addKeyButton(PAD_BUTTON_A);
|
||||
addKeyButton(PAD_BUTTON_B);
|
||||
addKeyButton(PAD_BUTTON_X);
|
||||
addKeyButton(PAD_BUTTON_Y);
|
||||
addKeyButton(PAD_BUTTON_START);
|
||||
addKeyButton(PAD_TRIGGER_Z);
|
||||
|
||||
pane.add_section("D-Pad");
|
||||
addKeyButton(PAD_BUTTON_UP);
|
||||
addKeyButton(PAD_BUTTON_DOWN);
|
||||
addKeyButton(PAD_BUTTON_LEFT);
|
||||
addKeyButton(PAD_BUTTON_RIGHT);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 buttonCount = 0;
|
||||
PADButtonMapping* mappings = PADGetButtonMappings(port, &buttonCount);
|
||||
if (mappings == nullptr) {
|
||||
@@ -407,6 +548,79 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
||||
break;
|
||||
}
|
||||
case Page::Triggers: {
|
||||
if (keyboard_active(port)) {
|
||||
auto addKeyButton = [&](PADButton button) {
|
||||
pane.add_select_button(
|
||||
{
|
||||
.key = PADGetButtonName(button),
|
||||
.getValue =
|
||||
[this, port, button] {
|
||||
if (mPendingKeyButton == static_cast<int>(button)) {
|
||||
return pending_key_label();
|
||||
}
|
||||
u32 count = 0;
|
||||
PADKeyButtonBinding* bindings =
|
||||
PADGetKeyButtonBindings(static_cast<u32>(port), &count);
|
||||
if (bindings == nullptr) {
|
||||
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");
|
||||
},
|
||||
})
|
||||
.on_pressed([this, port, button] {
|
||||
cancel_pending_binding();
|
||||
mPendingPort = port;
|
||||
mPendingBindingArmed = false;
|
||||
mPendingKeyButton = static_cast<int>(button);
|
||||
});
|
||||
};
|
||||
|
||||
auto addKeyAxis = [&](PADAxis axis) {
|
||||
pane.add_select_button(
|
||||
{
|
||||
.key = PADGetAxisName(axis),
|
||||
.getValue =
|
||||
[this, port, axis] {
|
||||
if (mPendingKeyAxis == static_cast<int>(axis)) {
|
||||
return pending_key_label();
|
||||
}
|
||||
u32 count = 0;
|
||||
PADKeyAxisBinding* bindings =
|
||||
PADGetKeyAxisBindings(static_cast<u32>(port), &count);
|
||||
if (bindings == nullptr) {
|
||||
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");
|
||||
},
|
||||
})
|
||||
.on_pressed([this, port, axis] {
|
||||
cancel_pending_binding();
|
||||
mPendingPort = port;
|
||||
mPendingBindingArmed = false;
|
||||
mPendingKeyAxis = static_cast<int>(axis);
|
||||
});
|
||||
};
|
||||
|
||||
pane.add_section("Analog");
|
||||
addKeyAxis(PAD_AXIS_TRIGGER_L);
|
||||
addKeyAxis(PAD_AXIS_TRIGGER_R);
|
||||
|
||||
pane.add_section("Digital");
|
||||
addKeyButton(PAD_TRIGGER_L);
|
||||
addKeyButton(PAD_TRIGGER_R);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 axisCount = 0;
|
||||
PADAxisMapping* axes = PADGetAxisMappings(port, &axisCount);
|
||||
u32 buttonCount = 0;
|
||||
@@ -473,6 +687,52 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
||||
break;
|
||||
}
|
||||
case Page::Sticks: {
|
||||
if (keyboard_active(port)) {
|
||||
auto addKeyAxis = [&](PADAxis axis) {
|
||||
pane.add_select_button(
|
||||
{
|
||||
.key = PADGetAxisDirectionLabel(axis),
|
||||
.getValue =
|
||||
[this, port, axis] {
|
||||
if (mPendingKeyAxis == static_cast<int>(axis)) {
|
||||
return pending_key_label();
|
||||
}
|
||||
u32 count = 0;
|
||||
PADKeyAxisBinding* bindings =
|
||||
PADGetKeyAxisBindings(static_cast<u32>(port), &count);
|
||||
if (bindings == nullptr) {
|
||||
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");
|
||||
},
|
||||
})
|
||||
.on_pressed([this, port, axis] {
|
||||
cancel_pending_binding();
|
||||
mPendingPort = port;
|
||||
mPendingBindingArmed = false;
|
||||
mPendingKeyAxis = static_cast<int>(axis);
|
||||
});
|
||||
};
|
||||
|
||||
pane.add_section("Control Stick");
|
||||
addKeyAxis(PAD_AXIS_LEFT_Y_POS);
|
||||
addKeyAxis(PAD_AXIS_LEFT_Y_NEG);
|
||||
addKeyAxis(PAD_AXIS_LEFT_X_NEG);
|
||||
addKeyAxis(PAD_AXIS_LEFT_X_POS);
|
||||
|
||||
pane.add_section("C Stick");
|
||||
addKeyAxis(PAD_AXIS_RIGHT_Y_POS);
|
||||
addKeyAxis(PAD_AXIS_RIGHT_Y_NEG);
|
||||
addKeyAxis(PAD_AXIS_RIGHT_X_NEG);
|
||||
addKeyAxis(PAD_AXIS_RIGHT_X_POS);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 axisCount = 0;
|
||||
PADAxisMapping* axes = PADGetAxisMappings(port, &axisCount);
|
||||
if (axes == nullptr) {
|
||||
@@ -549,6 +809,21 @@ void ControllerConfigWindow::poll_pending_binding() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPendingKeyButton >= 0 || mPendingKeyAxis >= 0) {
|
||||
const s32 scancode = keyboard_key_pressed();
|
||||
if (scancode != PAD_KEY_INVALID) {
|
||||
if (mPendingKeyButton >= 0) {
|
||||
PADSetKeyButtonBinding(static_cast<u32>(mPendingPort),
|
||||
{scancode, static_cast<PADButton>(mPendingKeyButton)});
|
||||
} else {
|
||||
PADSetKeyAxisBinding(static_cast<u32>(mPendingPort),
|
||||
{scancode, static_cast<PADAxis>(mPendingKeyAxis), 0});
|
||||
}
|
||||
finish_pending_key_binding();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPendingButtonMapping != nullptr) {
|
||||
const s32 nativeButton = PADGetNativeButtonPressed(mPendingPort);
|
||||
if (nativeButton != -1) {
|
||||
@@ -590,26 +865,40 @@ void ControllerConfigWindow::finish_pending_binding(int completedPort) {
|
||||
}
|
||||
|
||||
void ControllerConfigWindow::unmap_pending_binding() {
|
||||
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr) {
|
||||
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr &&
|
||||
mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int completedPort = mPendingPort;
|
||||
if (mPendingButtonMapping != nullptr) {
|
||||
mPendingButtonMapping->nativeButton = PAD_NATIVE_BUTTON_INVALID;
|
||||
}
|
||||
if (mPendingAxisMapping != nullptr) {
|
||||
finish_pending_binding(completedPort);
|
||||
} else if (mPendingAxisMapping != nullptr) {
|
||||
mPendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE};
|
||||
mPendingAxisMapping->nativeButton = -1;
|
||||
finish_pending_binding(completedPort);
|
||||
} else if (mPendingKeyButton >= 0) {
|
||||
PADSetKeyButtonBinding(static_cast<u32>(completedPort),
|
||||
{PAD_KEY_INVALID, static_cast<PADButton>(mPendingKeyButton)});
|
||||
finish_pending_key_binding();
|
||||
} else if (mPendingKeyAxis >= 0) {
|
||||
PADSetKeyAxisBinding(static_cast<u32>(completedPort),
|
||||
{PAD_KEY_INVALID, static_cast<PADAxis>(mPendingKeyAxis), 0});
|
||||
finish_pending_key_binding();
|
||||
}
|
||||
finish_pending_binding(completedPort);
|
||||
}
|
||||
|
||||
bool ControllerConfigWindow::capture_active() const {
|
||||
return mPendingButtonMapping != nullptr || mPendingAxisMapping != nullptr;
|
||||
return mPendingButtonMapping != nullptr || mPendingAxisMapping != nullptr ||
|
||||
mPendingKeyButton >= 0 || mPendingKeyAxis >= 0;
|
||||
}
|
||||
|
||||
bool ControllerConfigWindow::pending_input_neutral() const {
|
||||
if (mPendingKeyButton >= 0 || mPendingKeyAxis >= 0) {
|
||||
return keyboard_neutral();
|
||||
}
|
||||
return input_neutral(mPendingPort);
|
||||
}
|
||||
|
||||
@@ -623,16 +912,30 @@ Rml::String ControllerConfigWindow::pending_axis_label() const {
|
||||
|
||||
void ControllerConfigWindow::cancel_pending_binding() {
|
||||
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr &&
|
||||
!mSuppressNavigationUntilNeutral)
|
||||
!mSuppressNavigationUntilNeutral && mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mPendingButtonMapping = nullptr;
|
||||
mPendingAxisMapping = nullptr;
|
||||
mPendingKeyButton = -1;
|
||||
mPendingKeyAxis = -1;
|
||||
mPendingPort = -1;
|
||||
mPendingBindingArmed = false;
|
||||
mSuppressNavigationUntilNeutral = false;
|
||||
mSuppressNavigationPort = -1;
|
||||
}
|
||||
|
||||
void ControllerConfigWindow::finish_pending_key_binding() {
|
||||
mPendingKeyButton = -1;
|
||||
mPendingKeyAxis = -1;
|
||||
mPendingPort = -1;
|
||||
mPendingBindingArmed = false;
|
||||
PADSerializeMappings();
|
||||
}
|
||||
|
||||
Rml::String ControllerConfigWindow::pending_key_label() const {
|
||||
return mPendingBindingArmed ? "Press a key or mouse button..." : "Waiting...";
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -32,6 +32,8 @@ private:
|
||||
Rml::String pending_button_label() const;
|
||||
Rml::String pending_axis_label() const;
|
||||
void cancel_pending_binding();
|
||||
void finish_pending_key_binding();
|
||||
Rml::String pending_key_label() const;
|
||||
|
||||
Page mPage = Page::Controller;
|
||||
Pane* mRightPane = nullptr;
|
||||
@@ -42,6 +44,8 @@ private:
|
||||
int mSuppressNavigationPort = -1;
|
||||
PADButtonMapping* mPendingButtonMapping = nullptr;
|
||||
PADAxisMapping* mPendingAxisMapping = nullptr;
|
||||
int mPendingKeyButton = -1;
|
||||
int mPendingKeyAxis = -1;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -268,8 +268,8 @@ std::map<int, itemInfo> itemMap = {
|
||||
{dItemNo_DUNGEON_BACK_e, {"Ooccoo Jr.", ITEMTYPE_EQUIP_e}},
|
||||
{dItemNo_SWORD_e, {"Ordon Sword"}},
|
||||
{dItemNo_MASTER_SWORD_e, {"Master Sword"}},
|
||||
{dItemNo_WOOD_SHIELD_e, {"Wooden Shield"}},
|
||||
{dItemNo_SHIELD_e, {"Ordon Shield"}},
|
||||
{dItemNo_WOOD_SHIELD_e, {"Ordon Shield"}},
|
||||
{dItemNo_SHIELD_e, {"Wooden Shield"}},
|
||||
{dItemNo_HYLIA_SHIELD_e, {"Hylian Shield"}},
|
||||
{dItemNo_TKS_LETTER_e, {"Ooccoo's Note", ITEMTYPE_EQUIP_e}},
|
||||
{dItemNo_WEAR_CASUAL_e, {"Ordon Clothes"}},
|
||||
|
||||
+134
-6
@@ -1,11 +1,13 @@
|
||||
#include "overlay.hpp"
|
||||
|
||||
#include "aurora/lib/logging.hpp"
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "dusk/achievements.h"
|
||||
#include "magic_enum.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include <algorithm>
|
||||
#include <dolphin/pad.h>
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
@@ -27,6 +29,8 @@ constexpr std::array<std::pair<const char*, const char*>, 3> kAutoSaveLayers{{
|
||||
{"center", "res/org-icon-center.png"},
|
||||
}};
|
||||
|
||||
constexpr auto kMenuNotificationDuration = std::chrono::milliseconds(2500);
|
||||
|
||||
Rml::Element* create_toast(Rml::Element* parent, const Toast& toast) {
|
||||
if (toast.type == "autosave") {
|
||||
auto* logo = append(parent, "logo");
|
||||
@@ -75,6 +79,78 @@ Rml::Element* create_toast(Rml::Element* parent, const Toast& toast) {
|
||||
return elem;
|
||||
}
|
||||
|
||||
Rml::Element* create_controller_warning(Rml::Element* parent) {
|
||||
auto* elem = append(parent, "toast");
|
||||
elem->SetClass("controller-warning", true);
|
||||
|
||||
auto* heading = append(elem, "heading");
|
||||
auto* title = append(heading, "span");
|
||||
title->SetInnerRML("No controller 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.");
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
SDL_Gamepad* gamepad_for_port(u32 port) noexcept {
|
||||
const s32 index = PADGetIndexForPort(port);
|
||||
if (index < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return PADGetSDLGamepadForIndex(static_cast<u32>(index));
|
||||
}
|
||||
|
||||
Rml::String back_button_name() {
|
||||
if (auto* gamepad = gamepad_for_port(PAD_CHAN0)) {
|
||||
switch (SDL_GetGamepadType(gamepad)) {
|
||||
case SDL_GAMEPAD_TYPE_PS3:
|
||||
return "Select";
|
||||
case SDL_GAMEPAD_TYPE_PS4:
|
||||
return "Share";
|
||||
case SDL_GAMEPAD_TYPE_PS5:
|
||||
return "Create";
|
||||
case SDL_GAMEPAD_TYPE_XBOX360:
|
||||
return "Back";
|
||||
case SDL_GAMEPAD_TYPE_XBOXONE:
|
||||
return "View";
|
||||
case SDL_GAMEPAD_TYPE_GAMECUBE:
|
||||
return "R + Start";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "Back";
|
||||
}
|
||||
|
||||
Rml::Element* create_menu_notification(Rml::Element* parent) {
|
||||
auto* elem = append(parent, "toast");
|
||||
elem->SetClass("menu-notification", true);
|
||||
|
||||
auto* message = append(elem, "message");
|
||||
auto* row = append(message, "row");
|
||||
append(row, "span")->SetInnerRML("Press F1 or");
|
||||
auto* icon = append(row, "icon");
|
||||
icon->SetClass("controller", true);
|
||||
append(row, "span")->SetInnerRML(escape(back_button_name()));
|
||||
append(row, "span")->SetInnerRML("to open menu");
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
void remove_element(Rml::Element*& elem) noexcept {
|
||||
if (elem == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (auto* parent = elem->GetParentNode()) {
|
||||
parent->RemoveChild(elem);
|
||||
}
|
||||
elem = nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Overlay::Overlay() : Document(kDocumentSource) {
|
||||
@@ -86,6 +162,15 @@ Overlay::Overlay() : Document(kDocumentSource) {
|
||||
{
|
||||
mCurrentToast->SetPseudoClass("done", true);
|
||||
}
|
||||
} else if (mControllerWarning != nullptr &&
|
||||
event.GetTargetElement() == mControllerWarning &&
|
||||
!mControllerWarning->HasAttribute("open"))
|
||||
{
|
||||
mControllerWarning->SetPseudoClass("done", true);
|
||||
} else if (mMenuNotification != nullptr && event.GetTargetElement() == mMenuNotification &&
|
||||
!mMenuNotification->HasAttribute("open"))
|
||||
{
|
||||
mMenuNotification->SetPseudoClass("done", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -98,6 +183,50 @@ void Overlay::show() {
|
||||
|
||||
void Overlay::update() {
|
||||
Document::update();
|
||||
if (mDocument == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool showControllerWarning = PADGetIndexForPort(PAD_CHAN0) < 0 &&
|
||||
PADGetKeyButtonBindings(PAD_CHAN0, nullptr) == nullptr &&
|
||||
dynamic_cast<Window*>(top_document()) == nullptr;
|
||||
if (showControllerWarning && mControllerWarning == nullptr) {
|
||||
mControllerWarning = create_controller_warning(mDocument);
|
||||
} else if (showControllerWarning && mControllerWarning != nullptr) {
|
||||
mControllerWarning->SetAttribute("open", "");
|
||||
mControllerWarning->SetPseudoClass("opened", true);
|
||||
mControllerWarning->SetPseudoClass("done", false);
|
||||
} else if (!showControllerWarning && mControllerWarning != nullptr) {
|
||||
if (mControllerWarning->IsPseudoClassSet("done") ||
|
||||
!mControllerWarning->IsPseudoClassSet("opened"))
|
||||
{
|
||||
remove_element(mControllerWarning);
|
||||
} else {
|
||||
mControllerWarning->RemoveAttribute("open");
|
||||
}
|
||||
}
|
||||
|
||||
if (mMenuNotification != nullptr) {
|
||||
if (clock::now() >= mMenuNotificationStartTime + kMenuNotificationDuration) {
|
||||
if (mMenuNotification->IsPseudoClassSet("done") ||
|
||||
!mMenuNotification->IsPseudoClassSet("opened"))
|
||||
{
|
||||
remove_element(mMenuNotification);
|
||||
} else {
|
||||
mMenuNotification->RemoveAttribute("open");
|
||||
}
|
||||
} else {
|
||||
mMenuNotification->SetAttribute("open", "");
|
||||
mMenuNotification->SetPseudoClass("opened", true);
|
||||
mMenuNotification->SetPseudoClass("done", false);
|
||||
}
|
||||
}
|
||||
if (consume_menu_notification_request()) {
|
||||
if (mMenuNotification == nullptr) {
|
||||
mMenuNotification = create_menu_notification(mDocument);
|
||||
}
|
||||
mMenuNotificationStartTime = clock::now();
|
||||
}
|
||||
|
||||
auto& toasts = get_toasts();
|
||||
if (mCurrentToast == nullptr) {
|
||||
@@ -123,8 +252,7 @@ void Overlay::update() {
|
||||
// Fallback for large gaps in time where we never actually opened it
|
||||
!mCurrentToast->IsPseudoClassSet("opened"))
|
||||
{
|
||||
mCurrentToast->GetParentNode()->RemoveChild(mCurrentToast);
|
||||
mCurrentToast = nullptr;
|
||||
remove_element(mCurrentToast);
|
||||
toasts.pop_front();
|
||||
} else {
|
||||
mCurrentToast->RemoveAttribute("open");
|
||||
|
||||
@@ -17,7 +17,10 @@ protected:
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
|
||||
Rml::Element* mCurrentToast = nullptr;
|
||||
Rml::Element* mControllerWarning = nullptr;
|
||||
Rml::Element* mMenuNotification = nullptr;
|
||||
clock::time_point mCurrentToastStartTime;
|
||||
clock::time_point mMenuNotificationStartTime;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -260,6 +260,7 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
|
||||
}
|
||||
|
||||
mDoAud_seStartMenu(kSoundPlay);
|
||||
show_menu_notification();
|
||||
|
||||
if (getSettings().audio.menuSounds) {
|
||||
JAISoundHandle* handle = g_mEnvSeMgr.field_0x144.getHandle();
|
||||
@@ -281,7 +282,7 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
|
||||
});
|
||||
apply_intro_animation(mMenuButtons.back()->root(), "delay-1");
|
||||
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Options"));
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Settings"));
|
||||
mMenuButtons.back()->on_pressed([this] {
|
||||
mRestartSuppressed = false;
|
||||
push(std::make_unique<SettingsWindow>(true));
|
||||
|
||||
@@ -27,6 +27,7 @@ std::vector<std::unique_ptr<Document> > sDocumentStack;
|
||||
// Documents that don't participate in the focus stack
|
||||
std::vector<std::unique_ptr<Document> > sPassiveDocuments;
|
||||
std::deque<Toast> sToasts;
|
||||
bool sMenuNotificationRequested = false;
|
||||
|
||||
// Sometimes gamepads can connect and disconnect quickly, especially during
|
||||
// connection negotiation. In this case, we'll receive an _ADDED event for a
|
||||
@@ -361,4 +362,14 @@ std::deque<Toast>& get_toasts() noexcept {
|
||||
return sToasts;
|
||||
}
|
||||
|
||||
void show_menu_notification() noexcept {
|
||||
sMenuNotificationRequested = true;
|
||||
}
|
||||
|
||||
bool consume_menu_notification_request() noexcept {
|
||||
const bool requested = sMenuNotificationRequested;
|
||||
sMenuNotificationRequested = false;
|
||||
return requested;
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -84,6 +84,8 @@ Insets safe_area_insets(Rml::Context* context) noexcept;
|
||||
|
||||
void push_toast(Toast toast) noexcept;
|
||||
std::deque<Toast>& get_toasts() noexcept;
|
||||
void show_menu_notification() noexcept;
|
||||
bool consume_menu_notification_request() noexcept;
|
||||
|
||||
const char* battery_icon(SDL_PowerState state, int level) noexcept;
|
||||
const char* connection_state_icon(SDL_JoystickConnectionState state) noexcept;
|
||||
|
||||
@@ -280,7 +280,6 @@ void main01(void) {
|
||||
for (int sim_tick = 0; sim_tick < pacing.sim_ticks_to_run; ++sim_tick) {
|
||||
dusk::frame_interp::begin_sim_tick();
|
||||
mDoCPd_c::read();
|
||||
DuskDebugPad();
|
||||
dusk::gyro::read(pacing.sim_pace);
|
||||
fapGm_Execute();
|
||||
mDoAud_Execute();
|
||||
|
||||
Reference in New Issue
Block a user