Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod

This commit is contained in:
TakaRikka
2026-08-14 19:17:16 -07:00
28 changed files with 497 additions and 280 deletions
+1
View File
@@ -1436,6 +1436,7 @@ set(DUSK_FILES
src/dusk/gamepad_color.cpp
src/dusk/globals.cpp
src/dusk/gyro.cpp
src/dusk/hq_minimap.cpp
src/dusk/imgui/ImGuiActorSpawner.cpp
src/dusk/imgui/ImGuiBloomWindow.cpp
src/dusk/imgui/ImGuiBloomWindow.hpp
+7 -12
View File
@@ -38,7 +38,7 @@ void JFWDisplay::ctor_subroutine(bool enableAlpha) {
mTickRate = 0;
mCombinationRatio = 0.0f;
field_0x30 = 0;
field_0x2c = OSGetTick();
field_0x2c = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
field_0x34 = 0;
field_0x48 = 0;
field_0x4a = 0;
@@ -258,7 +258,7 @@ void JFWDisplay::beginRender() {
waitForTick(mTickRate, mFrameRate);
JUTVideo::getManager()->waitRetraceIfNeed();
OSTick tick = OSGetTick();
OSTick tick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
field_0x30 = tick - field_0x2c;
field_0x2c = tick;
field_0x34 = field_0x2c - JUTVideo::getVideoLastTick();
@@ -371,7 +371,7 @@ 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.Sleep(targetNs);
const auto sleepTime = limiter.Sleep(targetNs);
dusk::frameUsagePct =
100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(targetNs));
}
@@ -381,15 +381,12 @@ static void waitForTick(u32 p1, u16 p2) {
#if TARGET_PC
static Limiter limiter;
if (dusk::frame_interp::is_enabled() && !dusk::getTransientSettings().skipFrameRateLimit) {
dusk::frameUsagePct = 0.f;
return;
if (dusk::frame_interp::is_enabled() || dusk::getTransientSettings().turboMode) {
limiter.Reset();
dusk::frameUsagePct = 0.f;
return;
}
if (dusk::getTransientSettings().skipFrameRateLimit) {
p1 = OS_TIMER_CLOCK / 120;
}
if (fopOvlpM_IsPeek() && dusk::getTransientSettings().stateShareLoadActive) {
return;
}
@@ -399,7 +396,6 @@ static void waitForTick(u32 p1, u16 p2) {
if (p1 != 0) {
#if TARGET_PC
static Limiter limiter;
waitPrecise(limiter, static_cast<Uint64>(OSTicksToMicroseconds(p1)) * 1000ULL);
#else
static OSTime nextTick = OSGetTime();
@@ -413,7 +409,6 @@ static void waitForTick(u32 p1, u16 p2) {
} else {
u32 uVar1 = (p2 == 0) ? 1 : p2;
#if TARGET_PC
static Limiter limiter;
waitPrecise(limiter, static_cast<Uint64>((RETRACE_PERIOD * uVar1).count()));
#else
static u32 nextCount = VIGetRetraceCount();
+2 -2
View File
@@ -42,7 +42,7 @@ JUTVideo::JUTVideo(GXRenderModeObj const* param_0) {
mRetraceCount = VIGetRetraceCount();
field_0x10 = 1;
field_0x18 = 0;
sVideoLastTick = OSGetTick();
sVideoLastTick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
sVideoInterval = 670000;
mPreRetraceCallback = VISetPreRetraceCallback(preRetraceProc);
mPostRetraceCallback = VISetPostRetraceCallback(postRetraceProc);
@@ -68,7 +68,7 @@ void JUTVideo::preRetraceProc(u32 retrace_count) {
(*sManager->mPreCallback)(retrace_count);
}
OSTick tick = OSGetTick();
OSTick tick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
sVideoInterval = tick - sVideoLastTick;
sVideoLastTick = tick;
+1 -1
View File
@@ -2346,7 +2346,7 @@ void dScnKy_env_light_c::setLight() {
u8 next_pal_end_id;
#if TARGET_PC
const f32 deltaTime = dusk::game_clock::consume_interval(this);
timeScale = deltaTime / dusk::game_clock::period_for_original_frames(1.0f);
timeScale = deltaTime / dusk::game_clock::kSimPeriod;
#endif
setLight_palno_get(&g_env_light.PrevCol, &g_env_light.UseCol, &g_env_light.wether_pat0,
&g_env_light.wether_pat1, &prev_pal_start_id, &prev_pal_end_id,
+1 -1
View File
@@ -118,7 +118,7 @@ static int dKyeff_Create(kankyo_class* i_this) {
if (strcmp(dComIfGp_getStartStageName(), "Name") == 0) {
camera_process_class* camera = dComIfGp_getCamera(0);
OSTime time = OSGetTime();
OSTime time = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime());
OSTicksToCalendarTime(time, &calendar);
g_env_light.global_wind_influence.vec.x = 1.0f;
+3 -128
View File
@@ -16,23 +16,14 @@
#ifdef TARGET_PC
#include "dusk/settings.h"
#include "dusk/hq_minimap.hpp"
#include "m_Do/m_Do_graphic.h"
#include <dolphin/gx/GXAurora.h>
#include <aurora/math.hpp>
#include <algorithm>
#include <array>
#include <cmath>
#include <functional>
#include <limits>
#include <numbers>
#include <span>
constexpr u16 kMapIconResolutionMultiplier = 4;
constexpr u16 kMapImageSide = 16 * kMapIconResolutionMultiplier;
constexpr u32 kMapImageTotalPixels = kMapImageSide * kMapImageSide;
typedef std::function<u8(size_t, size_t)> PaintI8Fn;
u16 scaled_map_axis(u16 value, f32 scale) {
const auto scaledValue =
@@ -59,27 +50,6 @@ aurora::Vec2<u16> map_render_size_for(u16 width, u16 height) {
scaled_map_axis(height, irScaleY * hudScale),
};
}
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() {
@@ -100,106 +70,11 @@ void dMpath_n::dTexObjAggregate_c::create() {
JUT_ASSERT(72, image != NULL);
JUT_ASSERT(73, image->minFilter == GX_NEAR);
JUT_ASSERT(74, image->magFilter == GX_NEAR);
IF_DUSK(dusk::hq_minimap::register_pointer(data[lp1], reinterpret_cast<u8*>(image) + image->imageOffset));
mDoLib_setResTimgObj(image, mp_texObj[lp1], 0, NULL);
}
#if TARGET_PC
static bool hqTexsDrawn = false;
static u8 hqCircleData[kMapImageTotalPixels];
static u8 hqCircleAltData[kMapImageTotalPixels];
static u8 hqNijumaruData[kMapImageTotalPixels];
static u8 hqEnterData[kMapImageTotalPixels];
static u8 hqTryForceData[kMapImageTotalPixels];
if (!hqTexsDrawn) {
constexpr auto center = kMapImageSide / 2.0f;
constexpr auto radiusSq = center * center;
// 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;
});
// 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;
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;
}
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
IF_DUSK(dusk::hq_minimap::initialize_if_needed());
}
void dMpath_n::dTexObjAggregate_c::remove() {
+1 -1
View File
@@ -1798,7 +1798,7 @@ void dMenu_Ring_c::drawSelectItem() {
if (field_0x674[i] != 0) {
#if TARGET_PC
mSelectItemSlideElapsed[i] += dusk::game_clock::consume_interval(this);
const f32 u = std::min(mSelectItemSlideElapsed[i] / dusk::game_clock::period_for_original_frames(10.0f), 1.0f);
const f32 u = std::min(mSelectItemSlideElapsed[i] / (dusk::game_clock::kSimPeriod * 10.0f), 1.0f);
if (u >= 1.0f) {
setSelectItemForce(i);
} else {
+7 -5
View File
@@ -123,7 +123,13 @@ public:
#endif
}
void setCaptureFlag() { mFlag = 1; }
void setCaptureFlag() {
mFlag = 1;
#ifdef TARGET_PC
dusk::frame_interp::request_presentation_sync();
#endif
}
bool checkDraw() { return mFlag; }
u8 getAlpha() { return mAlpha; }
u8 getTopFlag() { return mTopFlag; }
@@ -1092,10 +1098,6 @@ 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() {
+2 -2
View File
@@ -1828,7 +1828,7 @@ int dSv_info_c::memory_to_card(char* card_ptr, int dataNum) {
savedata->getPlayer().getPlayerInfo().setTotalTime(play_time);
}
savedata->getPlayer().getPlayerStatusB().setDateIpl(OSGetTime());
savedata->getPlayer().getPlayerStatusB().setDateIpl(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()));
memcpy(card_ptr, savedata, sizeof(dSv_save_c));
card_ptr += 0x958;
@@ -2035,7 +2035,7 @@ void flagFile_c::listenPropertyEvent(const JORPropertyEvent* i_event) {
}
case 102: {
OSCalendarTime time;
OSTicksToCalendarTime(OSGetTime(), &time);
OSTicksToCalendarTime(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()), &time);
const char* start_stage_name = dComIfGp_getStartStageName();
char filename[64];
+9
View File
@@ -41,6 +41,15 @@ bool isActionBound(ActionBinds action, u32 port) {
return getActionBindButton(action, port) != PAD_NATIVE_BUTTON_INVALID;
}
bool isActionBoundAnyPort(ActionBinds action) {
for (u32 port = 0; port < PAD_CHANMAX; ++port) {
if (isActionBound(action, port)) {
return true;
}
}
return false;
}
void updateActionBindings() {
for (u32 port = 0; port < PAD_CHANMAX; ++port) {
// Move the current press to the previous frame
+1
View File
@@ -31,6 +31,7 @@ using ActionBindsMap = std::unordered_map<ActionBinds, ActionBindData>;
ActionBindsMap& getActionBinds();
bool isActionBound(ActionBinds action, u32 port);
bool isActionBoundAnyPort(ActionBinds action);
void updateActionBindings();
+4
View File
@@ -147,6 +147,10 @@ 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);
if (!g_enabled) {
g_interpolating = false;
clear_replacements();
}
}
bool is_enabled() {
+86 -55
View File
@@ -1,104 +1,135 @@
#include "dusk/game_clock.h"
#include <algorithm>
#include <aurora/time.hpp>
#include <chrono>
#include <cmath>
#include <unordered_map>
#include <dusk/frame_interpolation.h>
#include <unordered_map>
namespace dusk::game_clock {
using clock = std::chrono::steady_clock;
using native_clock = aurora::time::native_clock;
using game_clock = aurora::time::game_clock;
FrameTiming g_frameTiming;
namespace {
bool s_initialized = false;
clock::time_point s_previous_sample{};
clock::time_point s_current_snapshot_time{};
bool s_fixedStepActive = false;
bool s_simTickActive = false;
native_clock::time_point s_previousNativeSample{};
game_clock::time_point s_latestGameSample{};
game_clock::time_point s_currentSnapshotTime{};
game_clock::time_point s_pendingSimTime{};
std::unordered_map<uintptr_t, clock::time_point> s_interval_last_sample;
std::unordered_map<uintptr_t, game_clock::time_point> s_intervalLastSample;
constexpr clock::duration kSimPeriodDuration =
std::chrono::duration_cast<clock::duration>(std::chrono::duration<float>(sim_pace()));
constexpr clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250);
constexpr int kMaxSimTicksPerFrame = 2;
constexpr game_clock::duration kSimPeriodDuration =
std::chrono::duration_cast<game_clock::duration>(std::chrono::duration<float>(kSimPeriod));
constexpr native_clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250);
constexpr int kMaxSimTicksPerFrame = static_cast<int>(aurora::time::kMaximumTimeScale) * 4;
} // namespace
void ensure_initialized() {
void initialize() {
if (s_initialized) {
return;
}
s_previous_sample = clock::now();
s_current_snapshot_time = s_previous_sample;
s_previousNativeSample = native_clock::now();
s_latestGameSample = game_clock::now();
s_currentSnapshotTime = s_latestGameSample;
s_pendingSimTime = s_latestGameSample;
s_initialized = true;
}
void reset_frame_timer() {
s_previous_sample = clock::now();
s_current_snapshot_time = s_previous_sample - kSimPeriodDuration;
void reset() {
s_previousNativeSample = native_clock::now();
s_latestGameSample = game_clock::now();
s_currentSnapshotTime = s_latestGameSample - kSimPeriodDuration;
s_pendingSimTime = s_currentSnapshotTime;
s_simTickActive = false;
}
MainLoopPacer advance_main_loop() {
ensure_initialized();
const FrameTiming& advance() {
const auto nativeNow = native_clock::now();
const auto gameNow = game_clock::now();
const auto nativeFrameGap = nativeNow - s_previousNativeSample;
s_previousNativeSample = nativeNow;
s_latestGameSample = gameNow;
const clock::time_point now = clock::now();
const clock::duration frame_gap = now - s_previous_sample;
const float presentation_dt = std::chrono::duration<float>(frame_gap).count();
s_previous_sample = now;
auto& out = g_frameTiming;
out = {.dt = std::chrono::duration<float>().count()};
MainLoopPacer out{};
out.presentation_dt_seconds = presentation_dt;
const float timeScale = aurora::time::scale();
const bool interpolating =
getSettings().game.enableFrameInterpolation.getValue() != FrameInterpMode::Off;
const bool separatePresentation = interpolating || timeScale != 1.0f;
out.interpolating = interpolating;
out.separatePresentation = separatePresentation;
s_fixedStepActive = separatePresentation;
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();
if (!should_interpolate) {
s_current_snapshot_time = now;
out.sim_ticks_to_run = 1;
if (!separatePresentation) {
s_currentSnapshotTime = gameNow;
out.numSimTicks = 1;
return out;
}
if (frame_gap > kAbnormalGapResetThreshold) {
s_current_snapshot_time = now - kSimPeriodDuration;
out.sim_ticks_to_run = 0;
const auto simulationTarget = interpolating ? gameNow - kSimPeriodDuration : gameNow;
if (timeScale == 0.f || nativeFrameGap > kAbnormalGapResetThreshold) {
s_currentSnapshotTime = simulationTarget;
out.numSimTicks = 0;
return out;
}
int sim_ticks_to_run = 0;
clock::time_point projected_snapshot_time = s_current_snapshot_time;
const clock::time_point render_time = now - kSimPeriodDuration;
while (sim_ticks_to_run < kMaxSimTicksPerFrame && projected_snapshot_time < render_time) {
projected_snapshot_time += kSimPeriodDuration;
sim_ticks_to_run++;
int numSimTicks = 0;
auto projectedSnapshotTime = s_currentSnapshotTime;
while (numSimTicks < kMaxSimTicksPerFrame) {
const bool tickDue = interpolating ?
projectedSnapshotTime < simulationTarget :
projectedSnapshotTime + kSimPeriodDuration <= simulationTarget;
if (!tickDue) {
break;
}
projectedSnapshotTime += kSimPeriodDuration;
numSimTicks++;
}
out.sim_ticks_to_run = sim_ticks_to_run;
out.numSimTicks = numSimTicks;
return out;
}
void begin_sim_tick() {
s_pendingSimTime =
s_fixedStepActive ? s_currentSnapshotTime + kSimPeriodDuration : s_latestGameSample;
s_simTickActive = true;
}
void commit_sim_tick() {
ensure_initialized();
s_current_snapshot_time += kSimPeriodDuration;
if (s_simTickActive) {
s_currentSnapshotTime = s_pendingSimTime;
s_simTickActive = false;
} else {
s_currentSnapshotTime += kSimPeriodDuration;
}
}
float sample_interpolation_step() {
ensure_initialized();
const float step =
std::chrono::duration<float>(clock::now() - s_current_snapshot_time).count() / sim_pace();
std::chrono::duration<float>(game_clock::now() - s_currentSnapshotTime).count() /
kSimPeriod;
return std::clamp(step, 0.0f, 1.0f);
}
float consume_interval(const void* consumer) {
ensure_initialized();
const uintptr_t key = reinterpret_cast<uintptr_t>(consumer);
const clock::time_point now = clock::now();
float dt = ui_initial_dt();
const auto it = s_interval_last_sample.find(key);
if (it != s_interval_last_sample.end()) {
const auto key = reinterpret_cast<uintptr_t>(consumer);
const auto now = s_simTickActive ? s_pendingSimTime : game_clock::now();
const float timeScale = aurora::time::scale();
float dt = kUiInitialDt * timeScale;
if (const auto it = s_intervalLastSample.find(key); it != s_intervalLastSample.end()) {
dt = std::chrono::duration<float>(now - it->second).count();
dt = std::min(dt, ui_maximum_dt());
const float maximumDt = std::max(kUiMaximumDt * timeScale, kSimPeriod);
dt = std::min(dt, maximumDt);
}
s_interval_last_sample[key] = now;
s_intervalLastSample[key] = now;
return dt;
}
+18 -13
View File
@@ -2,22 +2,27 @@
namespace dusk::game_clock {
void ensure_initialized();
void reset_frame_timer();
// Amount of time that a simulation tick advances
constexpr float kSimPeriod = 1.0f / 30.0f;
constexpr float kUiMaximumDt = 0.05f;
constexpr float kUiInitialDt = 1.0f / 60.0f;
constexpr float sim_pace() { return 1.0f / 30.0f; }
constexpr float period_for_original_frames(float frame_count) { return frame_count * sim_pace(); }
constexpr float ui_maximum_dt() { return 0.05f; }
constexpr float ui_initial_dt() { return 1.0f / 60.0f; }
struct MainLoopPacer {
float presentation_dt_seconds;
bool is_interpolating;
int sim_ticks_to_run;
float sim_pace;
struct FrameTiming {
// Amount of time elapsed in seconds since the last advance
float dt;
// Whether interpolation is active
bool interpolating;
// Run simulation and presentation separately (for interpolation or time scaling)
bool separatePresentation;
// Number of simulation ticks to run
int numSimTicks;
};
extern FrameTiming g_frameTiming;
MainLoopPacer advance_main_loop();
void initialize();
void reset();
const FrameTiming& advance();
void begin_sim_tick();
void commit_sim_tick();
float sample_interpolation_step();
+210
View File
@@ -0,0 +1,210 @@
#include "aurora/texture.hpp"
#include "dusk/texture_replacements.hpp"
#include "fmt/format.h"
#include <array>
#include <cassert>
#include <span>
#include <numbers>
namespace {
constexpr u16 kMapIconResolutionMultiplier = 4;
constexpr u16 kMapImageSide = 16 * kMapIconResolutionMultiplier;
constexpr u32 kMapImageTotalPixels = kMapImageSide * kMapImageSide;
// give higher priority to user and mod replacements
constexpr auto kInternalTextureReplacementPriority = dusk::texture_replacements::kUserTextureReplacementPriority - 1;
typedef std::function<u8(size_t, size_t)> PaintI8Fn;
enum class ArcIndex : int {
Circle16 = 82, // map_icon_circle16x16_4i.bti - simple circle
Circle = 76, // im_map_icon_circle_4i.bti - outlined circle
Nijumaru = 78, // im_map_icon_nijumaru_4i.bti - concentric rings
Enter = 77, // im_map_icon_enter_4i.bti - outlined octagram
TryForce = 81, // im_map_icon_try_force_4i.bti - outlined circle with triangle
};
struct Replacement {
ArcIndex index;
PaintI8Fn painter;
};
struct Icon {
u8* origData = nullptr;
std::unique_ptr<u8[]> newData;
std::string label;
std::optional<aurora::texture::ReplacementRegistration> reg;
};
bool s_initialized = false;
bool s_active = false;
std::unordered_map<int, Icon> s_icons;
void paint_i8(std::span<u8> dst, size_t width, PaintI8Fn paint) {
assert(width % 8 == 0 && dst.size() % 32 == 0);
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);
}
}
void draw_all_replacements() {
constexpr auto center = kMapImageSide / 2.0f;
constexpr auto radiusSq = center * center;
// clang-format off
const auto replacements = std::to_array<Replacement>({
{
ArcIndex::Circle16,
[=](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;
}
},
{
ArcIndex::Circle,
[=](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;
return dSq < radiusSq ? (dSq < innerRadiusSq ? 0x22 : 0x11) : 0;
}
},
{
ArcIndex::Nijumaru,
[=](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) {
auto ringIndex = static_cast<size_t>(std::trunc(std::sqrt(dSq) / kMapImageSide * 12));
ringIndex = std::min(ringIndex, sizeof(nijumaruRings) - 1);
return nijumaruRings[ringIndex];
}
return u8{0};
}
},
{
ArcIndex::Enter,
[=](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);
}
},
{
ArcIndex::TryForce,
[=](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);
}
}
});
// clang-format on
for (const auto r : replacements) {
auto pixels = std::make_unique_for_overwrite<u8[]>(kMapImageTotalPixels);
paint_i8(std::span{pixels.get(), kMapImageTotalPixels}, kMapImageSide, r.painter);
auto& icon = s_icons[static_cast<int>(r.index)];
icon.newData = std::move(pixels);
icon.label = fmt::format("hq minimap icon {}", static_cast<int>(r.index));
}
}
} // namespace
namespace dusk::hq_minimap {
void register_pointer(int idx, u8* ptr) {
if (s_initialized) {
return;
}
s_icons[idx].origData = ptr;
}
void set_active(bool active) {
s_active = active;
}
void update() {
if (!s_initialized) {
return;
}
for (auto& [idx, icon] : s_icons) {
const bool shouldBeRegistered = s_active && icon.origData && icon.newData;
if (shouldBeRegistered && !icon.reg) {
aurora::texture::ReplacementKey key{aurora::texture::TexturePointerKey{icon.origData}};
aurora::texture::RawTextureReplacement repl{
.bytes = std::span{icon.newData.get(), kMapImageTotalPixels},
.width = kMapImageSide,
.height = kMapImageSide,
.mipCount = 1,
.gxFormat = GX_TF_I8,
.label = icon.label,
};
icon.reg = aurora::texture::register_replacement(
key, repl, {.priority = kInternalTextureReplacementPriority});
} else if (!shouldBeRegistered && icon.reg) {
aurora::texture::unregister_replacement(*icon.reg);
icon.reg.reset();
}
}
}
void initialize_if_needed() {
if (s_initialized) {
return;
}
draw_all_replacements();
s_initialized = true;
update();
}
} // namespace dusk::hq_minimap
+21
View File
@@ -0,0 +1,21 @@
#pragma once
namespace dusk::hq_minimap {
/// Adds a mapping of an image resource index (into `Always.arc`) to the pointer to its image data.
/// If `initialize_if_needed` has been called, this is a no-op. Pointers are expected to be stable
/// and valid for the program's entire lifetime.
void register_pointer(int idx, u8* ptr);
/// Sets whether HQ minimap texture replacements should be active or not. Does not manage
/// replacement registrations itself; see `update`.
void set_active(bool active);
/// Registers or unregisters texture replacements depending on active state.
void update();
/// Called once after registering image pointers, in which their HQ replacements are procedurally
/// drawn and `update` is called. Further calls are no-ops.
void initialize_if_needed();
}
+31 -5
View File
@@ -1,8 +1,9 @@
#include <algorithm>
#include <array>
#include <aurora/aurora.h>
#include <chrono>
#include <numeric>
#include <string_view>
#include <chrono>
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui.h"
@@ -20,6 +21,7 @@
#include "dusk/frame_interpolation.h"
#include "dusk/livesplit.h"
#include "dusk/main.h"
#include "dusk/presentation.hpp"
#include "dusk/settings.h"
#include "dusk/ui/ui.hpp"
#include "f_pc/f_pc_manager.h"
@@ -37,6 +39,8 @@ using namespace std::string_literals;
using namespace std::string_view_literals;
namespace {
constexpr float kTurboTimeScale = 4.f;
ImGuiWindow* FindDragScrollWindow(ImGuiWindow* window) {
while (window != nullptr) {
const bool canScrollX = window->ScrollMax.x > 0.0f;
@@ -235,10 +239,32 @@ namespace dusk {
}
void ImGuiConsole::UpdateSettings() {
getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind &&
(ImGui::IsKeyDown(ImGuiKey_Tab) || getActionBindHoldAnyPort(ActionBinds::TURBO_SPEED_BUTTON));
static bool previousTurboActive = false;
static bool previousSlowActive = false;
static float previousTimeScale = 1.0f;
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)) {
const bool turboBound = isActionBoundAnyPort(ActionBinds::TURBO_SPEED_BUTTON);
const bool turboActive =
getSettings().game.enableTurboKeybind &&
(turboBound ? getActionBindHoldAnyPort(ActionBinds::TURBO_SPEED_BUTTON) :
ImGui::IsKeyDown(ImGuiKey_Tab));
const bool slowDown = turboActive && ImGui::GetIO().KeyShift;
if (turboActive != previousTurboActive) {
getTransientSettings().turboMode = turboActive;
presentation::update_frame_rate_preference();
if (turboActive) {
previousTimeScale = aurora_get_timescale();
aurora_set_timescale(slowDown ? 1.f / kTurboTimeScale : kTurboTimeScale);
} else {
aurora_set_timescale(previousTimeScale);
}
} else if (turboActive && slowDown != previousSlowActive) {
aurora_set_timescale(slowDown ? 1.f / kTurboTimeScale : kTurboTimeScale);
}
previousTurboActive = turboActive;
previousSlowActive = slowDown;
if (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;
}
if (mDoMain::developmentMode != 1) {
@@ -270,7 +296,7 @@ namespace dusk {
m_isHidden = true;
}
}
bool showMenu = !m_isHidden;
// The menu bar renders with ImGuiCol_WindowBg behind it. We just want ImGuiCol_MenuBarBg,
-5
View File
@@ -25,10 +25,6 @@
#include <TargetConditionals.h>
#endif
namespace aurora::gx {
extern bool enableLodBias;
}
namespace dusk {
ImGuiMenuTools::ImGuiMenuTools() {}
@@ -76,7 +72,6 @@ namespace dusk {
getSettings().game.disableWaterRefraction.setValue(disableWaterRefraction);
config::save();
}
ImGui::Checkbox("Enable LOD Bias", &aurora::gx::enableLodBias);
ImGui::EndMenu();
}
+4
View File
@@ -10,6 +10,10 @@ namespace dusk::presentation {
namespace {
float preferred_frame_rate() {
if (getTransientSettings().turboMode) {
return 0.0f;
}
switch (getSettings().game.enableFrameInterpolation.getValue()) {
case FrameInterpMode::Off:
return 30.0f;
+3 -1
View File
@@ -76,6 +76,7 @@ UserSettings g_userSettings = {
.resampler {"game.resampler", Resampler::Bilinear},
.enableMapBackground {"game.enableMapBackground", true},
.disableCutscenePillarboxing {"game.disableCutscenePillarboxing", false},
.enableHighQualityMinimapTextures {"game.enableHighQualityMinimapTextures", true},
// Audio
.noLowHpSound {"game.noLowHpSound", false},
@@ -283,6 +284,7 @@ void registerSettings() {
Register(g_userSettings.game.shadowResolutionMultiplier);
Register(g_userSettings.game.enableMapBackground);
Register(g_userSettings.game.disableCutscenePillarboxing);
Register(g_userSettings.game.enableHighQualityMinimapTextures);
Register(g_userSettings.game.enableFastIronBoots);
Register(g_userSettings.game.canTransformAnywhere);
Register(g_userSettings.game.fastRoll);
@@ -394,7 +396,7 @@ static TransientSettings g_transientSettings = {
.colliderViewOpacity = 50.0f,
.drawRange = 100.0f,
},
.skipFrameRateLimit = false,
.turboMode = false,
};
TransientSettings& getTransientSettings() {
+2 -1
View File
@@ -208,6 +208,7 @@ struct UserSettings {
ConfigVar<Resampler> resampler;
ConfigVar<bool> enableMapBackground;
ConfigVar<bool> disableCutscenePillarboxing;
ConfigVar<bool> enableHighQualityMinimapTextures;
// Audio
ConfigVar<bool> noLowHpSound;
@@ -326,7 +327,7 @@ struct CollisionViewSettings {
struct TransientSettings {
CollisionViewSettings collisionView;
bool skipFrameRateLimit;
bool turboMode;
bool moveLinkActive;
bool stateShareLoadActive;
};
+12 -6
View File
@@ -6,18 +6,23 @@ namespace dusk {
struct SpeedrunInfo {
void startRun() {
m_isRunStarted = true;
m_startTimestamp = OSGetTime();
m_rtaStartTimestamp = OSGetNativeTime();
m_igtStartTimestamp = OSGetTime();
}
void stopRun() {
m_isRunStarted = false;
m_endTimestamp = OSGetTime() - m_startTimestamp;
m_rtaTimer = OSGetNativeTime() - m_rtaStartTimestamp;
if (!m_isPauseIGT) {
m_igtTimer = OSGetTime() - m_igtStartTimestamp - m_totalLoadTime;
}
}
void reset() {
m_isRunStarted = false;
m_startTimestamp = 0;
m_endTimestamp = 0;
m_rtaStartTimestamp = 0;
m_rtaTimer = 0;
m_igtStartTimestamp = 0;
m_isPauseIGT = false;
m_loadStartTimestamp = 0;
m_totalLoadTime = 0;
@@ -25,8 +30,9 @@ struct SpeedrunInfo {
}
bool m_isRunStarted = false;
OSTime m_startTimestamp = 0;
OSTime m_endTimestamp = 0;
OSTime m_rtaStartTimestamp = 0;
OSTime m_rtaTimer = 0;
OSTime m_igtStartTimestamp = 0;
bool m_isPauseIGT = false;
OSTime m_loadStartTimestamp = 0;
+23 -14
View File
@@ -203,10 +203,19 @@ void remove_element(Rml::Element*& elem) noexcept {
} // namespace
static std::string FormatTime(OSTime ticks) {
OSCalendarTime t;
OSTicksToCalendarTime(ticks, &t);
return fmt::format("{0:02}:{1:02}:{2:02}.{3:03}", t.hour, t.min, t.sec, t.msec);
static std::string FormatElapsedTime(OSTime ticksElapsed) {
using namespace std::chrono;
milliseconds ms{OSTicksToMilliseconds(ticksElapsed)};
const hours hr = duration_cast<hours>(ms);
ms -= hr;
const minutes min = duration_cast<minutes>(ms);
ms -= min;
const seconds sec = duration_cast<seconds>(ms);
ms -= sec;
return fmt::format("{0:02}:{1:02}:{2:02}.{3:03}", hr.count(), min.count(), sec.count(), ms.count());
}
Overlay::Overlay() : Document(kDocumentSource, true, DocumentScope::Overlay) {
@@ -317,33 +326,33 @@ void Overlay::update() {
mDoCPd_c::getHoldA(PAD_1) && mDoCPd_c::getTrigY(PAD_1))
{
if (m_speedrunInfo.m_isRunStarted) {
m_speedrunInfo.m_endTimestamp = OSGetTime() - m_speedrunInfo.m_startTimestamp;
m_speedrunInfo.m_isRunStarted = false;
m_speedrunInfo.stopRun();
}
}
OSTime elapsedTime = 0;
OSTime rtaElapsedTime = 0;
if (m_speedrunInfo.m_isRunStarted) {
elapsedTime = OSGetTime() - m_speedrunInfo.m_startTimestamp;
} else if (m_speedrunInfo.m_endTimestamp != 0) {
elapsedTime = m_speedrunInfo.m_endTimestamp;
rtaElapsedTime = OSGetNativeTime() - m_speedrunInfo.m_rtaStartTimestamp;
} else if (m_speedrunInfo.m_rtaTimer != 0) {
rtaElapsedTime = m_speedrunInfo.m_rtaTimer;
}
if (!m_speedrunInfo.m_isPauseIGT) {
m_speedrunInfo.m_igtTimer = elapsedTime - m_speedrunInfo.m_totalLoadTime;
if (m_speedrunInfo.m_isRunStarted && !m_speedrunInfo.m_isPauseIGT) {
m_speedrunInfo.m_igtTimer = OSGetTime() - m_speedrunInfo.m_igtStartTimestamp -
m_speedrunInfo.m_totalLoadTime;
}
mSpeedrunTimer->SetAttribute("open", "");
if (getSettings().game.showSpeedrunRTATimer) {
mSpeedrunRta->SetAttribute("open", "");
mSpeedrunRta->SetInnerRML(escape(fmt::format("RTA {}", FormatTime(elapsedTime))));
mSpeedrunRta->SetInnerRML(escape(fmt::format("RTA {}", FormatElapsedTime(rtaElapsedTime))));
} else {
mSpeedrunRta->RemoveAttribute("open");
}
mSpeedrunIgt->SetInnerRML(
escape(fmt::format("IGT {}", FormatTime(m_speedrunInfo.m_igtTimer))));
escape(fmt::format("IGT {}", FormatElapsedTime(m_speedrunInfo.m_igtTimer))));
} else {
mSpeedrunTimer->RemoveAttribute("open");
}
+4 -3
View File
@@ -19,15 +19,16 @@ static int fopOvlpReq_phase_Done(overlap_request_class* i_overlapReq) {
i_overlapReq->field_0x8 = 0;
i_overlapReq->field_0xc = 0;
#if TARGET_PC
#if TARGET_PC
if (dusk::getSettings().game.speedrunMode) {
if (dusk::m_speedrunInfo.m_isRunStarted) {
dusk::m_speedrunInfo.m_isPauseIGT = false;
dusk::m_speedrunInfo.m_totalLoadTime += OSGetTime() - dusk::m_speedrunInfo.m_loadStartTimestamp;
dusk::m_speedrunInfo.m_totalLoadTime +=
OSGetTime() - dusk::m_speedrunInfo.m_loadStartTimestamp;
dusk::m_speedrunInfo.m_loadStartTimestamp = OSGetTime();
}
}
#endif
#endif
return cPhs_NEXT_e;
}
+3 -3
View File
@@ -22,7 +22,7 @@
#include "m_Do/m_Do_controller_pad.h"
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#include "dusk/game_clock.h"
#endif
#include "tracy/Tracy.hpp"
@@ -68,8 +68,8 @@ void fpcM_Management(fpcM_ManagementFunc i_preExecuteFn, fpcM_ManagementFunc i_p
}
#ifdef TARGET_PC
// FRAME INTERP NOTE: Called in m_Do_main when interp is enabled
if (!dusk::frame_interp::is_enabled())
// The main loop manages painting when simulation and presentation are separated.
if (!dusk::game_clock::g_frameTiming.separatePresentation)
#endif
{
cAPIGph_Painter();
+1 -1
View File
@@ -320,7 +320,7 @@ static void mDoMemCdRWm_BuildHeader(mDoMemCdRWm_HeaderData* header) {
#endif
OSCalendarTime time;
OSTicksToCalendarTime(OSGetTime(), &time);
OSTicksToCalendarTime(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()), &time);
#if TARGET_PC
if (dusk::version::isRegionPal()) {
+1 -1
View File
@@ -527,7 +527,7 @@ void myExceptionCallback(u16, OSContext*, u32, u32) {
u32 btnHold;
u32 btnTrig;
mDoMain::sHungUpTime = OSGetTime();
mDoMain::sHungUpTime = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime());
OSReportEnable();
cAPICPad_recalibrate();
// "Vibration stopping & resetting to default\n"
+39 -20
View File
@@ -65,6 +65,7 @@
#include "dusk/iso_validate.hpp"
#include "dusk/logging.h"
#include "dusk/main.h"
#include "dusk/hq_minimap.hpp"
#include "dusk/mod_loader.hpp"
#include "dusk/mods/svc/window.hpp"
#include "dusk/mouse.h"
@@ -232,7 +233,7 @@ void main01(void) {
OSReport("Entering Main Loop (main01)...\n");
dusk::game_clock::ensure_initialized();
dusk::game_clock::initialize();
do {
// 1. Update Window Events
@@ -247,7 +248,7 @@ void main01(void) {
break;
case AURORA_UNPAUSED:
dusk::audio::SetPaused(false);
dusk::game_clock::reset_frame_timer();
dusk::game_clock::reset();
dusk::mouse::on_focus_gained();
break;
case AURORA_SDL_EVENT:
@@ -289,46 +290,56 @@ void main01(void) {
dusk::ui::update();
const auto pacing = dusk::game_clock::advance_main_loop();
if (pacing.is_interpolating) {
if (pacing.sim_ticks_to_run > 0) {
dusk::frame_interp::begin_frame(dusk::getSettings().game.enableFrameInterpolation, true, 0.0f);
const auto timing = dusk::game_clock::advance();
const auto interpolationMode = dusk::getSettings().game.enableFrameInterpolation.getValue();
if (timing.separatePresentation) {
if (timing.numSimTicks > 0) {
dusk::frame_interp::begin_frame(interpolationMode, true, 0.0f);
dusk::frame_interp::set_ui_tick_pending(true);
for (int sim_tick = 0; sim_tick < pacing.sim_ticks_to_run; ++sim_tick) {
dusk::frame_interp::begin_sim_tick();
for (int i = 0; i < timing.numSimTicks; ++i) {
if (timing.interpolating) {
dusk::frame_interp::begin_sim_tick();
}
dusk::game_clock::begin_sim_tick();
mDoCPd_c::read();
dusk::mouse::read();
dusk::gyro::read(pacing.sim_pace);
dusk::gyro::read(dusk::game_clock::kSimPeriod);
fapGm_Execute();
mDoAud_Execute();
dusk::game_clock::commit_sim_tick();
}
}
dusk::frame_interp::begin_frame(dusk::getSettings().game.enableFrameInterpolation, false,
dusk::game_clock::sample_interpolation_step());
dusk::frame_interp::interpolate();
dusk::frame_interp::begin_presentation_camera();
// run draw functions for anything specially marked to handle interp
const float interpolationStep =
timing.interpolating ? dusk::game_clock::sample_interpolation_step() : 1.0f;
dusk::frame_interp::begin_frame(interpolationMode, false, interpolationStep);
if (timing.interpolating) {
dusk::frame_interp::interpolate();
dusk::frame_interp::begin_presentation_camera();
}
fpcM_DrawIterater((fpcM_DrawIteraterFunc)fpcM_Draw);
cAPIGph_Painter();
dusk::frame_interp::end_presentation_camera();
if (timing.interpolating) {
dusk::frame_interp::end_presentation_camera();
}
dusk::frame_interp::set_ui_tick_pending(false);
} else {
dusk::frame_interp::begin_frame(dusk::FrameInterpMode::Off, true, 0.0f);
dusk::frame_interp::set_ui_tick_pending(true);
dusk::game_clock::begin_sim_tick();
// Game Inputs
mDoCPd_c::read();
dusk::mouse::read();
dusk::gyro::read(pacing.presentation_dt_seconds);
dusk::gyro::read(timing.dt);
// EXECUTE GAME LOGIC & RENDER
// This calls mDoGph_Painter -> JFWDisplay -> GX Functions
fapGm_Execute();
mDoAud_Execute();
dusk::game_clock::commit_sim_tick();
}
aurora_end_frame();
@@ -344,7 +355,10 @@ void main01(void) {
static double last_fps_setting = 0.0;
static Limiter::duration_t target_ns = 0;
if (dusk::getSettings().game.enableFrameInterpolation.getValue() == dusk::FrameInterpMode::Capped && !dusk::getTransientSettings().skipFrameRateLimit) {
if (dusk::getSettings().game.enableFrameInterpolation.getValue() ==
dusk::FrameInterpMode::Capped &&
!dusk::getTransientSettings().turboMode)
{
ZoneScopedN("Frame limiter");
double current_fps = dusk::getSettings().video.maxFrameRate.getValue();
if (current_fps != last_fps_setting) {
@@ -353,7 +367,8 @@ void main01(void) {
}
Limiter::duration_t sleepTime = main_loop_limiter.Sleep(target_ns);
dusk::frameUsagePct = 100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(target_ns));
dusk::frameUsagePct =
100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(target_ns));
} else {
main_loop_limiter.Reset();
}
@@ -711,6 +726,10 @@ int game_main(int argc, char* argv[]) {
return 0;
}
if (dusk::getSettings().game.enableHighQualityMinimapTextures.getValue()) {
dusk::hq_minimap::set_active(true);
}
dusk::texture_replacements::reload();
dusk::ui::initialize();
dusk::ui::push_document(std::make_unique<dusk::ui::Overlay>(), true, true);
@@ -840,7 +859,7 @@ int game_main(int argc, char* argv[]) {
OSInit();
mDoMain::sPowerOnTime = OSGetTime();
mDoMain::sPowerOnTime = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime());
// Reset Data
static mDoRstData sResetData = {0};