mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-09-10 11:05:07 -04:00
Merge remote-tracking branch 'origin/main' into android-building
This commit is contained in:
@@ -0,0 +1,409 @@
|
||||
#include "dusk/frame_interpolation.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
enum class Op : uint8_t {
|
||||
OpenChild,
|
||||
FinalMtx,
|
||||
};
|
||||
|
||||
struct Label {
|
||||
const void* key = nullptr;
|
||||
int32_t id = 0;
|
||||
|
||||
bool operator==(const Label& other) const {
|
||||
return key == other.key && id == other.id;
|
||||
}
|
||||
};
|
||||
|
||||
struct Data {
|
||||
Label child_label{};
|
||||
size_t child_index = 0;
|
||||
Mtx matrix{};
|
||||
const Mtx* dest = nullptr;
|
||||
};
|
||||
|
||||
struct Path;
|
||||
|
||||
struct ChildBucket {
|
||||
Label label{};
|
||||
std::vector<std::unique_ptr<Path>> nodes;
|
||||
};
|
||||
|
||||
struct OpBucket {
|
||||
Op op = Op::OpenChild;
|
||||
std::vector<Data> values;
|
||||
};
|
||||
|
||||
struct Path {
|
||||
std::vector<ChildBucket> children;
|
||||
std::vector<OpBucket> ops;
|
||||
std::vector<std::pair<Op, size_t>> items;
|
||||
};
|
||||
|
||||
struct Recording {
|
||||
Path root;
|
||||
};
|
||||
|
||||
struct MatrixValue {
|
||||
Mtx value;
|
||||
};
|
||||
|
||||
using FinalMtxLookup = std::unordered_map<const Mtx*, const Data*>;
|
||||
|
||||
bool s_initialized = false;
|
||||
|
||||
bool g_enabled = false;
|
||||
bool g_recording = false;
|
||||
bool g_interpolating = false;
|
||||
float g_step = 0.0f;
|
||||
uint32_t g_pending_presentation_ui_ticks = 0;
|
||||
uint32_t g_current_presentation_ui_ticks = 0;
|
||||
|
||||
Recording g_current_recording;
|
||||
Recording g_previous_recording;
|
||||
std::vector<Path*> g_current_path;
|
||||
|
||||
std::unordered_map<const Mtx*, MatrixValue> g_replacements;
|
||||
|
||||
inline void copy_matrix(const Mtx src, Mtx dst) {
|
||||
MTXCopy(src, dst);
|
||||
}
|
||||
|
||||
inline void concat_matrix(const Mtx lhs, const Mtx rhs, Mtx out) {
|
||||
MTXConcat(lhs, rhs, out);
|
||||
}
|
||||
|
||||
inline void lerp_matrix(Mtx out, const Mtx lhs, const Mtx rhs, float step) {
|
||||
const float old_weight = 1.0f - step;
|
||||
for (size_t row = 0; row < 3; ++row) {
|
||||
for (size_t col = 0; col < 4; ++col) {
|
||||
out[row][col] = lhs[row][col] * old_weight + rhs[row][col] * step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool matrix_differs(const Mtx lhs, const Mtx rhs, float epsilon = 0.0001f) {
|
||||
for (size_t row = 0; row < 3; ++row) {
|
||||
for (size_t col = 0; col < 4; ++col) {
|
||||
if (std::abs(lhs[row][col] - rhs[row][col]) > epsilon) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Data& append_op(Op op) {
|
||||
auto& items = g_current_path.back()->items;
|
||||
auto& buckets = g_current_path.back()->ops;
|
||||
auto it = std::find_if(buckets.begin(), buckets.end(),
|
||||
[op](const OpBucket& bucket) { return bucket.op == op; });
|
||||
if (it == buckets.end()) {
|
||||
buckets.push_back({op, {}});
|
||||
it = buckets.end() - 1;
|
||||
}
|
||||
items.emplace_back(op, it->values.size());
|
||||
return it->values.emplace_back();
|
||||
}
|
||||
|
||||
const Data* find_matching_data(const Path& path, Op op, size_t index) {
|
||||
auto it = std::find_if(path.ops.begin(), path.ops.end(),
|
||||
[op](const OpBucket& bucket) { return bucket.op == op; });
|
||||
if (it == path.ops.end() || index >= it->values.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->values[index];
|
||||
}
|
||||
|
||||
const OpBucket* find_op_bucket(const Path& path, Op op) {
|
||||
auto it = std::find_if(path.ops.begin(), path.ops.end(),
|
||||
[op](const OpBucket& bucket) { return bucket.op == op; });
|
||||
if (it == path.ops.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &*it;
|
||||
}
|
||||
|
||||
void build_final_mtx_lookup(const Path& path, FinalMtxLookup& lookup) {
|
||||
lookup.clear();
|
||||
|
||||
const OpBucket* bucket = find_op_bucket(path, Op::FinalMtx);
|
||||
if (bucket == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const Data& data : bucket->values) {
|
||||
if (data.dest == nullptr) {
|
||||
continue;
|
||||
}
|
||||
lookup[data.dest] = &data;
|
||||
}
|
||||
}
|
||||
|
||||
const Data* find_matching_final_mtx(const FinalMtxLookup& lookup, const Data& new_data) {
|
||||
if (new_data.dest == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = lookup.find(new_data.dest);
|
||||
if (it == lookup.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ChildBucket& get_child_bucket(Path& path, const Label& label) {
|
||||
auto it = std::find_if(path.children.begin(), path.children.end(),
|
||||
[&label](const ChildBucket& bucket) { return bucket.label == label; });
|
||||
if (it == path.children.end()) {
|
||||
path.children.push_back({});
|
||||
it = path.children.end() - 1;
|
||||
it->label = label;
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
const ChildBucket* find_child_bucket(const Path& path, const Label& label) {
|
||||
auto it = std::find_if(path.children.begin(), path.children.end(),
|
||||
[&label](const ChildBucket& bucket) { return bucket.label == label; });
|
||||
if (it == path.children.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &*it;
|
||||
}
|
||||
|
||||
void store_replacement(const Data& old_data, const Data& new_data, float step) {
|
||||
if (new_data.dest == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& replacement = g_replacements[new_data.dest];
|
||||
lerp_matrix(replacement.value, old_data.matrix, new_data.matrix, step);
|
||||
}
|
||||
|
||||
void interpolate_branch(const Path& old_path, const Path& new_path, float step) {
|
||||
FinalMtxLookup old_final_mtx_lookup;
|
||||
build_final_mtx_lookup(old_path, old_final_mtx_lookup);
|
||||
|
||||
for (const auto& item : new_path.items) {
|
||||
const Op op = item.first;
|
||||
const size_t index = item.second;
|
||||
const Data* new_data = find_matching_data(new_path, op, index);
|
||||
if (new_data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op == Op::OpenChild) {
|
||||
const ChildBucket* new_children = find_child_bucket(new_path, new_data->child_label);
|
||||
if (new_children == nullptr || new_data->child_index >= new_children->nodes.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const Path& new_child = *new_children->nodes[new_data->child_index];
|
||||
const ChildBucket* old_children = find_child_bucket(old_path, new_data->child_label);
|
||||
if (old_children != nullptr && new_data->child_index < old_children->nodes.size())
|
||||
{
|
||||
interpolate_branch(*old_children->nodes[new_data->child_index], new_child, step);
|
||||
} else {
|
||||
interpolate_branch(new_child, new_child, step);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const Data* indexed_old_data = find_matching_data(old_path, op, index);
|
||||
const Data* old_data = op == Op::FinalMtx ? find_matching_final_mtx(old_final_mtx_lookup, *new_data) : indexed_old_data;
|
||||
if (op == Op::FinalMtx) {
|
||||
store_replacement(old_data != nullptr ? *old_data : *new_data, *new_data, step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Mtx* resolve_replacement(const Mtx* source, Mtx* scratch) {
|
||||
if (!g_interpolating || source == nullptr) {
|
||||
return source;
|
||||
}
|
||||
|
||||
auto it = g_replacements.find(source);
|
||||
if (it == g_replacements.end()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
copy_matrix(it->second.value, *scratch);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
bool has_recording_data(const Recording& recording) {
|
||||
return !recording.root.items.empty() || !recording.root.children.empty();
|
||||
}
|
||||
|
||||
void clear_replacements() {
|
||||
g_replacements.clear();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace dusk {
|
||||
namespace frame_interp {
|
||||
|
||||
void ensure_initialized() {
|
||||
g_enabled = getSettings().game.enableFrameInterpolation;
|
||||
s_initialized = true;
|
||||
}
|
||||
|
||||
void begin_record() {
|
||||
ensure_initialized();
|
||||
if (!g_enabled) {
|
||||
g_interpolating = false;
|
||||
g_previous_recording = {};
|
||||
g_current_recording = {};
|
||||
g_current_path.clear();
|
||||
clear_replacements();
|
||||
return;
|
||||
}
|
||||
|
||||
g_previous_recording = std::move(g_current_recording);
|
||||
g_current_recording = {};
|
||||
g_current_path.clear();
|
||||
g_current_path.push_back(&g_current_recording.root);
|
||||
g_recording = true;
|
||||
g_interpolating = false;
|
||||
clear_replacements();
|
||||
}
|
||||
|
||||
void end_record() {
|
||||
g_recording = false;
|
||||
}
|
||||
|
||||
void interpolate(float step) {
|
||||
ensure_initialized();
|
||||
clear_replacements();
|
||||
g_step = std::clamp(step, 0.0f, 1.0f);
|
||||
g_interpolating = g_enabled && !g_recording && has_recording_data(g_current_recording);
|
||||
if (!g_interpolating) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!has_recording_data(g_previous_recording)) {
|
||||
interpolate_branch(g_current_recording.root, g_current_recording.root, g_step);
|
||||
return;
|
||||
}
|
||||
|
||||
interpolate_branch(g_previous_recording.root, g_current_recording.root, g_step);
|
||||
}
|
||||
|
||||
float get_interpolation_step() {
|
||||
return g_step;
|
||||
}
|
||||
|
||||
void notify_sim_tick_complete() {
|
||||
ensure_initialized();
|
||||
g_pending_presentation_ui_ticks++;
|
||||
}
|
||||
|
||||
uint32_t begin_presentation_ui_pass() {
|
||||
ensure_initialized();
|
||||
g_current_presentation_ui_ticks = g_pending_presentation_ui_ticks;
|
||||
g_pending_presentation_ui_ticks = 0;
|
||||
return g_current_presentation_ui_ticks;
|
||||
}
|
||||
|
||||
uint32_t get_presentation_ui_advance_ticks() {
|
||||
if (!s_initialized) {
|
||||
return 0;
|
||||
}
|
||||
if (!g_enabled) {
|
||||
return 1;
|
||||
}
|
||||
return g_current_presentation_ui_ticks;
|
||||
}
|
||||
|
||||
void end_presentation_ui_pass() {
|
||||
if (!s_initialized) {
|
||||
return;
|
||||
}
|
||||
g_current_presentation_ui_ticks = 0;
|
||||
}
|
||||
|
||||
void open_child(const void* key, int32_t id) {
|
||||
if (!s_initialized || !g_recording) {
|
||||
return;
|
||||
}
|
||||
|
||||
Label label{key, id};
|
||||
auto& siblings = get_child_bucket(*g_current_path.back(), label).nodes;
|
||||
Data& data = append_op(Op::OpenChild);
|
||||
data.child_label = label;
|
||||
data.child_index = siblings.size();
|
||||
siblings.emplace_back(std::make_unique<Path>());
|
||||
g_current_path.push_back(siblings.back().get());
|
||||
}
|
||||
|
||||
void close_child() {
|
||||
if (!s_initialized || !g_recording || g_current_path.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_current_path.pop_back();
|
||||
}
|
||||
|
||||
void record_final_mtx_raw(const Mtx* dest, const Mtx src) {
|
||||
if (!s_initialized || !g_recording || dest == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Data& data = append_op(Op::FinalMtx);
|
||||
data.dest = dest;
|
||||
copy_matrix(src, data.matrix);
|
||||
}
|
||||
|
||||
bool lookup_replacement(const void* source, Mtx out) {
|
||||
if (!s_initialized || !g_interpolating || source == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = g_replacements.find(reinterpret_cast<const Mtx*>(source));
|
||||
if (it == g_replacements.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
copy_matrix(it->second.value, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) {
|
||||
if (!s_initialized || !g_interpolating || lhs == nullptr || rhs == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Mtx lhs_scratch;
|
||||
Mtx rhs_scratch;
|
||||
const Mtx* resolved_lhs = resolve_replacement(reinterpret_cast<const Mtx*>(lhs), &lhs_scratch);
|
||||
const Mtx* resolved_rhs = resolve_replacement(reinterpret_cast<const Mtx*>(rhs), &rhs_scratch);
|
||||
if (resolved_lhs == reinterpret_cast<const Mtx*>(lhs) &&
|
||||
resolved_rhs == reinterpret_cast<const Mtx*>(rhs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
concat_matrix(*resolved_lhs, *resolved_rhs, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Is there already a built-in function for this?
|
||||
void camera_eye_from_view_mtx(MtxP view_mtx, cXyz* o_eye) {
|
||||
o_eye->x = -(view_mtx[0][0] * view_mtx[0][3] + view_mtx[1][0] * view_mtx[1][3] + view_mtx[2][0] * view_mtx[2][3]);
|
||||
o_eye->y = -(view_mtx[0][1] * view_mtx[0][3] + view_mtx[1][1] * view_mtx[1][3] + view_mtx[2][1] * view_mtx[2][3]);
|
||||
o_eye->z = -(view_mtx[0][2] * view_mtx[0][3] + view_mtx[1][2] * view_mtx[1][3] + view_mtx[2][2] * view_mtx[2][3]);
|
||||
}
|
||||
|
||||
} // namespace frame_interp
|
||||
} // namespace dusk
|
||||
@@ -206,10 +206,6 @@ namespace dusk {
|
||||
|
||||
void ImGuiConsole::PreDraw() {
|
||||
ZoneScoped;
|
||||
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
|
||||
m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f);
|
||||
m_isLaunchInitialized = true;
|
||||
}
|
||||
|
||||
UpdateSettings();
|
||||
|
||||
@@ -241,6 +237,16 @@ namespace dusk {
|
||||
m_preLaunchWindow.draw();
|
||||
}
|
||||
|
||||
if (!getSettings().backend.wasPresetChosen) {
|
||||
m_firstRunPreset.draw();
|
||||
return;
|
||||
}
|
||||
|
||||
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
|
||||
m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f);
|
||||
m_isLaunchInitialized = true;
|
||||
}
|
||||
|
||||
m_menuGame.windowControllerConfig();
|
||||
m_menuGame.windowInputViewer();
|
||||
if (dusk::IsGameLaunched) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "ImGuiFirstRunPreset.hpp"
|
||||
#include "ImGuiMenuEnhancements.hpp"
|
||||
#include "ImGuiMenuGame.hpp"
|
||||
#include "ImGuiMenuTools.hpp"
|
||||
@@ -35,6 +36,7 @@ private:
|
||||
bool m_isLaunchInitialized = false;
|
||||
std::deque<Toast> m_toasts;
|
||||
|
||||
ImGuiFirstRunPreset m_firstRunPreset;
|
||||
ImGuiMenuGame m_menuGame;
|
||||
ImGuiMenuEnhancements m_menuEnhancements;
|
||||
ImGuiPreLaunchWindow m_preLaunchWindow;
|
||||
|
||||
@@ -144,7 +144,7 @@ void ImGuiEngine_Initialize(float scale) {
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f);
|
||||
}
|
||||
|
||||
Image GetImage(const std::string& path) {
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
#include "ImGuiFirstRunPreset.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "ImGuiEngine.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/config.hpp"
|
||||
#include <dusk/dusk.h>
|
||||
|
||||
namespace dusk {
|
||||
|
||||
static void ApplyPresetClassic() {
|
||||
auto& s = getSettings();
|
||||
s.video.lockAspectRatio.setValue(true);
|
||||
VILockAspectRatio(defaultAspectRatioW, defaultAspectRatioH);
|
||||
}
|
||||
|
||||
static void ApplyPresetHD() {
|
||||
auto& s = getSettings();
|
||||
s.game.hideTvSettingsScreen.setValue(true);
|
||||
s.game.noReturnRupees.setValue(true);
|
||||
s.game.disableRupeeCutscenes.setValue(true);
|
||||
s.game.noSwordRecoil.setValue(true);
|
||||
s.game.fastClimbing.setValue(true);
|
||||
s.game.noMissClimbing.setValue(true);
|
||||
s.game.fastTears.setValue(true);
|
||||
s.game.biggerWallets.setValue(true);
|
||||
s.game.invertCameraXAxis.setValue(true);
|
||||
}
|
||||
|
||||
static void ApplyPresetDusk() {
|
||||
ApplyPresetHD();
|
||||
|
||||
auto& s = getSettings();
|
||||
s.game.enableQuickTransform.setValue(true);
|
||||
s.game.instantSaves.setValue(true);
|
||||
s.game.midnasLamentNonStop.setValue(true);
|
||||
s.game.enableFrameInterpolation.setValue(true);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
|
||||
void ImGuiFirstRunPreset::draw() {
|
||||
const char* modalTitle = "Welcome to Dusk!";
|
||||
|
||||
if (m_done) return;
|
||||
|
||||
if (!m_opened) {
|
||||
ImGui::OpenPopup(modalTitle);
|
||||
m_opened = true;
|
||||
}
|
||||
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||
ImGui::SetNextWindowSize(ImVec2(800.0f * ImGuiScale(), 0.0f), ImGuiCond_Always);
|
||||
|
||||
if (!ImGui::BeginPopupModal(modalTitle, nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) {
|
||||
// force the user to actually pick one, and not just hit escape to skip the dialog
|
||||
m_opened = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("Choose a preset to get started. You can change any setting later from the Enhancements menu.");
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
int chosen = -1;
|
||||
|
||||
if (ImGui::BeginTable("##presets", 5, ImGuiTableFlags_None)) {
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, 16.0f * ImGuiScale());
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, 16.0f * ImGuiScale());
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::PushFont(ImGuiEngine::fontLarge);
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
if (ImGui::Button("Classic##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) {
|
||||
chosen = 0;
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
if (ImGui::Button("HD##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) {
|
||||
chosen = 1;
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
if (ImGui::Button("Dusk##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale())))
|
||||
{
|
||||
chosen = 2;
|
||||
}
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("All enhancements disabled to match the GameCube version. Good for speedrunning or simple nostalgia!");
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("Some enhancements enabled to match the HD version. A good starting point for most players!");
|
||||
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("More enhancements enabled than the HD preset. Veteran players will appreciate the additional tweaks!");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (chosen >= 0) {
|
||||
if (chosen == 0) ApplyPresetClassic();
|
||||
if (chosen == 1) ApplyPresetHD();
|
||||
if (chosen == 2) ApplyPresetDusk();
|
||||
|
||||
getSettings().backend.wasPresetChosen.setValue(true);
|
||||
config::Save();
|
||||
|
||||
m_done = true;
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
} // namespace dusk
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace dusk {
|
||||
|
||||
class ImGuiFirstRunPreset {
|
||||
public:
|
||||
void draw();
|
||||
|
||||
private:
|
||||
bool m_opened = false;
|
||||
bool m_done = false;
|
||||
};
|
||||
|
||||
} // namespace dusk
|
||||
@@ -14,48 +14,48 @@ namespace dusk {
|
||||
|
||||
config::ImGuiCheckbox("Bigger Wallets", getSettings().game.biggerWallets);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Wallet sizes are like in the HD version (500, 1000, 2000)");
|
||||
ImGui::SetTooltip("Wallet sizes are like in the HD version. (500, 1000, 2000)");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full");
|
||||
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Disable Rupee Cutscenes", getSettings().game.disableRupeeCutscenes);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time");
|
||||
ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Link won't recoil when his sword hits walls");
|
||||
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Faster Climbing", getSettings().game.fastClimbing);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version");
|
||||
ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Prevents Link from playing a struggle animation\n"
|
||||
"when using the Clawshot on vines at a weird angle");
|
||||
"when grabbing ledges or climbing on vines.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version");
|
||||
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Hide TV Settings Screen", getSettings().game.hideTvSettingsScreen);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save");
|
||||
ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save.");
|
||||
}
|
||||
|
||||
|
||||
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Skip the delay when writing to the Memory Card");
|
||||
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
@@ -64,7 +64,7 @@ namespace dusk {
|
||||
if (ImGui::BeginMenu("Preferences")) {
|
||||
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game");
|
||||
ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
|
||||
@@ -73,12 +73,21 @@ namespace dusk {
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Graphics")) {
|
||||
config::ImGuiCheckbox("Native Bloom", getSettings().game.enableBloom);
|
||||
config::ImGuiCheckbox("Unlock Framerate", getSettings().game.enableFrameInterpolation);
|
||||
const bool frameInterpolationHovered = ImGui::IsItemHovered();
|
||||
|
||||
config::ImGuiCheckbox("Water Projection Offset", getSettings().game.useWaterProjectionOffset);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.72f, 0.2f, 1.0f));
|
||||
ImGui::TextUnformatted("[EXPERIMENTAL]");
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (frameInterpolationHovered || ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Uses inter-frame interpolation to enable higher frame rates.\nVisual artifacts, animation glitches, or instability may occur.");
|
||||
}
|
||||
|
||||
config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Adds GC-specific -0.01 transS offset\n"
|
||||
"that causes ~6px ghost artifacts in water reflections");
|
||||
ImGui::SetTooltip("Improves the shadow resolution, making them higher quality.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
@@ -87,12 +96,12 @@ namespace dusk {
|
||||
if (ImGui::BeginMenu("Audio")) {
|
||||
config::ImGuiCheckbox("No Low HP Sound", getSettings().game.noLowHpSound);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Disable the beeping sound when having low health");
|
||||
ImGui::SetTooltip("Disable the beeping sound when having low health.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Non-Stop Midna's Lament", getSettings().game.midnasLamentNonStop);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing");
|
||||
ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
@@ -103,7 +112,7 @@ namespace dusk {
|
||||
|
||||
config::ImGuiCheckbox("Can Transform Anywhere", getSettings().game.canTransformAnywhere);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Allows you to transform even if NPCs are looking");
|
||||
ImGui::SetTooltip("Allows you to transform even if NPCs are looking.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Fast Spinner", getSettings().game.fastSpinner);
|
||||
@@ -124,7 +133,7 @@ namespace dusk {
|
||||
|
||||
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Any hit will instantly kill you");
|
||||
ImGui::SetTooltip("Any hit will instantly kill you.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
@@ -134,7 +143,7 @@ namespace dusk {
|
||||
config::ImGuiCheckbox("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n"
|
||||
"the first released version");
|
||||
"the first released version.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
@@ -142,6 +151,10 @@ namespace dusk {
|
||||
|
||||
if (ImGui::BeginMenu("Tools")) {
|
||||
config::ImGuiCheckbox("Enable Turbo Key", getSettings().game.enableTurboKeybind);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Holding TAB will speed up the game.\n"
|
||||
"This will not work with the \"Unlock Framerate\" enhancement.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
@@ -62,6 +62,10 @@ namespace dusk {
|
||||
config::Save();
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Native Bloom", getSettings().game.enableBloom);
|
||||
|
||||
config::ImGuiCheckbox("Enable Water Refraction", getSettings().game.enableWaterRefraction);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "d/d_item_data.h"
|
||||
#include "d/d_meter2_info.h"
|
||||
#include "d/d_save.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -280,6 +282,132 @@ namespace dusk {
|
||||
{ dItemNo_NONE_e, {"None"} },
|
||||
};
|
||||
|
||||
static constexpr int BUG_SPECIES_COUNT = 12;
|
||||
|
||||
static const u8 sBugItemIds[BUG_SPECIES_COUNT * 2] = {
|
||||
dItemNo_M_ANT_e, dItemNo_F_ANT_e,
|
||||
dItemNo_M_MAYFLY_e, dItemNo_F_MAYFLY_e,
|
||||
dItemNo_M_BEETLE_e, dItemNo_F_BEETLE_e,
|
||||
dItemNo_M_MANTIS_e, dItemNo_F_MANTIS_e,
|
||||
dItemNo_M_STAG_BEETLE_e, dItemNo_F_STAG_BEETLE_e,
|
||||
dItemNo_M_DANGOMUSHI_e, dItemNo_F_DANGOMUSHI_e,
|
||||
dItemNo_M_BUTTERFLY_e, dItemNo_F_BUTTERFLY_e,
|
||||
dItemNo_M_LADYBUG_e, dItemNo_F_LADYBUG_e,
|
||||
dItemNo_M_SNAIL_e, dItemNo_F_SNAIL_e,
|
||||
dItemNo_M_NANAFUSHI_e, dItemNo_F_NANAFUSHI_e,
|
||||
dItemNo_M_GRASSHOPPER_e, dItemNo_F_GRASSHOPPER_e,
|
||||
dItemNo_M_DRAGONFLY_e, dItemNo_F_DRAGONFLY_e,
|
||||
};
|
||||
|
||||
static const u16 sBugTurnInFlags[BUG_SPECIES_COUNT * 2] = {
|
||||
dSv_event_flag_c::F_0421, dSv_event_flag_c::F_0422,
|
||||
dSv_event_flag_c::F_0423, dSv_event_flag_c::F_0424,
|
||||
dSv_event_flag_c::F_0401, dSv_event_flag_c::F_0402,
|
||||
dSv_event_flag_c::F_0413, dSv_event_flag_c::F_0414,
|
||||
dSv_event_flag_c::F_0405, dSv_event_flag_c::F_0406,
|
||||
dSv_event_flag_c::F_0411, dSv_event_flag_c::F_0412,
|
||||
dSv_event_flag_c::F_0403, dSv_event_flag_c::F_0404,
|
||||
dSv_event_flag_c::F_0415, dSv_event_flag_c::F_0416,
|
||||
dSv_event_flag_c::F_0417, dSv_event_flag_c::F_0418,
|
||||
dSv_event_flag_c::F_0409, dSv_event_flag_c::F_0410,
|
||||
dSv_event_flag_c::F_0407, dSv_event_flag_c::F_0408,
|
||||
dSv_event_flag_c::F_0419, dSv_event_flag_c::F_0420,
|
||||
};
|
||||
|
||||
static const char* sBugSpeciesNames[BUG_SPECIES_COUNT] = {
|
||||
"Ant", "Dayfly", "Beetle", "Mantis",
|
||||
"Stag Beetle", "Pill Bug", "Butterfly", "Ladybug",
|
||||
"Snail", "Phasmid", "Grasshopper", "Dragonfly",
|
||||
};
|
||||
|
||||
static constexpr int HIDDEN_SKILL_COUNT = 7;
|
||||
|
||||
static const u16 sHiddenSkillFlags[HIDDEN_SKILL_COUNT] = {
|
||||
dSv_event_flag_c::F_0339, dSv_event_flag_c::F_0338,
|
||||
dSv_event_flag_c::F_0340, dSv_event_flag_c::F_0341,
|
||||
dSv_event_flag_c::F_0342, dSv_event_flag_c::F_0343,
|
||||
dSv_event_flag_c::F_0344,
|
||||
};
|
||||
|
||||
static const char* sHiddenSkillNames[HIDDEN_SKILL_COUNT] = {
|
||||
"Ending Blow", "Shield Attack", "Back Slice", "Helm Splitter",
|
||||
"Mortal Draw", "Jump Strike", "Great Spin",
|
||||
};
|
||||
|
||||
static constexpr int LETTER_COUNT = 16;
|
||||
|
||||
static const char* sLetterSenders[LETTER_COUNT] = {
|
||||
"Renado", "Ooccoo 1", "Ooccoo 2", "The Postman",
|
||||
"Kakariko Goods", "Barnes 1", "Barnes 2", "Barnes Bombs",
|
||||
"Malo Mart", "Telma", "Purlo", "From Jr.",
|
||||
"Princess Agitha", "Lanayru Tourism", "Shad", "Yeta",
|
||||
};
|
||||
|
||||
static constexpr int FISH_COUNT = 6;
|
||||
|
||||
static const struct {
|
||||
u8 index;
|
||||
const char* name;
|
||||
} sFishSpecies[FISH_COUNT] = {
|
||||
{ 3, "Ordon Catfish" },
|
||||
{ 5, "Greengill" },
|
||||
{ 4, "Reekfish" },
|
||||
{ 0, "Hyrule Bass" },
|
||||
{ 2, "Hylian Pike" },
|
||||
{ 1, "Hylian Loach" },
|
||||
};
|
||||
|
||||
static const char* sSwordNames[4] = {
|
||||
"Ordon Sword", "Master Sword", "Wooden Sword", "Light Sword",
|
||||
};
|
||||
|
||||
static const char* sShieldNames[3] = {
|
||||
"Wooden Shield", "Ordon Shield", "Hylian Shield",
|
||||
};
|
||||
|
||||
static const char* sFusedShadowNames[3] = {
|
||||
"Forest Temple",
|
||||
"Goron Mines",
|
||||
"Lakebed Temple",
|
||||
};
|
||||
|
||||
static const struct {
|
||||
u8 index;
|
||||
const char* name;
|
||||
} sMirrorShards[3] = {
|
||||
{ 1, "Snowpeak Ruins" },
|
||||
{ 2, "Temple of Time" },
|
||||
{ 3, "City in the Sky" },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
u8 slot;
|
||||
u8 item;
|
||||
} sDefaultInventory[] = {
|
||||
{ SLOT_0, dItemNo_BOOMERANG_e },
|
||||
{ SLOT_1, dItemNo_KANTERA_e },
|
||||
{ SLOT_2, dItemNo_SPINNER_e },
|
||||
{ SLOT_3, dItemNo_HVY_BOOTS_e },
|
||||
{ SLOT_4, dItemNo_BOW_e },
|
||||
{ SLOT_5, dItemNo_HAWK_EYE_e },
|
||||
{ SLOT_6, dItemNo_IRONBALL_e },
|
||||
{ SLOT_8, dItemNo_COPY_ROD_e },
|
||||
{ SLOT_9, dItemNo_HOOKSHOT_e },
|
||||
{ SLOT_10, dItemNo_W_HOOKSHOT_e },
|
||||
{ SLOT_11, dItemNo_EMPTY_BOTTLE_e },
|
||||
{ SLOT_12, dItemNo_EMPTY_BOTTLE_e },
|
||||
{ SLOT_13, dItemNo_EMPTY_BOTTLE_e },
|
||||
{ SLOT_14, dItemNo_EMPTY_BOTTLE_e },
|
||||
{ SLOT_15, dItemNo_NORMAL_BOMB_e },
|
||||
{ SLOT_16, dItemNo_WATER_BOMB_e },
|
||||
{ SLOT_17, dItemNo_POKE_BOMB_e },
|
||||
{ SLOT_18, dItemNo_DUNGEON_EXIT_e },
|
||||
{ SLOT_20, dItemNo_FISHING_ROD_1_e},
|
||||
{ SLOT_21, dItemNo_HORSE_FLUTE_e },
|
||||
{ SLOT_22, dItemNo_ANCIENT_DOCUMENT_e },
|
||||
{ SLOT_23, dItemNo_PACHINKO_e },
|
||||
};
|
||||
|
||||
ImGuiSaveEditor::ImGuiSaveEditor() {}
|
||||
|
||||
void ImGuiSaveEditor::draw(bool& open) {
|
||||
@@ -307,7 +435,7 @@ namespace dusk {
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Collection")) {
|
||||
//DrawFlagsTab();
|
||||
drawCollectionTab();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
@@ -675,9 +803,30 @@ namespace dusk {
|
||||
}
|
||||
}
|
||||
|
||||
static u8 getSlotDefault(int slot) {
|
||||
for (size_t i = 0; i < sizeof(sDefaultInventory) / sizeof(sDefaultInventory[0]); i++) {
|
||||
if (sDefaultInventory[i].slot == slot) {
|
||||
return sDefaultInventory[i].item;
|
||||
}
|
||||
}
|
||||
return dItemNo_NONE_e;
|
||||
}
|
||||
|
||||
void ImGuiSaveEditor::drawInventoryTab() {
|
||||
dSv_player_item_c& item = dComIfGs_getSaveData()->getPlayer().getItem();
|
||||
|
||||
if (ImGui::Button("Default All##inv_default_all")) {
|
||||
for (int slot = 0; slot < 24; slot++) {
|
||||
dComIfGs_setItem(slot, getSlotDefault(slot));
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear All##inv_clear_all")) {
|
||||
for (int slot = 0; slot < 24; slot++) {
|
||||
dComIfGs_setItem(slot, dItemNo_NONE_e);
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiBeginGroupPanel("Items", { 200, 100 });
|
||||
for (int slot = 0; slot < 24; slot++) {
|
||||
ImGui::Text("Slot %02d: ", slot);
|
||||
@@ -696,12 +845,369 @@ namespace dusk {
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(fmt::format("Default##slot_d_{}", slot).c_str())) {
|
||||
dComIfGs_setItem(slot, getSlotDefault(slot));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(fmt::format("Clear##slot_c_{}", slot).c_str())) {
|
||||
dComIfGs_setItem(slot, dItemNo_NONE_e);
|
||||
}
|
||||
}
|
||||
ImGuiEndGroupPanel();
|
||||
|
||||
|
||||
}
|
||||
|
||||
static inline void setItemFirstBit(u8 itemNo, bool owned) {
|
||||
if (owned) {
|
||||
dComIfGs_onItemFirstBit(itemNo);
|
||||
} else {
|
||||
dComIfGs_offItemFirstBit(itemNo);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void setEventBit(u16 flag, bool on) {
|
||||
if (on) {
|
||||
dComIfGs_onEventBit(flag);
|
||||
} else {
|
||||
dComIfGs_offEventBit(flag);
|
||||
}
|
||||
}
|
||||
|
||||
static void setLetterGetFlag(int idx, bool received) {
|
||||
dSv_letter_info_c& info = dComIfGs_getSaveData()->getPlayer().getLetterInfo();
|
||||
if (received) {
|
||||
if (dComIfGs_isLetterGetFlag(idx)) return;
|
||||
dComIfGs_onLetterGetFlag(idx);
|
||||
u8 slot = dMeter2Info_getRecieveLetterNum() - 1;
|
||||
if (slot < 64) {
|
||||
dComIfGs_setGetNumber(slot, (u8)(idx + 1));
|
||||
}
|
||||
} else {
|
||||
if (!dComIfGs_isLetterGetFlag(idx)) return;
|
||||
info.mLetterGetFlags[idx >> 5] &= ~(1u << (idx & 0x1F));
|
||||
for (int j = 0; j < 64; j++) {
|
||||
if (dComIfGs_getGetNumber(j) == idx + 1) {
|
||||
for (int k = j; k < 63; k++) {
|
||||
dComIfGs_setGetNumber(k, dComIfGs_getGetNumber(k + 1));
|
||||
}
|
||||
dComIfGs_setGetNumber(63, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiSaveEditor::drawCollectionTab() {
|
||||
if (ImGui::TreeNode("Equipment")) {
|
||||
if (ImGui::TreeNode("Swords")) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bool got = dComIfGs_isCollectSword((u8)i) != 0;
|
||||
if (ImGui::Checkbox(fmt::format("{0}##sword_{1}", sSwordNames[i], i).c_str(), &got)) {
|
||||
if (got) dComIfGs_setCollectSword((u8)i);
|
||||
else dComIfGs_offCollectSword((u8)i);
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Shields")) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
bool got = dComIfGs_isCollectShield((u8)i) != 0;
|
||||
if (ImGui::Checkbox(fmt::format("{0}##shield_{1}", sShieldNames[i], i).c_str(), &got)) {
|
||||
if (got) dComIfGs_setCollectShield((u8)i);
|
||||
else dComIfGs_offCollectShield((u8)i);
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Tunics")) {
|
||||
bool ordonClothes = dComIfGs_isItemFirstBit(dItemNo_WEAR_CASUAL_e) != 0;
|
||||
if (ImGui::Checkbox("Ordon Clothes##tunic_ordon", &ordonClothes)) {
|
||||
setItemFirstBit(dItemNo_WEAR_CASUAL_e, ordonClothes);
|
||||
}
|
||||
|
||||
bool greenTunic = dComIfGs_isCollectClothes(KOKIRI_CLOTHES_FLAG) != 0;
|
||||
if (ImGui::Checkbox("Hero's Clothes##tunic_green", &greenTunic)) {
|
||||
if (greenTunic) dComIfGs_setCollectClothes(KOKIRI_CLOTHES_FLAG);
|
||||
else dComIfGs_offCollectClothes(KOKIRI_CLOTHES_FLAG);
|
||||
}
|
||||
|
||||
bool zoraArmor = dComIfGs_isItemFirstBit(dItemNo_WEAR_ZORA_e) != 0;
|
||||
if (ImGui::Checkbox("Zora Armor##tunic_zora", &zoraArmor)) {
|
||||
setItemFirstBit(dItemNo_WEAR_ZORA_e, zoraArmor);
|
||||
}
|
||||
|
||||
bool magicArmor = dComIfGs_isItemFirstBit(dItemNo_ARMOR_e) != 0;
|
||||
if (ImGui::Checkbox("Magic Armor##tunic_magic", &magicArmor)) {
|
||||
setItemFirstBit(dItemNo_ARMOR_e, magicArmor);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Key Items")) {
|
||||
if (ImGui::TreeNode("Fused Shadows")) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
bool got = dComIfGs_isCollectCrystal((u8)i) != 0;
|
||||
if (ImGui::Checkbox(
|
||||
fmt::format("{0}##fs_{1}", sFusedShadowNames[i], i).c_str(), &got)) {
|
||||
if (got) dComIfGs_onCollectCrystal((u8)i);
|
||||
else dComIfGs_offCollectCrystal((u8)i);
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("All##fs_all")) {
|
||||
for (int i = 0; i < 3; i++) dComIfGs_onCollectCrystal((u8)i);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("None##fs_clear")) {
|
||||
for (int i = 0; i < 3; i++) dComIfGs_offCollectCrystal((u8)i);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Mirror Shards")) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
u8 idx = sMirrorShards[i].index;
|
||||
bool got = dComIfGs_isCollectMirror(idx) != 0;
|
||||
if (ImGui::Checkbox(
|
||||
fmt::format("{0}##ms_{1}", sMirrorShards[i].name, i).c_str(), &got)) {
|
||||
if (got) dComIfGs_onCollectMirror(idx);
|
||||
else dComIfGs_offCollectMirror(idx);
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("All##ms_all")) {
|
||||
for (int i = 0; i < 3; i++) dComIfGs_onCollectMirror(sMirrorShards[i].index);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("None##ms_clear")) {
|
||||
for (int i = 0; i < 3; i++) dComIfGs_offCollectMirror(sMirrorShards[i].index);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Heart Pieces & Poe Souls")) {
|
||||
if (ImGui::TreeNode("Poe Souls")) {
|
||||
int poeCount = dComIfGs_getPohSpiritNum();
|
||||
ImGui::Text("Collected:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(100.0f);
|
||||
if (ImGui::InputInt("##poe_count", &poeCount)) {
|
||||
if (poeCount < 0) poeCount = 0;
|
||||
if (poeCount > 60) poeCount = 60;
|
||||
dComIfGs_setPohSpiritNum((u8)poeCount);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("/ 60");
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("All 60##poe_all")) {
|
||||
dComIfGs_setPohSpiritNum(60);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear##poe_clear")) {
|
||||
dComIfGs_setPohSpiritNum(0);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Heart Pieces")) {
|
||||
int maxLife = dComIfGs_getMaxLife();
|
||||
int hearts = maxLife / 5;
|
||||
int pieces = maxLife % 5;
|
||||
ImGui::Text("Max Life:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(160.0f);
|
||||
if (ImGui::InputInt("##max_life", &maxLife, 1, 5)) {
|
||||
if (maxLife < 15) maxLife = 15;
|
||||
if (maxLife > 100) maxLife = 100;
|
||||
dComIfGs_setMaxLife((u8)maxLife);
|
||||
u16 maxHealth = (dComIfGs_getMaxLife() / 5) * 4;
|
||||
if (dComIfGs_getLife() > maxHealth) {
|
||||
dComIfGs_setLife(maxHealth);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("(%d hearts + %d pieces)", hearts, pieces);
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("3 Hearts##hp_min")) {
|
||||
dComIfGs_setMaxLife(15);
|
||||
dComIfGs_setLife(12);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("20 Hearts##hp_max")) {
|
||||
dComIfGs_setMaxLife(100);
|
||||
dComIfGs_setLife(80);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Golden Bugs")) {
|
||||
if (ImGui::BeginTable("GoldenBugTable", 5,
|
||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Species");
|
||||
ImGui::TableSetupColumn("Male");
|
||||
ImGui::TableSetupColumn("Female");
|
||||
ImGui::TableSetupColumn("M \xe2\x86\x92 Agitha"); // M → Agitha
|
||||
ImGui::TableSetupColumn("F \xe2\x86\x92 Agitha"); // F → Agitha
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (int species = 0; species < BUG_SPECIES_COUNT; species++) {
|
||||
int maleIdx = species * 2;
|
||||
int femaleIdx = species * 2 + 1;
|
||||
u8 maleItem = sBugItemIds[maleIdx];
|
||||
u8 femaleItem = sBugItemIds[femaleIdx];
|
||||
u16 maleFlag = sBugTurnInFlags[maleIdx];
|
||||
u16 femaleFlag = sBugTurnInFlags[femaleIdx];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TextUnformatted(sBugSpeciesNames[species]);
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
bool maleOwned = dComIfGs_isItemFirstBit(maleItem) != 0;
|
||||
if (ImGui::Checkbox(fmt::format("##bugM_own_{}", species).c_str(), &maleOwned)) {
|
||||
setItemFirstBit(maleItem, maleOwned);
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
bool femaleOwned = dComIfGs_isItemFirstBit(femaleItem) != 0;
|
||||
if (ImGui::Checkbox(fmt::format("##bugF_own_{}", species).c_str(), &femaleOwned)) {
|
||||
setItemFirstBit(femaleItem, femaleOwned);
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
bool maleGiven = dComIfGs_isEventBit(maleFlag) != 0;
|
||||
if (ImGui::Checkbox(fmt::format("##bugM_giv_{}", species).c_str(), &maleGiven)) {
|
||||
setEventBit(maleFlag, maleGiven);
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
bool femaleGiven = dComIfGs_isEventBit(femaleFlag) != 0;
|
||||
if (ImGui::Checkbox(fmt::format("##bugF_giv_{}", species).c_str(), &femaleGiven)) {
|
||||
setEventBit(femaleFlag, femaleGiven);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("Collect All##bugs_all")) {
|
||||
for (int i = 0; i < BUG_SPECIES_COUNT * 2; i++) {
|
||||
dComIfGs_onItemFirstBit(sBugItemIds[i]);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear All##bugs_clear")) {
|
||||
for (int i = 0; i < BUG_SPECIES_COUNT * 2; i++) {
|
||||
dComIfGs_offItemFirstBit(sBugItemIds[i]);
|
||||
dComIfGs_offEventBit(sBugTurnInFlags[i]);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Give All to Agitha##bugs_giveall")) {
|
||||
for (int i = 0; i < BUG_SPECIES_COUNT * 2; i++) {
|
||||
dComIfGs_onEventBit(sBugTurnInFlags[i]);
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Hidden Skills")) {
|
||||
for (int i = 0; i < HIDDEN_SKILL_COUNT; i++) {
|
||||
bool learned = dComIfGs_isEventBit(sHiddenSkillFlags[i]) != 0;
|
||||
if (ImGui::Checkbox(
|
||||
fmt::format("{0}##skill_{1}", sHiddenSkillNames[i], i).c_str(), &learned)) {
|
||||
setEventBit(sHiddenSkillFlags[i], learned);
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("Learn All##skills_all")) {
|
||||
for (int i = 0; i < HIDDEN_SKILL_COUNT; i++) {
|
||||
dComIfGs_onEventBit(sHiddenSkillFlags[i]);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Forget All##skills_clear")) {
|
||||
for (int i = 0; i < HIDDEN_SKILL_COUNT; i++) {
|
||||
dComIfGs_offEventBit(sHiddenSkillFlags[i]);
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Collection Logs")) {
|
||||
if (ImGui::TreeNode("Postman Letters")) {
|
||||
for (int i = 0; i < LETTER_COUNT; i++) {
|
||||
bool had = dComIfGs_isLetterGetFlag(i) != 0;
|
||||
if (ImGui::Checkbox(
|
||||
fmt::format("{0}##letter_{1}", sLetterSenders[i], i).c_str(), &had)) {
|
||||
setLetterGetFlag(i, had);
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("Receive All##letters_all")) {
|
||||
for (int i = 0; i < LETTER_COUNT; i++) setLetterGetFlag(i, true);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear All##letters_clear")) {
|
||||
for (int i = 0; i < LETTER_COUNT; i++) setLetterGetFlag(i, false);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Fishing Log")) {
|
||||
dSv_fishing_info_c& fish = dComIfGs_getSaveData()->getPlayer().getFishingInfo();
|
||||
if (ImGui::BeginTable("FishTable", 3,
|
||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Species");
|
||||
ImGui::TableSetupColumn("Caught");
|
||||
ImGui::TableSetupColumn("Biggest (cm)");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (int i = 0; i < FISH_COUNT; i++) {
|
||||
u8 idx = sFishSpecies[i].index;
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TextUnformatted(sFishSpecies[i].name);
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
int count = dComIfGs_getFishNum(idx);
|
||||
ImGui::SetNextItemWidth(100.0f);
|
||||
if (ImGui::InputInt(fmt::format("##fish_c_{}", i).c_str(), &count, 1, 10)) {
|
||||
if (count < 0) count = 0;
|
||||
if (count > 999) count = 999;
|
||||
fish.mFishCount[idx] = (u16)count;
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
int size = dComIfGs_getFishSize(idx);
|
||||
ImGui::SetNextItemWidth(100.0f);
|
||||
if (ImGui::InputInt(fmt::format("##fish_s_{}", i).c_str(), &size, 1, 10)) {
|
||||
if (size < 0) size = 0;
|
||||
if (size > 255) size = 255;
|
||||
dComIfGs_setFishSize(idx, (u8)size);
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
void drawFlagList(const char* id, BE(u32)& flags) {
|
||||
u32 tempFlagField = flags;
|
||||
|
||||
@@ -845,5 +1351,19 @@ namespace dusk {
|
||||
if (ImGui::BeginCombo("Target Type", "Hold")) {
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
static const char* kSoundModeNames[] = { "Mono", "Stereo", "Surround" };
|
||||
const char* current = (config.mSoundMode < 3) ? kSoundModeNames[config.mSoundMode]
|
||||
: "Unknown";
|
||||
if (ImGui::BeginCombo("Sound", current)) {
|
||||
for (u8 i = 0; i < 3; i++) {
|
||||
bool selected = (config.mSoundMode == i);
|
||||
if (ImGui::Selectable(kSoundModeNames[i], selected)) {
|
||||
config.mSoundMode = i;
|
||||
}
|
||||
if (selected) ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ namespace dusk {
|
||||
void drawPlayerStatusTab();
|
||||
void drawLocationTab();
|
||||
void drawInventoryTab();
|
||||
void drawCollectionTab();
|
||||
void drawFlagsTab();
|
||||
void drawConfigTab();
|
||||
|
||||
|
||||
+10
-4
@@ -40,7 +40,9 @@ UserSettings g_userSettings = {
|
||||
|
||||
// Graphics
|
||||
.enableBloom {"game.enableBloom", true},
|
||||
.useWaterProjectionOffset {"game.useWaterProjectionOffset", false},
|
||||
.enableWaterRefraction {"game.enableWaterRefraction", true},
|
||||
.enableFrameInterpolation = {"game.enableFrameInterpolation", false},
|
||||
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
|
||||
|
||||
// Audio
|
||||
.noLowHpSound {"game.noLowHpSound", false},
|
||||
@@ -56,14 +58,15 @@ UserSettings g_userSettings = {
|
||||
.restoreWiiGlitches {"game.restoreWiiGlitches", false},
|
||||
|
||||
// Controls
|
||||
.enableTurboKeybind {"game.enableTurboKeybind", true},
|
||||
.enableTurboKeybind {"game.enableTurboKeybind", false},
|
||||
},
|
||||
|
||||
.backend = {
|
||||
.isoPath {"backend.isoPath", ""},
|
||||
.graphicsBackend {"backend.graphicsBackend", "auto"},
|
||||
.skipPreLaunchUI {"backend.skipPreLaunchUI", false},
|
||||
.showPipelineCompilation{"backend.showPipelineCompilation", false}
|
||||
.showPipelineCompilation {"backend.showPipelineCompilation", false},
|
||||
.wasPresetChosen {"backend.wasPresetChosen", false}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -100,7 +103,8 @@ void registerSettings() {
|
||||
Register(g_userSettings.game.enableMirrorMode);
|
||||
Register(g_userSettings.game.invertCameraXAxis);
|
||||
Register(g_userSettings.game.enableBloom);
|
||||
Register(g_userSettings.game.useWaterProjectionOffset);
|
||||
Register(g_userSettings.game.enableWaterRefraction);
|
||||
Register(g_userSettings.game.shadowResolutionMultiplier);
|
||||
Register(g_userSettings.game.enableFastIronBoots);
|
||||
Register(g_userSettings.game.canTransformAnywhere);
|
||||
Register(g_userSettings.game.freeMagicArmor);
|
||||
@@ -110,11 +114,13 @@ void registerSettings() {
|
||||
Register(g_userSettings.game.midnasLamentNonStop);
|
||||
Register(g_userSettings.game.enableTurboKeybind);
|
||||
Register(g_userSettings.game.fastSpinner);
|
||||
Register(g_userSettings.game.enableFrameInterpolation);
|
||||
|
||||
Register(g_userSettings.backend.isoPath);
|
||||
Register(g_userSettings.backend.graphicsBackend);
|
||||
Register(g_userSettings.backend.skipPreLaunchUI);
|
||||
Register(g_userSettings.backend.showPipelineCompilation);
|
||||
Register(g_userSettings.backend.wasPresetChosen);
|
||||
}
|
||||
|
||||
// Transient settings
|
||||
|
||||
Reference in New Issue
Block a user