Merge branch 'main' into rmlui-integration

# Conflicts:
#	extern/aurora
This commit is contained in:
Luke Street
2026-04-30 12:04:21 -06:00
29 changed files with 414 additions and 178 deletions
+127 -8
View File
@@ -8,6 +8,7 @@
#include "d/actor/d_a_player.h"
#include "d/d_demo.h"
#include "f_pc/f_pc_name.h"
#include "f_op/f_op_actor_mng.h"
#include <filesystem>
#include <algorithm>
@@ -46,6 +47,21 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
},
{}
},
{
{
"plumm_max",
"Thank You Berry Much",
"Score 61,454 points in the Plumm minigame.",
AchievementCategory::Minigame,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (dComIfGs_getBalloonScore() >= 61454) {
a.progress = 1;
}
},
{}
},
{
{
"rollgoal_8",
@@ -258,6 +274,58 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
},
{}
},
{
{
"friendly_fire",
"Friendly Fire",
"Get hit by your own cannonball.",
AchievementCategory::Misc,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (AchievementSystem::get().hasSignal("iron_ball_hit_player")) {
a.progress = 1;
}
},
{}
},
{
{
"long_jump_attack",
"Long Jump Attack",
"Travel more than 20 meters in a single jump attack before landing.",
AchievementCategory::Misc,
false, 0, 0, false
},
[](Achievement& a, json&) {
static bool inJump = false;
static float startX = 0.0f, startZ = 0.0f;
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link == nullptr) {
inJump = false;
return;
}
if (!inJump) {
if (link->mProcID == daAlink_c::PROC_CUT_JUMP) {
inJump = true;
startX = link->current.pos.x;
startZ = link->current.pos.z;
}
} else if (link->mProcID == daAlink_c::PROC_CUT_JUMP_LAND) {
inJump = false;
const float dx = link->current.pos.x - startX;
const float dz = link->current.pos.z - startZ;
if (dx * dx + dz * dz >= 2000.0f * 2000.0f) {
a.progress = 1;
}
} else if (link->mProcID != daAlink_c::PROC_CUT_JUMP) {
inJump = false;
}
},
{}
},
{
{
"back_in_time",
@@ -267,18 +335,13 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
false, 0, 0, false
},
[](Achievement& a, json&) {
static int titleNoDemoFrames = 0;
if (fopAcM_SearchByName(fpcNm_TITLE_e) == nullptr) {
titleNoDemoFrames = 0;
return;
}
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link != nullptr && dDemo_c::getMode() == 0) {
if (++titleNoDemoFrames >= 60) {
const auto* player = static_cast<const daPy_py_c*>(daPy_getPlayerActorClass());
if (player != nullptr && player->mDemo.getDemoMode() == 1) {
a.progress = 1;
}
} else {
titleNoDemoFrames = 0;
}
},
{}
@@ -345,6 +408,41 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
}
},
{}
},
{
{
"email_me",
"Email Me",
"Read a letter during the Dark Beast Ganon fight.",
AchievementCategory::Misc,
false, 0, 0, false
},
[](Achievement& a, json&) {
void* dbgExists = fopAcM_SearchByName(fpcNm_B_MGN_e);
if (dbgExists && AchievementSystem::get().hasSignal("open_letter")) {
a.progress = 1;
}
},
{}
},
{
{
"heavy-hitter",
"Heavy Hitter",
"Wear the Iron Boots during the end credits.",
AchievementCategory::Misc,
false, 0, 0, false
},
[](Achievement& a, json&) {
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) {
return;
}
if (daPy_getPlayerActorClass()->checkEquipHeavyBoots()) {
a.progress = 1;
}
},
{}
}
};
}
@@ -426,6 +524,26 @@ void AchievementSystem::clearAll() {
save();
}
void AchievementSystem::signal(const char* key) {
m_signals.insert(key);
}
bool AchievementSystem::hasSignal(const char* key) const {
return m_signals.count(key) > 0;
}
void AchievementSystem::clearOne(const char* key) {
for (auto& e : m_entries) {
if (std::string(e.achievement.key) == key) {
e.achievement.progress = 0;
e.achievement.unlocked = false;
e.extra = {};
break;
}
}
save();
}
void AchievementSystem::processEntry(Entry& e) {
if (e.achievement.unlocked) {
return;
@@ -458,6 +576,7 @@ void AchievementSystem::tick() {
for (auto& e : m_entries) {
processEntry(e);
}
m_signals.clear();
if (m_dirty) {
save();
m_dirty = false;
+20 -3
View File
@@ -76,8 +76,8 @@ void ImGuiAchievements::draw(bool& open) {
return;
}
ImGui::SetNextWindowSizeConstraints(ImVec2(640, 200), ImVec2(800, 900));
ImGui::SetNextWindowSize(ImVec2(640, 480), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(800, 200), ImVec2(1280, 900));
ImGui::SetNextWindowSize(ImVec2(800, 480), ImGuiCond_FirstUseEver);
if (!ImGui::Begin(
"Achievements", &open,
@@ -111,6 +111,7 @@ void ImGuiAchievements::draw(bool& open) {
{AchievementCategory::Collection, "Collection", ImVec4(0.3f, 0.85f, 0.4f, 1.0f)},
{AchievementCategory::Challenge, "Challenge", ImVec4(1.0f, 0.65f, 0.15f, 1.0f)},
{AchievementCategory::Minigame, "Minigame", ImVec4(0.5f, 0.85f, 1.0f, 1.0f)},
{AchievementCategory::Misc, "Misc", ImVec4(0.65f, 0.65f, 0.65f, 1.0f)},
{AchievementCategory::Glitched, "Glitched", ImVec4(0.75f, 0.4f, 1.0f, 1.0f)},
};
@@ -131,7 +132,7 @@ void ImGuiAchievements::draw(bool& open) {
continue;
}
const std::string tabLabel = fmt::format("{} ({}/{})", catInfo.label, catUnlocked, catTotal);
const std::string tabLabel = fmt::format("{} ({}/{})###{}", catInfo.label, catUnlocked, catTotal, catInfo.label);
ImGui::PushStyleColor(ImGuiCol_Text, catInfo.color);
const bool tabOpen = ImGui::BeginTabItem(tabLabel.c_str());
@@ -152,6 +153,7 @@ void ImGuiAchievements::draw(bool& open) {
continue;
}
ImGui::PushID(a.key);
ImGui::BeginGroup();
ImGui::PushStyleColor(
ImGuiCol_Text,
@@ -190,6 +192,21 @@ void ImGuiAchievements::draw(bool& open) {
ImGui::PopStyleColor();
}
ImGui::EndGroup();
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
ImGui::OpenPopup("##ctx");
}
if (ImGui::BeginPopup("##ctx")) {
ImGui::TextDisabled("%s", a.name);
ImGui::Separator();
if (ImGui::MenuItem("Clear Achievement")) {
AchievementSystem::get().clearOne(a.key);
}
ImGui::EndPopup();
}
ImGui::Spacing();
ImGui::PopID();
}
+2 -31
View File
@@ -56,21 +56,6 @@ ImGuiWindow* FindDragScrollWindow(ImGuiWindow* window) {
}
return nullptr;
}
void FocusLastMenuBarItem() {
ImGuiContext& g = *ImGui::GetCurrentContext();
ImGuiWindow* window = ImGui::GetCurrentWindow();
const ImGuiID itemId = g.LastItemData.ID;
if (window == nullptr || itemId == 0) {
return;
}
ImGui::FocusWindow(window);
ImGui::SetNavID(itemId, ImGuiNavLayer_Menu, g.CurrentFocusScopeId,
ImGui::WindowRectAbsToRel(window, g.LastItemData.NavRect));
ImGui::SetNavCursorVisibleAfterMove();
g.NavHighlightItemUnderNav = true;
}
} // namespace
namespace dusk {
@@ -344,17 +329,7 @@ namespace dusk {
}
m_isHidden = !getSettings().backend.duskMenuOpen;
if (dusk::IsGameLaunched) {
if (ImGui::IsKeyPressed(ImGuiKey_F1)) {
m_isHidden = !m_isHidden;
}
if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack)) {
m_isHidden = !m_isHidden;
m_focusMenuBar = !m_isHidden;
}
}
bool showMenu = !dusk::IsGameLaunched || !m_isHidden;
bool showMenu = !dusk::IsGameLaunched || !CheckMenuViewToggle(ImGuiKey_F1, m_isHidden);
if (dusk::IsGameLaunched) {
const bool menuOpen = !m_isHidden;
if (getSettings().backend.duskMenuOpen != menuOpen) {
@@ -368,10 +343,6 @@ namespace dusk {
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
if (showMenu && ImGui::BeginMainMenuBar()) {
m_menuGame.draw();
if (m_focusMenuBar) {
FocusLastMenuBarItem();
m_focusMenuBar = false;
}
m_menuTools.draw();
const auto fpsLabel =
@@ -396,7 +367,7 @@ namespace dusk {
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
m_toasts.emplace_back(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ?
"Tap to toggle menu"s :
"Press F1 or Minus/Back to toggle menu"s,
"Press F1 to toggle menu"s,
2.5f);
m_isLaunchInitialized = true;
if (getSettings().game.liveSplitEnabled) {
-1
View File
@@ -41,7 +41,6 @@ private:
float mouseHideTimer = 0.0f;
bool m_isHidden = true;
bool m_focusMenuBar = false;
bool m_isLaunchInitialized = false;
bool m_touchTapActive = false;
bool m_touchTapMoved = false;
+1
View File
@@ -30,6 +30,7 @@ static void ApplyPresetHD() {
s.game.biggerWallets.setValue(true);
s.game.invertCameraXAxis.setValue(true);
s.game.freeCamera.setValue(true);
s.game.no2ndFishForCat.setValue(true);
}
static void ApplyPresetDusk() {
+21 -84
View File
@@ -23,15 +23,6 @@
namespace {
constexpr int kInternalResolutionScaleMax = 12;
bool is_controller_neutral(int port) {
if (port < 0) {
return true;
}
return PADGetNativeButtonPressed(port) == -1 &&
PADGetNativeAxisPulled(port).nativeAxis == -1;
}
} // namespace
namespace aurora::gx {
@@ -205,7 +196,7 @@ namespace dusk {
ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n"
"the first released version.");
}
config::ImGuiCheckbox("Enable Rotating Link Doll", getSettings().game.enableLinkDollRotation);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Enables rotating Link in the collection menu with the C-Stick");
@@ -276,6 +267,11 @@ namespace dusk {
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("No 2nd Fish for Cat", getSettings().game.no2ndFishForCat);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Only need to fish once for Sera's cat to return.");
}
config::ImGuiCheckbox("Skip TV Settings Screen", getSettings().game.hideTvSettingsScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the TV calibration screen shown when loading a save.");
@@ -651,90 +647,39 @@ namespace dusk {
void ImGuiMenuGame::windowControllerConfig() {
if (!m_showControllerConfig) {
if (m_controllerConfig.m_isReading ||
m_controllerConfig.m_suppressRemapActivationUntilRelease)
{
m_controllerConfig.m_isReading = false;
m_controllerConfig.m_pendingButtonMapping = nullptr;
m_controllerConfig.m_pendingAxisMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
m_controllerConfig.m_waitForInputRelease = false;
m_controllerConfig.m_suppressRemapActivationUntilRelease = false;
m_controllerConfig.m_suppressRemapActivationPort = -1;
PADBlockInput(false);
}
return;
}
bool suppressRemapActivationThisFrame = m_controllerConfig.m_suppressRemapActivationUntilRelease;
if (m_controllerConfig.m_suppressRemapActivationUntilRelease &&
is_controller_neutral(m_controllerConfig.m_suppressRemapActivationPort))
{
m_controllerConfig.m_suppressRemapActivationUntilRelease = false;
m_controllerConfig.m_suppressRemapActivationPort = -1;
PADBlockInput(false);
}
if ((m_controllerConfig.m_pendingButtonMapping != nullptr ||
m_controllerConfig.m_pendingAxisMapping != nullptr) &&
m_controllerConfig.m_waitForInputRelease)
{
m_controllerConfig.m_waitForInputRelease =
!is_controller_neutral(m_controllerConfig.m_pendingPort);
}
// if pending for a button mapping, check to set new input
if (m_controllerConfig.m_pendingButtonMapping != nullptr &&
!m_controllerConfig.m_waitForInputRelease)
{
if (m_controllerConfig.m_pendingButtonMapping != nullptr) {
s32 nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort);
if (nativeButton != -1) {
const int suppressPort = m_controllerConfig.m_pendingPort;
m_controllerConfig.m_pendingButtonMapping->nativeButton = nativeButton;
m_controllerConfig.m_pendingButtonMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
m_controllerConfig.m_isReading = false;
m_controllerConfig.m_waitForInputRelease = false;
m_controllerConfig.m_suppressRemapActivationUntilRelease = true;
m_controllerConfig.m_suppressRemapActivationPort = suppressPort;
suppressRemapActivationThisFrame = true;
PADBlockInput(true);
PADBlockInput(false);
PADSerializeMappings();
}
}
// if pending for an axis mapping, check to set new input
if (m_controllerConfig.m_pendingAxisMapping != nullptr &&
!m_controllerConfig.m_waitForInputRelease)
{
if (m_controllerConfig.m_pendingAxisMapping != nullptr) {
auto nativeAxis = PADGetNativeAxisPulled(m_controllerConfig.m_pendingPort);
if (nativeAxis.nativeAxis != -1) {
const int suppressPort = m_controllerConfig.m_pendingPort;
m_controllerConfig.m_pendingAxisMapping->nativeAxis = nativeAxis;
m_controllerConfig.m_pendingAxisMapping->nativeButton = -1;
m_controllerConfig.m_pendingAxisMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
m_controllerConfig.m_isReading = false;
m_controllerConfig.m_waitForInputRelease = false;
m_controllerConfig.m_suppressRemapActivationUntilRelease = true;
m_controllerConfig.m_suppressRemapActivationPort = suppressPort;
suppressRemapActivationThisFrame = true;
PADBlockInput(true);
PADBlockInput(false);
PADSerializeMappings();
} else {
auto nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort);
if (nativeButton != -1) {
const int suppressPort = m_controllerConfig.m_pendingPort;
m_controllerConfig.m_pendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE};
m_controllerConfig.m_pendingAxisMapping->nativeButton = nativeButton;
m_controllerConfig.m_pendingAxisMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
m_controllerConfig.m_isReading = false;
m_controllerConfig.m_waitForInputRelease = false;
m_controllerConfig.m_suppressRemapActivationUntilRelease = true;
m_controllerConfig.m_suppressRemapActivationPort = suppressPort;
suppressRemapActivationThisFrame = true;
PADBlockInput(true);
PADBlockInput(false);
PADSerializeMappings();
}
}
@@ -770,10 +715,6 @@ namespace dusk {
m_controllerConfig.m_pendingButtonMapping = nullptr;
m_controllerConfig.m_pendingAxisMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
m_controllerConfig.m_waitForInputRelease = false;
m_controllerConfig.m_isReading = false;
m_controllerConfig.m_suppressRemapActivationUntilRelease = false;
m_controllerConfig.m_suppressRemapActivationPort = -1;
PADBlockInput(false);
}
@@ -850,7 +791,7 @@ namespace dusk {
std::string dispName;
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingButtonMapping == &btnMappingList[i]) {
dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", btnName);
dispName = fmt::format("Press a Key...##{}", btnName);
} else {
const char* nativeName = GetNameForGamepadButton(gamepad, btnMappingList[i].nativeButton);
if (nativeName == nullptr) {
@@ -861,11 +802,10 @@ namespace dusk {
bool pressed = ImGui::Button(dispName.c_str(),
btnSize);
if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) {
if (pressed) {
m_controllerConfig.m_isReading = true;
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
m_controllerConfig.m_pendingButtonMapping = &btnMappingList[i];
m_controllerConfig.m_waitForInputRelease = true;
PADBlockInput(true);
}
}
@@ -895,18 +835,17 @@ namespace dusk {
std::string dispName;
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[trigger]) {
dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", axisName);
dispName = fmt::format("Press a Key...##{}", axisName);
} else {
dispName = fmt::format("{0}##-{1}", PADGetNativeAxisName(axisMappingList[trigger].nativeAxis), trigger);
}
bool pressed = ImGui::Button(dispName.c_str(),
btnSize);
if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) {
if (pressed) {
m_controllerConfig.m_isReading = true;
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[trigger];
m_controllerConfig.m_waitForInputRelease = true;
PADBlockInput(true);
}
}
@@ -963,7 +902,7 @@ namespace dusk {
std::string dispName;
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) {
dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", label);
dispName = fmt::format("Press a Key...##{}", label);
} else {
if (axisMappingList[axis].nativeAxis.nativeAxis != -1) {
const char* signStr;
@@ -982,11 +921,10 @@ namespace dusk {
}
bool pressed = ImGui::Button(dispName.c_str(), btnSize);
if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) {
if (pressed) {
m_controllerConfig.m_isReading = true;
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis];
m_controllerConfig.m_waitForInputRelease = true;
PADBlockInput(true);
}
}
@@ -1027,7 +965,7 @@ namespace dusk {
std::string dispName;
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) {
dispName = fmt::format("{}##sub{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", label);
dispName = fmt::format("Press a Key...##sub{}", label);
} else {
if (axisMappingList[axis].nativeAxis.nativeAxis != -1) {
const char* signStr;
@@ -1046,11 +984,10 @@ namespace dusk {
}
bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), btnSize);
if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) {
if (pressed) {
m_controllerConfig.m_isReading = true;
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis];
m_controllerConfig.m_waitForInputRelease = true;
PADBlockInput(true);
}
}
@@ -1081,7 +1018,7 @@ namespace dusk {
PADSerializeMappings();
}
}
if (PADSupportsRumbleIntensity(m_controllerConfig.m_selectedPort)) {
ImGuiBeginGroupPanel("Rumble Intensity", ImVec2(150 * scale, -1));
u16 low;
@@ -1100,7 +1037,7 @@ namespace dusk {
if (ImGui::Button(fmt::format("{0}...##rumbleTest", m_controllerConfig.m_isRumbling ? "Stop": "Test").c_str(), {-1, 0})) {
PADControlMotor(m_controllerConfig.m_selectedPort, !m_controllerConfig.m_isRumbling ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP_HARD);
m_controllerConfig.m_isRumbling ^= 1;
}
}
ImGuiEndGroupPanel();
}
ImGuiEndGroupPanel();
-3
View File
@@ -68,9 +68,6 @@ namespace dusk {
PADButtonMapping* m_pendingButtonMapping = nullptr;
PADAxisMapping* m_pendingAxisMapping = nullptr;
int m_pendingPort = -1;
bool m_waitForInputRelease = false;
bool m_suppressRemapActivationUntilRelease = false;
int m_suppressRemapActivationPort = -1;
bool m_isRumbling = false;
} m_controllerConfig;
+2
View File
@@ -36,6 +36,7 @@ UserSettings g_userSettings = {
.fastClimbing {"game.fastClimbing", false},
.noMissClimbing {"game.noMissClimbing", false},
.fastTears {"game.fastTears", false},
.no2ndFishForCat {"game.no2ndFishForCat", false},
.instantSaves {"game.instantSaves", false},
.instantText {"game.instantText", false},
.sunsSong {"game.sunsSong", false},
@@ -147,6 +148,7 @@ void registerSettings() {
Register(g_userSettings.game.instantDeath);
Register(g_userSettings.game.fastClimbing);
Register(g_userSettings.game.fastTears);
Register(g_userSettings.game.no2ndFishForCat);
Register(g_userSettings.game.instantSaves);
Register(g_userSettings.game.instantText);
Register(g_userSettings.game.sunsSong);