mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-05 11:33:39 -04:00
Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 27274e7341 | |||
| 1cb8b19520 | |||
| d109cd891e | |||
| 63c4002ca6 | |||
| 6954efcd15 | |||
| 78c2147771 | |||
| c938d5468e | |||
| 35590c5312 | |||
| e976b10e2a | |||
| 3949706b28 | |||
| ce55916845 | |||
| 9b252cbdd2 | |||
| 97fa09f6ee | |||
| 0e7a7cccb9 | |||
| 1c0cdcc176 | |||
| 280305c2ba | |||
| d1f1d579bc | |||
| eeeb3ffe25 | |||
| 1ee0f862e1 | |||
| 6b327c9f61 | |||
| f148e0ebc1 | |||
| 6f20f4d629 | |||
| 94670270d6 | |||
| 8e21247a97 | |||
| aa84004cb4 | |||
| e1c201f4bd | |||
| 425a1d15a0 | |||
| e3ad41792a | |||
| c1ba10fc8b | |||
| 1c5686f71b | |||
| b9e0f2b9ca | |||
| ede175f141 | |||
| a301874e30 | |||
| 9b4a9a6bd9 | |||
| 04274b1884 | |||
| ca783b8424 | |||
| 2e4c2b5b46 | |||
| 4c09d8b910 | |||
| fb1b260d09 | |||
| 98eb8f718e | |||
| 68b2e0ee2d | |||
| 81771a0522 | |||
| 93c8bcc210 | |||
| 176bf5f0c4 | |||
| 65a5945778 | |||
| 8f7b9cdfdd | |||
| acecba7ff7 | |||
| 507e0aadbc | |||
| 8406d9b192 | |||
| 6f3170cb56 | |||
| 835582224e | |||
| 52879f50f0 | |||
| 2b505f1be4 | |||
| f089f9024d | |||
| 43fb421a93 | |||
| f06d6b50a9 | |||
| 2c987b0211 | |||
| 3d860ad454 | |||
| b5bf19569b | |||
| 5a7f5cb4a7 | |||
| 1affefbbfc | |||
| e4ff38a712 | |||
| f2ac4d6f44 | |||
| 48b98a5432 | |||
| a4752154f7 | |||
| 0d973a497b | |||
| ab4eccf1df | |||
| e0c449f28e | |||
| ce9a5c06d5 | |||
| 2bbba1e4e8 | |||
| 39298f8d8f | |||
| 32b4c0567a | |||
| 7ff1b5332e | |||
| 5e77a60bd6 | |||
| 9629c000bd | |||
| 9dc5fed686 | |||
| 2cc9db77dd | |||
| 8aa08c9443 | |||
| dca3e2eba6 | |||
| 57061fba38 | |||
| cee6a24309 | |||
| 62edb3abc6 | |||
| 1c9e1c0a66 | |||
| a06aeb10c1 | |||
| b84b309e00 | |||
| 62eecb3ccd | |||
| ec07572ced | |||
| b45e2fa34d | |||
| 289a718446 | |||
| 292a2a6c34 | |||
| 2e8415b950 | |||
| dbf1f6e354 | |||
| 9a2fe9745d | |||
| b5ca343fac | |||
| 88c7ff63ff | |||
| 7a438ad30f | |||
| 89649de7c3 | |||
| a1960eaa33 | |||
| d6820c9233 | |||
| 5c4bb8d33d | |||
| e3ce1f01c9 | |||
| 9b6b344ecf | |||
| 3db85d5b44 | |||
| b5871d72d9 | |||
| b70a714f88 | |||
| cad5a8d1bc | |||
| 93f8a5fa8f | |||
| b0809ea78c | |||
| b0e9033736 | |||
| ce0d89058a | |||
| 1ac6df8de7 | |||
| 5899b2157a | |||
| 3185f578fb | |||
| 4fc09799b6 | |||
| fe0e3cad72 | |||
| 37d1aa7f40 | |||
| 4a12554bf4 | |||
| fecd1d5683 | |||
| bce9bf6fd9 | |||
| fbf63b075a | |||
| b86d6e90e2 | |||
| 6425b452e7 | |||
| 1657fe8083 | |||
| ecd74a4cbd | |||
| 36dc43c602 | |||
| d92515f0d4 | |||
| f147dcac0c | |||
| ee4c84f39b | |||
| b8a83c6f59 | |||
| 4462c0ef69 | |||
| c803bfb545 | |||
| 2623c44cab | |||
| 24dd02fc81 | |||
| a97602b6dc | |||
| e2943e90dc | |||
| 3cb7fbd030 | |||
| afe54f22ab | |||
| e15f5bcee9 | |||
| 1e372a856d | |||
| b26896cad5 | |||
| f75faf6b06 | |||
| b48d9aa052 | |||
| d899706208 | |||
| 3e05789b58 | |||
| 9a7b62cbc6 | |||
| 8e0f0e878e | |||
| 79344edf0d | |||
| e59bfd1a9c | |||
| 4d12cc8ea2 | |||
| 2ed2268579 | |||
| 94a99e8da0 | |||
| ddaf50c01d | |||
| 7566949b42 | |||
| bb6db3caea | |||
| 2dc494dc1c | |||
| 3c25633ee9 | |||
| d99ed2729b | |||
| 782455d48b | |||
| 47863b34c2 | |||
| 92391d5eb8 | |||
| 0d37cb4e54 | |||
| ea528ed9d9 | |||
| f7b880c5ea | |||
| ff78bc8d6c | |||
| 6503b4e7eb | |||
| 30b7087f30 | |||
| 0c1372f986 | |||
| 8fb4ba8924 | |||
| 5f675c6f2b | |||
| b3333241c5 | |||
| e39079c0f8 | |||
| c3317d9232 | |||
| 36092f1fdb | |||
| a4777045fe | |||
| bbe8ea6aa6 | |||
| d662db69f0 | |||
| 5eb3184174 | |||
| 9b259143be | |||
| 025cb58493 | |||
| b3dee825e8 | |||
| f6c5aac3c8 | |||
| 25e9064d09 | |||
| 3e1e8f1244 | |||
| 0bf663141a | |||
| d7dced7ddf | |||
| 78b0563c0e | |||
| 871d18e294 | |||
| c157564da6 | |||
| ecc3b00c51 | |||
| 8afb1141ab | |||
| 8c5673d9b8 | |||
| 916dfcd9da | |||
| 842210e539 | |||
| 39d951d0cb | |||
| a4be0841e5 |
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
Language: Cpp
|
Language: Cpp
|
||||||
Standard: C++03
|
Standard: C++03
|
||||||
AccessModifierOffset: -4
|
AccessModifierOffset: -4
|
||||||
AlignAfterOpenBracket: Align
|
AlignAfterOpenBracket: DontAlign
|
||||||
AlignConsecutiveAssignments: false
|
AlignConsecutiveAssignments: false
|
||||||
AlignConsecutiveDeclarations: false
|
AlignConsecutiveDeclarations: false
|
||||||
AlignOperands: true
|
AlignOperands: true
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
|||||||
endif ()
|
endif ()
|
||||||
set(AURORA_ENABLE_DVD ON CACHE BOOL "Enable DVD API support" FORCE)
|
set(AURORA_ENABLE_DVD ON CACHE BOOL "Enable DVD API support" FORCE)
|
||||||
set(AURORA_ENABLE_CARD ON CACHE BOOL "Enable CARD API support" FORCE)
|
set(AURORA_ENABLE_CARD ON CACHE BOOL "Enable CARD API support" FORCE)
|
||||||
|
set(AURORA_ENABLE_RMLUI ON CACHE BOOL "Enable RmlUi UI support" FORCE)
|
||||||
add_subdirectory(extern/aurora EXCLUDE_FROM_ALL)
|
add_subdirectory(extern/aurora EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
add_subdirectory(libs/freeverb)
|
add_subdirectory(libs/freeverb)
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||

|

|
||||||
|
|
||||||
- ### **[Official Website](https://twilitrealm.dev)**
|
- ### **[Official Website](https://twilitrealm.dev)**
|
||||||
- ### **[Discord](https://discord.gg/QACynxeyna)**
|
- ### **[Discord](https://discord.gg/QACynxeyna)**
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
Dusk is a reverse-engineered reimplementation of Twilight Princess.
|
||||||
|
It aims to be as accurate as possible to the original while also providing new options, enhancements, and tools to customize your experience.
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
**⚠️ Dusk does NOT provide any copyrighted assets. You must provide your own copy of the game.**
|
**⚠️ Dusk does NOT provide any copyrighted assets. You must provide your own copy of the game.**
|
||||||
|
|
||||||
@@ -27,5 +31,7 @@ First make sure your dump of the game is clean and supported by Dusk. You can do
|
|||||||
# Building
|
# Building
|
||||||
If you'd like to build Dusk from source, please read the [build instructions](docs/building.md).
|
If you'd like to build Dusk from source, please read the [build instructions](docs/building.md).
|
||||||
|
|
||||||
|
Pull Requests are welcomed! Note that we do not accept contributions that are primarily AI generated and will close your PR if we suspect as much.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusk/graphs/contributors).
|
Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusk/graphs/contributors).
|
||||||
|
|||||||
Vendored
+1
-1
Submodule extern/aurora updated: 7784b6fc95...41d5c9c5a2
+42
-1
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
set(DOLZEL_FILES
|
set(DOLZEL_FILES
|
||||||
src/m_Do/m_Do_main.cpp
|
src/m_Do/m_Do_main.cpp
|
||||||
src/m_Do/m_Do_printf.cpp
|
#src/m_Do/m_Do_printf.cpp
|
||||||
src/m_Do/m_Do_audio.cpp
|
src/m_Do/m_Do_audio.cpp
|
||||||
src/m_Do/m_Do_controller_pad.cpp
|
src/m_Do/m_Do_controller_pad.cpp
|
||||||
#src/m_Do/m_Re_controller_pad.cpp
|
#src/m_Do/m_Re_controller_pad.cpp
|
||||||
@@ -1429,6 +1429,7 @@ set(DUSK_FILES
|
|||||||
src/dusk/globals.cpp
|
src/dusk/globals.cpp
|
||||||
src/dusk/gyro.cpp
|
src/dusk/gyro.cpp
|
||||||
src/dusk/gamepad_color.cpp
|
src/dusk/gamepad_color.cpp
|
||||||
|
src/dusk/autosave.cpp
|
||||||
src/dusk/io.cpp
|
src/dusk/io.cpp
|
||||||
src/dusk/layout.cpp
|
src/dusk/layout.cpp
|
||||||
src/dusk/logging.cpp
|
src/dusk/logging.cpp
|
||||||
@@ -1462,11 +1463,51 @@ set(DUSK_FILES
|
|||||||
src/dusk/imgui/ImGuiStateShare.cpp
|
src/dusk/imgui/ImGuiStateShare.cpp
|
||||||
src/dusk/imgui/ImGuiAchievements.hpp
|
src/dusk/imgui/ImGuiAchievements.hpp
|
||||||
src/dusk/imgui/ImGuiAchievements.cpp
|
src/dusk/imgui/ImGuiAchievements.cpp
|
||||||
|
src/dusk/ui/bool_button.cpp
|
||||||
|
src/dusk/ui/bool_button.hpp
|
||||||
|
src/dusk/ui/button.cpp
|
||||||
|
src/dusk/ui/button.hpp
|
||||||
|
src/dusk/ui/component.cpp
|
||||||
|
src/dusk/ui/component.hpp
|
||||||
|
src/dusk/ui/document.cpp
|
||||||
|
src/dusk/ui/document.hpp
|
||||||
|
src/dusk/ui/editor.cpp
|
||||||
|
src/dusk/ui/editor.hpp
|
||||||
|
src/dusk/ui/event.cpp
|
||||||
|
src/dusk/ui/event.hpp
|
||||||
|
src/dusk/ui/input.cpp
|
||||||
|
src/dusk/ui/input.hpp
|
||||||
|
src/dusk/ui/nav_types.hpp
|
||||||
|
src/dusk/ui/number_button.cpp
|
||||||
|
src/dusk/ui/number_button.hpp
|
||||||
|
src/dusk/ui/overlay.cpp
|
||||||
|
src/dusk/ui/overlay.hpp
|
||||||
|
src/dusk/ui/pane.cpp
|
||||||
|
src/dusk/ui/pane.hpp
|
||||||
|
src/dusk/ui/popup.cpp
|
||||||
|
src/dusk/ui/popup.hpp
|
||||||
|
src/dusk/ui/prelaunch.cpp
|
||||||
|
src/dusk/ui/prelaunch.hpp
|
||||||
|
src/dusk/ui/prelaunch_options.cpp
|
||||||
|
src/dusk/ui/prelaunch_options.hpp
|
||||||
|
src/dusk/ui/select_button.cpp
|
||||||
|
src/dusk/ui/select_button.hpp
|
||||||
|
src/dusk/ui/settings.cpp
|
||||||
|
src/dusk/ui/settings.hpp
|
||||||
|
src/dusk/ui/string_button.cpp
|
||||||
|
src/dusk/ui/string_button.hpp
|
||||||
|
src/dusk/ui/tab_bar.cpp
|
||||||
|
src/dusk/ui/tab_bar.hpp
|
||||||
|
src/dusk/ui/ui.cpp
|
||||||
|
src/dusk/ui/ui.hpp
|
||||||
|
src/dusk/ui/window.cpp
|
||||||
|
src/dusk/ui/window.hpp
|
||||||
src/dusk/achievements.cpp
|
src/dusk/achievements.cpp
|
||||||
src/dusk/iso_validate.cpp
|
src/dusk/iso_validate.cpp
|
||||||
src/dusk/livesplit.cpp
|
src/dusk/livesplit.cpp
|
||||||
src/dusk/offset_ptr.cpp
|
src/dusk/offset_ptr.cpp
|
||||||
src/dusk/OSContext.cpp
|
src/dusk/OSContext.cpp
|
||||||
|
src/dusk/OSReport.cpp
|
||||||
src/dusk/OSThread.cpp
|
src/dusk/OSThread.cpp
|
||||||
src/dusk/OSMutex.cpp
|
src/dusk/OSMutex.cpp
|
||||||
src/dusk/discord_presence.cpp
|
src/dusk/discord_presence.cpp
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public:
|
|||||||
/* 0x17C */ cXyz mViewScale;
|
/* 0x17C */ cXyz mViewScale;
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
bool mbReset = false;
|
bool mbReset = false;
|
||||||
|
bool mbHadEntry = false;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ public:
|
|||||||
void calcCursor();
|
void calcCursor();
|
||||||
void drawCursor();
|
void drawCursor();
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void dMapBgWide();
|
||||||
|
#endif
|
||||||
|
|
||||||
void setDPDFloorSelCurPos(s8 i_pos) { field_0xdd6 = i_pos; }
|
void setDPDFloorSelCurPos(s8 i_pos) { field_0xdd6 = i_pos; }
|
||||||
|
|
||||||
f32 getMapWidth() { return mMapWidth; }
|
f32 getMapWidth() { return mMapWidth; }
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ public:
|
|||||||
void calcDrawPriority();
|
void calcDrawPriority();
|
||||||
void setArrowPosAxis(f32, f32);
|
void setArrowPosAxis(f32, f32);
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void fMapBackWide();
|
||||||
|
#endif
|
||||||
|
|
||||||
virtual void draw();
|
virtual void draw();
|
||||||
virtual ~dMenu_Fmap2DBack_c();
|
virtual ~dMenu_Fmap2DBack_c();
|
||||||
|
|
||||||
@@ -330,6 +334,10 @@ public:
|
|||||||
void setHIO(bool);
|
void setHIO(bool);
|
||||||
bool isWarpAccept();
|
bool isWarpAccept();
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void fMapTopWide();
|
||||||
|
#endif
|
||||||
|
|
||||||
virtual void draw();
|
virtual void draw();
|
||||||
virtual ~dMenu_Fmap2DTop_c();
|
virtual ~dMenu_Fmap2DTop_c();
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ public:
|
|||||||
bool isStaffMessage();
|
bool isStaffMessage();
|
||||||
bool isSaveMessage();
|
bool isSaveMessage();
|
||||||
bool isTalkMessage();
|
bool isTalkMessage();
|
||||||
|
#if TARGET_PC
|
||||||
|
bool isShopItemMessage();
|
||||||
|
#endif
|
||||||
const char* getSmellName();
|
const char* getSmellName();
|
||||||
const char* getPortalName();
|
const char* getPortalName();
|
||||||
const char* getBombName();
|
const char* getBombName();
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@ enum class AchievementCategory : uint8_t {
|
|||||||
Collection,
|
Collection,
|
||||||
Challenge,
|
Challenge,
|
||||||
Minigame,
|
Minigame,
|
||||||
|
Misc,
|
||||||
Glitched
|
Glitched
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,6 +43,11 @@ public:
|
|||||||
void save();
|
void save();
|
||||||
void tick();
|
void tick();
|
||||||
void clearAll();
|
void clearAll();
|
||||||
|
void clearOne(const char* key);
|
||||||
|
|
||||||
|
// Signals are visible to all achievement checks within the same tick, then cleared.
|
||||||
|
void signal(const char* key);
|
||||||
|
bool hasSignal(const char* key) const;
|
||||||
|
|
||||||
std::vector<Achievement> getAchievements() const;
|
std::vector<Achievement> getAchievements() const;
|
||||||
bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); }
|
bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); }
|
||||||
@@ -57,6 +65,7 @@ private:
|
|||||||
void processEntry(Entry& e);
|
void processEntry(Entry& e);
|
||||||
|
|
||||||
std::vector<Entry> m_entries;
|
std::vector<Entry> m_entries;
|
||||||
|
std::unordered_set<std::string_view> m_signals;
|
||||||
bool m_loaded = false;
|
bool m_loaded = false;
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
std::queue<std::string> m_pendingUnlocks;
|
std::queue<std::string> m_pendingUnlocks;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef AUTOSAVE_H
|
||||||
|
#define AUTOSAVE_H
|
||||||
|
|
||||||
|
#include <m_Do/m_Do_MemCardRWmng.h>
|
||||||
|
#include <m_Do/m_Do_MemCard.h>
|
||||||
|
|
||||||
|
void noAutoSave();
|
||||||
|
void triggerAutoSave();
|
||||||
|
void updateAutoSave();
|
||||||
|
void enterAutoSave();
|
||||||
|
void autoSaving();
|
||||||
|
void waitingForWrite();
|
||||||
|
void endAutoSave();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -55,6 +55,7 @@ struct UserSettings {
|
|||||||
ConfigVar<int> soundEffectsVolume;
|
ConfigVar<int> soundEffectsVolume;
|
||||||
ConfigVar<int> fanfareVolume;
|
ConfigVar<int> fanfareVolume;
|
||||||
ConfigVar<bool> enableReverb;
|
ConfigVar<bool> enableReverb;
|
||||||
|
ConfigVar<bool> enableHrtf;
|
||||||
} audio;
|
} audio;
|
||||||
|
|
||||||
// Game settings
|
// Game settings
|
||||||
@@ -71,14 +72,17 @@ struct UserSettings {
|
|||||||
ConfigVar<bool> disableRupeeCutscenes;
|
ConfigVar<bool> disableRupeeCutscenes;
|
||||||
ConfigVar<bool> noSwordRecoil;
|
ConfigVar<bool> noSwordRecoil;
|
||||||
ConfigVar<int> damageMultiplier;
|
ConfigVar<int> damageMultiplier;
|
||||||
|
ConfigVar<bool> hyperEnemies;
|
||||||
ConfigVar<bool> noHeartDrops;
|
ConfigVar<bool> noHeartDrops;
|
||||||
ConfigVar<bool> instantDeath;
|
ConfigVar<bool> instantDeath;
|
||||||
ConfigVar<bool> fastClimbing;
|
ConfigVar<bool> fastClimbing;
|
||||||
ConfigVar<bool> noMissClimbing;
|
ConfigVar<bool> noMissClimbing;
|
||||||
ConfigVar<bool> fastTears;
|
ConfigVar<bool> fastTears;
|
||||||
|
ConfigVar<bool> no2ndFishForCat;
|
||||||
ConfigVar<bool> instantSaves;
|
ConfigVar<bool> instantSaves;
|
||||||
ConfigVar<bool> instantText;
|
ConfigVar<bool> instantText;
|
||||||
ConfigVar<bool> sunsSong;
|
ConfigVar<bool> sunsSong;
|
||||||
|
ConfigVar<bool> autoSave;
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
ConfigVar<bool> enableMirrorMode;
|
ConfigVar<bool> enableMirrorMode;
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ public:
|
|||||||
bool isActive() const { return mSeqList.getNumLinks() != 0; }
|
bool isActive() const { return mSeqList.getNumLinks() != 0; }
|
||||||
int getNumActiveSeqs() const { return mSeqList.getNumLinks(); }
|
int getNumActiveSeqs() const { return mSeqList.getNumLinks(); }
|
||||||
void pause(bool paused) { mActivity.field_0x0.flags.flag2 = paused; }
|
void pause(bool paused) { mActivity.field_0x0.flags.flag2 = paused; }
|
||||||
|
#if TARGET_PC
|
||||||
|
JSUList<JAISeq>* getSeqList() { return &mSeqList; }
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* 0x08 */ JAIAudience* mAudience;
|
/* 0x08 */ JAIAudience* mAudience;
|
||||||
|
|||||||
@@ -207,4 +207,11 @@ void JPARegistAlphaEnv(JPAEmitterWorkData*, JPABaseParticle*);
|
|||||||
void JPARegistPrmAlpha(JPAEmitterWorkData*, JPABaseParticle*);
|
void JPARegistPrmAlpha(JPAEmitterWorkData*, JPABaseParticle*);
|
||||||
void JPARegistPrmAlphaEnv(JPAEmitterWorkData*, JPABaseParticle*);
|
void JPARegistPrmAlphaEnv(JPAEmitterWorkData*, JPABaseParticle*);
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void JPAInterpBillboard(JPAEmitterWorkData*, JPABaseParticle*);
|
||||||
|
void JPAInterpRotBillboard(JPAEmitterWorkData*, JPABaseParticle*);
|
||||||
|
void JPAInterpDirection(JPAEmitterWorkData*, JPABaseParticle*);
|
||||||
|
void JPAInterpRotDirection(JPAEmitterWorkData*, JPABaseParticle*);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* JPABASESHAPE_H */
|
#endif /* JPABASESHAPE_H */
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ public:
|
|||||||
void init_c(JPAEmitterWorkData*, JPABaseParticle*);
|
void init_c(JPAEmitterWorkData*, JPABaseParticle*);
|
||||||
bool calc_p(JPAEmitterWorkData*);
|
bool calc_p(JPAEmitterWorkData*);
|
||||||
bool calc_c(JPAEmitterWorkData*);
|
bool calc_c(JPAEmitterWorkData*);
|
||||||
|
#if TARGET_PC
|
||||||
|
void interp(JPAEmitterWorkData*, void const* drawFunc);
|
||||||
|
#endif
|
||||||
bool canCreateChild(JPAEmitterWorkData*);
|
bool canCreateChild(JPAEmitterWorkData*);
|
||||||
f32 getWidth(JPABaseEmitter const*) const;
|
f32 getWidth(JPABaseEmitter const*) const;
|
||||||
f32 getHeight(JPABaseEmitter const*) const;
|
f32 getHeight(JPABaseEmitter const*) const;
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ public:
|
|||||||
JUTTransparency getTransparency() const { return JUTTransparency(mTransparency); }
|
JUTTransparency getTransparency() const { return JUTTransparency(mTransparency); }
|
||||||
u16 getNumColors() const { return mNumColors; }
|
u16 getNumColors() const { return mNumColors; }
|
||||||
ResTLUT* getColorTable() const { return mColorTable; }
|
ResTLUT* getColorTable() const { return mColorTable; }
|
||||||
|
#if TARGET_PC
|
||||||
|
void dataUploaded();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* 0x00 */ GXTlutObj mTlutObj;
|
/* 0x00 */ GXTlutObj mTlutObj;
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ public:
|
|||||||
s32 getTransparency() const { return mTexInfo->alphaEnabled; }
|
s32 getTransparency() const { return mTexInfo->alphaEnabled; }
|
||||||
s32 getWidth() const { return mTexInfo->width; }
|
s32 getWidth() const { return mTexInfo->width; }
|
||||||
s32 getHeight() const { return mTexInfo->height; }
|
s32 getHeight() const { return mTexInfo->height; }
|
||||||
|
JUTPalette* getPalette() const { return mPalette; }
|
||||||
void setCaptureFlag(bool flag) { mFlags &= 2 | flag; }
|
void setCaptureFlag(bool flag) { mFlags &= 2 | flag; }
|
||||||
bool getCaptureFlag() const { return mFlags & 1; }
|
bool getCaptureFlag() const { return mFlags & 1; }
|
||||||
bool getEmbPaletteDelFlag() const { return mFlags & 2; }
|
bool getEmbPaletteDelFlag() const { return mFlags & 2; }
|
||||||
@@ -82,7 +83,7 @@ public:
|
|||||||
int getTlutName() const { return mTlutName; }
|
int getTlutName() const { return mTlutName; }
|
||||||
bool operator==(const JUTTexture& other) {
|
bool operator==(const JUTTexture& other) {
|
||||||
return mTexInfo == other.mTexInfo
|
return mTexInfo == other.mTexInfo
|
||||||
&& field_0x2c == other.field_0x2c
|
&& mPalette == other.mPalette
|
||||||
&& mWrapS == other.mWrapS
|
&& mWrapS == other.mWrapS
|
||||||
&& mWrapT == other.mWrapT
|
&& mWrapT == other.mWrapT
|
||||||
&& mMinFilter == other.mMinFilter
|
&& mMinFilter == other.mMinFilter
|
||||||
@@ -100,7 +101,7 @@ private:
|
|||||||
/* 0x20 */ const ResTIMG* mTexInfo;
|
/* 0x20 */ const ResTIMG* mTexInfo;
|
||||||
/* 0x24 */ void* mTexData;
|
/* 0x24 */ void* mTexData;
|
||||||
/* 0x28 */ JUTPalette* mEmbPalette;
|
/* 0x28 */ JUTPalette* mEmbPalette;
|
||||||
/* 0x2C */ JUTPalette* field_0x2c;
|
/* 0x2C */ JUTPalette* mPalette;
|
||||||
/* 0x30 */ u8 mWrapS;
|
/* 0x30 */ u8 mWrapS;
|
||||||
/* 0x31 */ u8 mWrapT;
|
/* 0x31 */ u8 mWrapT;
|
||||||
/* 0x32 */ u8 mMinFilter;
|
/* 0x32 */ u8 mMinFilter;
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#include "JSystem/JSystem.h" // IWYU pragma: keep
|
#include "JSystem/JSystem.h" // IWYU pragma: keep
|
||||||
|
|
||||||
#include "JSystem/JAudio2/JASChannel.h"
|
#include "JSystem/JAudio2/JASChannel.h"
|
||||||
|
#if TARGET_PC
|
||||||
|
#include "dusk/audio/DuskDsp.hpp"
|
||||||
|
#endif
|
||||||
#include "JSystem/JAudio2/JASAiCtrl.h"
|
#include "JSystem/JAudio2/JASAiCtrl.h"
|
||||||
#include "JSystem/JAudio2/JASCalc.h"
|
#include "JSystem/JAudio2/JASCalc.h"
|
||||||
#include "JSystem/JAudio2/JASDriverIF.h"
|
#include "JSystem/JAudio2/JASDriverIF.h"
|
||||||
@@ -170,7 +173,12 @@ void JASChannel::updateEffectorParam(JASDsp::TChannel* i_channel, u16* i_mixerVo
|
|||||||
|
|
||||||
f32 pan = 0.5f;
|
f32 pan = 0.5f;
|
||||||
f32 dolby = 0.0f;
|
f32 dolby = 0.0f;
|
||||||
switch (JASDriver::getOutputMode()) {
|
#if TARGET_PC
|
||||||
|
u32 effectiveOutputMode = dusk::audio::EnableHrtf ? JAS_OUTPUT_SURROUND : JASDriver::getOutputMode();
|
||||||
|
#else
|
||||||
|
u32 effectiveOutputMode = JASDriver::getOutputMode();
|
||||||
|
#endif
|
||||||
|
switch (effectiveOutputMode) {
|
||||||
case JAS_OUTPUT_MONO:
|
case JAS_OUTPUT_MONO:
|
||||||
break;
|
break;
|
||||||
case JAS_OUTPUT_STEREO:
|
case JAS_OUTPUT_STEREO:
|
||||||
|
|||||||
@@ -302,7 +302,6 @@ void JASKernel::setupRootHeap(JKRSolidHeap* heap, u32 size) {
|
|||||||
JKRHEAP_NAME(sSystemHeap, "JASKernel::sSystemHeap");
|
JKRHEAP_NAME(sSystemHeap, "JASKernel::sSystemHeap");
|
||||||
JUT_ASSERT(787, sSystemHeap);
|
JUT_ASSERT(787, sSystemHeap);
|
||||||
sCommandHeap = JKR_NEW_ARGS (heap, 0) JASMemChunkPool<1024, JASThreadingModel::ObjectLevelLockable>;
|
sCommandHeap = JKR_NEW_ARGS (heap, 0) JASMemChunkPool<1024, JASThreadingModel::ObjectLevelLockable>;
|
||||||
JKRHEAP_NAME(sSystemHeap, "JASKernel::sCommandHeap");
|
|
||||||
JUT_ASSERT(790, sCommandHeap);
|
JUT_ASSERT(790, sCommandHeap);
|
||||||
JASDram = heap;
|
JASDram = heap;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -442,6 +442,7 @@ static JAUSectionHeap* JAUNewSectionHeap(JKRSolidHeap* heap, bool param_1) {
|
|||||||
JAUSectionHeap* JAUNewSectionHeap(bool param_0) {
|
JAUSectionHeap* JAUNewSectionHeap(bool param_0) {
|
||||||
s32 freeSize = JASDram->getFreeSize();
|
s32 freeSize = JASDram->getFreeSize();
|
||||||
JKRSolidHeap* sectionHeap = JKRCreateSolidHeap(freeSize, JASDram, true);
|
JKRSolidHeap* sectionHeap = JKRCreateSolidHeap(freeSize, JASDram, true);
|
||||||
|
JKRHEAP_NAME(sectionHeap, "sectionHeap");
|
||||||
JUT_ASSERT(821, sectionHeap);
|
JUT_ASSERT(821, sectionHeap);
|
||||||
return JAUNewSectionHeap(sectionHeap, param_0);
|
return JAUNewSectionHeap(sectionHeap, param_0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,16 +222,11 @@ void* JKRExpHeap::do_alloc(u32 size, int alignment) {
|
|||||||
OSReport_Error("Free block list as follows:\n");
|
OSReport_Error("Free block list as follows:\n");
|
||||||
OSReport_Error("Start | End | Size \n");
|
OSReport_Error("Start | End | Size \n");
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (const CMemBlock* block = mHeadFreeList; block; block = block->mNext) {
|
for (const CMemBlock* block = mHeadFreeList; block; block = block->mNext) {
|
||||||
if (block->mMagic) {
|
if (block->mMagic) {
|
||||||
// Allocated, ignore.
|
// Allocated, ignore.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (i++ > 10) {
|
|
||||||
OSReport_Error("<more>\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto blockStart = (uintptr_t)block - (uintptr_t)mStart;
|
auto blockStart = (uintptr_t)block - (uintptr_t)mStart;
|
||||||
auto blockEnd = (uintptr_t)block + block->size - (uintptr_t)mStart;
|
auto blockEnd = (uintptr_t)block + block->size - (uintptr_t)mStart;
|
||||||
@@ -239,6 +234,14 @@ void* JKRExpHeap::do_alloc(u32 size, int alignment) {
|
|||||||
OSReport_Error("%08X | %08X | %08X\n", (u32) blockStart, (u32) blockEnd, (u32) blockSize);
|
OSReport_Error("%08X | %08X | %08X\n", (u32) blockStart, (u32) blockEnd, (u32) blockSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OSReport_Error("Child heaps as follows:\n");
|
||||||
|
OSReport_Error("Start | End | Name \n");
|
||||||
|
|
||||||
|
const JSUTree<JKRHeap>& tree = getHeapTree();
|
||||||
|
for (JSUTreeIterator iter(tree.getFirstChild()); iter != tree.getEndChild(); ++iter) {
|
||||||
|
OSReport_Error("%08X | %08X | %s\n", iter->getStartAddr(), iter->getEndAddr(), iter->getName());
|
||||||
|
}
|
||||||
|
|
||||||
CRASH("Aborting due to allocation failure!");
|
CRASH("Aborting due to allocation failure!");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
#include <mtx.h>
|
#include <mtx.h>
|
||||||
#include <gx.h>
|
#include <gx.h>
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
#include "dusk/frame_interpolation.h"
|
||||||
|
#endif
|
||||||
#include "tracy/Tracy.hpp"
|
#include "tracy/Tracy.hpp"
|
||||||
|
|
||||||
void JPASetPointSize(JPAEmitterWorkData* work) {
|
void JPASetPointSize(JPAEmitterWorkData* work) {
|
||||||
@@ -418,50 +421,95 @@ static projectionFunc p_prj[3] = {
|
|||||||
loadPrjAnm,
|
loadPrjAnm,
|
||||||
};
|
};
|
||||||
|
|
||||||
void JPADrawBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
#if TARGET_PC
|
||||||
if (param_1->checkStatus(JPAPtclStts_Invisible)) {
|
void JPAInterpBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||||
|
Mtx ptclPosMtx;
|
||||||
|
MTXTrans(ptclPosMtx, ptcl->mPosition.x, ptcl->mPosition.y, ptcl->mPosition.z);
|
||||||
|
dusk::frame_interp::record_final_mtx(ptclPosMtx, ptcl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JPAInterpRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||||
|
Mtx ptclPosMtx;
|
||||||
|
f32 sinRot = JMASSin(ptcl->mRotateAngle);
|
||||||
|
f32 cosRot = JMASCos(ptcl->mRotateAngle);
|
||||||
|
MTXTrans(ptclPosMtx, ptcl->mPosition.x, ptcl->mPosition.y, ptcl->mPosition.z);
|
||||||
|
ptclPosMtx[0][0] = cosRot;
|
||||||
|
ptclPosMtx[0][1] = -sinRot;
|
||||||
|
ptclPosMtx[1][0] = sinRot;
|
||||||
|
ptclPosMtx[1][1] = cosRot;
|
||||||
|
dusk::frame_interp::record_final_mtx(ptclPosMtx, ptcl);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void JPADrawBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||||
|
if (ptcl->checkStatus(JPAPtclStts_Invisible)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JGeometry::TVec3<f32> local_48;
|
JGeometry::TVec3<f32> pos;
|
||||||
MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48);
|
#if TARGET_PC
|
||||||
Mtx local_38;
|
Mtx ptclPosMtx;
|
||||||
local_38[0][0] = work->mGlobalPtclScl.x * param_1->mParticleScaleX;
|
if (dusk::frame_interp::lookup_replacement(ptcl, ptclPosMtx)) {
|
||||||
local_38[0][3] = local_48.x;
|
pos.set(ptclPosMtx[0][3], ptclPosMtx[1][3], ptclPosMtx[2][3]);
|
||||||
local_38[1][1] = work->mGlobalPtclScl.y * param_1->mParticleScaleY;
|
MTXMultVec(work->mPosCamMtx, &pos, &pos);
|
||||||
local_38[1][3] = local_48.y;
|
} else
|
||||||
local_38[2][2] = 1.0f;
|
#endif
|
||||||
local_38[2][3] = local_48.z;
|
{
|
||||||
local_38[0][1] = local_38[0][2] = local_38[1][0] = local_38[1][2] = local_38[2][0] = local_38[2][1] = 0.0f;
|
MTXMultVec(work->mPosCamMtx, &ptcl->mPosition, &pos);
|
||||||
GXLoadPosMtxImm(local_38, 0);
|
}
|
||||||
p_prj[work->mPrjType](work, local_38);
|
Mtx posMtx;
|
||||||
|
posMtx[0][0] = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||||
|
posMtx[0][3] = pos.x;
|
||||||
|
posMtx[1][1] = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||||
|
posMtx[1][3] = pos.y;
|
||||||
|
posMtx[2][2] = 1.0f;
|
||||||
|
posMtx[2][3] = pos.z;
|
||||||
|
posMtx[0][1] = posMtx[0][2] = posMtx[1][0] = posMtx[1][2] = posMtx[2][0] = posMtx[2][1] = 0.0f;
|
||||||
|
GXLoadPosMtxImm(posMtx, GX_PNMTX0);
|
||||||
|
p_prj[work->mPrjType](work, posMtx);
|
||||||
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JPADrawRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
void JPADrawRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||||
if (param_1->checkStatus(JPAPtclStts_Invisible)) {
|
if (ptcl->checkStatus(JPAPtclStts_Invisible)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JGeometry::TVec3<f32> local_48;
|
if (work->mpRes->getUsrIdx() == 0x89d7) {
|
||||||
MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48);
|
int a = 0;
|
||||||
f32 sinRot = JMASSin(param_1->mRotateAngle);
|
}
|
||||||
f32 cosRot = JMASCos(param_1->mRotateAngle);
|
|
||||||
f32 particleX = work->mGlobalPtclScl.x * param_1->mParticleScaleX;
|
|
||||||
f32 particleY = work->mGlobalPtclScl.y * param_1->mParticleScaleY;
|
|
||||||
|
|
||||||
Mtx local_38;
|
JGeometry::TVec3<f32> pos;
|
||||||
local_38[0][0] = cosRot * particleX;
|
f32 sinRot, cosRot;
|
||||||
local_38[0][1] = -sinRot * particleY;
|
#if TARGET_PC
|
||||||
local_38[0][3] = local_48.x;
|
Mtx ptclPosMtx;
|
||||||
local_38[1][0] = sinRot * particleX;
|
MTXTrans(ptclPosMtx, ptcl->mPosition.x, ptcl->mPosition.y, ptcl->mPosition.z);
|
||||||
local_38[1][1] = cosRot * particleY;
|
if (dusk::frame_interp::lookup_replacement(ptcl, ptclPosMtx)) {
|
||||||
local_38[1][3] = local_48.y;
|
pos.set(ptclPosMtx[0][3], ptclPosMtx[1][3], ptclPosMtx[2][3]);
|
||||||
local_38[2][2] = 1.0f;
|
sinRot = ptclPosMtx[1][0];
|
||||||
local_38[2][3] = local_48.z;
|
cosRot = ptclPosMtx[0][0];
|
||||||
local_38[0][2] = local_38[1][2] = local_38[2][0] = local_38[2][1] = 0.0f;
|
MTXMultVec(work->mPosCamMtx, &pos, &pos);
|
||||||
GXLoadPosMtxImm(local_38, 0);
|
} else
|
||||||
p_prj[work->mPrjType](work, local_38);
|
#endif
|
||||||
|
{
|
||||||
|
MTXMultVec(work->mPosCamMtx, &ptcl->mPosition, &pos);
|
||||||
|
sinRot = JMASSin(ptcl->mRotateAngle);
|
||||||
|
cosRot = JMASCos(ptcl->mRotateAngle);
|
||||||
|
}
|
||||||
|
f32 particleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||||
|
f32 particleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||||
|
Mtx posMtx;
|
||||||
|
posMtx[0][0] = cosRot * particleX;
|
||||||
|
posMtx[0][1] = -sinRot * particleY;
|
||||||
|
posMtx[0][3] = pos.x;
|
||||||
|
posMtx[1][0] = sinRot * particleX;
|
||||||
|
posMtx[1][1] = cosRot * particleY;
|
||||||
|
posMtx[1][3] = pos.y;
|
||||||
|
posMtx[2][2] = 1.0f;
|
||||||
|
posMtx[2][3] = pos.z;
|
||||||
|
posMtx[0][2] = posMtx[1][2] = posMtx[2][0] = posMtx[2][1] = 0.0f;
|
||||||
|
GXLoadPosMtxImm(posMtx, GX_PNMTX0);
|
||||||
|
p_prj[work->mPrjType](work, posMtx);
|
||||||
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,7 +532,7 @@ void JPADrawYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
|||||||
local_38[2][2] = work->mYBBCamMtx[2][2];
|
local_38[2][2] = work->mYBBCamMtx[2][2];
|
||||||
local_38[2][3] = local_48.z;
|
local_38[2][3] = local_48.z;
|
||||||
local_38[0][1] = local_38[0][2] = local_38[1][0] = local_38[2][0] = 0.0f;
|
local_38[0][1] = local_38[0][2] = local_38[1][0] = local_38[2][0] = 0.0f;
|
||||||
GXLoadPosMtxImm(local_38, 0);
|
GXLoadPosMtxImm(local_38, GX_PNMTX0);
|
||||||
p_prj[work->mPrjType](work, local_38);
|
p_prj[work->mPrjType](work, local_38);
|
||||||
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
||||||
}
|
}
|
||||||
@@ -517,7 +565,7 @@ void JPADrawRotYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
|||||||
local_38[2][1] = local_94 * fVar1;
|
local_38[2][1] = local_94 * fVar1;
|
||||||
local_38[2][2] = local_90;
|
local_38[2][2] = local_90;
|
||||||
local_38[2][3] = local_48.z;
|
local_38[2][3] = local_48.z;
|
||||||
GXLoadPosMtxImm(local_38, 0);
|
GXLoadPosMtxImm(local_38, GX_PNMTX0);
|
||||||
p_prj[work->mPrjType](work, local_38);
|
p_prj[work->mPrjType](work, local_38);
|
||||||
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
||||||
}
|
}
|
||||||
@@ -681,103 +729,197 @@ static u8* p_dl[2] = {
|
|||||||
jpa_dl_x,
|
jpa_dl_x,
|
||||||
};
|
};
|
||||||
|
|
||||||
void JPADrawDirection(JPAEmitterWorkData* param_0, JPABaseParticle* param_1) {
|
#if TARGET_PC
|
||||||
if (param_1->checkStatus(JPAPtclStts_Invisible)) {
|
void JPAInterpDirection(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||||
|
JGeometry::TVec3<f32> axisY;
|
||||||
|
JGeometry::TVec3<f32> axisZ;
|
||||||
|
p_direction[work->mDirType](work, ptcl, &axisY);
|
||||||
|
|
||||||
|
if (axisY.isZero()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoneScoped;
|
axisY.normalize();
|
||||||
|
axisZ.cross(ptcl->mBaseAxis, axisY);
|
||||||
|
|
||||||
JGeometry::TVec3<f32> local_6c;
|
if (axisZ.isZero()) {
|
||||||
JGeometry::TVec3<f32> local_78;
|
|
||||||
p_direction[param_0->mDirType](param_0, param_1, &local_6c);
|
|
||||||
|
|
||||||
if (local_6c.isZero()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
local_6c.normalize();
|
axisZ.normalize();
|
||||||
local_78.cross(param_1->mBaseAxis, local_6c);
|
ptcl->mBaseAxis.cross(axisY, axisZ);
|
||||||
|
ptcl->mBaseAxis.normalize();
|
||||||
if (local_78.isZero()) {
|
Mtx posMtx;
|
||||||
return;
|
f32 scaleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||||
}
|
f32 scaleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||||
|
posMtx[0][0] = ptcl->mBaseAxis.x;
|
||||||
local_78.normalize();
|
posMtx[0][1] = axisY.x;
|
||||||
param_1->mBaseAxis.cross(local_6c, local_78);
|
posMtx[0][2] = axisZ.x;
|
||||||
param_1->mBaseAxis.normalize();
|
posMtx[0][3] = ptcl->mPosition.x;
|
||||||
Mtx local_60;
|
posMtx[1][0] = ptcl->mBaseAxis.y;
|
||||||
f32 fVar1 = param_0->mGlobalPtclScl.x * param_1->mParticleScaleX;
|
posMtx[1][1] = axisY.y;
|
||||||
f32 fVar2 = param_0->mGlobalPtclScl.y * param_1->mParticleScaleY;
|
posMtx[1][2] = axisZ.y;
|
||||||
local_60[0][0] = param_1->mBaseAxis.x;
|
posMtx[1][3] = ptcl->mPosition.y;
|
||||||
local_60[0][1] = local_6c.x;
|
posMtx[2][0] = ptcl->mBaseAxis.z;
|
||||||
local_60[0][2] = local_78.x;
|
posMtx[2][1] = axisY.z;
|
||||||
local_60[0][3] = param_1->mPosition.x;
|
posMtx[2][2] = axisZ.z;
|
||||||
local_60[1][0] = param_1->mBaseAxis.y;
|
posMtx[2][3] = ptcl->mPosition.z;
|
||||||
local_60[1][1] = local_6c.y;
|
p_plane[work->mPlaneType](posMtx, scaleX, scaleY);
|
||||||
local_60[1][2] = local_78.y;
|
dusk::frame_interp::record_final_mtx(posMtx, ptcl);
|
||||||
local_60[1][3] = param_1->mPosition.y;
|
|
||||||
local_60[2][0] = param_1->mBaseAxis.z;
|
|
||||||
local_60[2][1] = local_6c.z;
|
|
||||||
local_60[2][2] = local_78.z;
|
|
||||||
local_60[2][3] = param_1->mPosition.z;
|
|
||||||
p_plane[param_0->mPlaneType](local_60, fVar1, fVar2);
|
|
||||||
MTXConcat(param_0->mPosCamMtx, local_60, local_60);
|
|
||||||
GXLoadPosMtxImm(local_60, 0);
|
|
||||||
p_prj[param_0->mPrjType](param_0, local_60);
|
|
||||||
GXCallDisplayList(p_dl[param_0->mDLType], sizeof(jpa_dl));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JPADrawRotDirection(JPAEmitterWorkData* param_0, JPABaseParticle* param_1) {
|
void JPAInterpRotDirection(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||||
if (param_1->checkStatus(JPAPtclStts_Invisible)) {
|
f32 sinRot = JMASSin(ptcl->mRotateAngle);
|
||||||
|
f32 cosRot = JMASCos(ptcl->mRotateAngle);
|
||||||
|
JGeometry::TVec3<f32> axisY;
|
||||||
|
JGeometry::TVec3<f32> axisZ;
|
||||||
|
p_direction[work->mDirType](work, ptcl, &axisY);
|
||||||
|
|
||||||
|
if (axisY.isZero()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axisY.normalize();
|
||||||
|
axisZ.cross(ptcl->mBaseAxis, axisY);
|
||||||
|
|
||||||
|
if (axisZ.isZero()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axisZ.normalize();
|
||||||
|
ptcl->mBaseAxis.cross(axisY, axisZ);
|
||||||
|
ptcl->mBaseAxis.normalize();
|
||||||
|
f32 scaleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||||
|
f32 scaleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||||
|
Mtx mtx1;
|
||||||
|
Mtx mtx2;
|
||||||
|
p_rot[work->mRotType](sinRot, cosRot, mtx1);
|
||||||
|
p_plane[work->mPlaneType](mtx1, scaleX, scaleY);
|
||||||
|
mtx2[0][0] = ptcl->mBaseAxis.x;
|
||||||
|
mtx2[0][1] = axisY.x;
|
||||||
|
mtx2[0][2] = axisZ.x;
|
||||||
|
mtx2[0][3] = ptcl->mPosition.x;
|
||||||
|
mtx2[1][0] = ptcl->mBaseAxis.y;
|
||||||
|
mtx2[1][1] = axisY.y;
|
||||||
|
mtx2[1][2] = axisZ.y;
|
||||||
|
mtx2[1][3] = ptcl->mPosition.y;
|
||||||
|
mtx2[2][0] = ptcl->mBaseAxis.z;
|
||||||
|
mtx2[2][1] = axisY.z;
|
||||||
|
mtx2[2][2] = axisZ.z;
|
||||||
|
mtx2[2][3] = ptcl->mPosition.z;
|
||||||
|
MTXConcat(mtx2, mtx1, mtx1);
|
||||||
|
dusk::frame_interp::record_final_mtx(mtx1, ptcl);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void JPADrawDirection(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||||
|
if (ptcl->checkStatus(JPAPtclStts_Invisible)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoneScoped;
|
ZoneScoped;
|
||||||
|
|
||||||
f32 sinRot = JMASSin(param_1->mRotateAngle);
|
Mtx posMtx;
|
||||||
f32 cosRot = JMASCos(param_1->mRotateAngle);
|
#if TARGET_PC
|
||||||
JGeometry::TVec3<f32> local_6c;
|
if (!dusk::frame_interp::lookup_replacement(ptcl, posMtx))
|
||||||
JGeometry::TVec3<f32> local_78;
|
#endif
|
||||||
p_direction[param_0->mDirType](param_0, param_1, &local_6c);
|
{
|
||||||
|
JGeometry::TVec3<f32> axisY;
|
||||||
|
JGeometry::TVec3<f32> axisZ;
|
||||||
|
p_direction[work->mDirType](work, ptcl, &axisY);
|
||||||
|
|
||||||
if (local_6c.isZero()) {
|
if (axisY.isZero()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axisY.normalize();
|
||||||
|
axisZ.cross(ptcl->mBaseAxis, axisY);
|
||||||
|
|
||||||
|
if (axisZ.isZero()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axisZ.normalize();
|
||||||
|
ptcl->mBaseAxis.cross(axisY, axisZ);
|
||||||
|
ptcl->mBaseAxis.normalize();
|
||||||
|
f32 scaleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||||
|
f32 scaleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||||
|
posMtx[0][0] = ptcl->mBaseAxis.x;
|
||||||
|
posMtx[0][1] = axisY.x;
|
||||||
|
posMtx[0][2] = axisZ.x;
|
||||||
|
posMtx[0][3] = ptcl->mPosition.x;
|
||||||
|
posMtx[1][0] = ptcl->mBaseAxis.y;
|
||||||
|
posMtx[1][1] = axisY.y;
|
||||||
|
posMtx[1][2] = axisZ.y;
|
||||||
|
posMtx[1][3] = ptcl->mPosition.y;
|
||||||
|
posMtx[2][0] = ptcl->mBaseAxis.z;
|
||||||
|
posMtx[2][1] = axisY.z;
|
||||||
|
posMtx[2][2] = axisZ.z;
|
||||||
|
posMtx[2][3] = ptcl->mPosition.z;
|
||||||
|
p_plane[work->mPlaneType](posMtx, scaleX, scaleY);
|
||||||
|
}
|
||||||
|
|
||||||
|
MTXConcat(work->mPosCamMtx, posMtx, posMtx);
|
||||||
|
GXLoadPosMtxImm(posMtx, GX_PNMTX0);
|
||||||
|
p_prj[work->mPrjType](work, posMtx);
|
||||||
|
GXCallDisplayList(p_dl[work->mDLType], sizeof(jpa_dl));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JPADrawRotDirection(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||||
|
if (ptcl->checkStatus(JPAPtclStts_Invisible)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
local_6c.normalize();
|
ZoneScoped;
|
||||||
local_78.cross(param_1->mBaseAxis, local_6c);
|
|
||||||
|
|
||||||
if (local_78.isZero()) {
|
Mtx mtx1;
|
||||||
return;
|
Mtx mtx2;
|
||||||
|
#if TARGET_PC
|
||||||
|
if (!dusk::frame_interp::lookup_replacement(ptcl, mtx1))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
f32 sinRot = JMASSin(ptcl->mRotateAngle);
|
||||||
|
f32 cosRot = JMASCos(ptcl->mRotateAngle);
|
||||||
|
JGeometry::TVec3<f32> axisY;
|
||||||
|
JGeometry::TVec3<f32> axisZ;
|
||||||
|
p_direction[work->mDirType](work, ptcl, &axisY);
|
||||||
|
|
||||||
|
if (axisY.isZero()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axisY.normalize();
|
||||||
|
axisZ.cross(ptcl->mBaseAxis, axisY);
|
||||||
|
|
||||||
|
if (axisZ.isZero()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axisZ.normalize();
|
||||||
|
ptcl->mBaseAxis.cross(axisY, axisZ);
|
||||||
|
ptcl->mBaseAxis.normalize();
|
||||||
|
f32 scaleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||||
|
f32 scaleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||||
|
p_rot[work->mRotType](sinRot, cosRot, mtx1);
|
||||||
|
p_plane[work->mPlaneType](mtx1, scaleX, scaleY);
|
||||||
|
mtx2[0][0] = ptcl->mBaseAxis.x;
|
||||||
|
mtx2[0][1] = axisY.x;
|
||||||
|
mtx2[0][2] = axisZ.x;
|
||||||
|
mtx2[0][3] = ptcl->mPosition.x;
|
||||||
|
mtx2[1][0] = ptcl->mBaseAxis.y;
|
||||||
|
mtx2[1][1] = axisY.y;
|
||||||
|
mtx2[1][2] = axisZ.y;
|
||||||
|
mtx2[1][3] = ptcl->mPosition.y;
|
||||||
|
mtx2[2][0] = ptcl->mBaseAxis.z;
|
||||||
|
mtx2[2][1] = axisY.z;
|
||||||
|
mtx2[2][2] = axisZ.z;
|
||||||
|
mtx2[2][3] = ptcl->mPosition.z;
|
||||||
|
MTXConcat(mtx2, mtx1, mtx1);
|
||||||
}
|
}
|
||||||
|
MTXConcat(work->mPosCamMtx, mtx1, mtx2);
|
||||||
local_78.normalize();
|
GXLoadPosMtxImm(mtx2, GX_PNMTX0);
|
||||||
param_1->mBaseAxis.cross(local_6c, local_78);
|
p_prj[work->mPrjType](work, mtx2);
|
||||||
param_1->mBaseAxis.normalize();
|
GXCallDisplayList(p_dl[work->mDLType], sizeof(jpa_dl));
|
||||||
f32 particleX = param_0->mGlobalPtclScl.x * param_1->mParticleScaleX;
|
|
||||||
f32 particleY = param_0->mGlobalPtclScl.y * param_1->mParticleScaleY;
|
|
||||||
Mtx auStack_80;
|
|
||||||
Mtx local_60;
|
|
||||||
p_rot[param_0->mRotType](sinRot, cosRot, auStack_80);
|
|
||||||
p_plane[param_0->mPlaneType](auStack_80, particleX, particleY);
|
|
||||||
local_60[0][0] = param_1->mBaseAxis.x;
|
|
||||||
local_60[0][1] = local_6c.x;
|
|
||||||
local_60[0][2] = local_78.x;
|
|
||||||
local_60[0][3] = param_1->mPosition.x;
|
|
||||||
local_60[1][0] = param_1->mBaseAxis.y;
|
|
||||||
local_60[1][1] = local_6c.y;
|
|
||||||
local_60[1][2] = local_78.y;
|
|
||||||
local_60[1][3] = param_1->mPosition.y;
|
|
||||||
local_60[2][0] = param_1->mBaseAxis.z;
|
|
||||||
local_60[2][1] = local_6c.z;
|
|
||||||
local_60[2][2] = local_78.z;
|
|
||||||
local_60[2][3] = param_1->mPosition.z;
|
|
||||||
MTXConcat(local_60, auStack_80, auStack_80);
|
|
||||||
MTXConcat(param_0->mPosCamMtx, auStack_80, local_60);
|
|
||||||
GXLoadPosMtxImm(local_60, 0);
|
|
||||||
p_prj[param_0->mPrjType](param_0, local_60);
|
|
||||||
GXCallDisplayList(p_dl[param_0->mDLType], sizeof(jpa_dl));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JPADrawDBillboard(JPAEmitterWorkData* param_0, JPABaseParticle* param_1) {
|
void JPADrawDBillboard(JPAEmitterWorkData* param_0, JPABaseParticle* param_1) {
|
||||||
|
|||||||
@@ -204,6 +204,28 @@ void JPABaseParticle::init_c(JPAEmitterWorkData* work, JPABaseParticle* parent)
|
|||||||
mTexAnmIdx = 0;
|
mTexAnmIdx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void JPABaseParticle::interp(JPAEmitterWorkData* work, void const* drawFunc) {
|
||||||
|
static bool enable = false;
|
||||||
|
if (!enable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// don't interpolate the first frame
|
||||||
|
if (mAge == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (drawFunc == JPADrawBillboard) {
|
||||||
|
JPAInterpBillboard(work, this);
|
||||||
|
} else if (drawFunc == JPADrawRotBillboard) {
|
||||||
|
JPAInterpRotBillboard(work, this);
|
||||||
|
} else if (drawFunc == JPADrawDirection) {
|
||||||
|
JPAInterpDirection(work, this);
|
||||||
|
} else if (drawFunc == JPADrawRotDirection) {
|
||||||
|
JPAInterpRotDirection(work, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool JPABaseParticle::calc_p(JPAEmitterWorkData* work) {
|
bool JPABaseParticle::calc_p(JPAEmitterWorkData* work) {
|
||||||
if (++mAge >= mLifeTime) {
|
if (++mAge >= mLifeTime) {
|
||||||
return true;
|
return true;
|
||||||
@@ -247,6 +269,17 @@ bool JPABaseParticle::calc_p(JPAEmitterWorkData* work) {
|
|||||||
mOffsetPosition.y + mLocalPosition.y * work->mPublicScale.y,
|
mOffsetPosition.y + mLocalPosition.y * work->mPublicScale.y,
|
||||||
mOffsetPosition.z + mLocalPosition.z * work->mPublicScale.z);
|
mOffsetPosition.z + mLocalPosition.z * work->mPublicScale.z);
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
JPABaseShape* pBsp = work->mpRes->getBsp();
|
||||||
|
work->mGlobalPtclScl.x = work->mpEmtr->mGlobalPScl.x * pBsp->getBaseSizeX();
|
||||||
|
work->mGlobalPtclScl.y = work->mpEmtr->mGlobalPScl.y * pBsp->getBaseSizeY();
|
||||||
|
work->mDirType = pBsp->getDirType();
|
||||||
|
work->mRotType = pBsp->getRotType();
|
||||||
|
work->mDLType = pBsp->getType() == 4 || pBsp->getType() == 8;
|
||||||
|
work->mPlaneType = work->mDLType ? 2 : pBsp->getBasePlaneType();
|
||||||
|
interp(work, (void const*)work->mpRes->mpDrawParticleFuncList[0]);
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +322,23 @@ bool JPABaseParticle::calc_c(JPAEmitterWorkData* work) {
|
|||||||
mOffsetPosition.y + mLocalPosition.y * work->mPublicScale.y,
|
mOffsetPosition.y + mLocalPosition.y * work->mPublicScale.y,
|
||||||
mOffsetPosition.z + mLocalPosition.z * work->mPublicScale.z);
|
mOffsetPosition.z + mLocalPosition.z * work->mPublicScale.z);
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
JPABaseShape* pBsp = work->mpRes->getBsp();
|
||||||
|
JPAChildShape* pCsp = work->mpRes->getCsp();
|
||||||
|
if (pCsp->isScaleInherited()) {
|
||||||
|
work->mGlobalPtclScl.x = work->mpEmtr->mGlobalPScl.x * pBsp->getBaseSizeX();
|
||||||
|
work->mGlobalPtclScl.y = work->mpEmtr->mGlobalPScl.y * pBsp->getBaseSizeY();
|
||||||
|
} else {
|
||||||
|
work->mGlobalPtclScl.x = work->mpEmtr->mGlobalPScl.x * pCsp->getScaleX();
|
||||||
|
work->mGlobalPtclScl.y = work->mpEmtr->mGlobalPScl.y * pCsp->getScaleY();
|
||||||
|
}
|
||||||
|
work->mDirType = pCsp->getDirType();
|
||||||
|
work->mRotType = pCsp->getRotType();
|
||||||
|
work->mDLType = pCsp->getType() == 4 || pCsp->getType() == 8;
|
||||||
|
work->mPlaneType = work->mDLType ? 2 : pCsp->getBasePlaneType();
|
||||||
|
interp(work, (void const*)work->mpRes->mpDrawParticleChildFuncList[0]);
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,3 +38,9 @@ bool JUTPalette::load() {
|
|||||||
|
|
||||||
return check;
|
return check;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void JUTPalette::dataUploaded() {
|
||||||
|
GXInitTlutObjData(&mTlutObj, (void*)mColorTable);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ void JUTTexture::storeTIMG(ResTIMG const* param_0, u8 param_1) {
|
|||||||
mTexData = (void*)((intptr_t)mTexInfo + 0x20);
|
mTexData = (void*)((intptr_t)mTexInfo + 0x20);
|
||||||
}
|
}
|
||||||
|
|
||||||
field_0x2c = NULL;
|
mPalette = NULL;
|
||||||
mTlutName = 0;
|
mTlutName = 0;
|
||||||
mWrapS = mTexInfo->wrapS;
|
mWrapS = mTexInfo->wrapS;
|
||||||
mWrapT = mTexInfo->wrapT;
|
mWrapT = mTexInfo->wrapT;
|
||||||
@@ -95,7 +95,7 @@ void JUTTexture::storeTIMG(ResTIMG const* param_0, JUTPalette* param_1, GXTlut p
|
|||||||
}
|
}
|
||||||
mEmbPalette = param_1;
|
mEmbPalette = param_1;
|
||||||
setEmbPaletteDelFlag(false);
|
setEmbPaletteDelFlag(false);
|
||||||
field_0x2c = NULL;
|
mPalette = NULL;
|
||||||
if (param_1 != NULL) {
|
if (param_1 != NULL) {
|
||||||
mTlutName = param_2;
|
mTlutName = param_2;
|
||||||
if (param_2 != param_1->getTlutName()) {
|
if (param_2 != param_1->getTlutName()) {
|
||||||
@@ -120,11 +120,11 @@ void JUTTexture::storeTIMG(ResTIMG const* param_0, JUTPalette* param_1, GXTlut p
|
|||||||
void JUTTexture::attachPalette(JUTPalette* param_0) {
|
void JUTTexture::attachPalette(JUTPalette* param_0) {
|
||||||
if (mTexInfo->indexTexture) {
|
if (mTexInfo->indexTexture) {
|
||||||
if (param_0 == NULL && mEmbPalette != NULL) {
|
if (param_0 == NULL && mEmbPalette != NULL) {
|
||||||
field_0x2c = mEmbPalette;
|
mPalette = mEmbPalette;
|
||||||
} else {
|
} else {
|
||||||
field_0x2c = param_0;
|
mPalette = param_0;
|
||||||
}
|
}
|
||||||
initTexObj(field_0x2c->getTlutName());
|
initTexObj(mPalette->getTlutName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,9 +133,9 @@ void JUTTexture::init() {
|
|||||||
initTexObj();
|
initTexObj();
|
||||||
} else {
|
} else {
|
||||||
if (mEmbPalette != NULL) {
|
if (mEmbPalette != NULL) {
|
||||||
field_0x2c = mEmbPalette;
|
mPalette = mEmbPalette;
|
||||||
|
|
||||||
initTexObj(field_0x2c->getTlutName());
|
initTexObj(mPalette->getTlutName());
|
||||||
} else {
|
} else {
|
||||||
OS_REPORT("This texture is CI-Format, but EmbPalette is NULL.\n");
|
OS_REPORT("This texture is CI-Format, but EmbPalette is NULL.\n");
|
||||||
}
|
}
|
||||||
@@ -179,8 +179,8 @@ void JUTTexture::initTexObj(GXTlut param_0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JUTTexture::load(GXTexMapID param_0) {
|
void JUTTexture::load(GXTexMapID param_0) {
|
||||||
if (field_0x2c) {
|
if (mPalette) {
|
||||||
field_0x2c->load();
|
mPalette->load();
|
||||||
}
|
}
|
||||||
GXLoadTexObj(&mTexObj, param_0);
|
GXLoadTexObj(&mTexObj, param_0);
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 457 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 188 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,147 @@
|
|||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: visible;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: "Fira Sans Condensed";
|
||||||
|
font-size: 24dp;
|
||||||
|
color: #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-root {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 45%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: stretch;
|
||||||
|
decorator: vertical-gradient(#00000000 #151610F2);
|
||||||
|
padding: 48dp 0 40dp 0;
|
||||||
|
filter: opacity(0);
|
||||||
|
transition: filter 0.2s linear-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-root[open] {
|
||||||
|
filter: opacity(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1216dp;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24dp;
|
||||||
|
padding: 0 32dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 800dp) {
|
||||||
|
.overlay-root {
|
||||||
|
min-height: 38%;
|
||||||
|
padding: 32dp 0 28dp 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
gap: 16dp;
|
||||||
|
padding: 0 24dp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-size: 18dp;
|
||||||
|
line-height: 22dp;
|
||||||
|
color: rgba(255, 255, 255, 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin: 1dp 0;
|
||||||
|
border-top: 1dp rgba(217, 217, 217, 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer-button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 220dp;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
font-family: "Fira Sans Condensed";
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20dp;
|
||||||
|
line-height: 24dp;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #FFFFFF;
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer-button.return {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer-button.reset {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepped-carousel {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16dp;
|
||||||
|
width: auto;
|
||||||
|
min-width: 246dp;
|
||||||
|
padding: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
font-family: "Fira Sans Condensed";
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepped-carousel-value {
|
||||||
|
line-height: 29dp;
|
||||||
|
min-width: 166dp;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepped-carousel-arrow {
|
||||||
|
width: 24dp;
|
||||||
|
height: 24dp;
|
||||||
|
min-width: 24dp;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: visible;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: "Fira Sans Condensed";
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18dp;
|
||||||
|
color: #E0DBC8;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
focus: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
popup {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 64dp;
|
||||||
|
background-color: rgba(21, 22, 16, 80%);
|
||||||
|
border-bottom: 2dp #92875B;
|
||||||
|
backdrop-filter: blur(5dp);
|
||||||
|
transform: translateY(-64dp);
|
||||||
|
transition: transform 0.2s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
popup[open] {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
popup tab-bar {
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
popup tab-bar tab {
|
||||||
|
opacity: 0.35;
|
||||||
|
color: #E0DBC8;
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-family: "Fira Sans";
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 20dp;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background-color: #000000;
|
||||||
|
decorator: image(../prelaunch-bg.png cover left center);
|
||||||
|
filter: opacity(0);
|
||||||
|
transition: filter 1s 0.1s linear-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[open] {
|
||||||
|
filter: opacity(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
content {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
filter: opacity(0);
|
||||||
|
transition: filter 0.2s linear-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
content[open] {
|
||||||
|
filter: opacity(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu {
|
||||||
|
position: absolute;
|
||||||
|
left: 96dp;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
/* Scale based on a reference screen width, 428/1216 */
|
||||||
|
width: 35.230264vw;
|
||||||
|
min-width: 428dp;
|
||||||
|
max-width: 856dp;
|
||||||
|
height: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 48dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
hero {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
hero img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: "Alegreya SC";
|
||||||
|
font-size: 32dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1216dp) {
|
||||||
|
.eyebrow {
|
||||||
|
/* Same logic as .menu, 32/1216 */
|
||||||
|
font-size: 2.631579vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow span {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu-list button {
|
||||||
|
width: 428dp;
|
||||||
|
height: 54dp;
|
||||||
|
padding: 8dp 16dp;
|
||||||
|
border-radius: 8dp;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-family: "Fira Sans Condensed";
|
||||||
|
font-size: 32dp;
|
||||||
|
font-weight: normal;
|
||||||
|
cursor: pointer;
|
||||||
|
/* Define a fully transparent gradient as the default state, otherwise a white flash occurs */
|
||||||
|
decorator: horizontal-gradient(#00000000 #00000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu-list button.anim-done {
|
||||||
|
transition: decorator color 0.1s linear-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu-list button:hover,
|
||||||
|
#menu-list button:focus-visible {
|
||||||
|
color: black;
|
||||||
|
decorator: horizontal-gradient(#FEE685FF #FEE68500);
|
||||||
|
}
|
||||||
|
|
||||||
|
disk-status {
|
||||||
|
position: absolute;
|
||||||
|
left: 96dp;
|
||||||
|
bottom: 72dp;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
version-info {
|
||||||
|
position: absolute;
|
||||||
|
right: 96dp;
|
||||||
|
bottom: 72dp;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8dp;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status,
|
||||||
|
.version {
|
||||||
|
font-size: 24dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status,
|
||||||
|
.update {
|
||||||
|
color: #D8F999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status[bad] {
|
||||||
|
color: #FFC9C9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Hidden until an actual update checker is introduced */
|
||||||
|
.update {
|
||||||
|
display: none;
|
||||||
|
font-size: 16dp;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail,
|
||||||
|
.update span {
|
||||||
|
color: #A6A09B;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Startup animation */
|
||||||
|
.intro-item {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10dp);
|
||||||
|
transition: opacity transform 0.3s 0.1s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.animate-in .intro-item {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delay-0 {
|
||||||
|
transition: opacity transform 0.3s 0.1s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delay-1 {
|
||||||
|
transition: opacity transform 0.3s 0.2s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delay-2 {
|
||||||
|
transition: opacity transform 0.3s 0.3s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delay-3 {
|
||||||
|
transition: opacity transform 0.3s 0.4s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delay-4 {
|
||||||
|
transition: opacity transform 0.3s 0.5s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delay-5 {
|
||||||
|
transition: opacity transform 0.3s 0.6s cubic-in-out;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
tab-bar {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: auto hidden;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab-bar tab {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: 0 24dp;
|
||||||
|
line-height: 64dp;
|
||||||
|
white-space: nowrap;
|
||||||
|
decorator: vertical-gradient(#c2a42d00 #c2a42d00);
|
||||||
|
transition: decorator 0.1s linear-in-out, opacity 0.1s linear-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab-bar tab:selected {
|
||||||
|
opacity: 1;
|
||||||
|
border-bottom: 4dp #C2A42D;
|
||||||
|
font-effect: glow(0dp 4dp 0dp 4dp black);
|
||||||
|
}
|
||||||
|
|
||||||
|
tab-bar tab:focus-visible,
|
||||||
|
tab-bar tab:hover {
|
||||||
|
opacity: 1;
|
||||||
|
font-effect: glow(0dp 4dp 0dp 4dp black);
|
||||||
|
decorator: vertical-gradient(#c2a42d00 #c2a42d26);
|
||||||
|
}
|
||||||
|
|
||||||
|
tab-bar tab:active {
|
||||||
|
decorator: vertical-gradient(#c2a42d10 #c2a42d40);
|
||||||
|
}
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 64dp;
|
||||||
|
font-family: "Fira Sans";
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 15dp;
|
||||||
|
color: #E0DBC8;
|
||||||
|
}
|
||||||
|
|
||||||
|
window {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 1088dp;
|
||||||
|
max-height: 768dp;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 14dp;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 2dp #92875B;
|
||||||
|
backdrop-filter: blur(5dp);
|
||||||
|
box-shadow: 0 0 25dp 5dp;
|
||||||
|
background-color: rgba(21, 22, 16, 90%);
|
||||||
|
filter: opacity(0);
|
||||||
|
transform: scale(0.9);
|
||||||
|
transform-origin: center;
|
||||||
|
transition: filter transform 0.2s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
window[open] {
|
||||||
|
filter: opacity(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 640dp) {
|
||||||
|
body {
|
||||||
|
padding: 16dp;
|
||||||
|
}
|
||||||
|
window {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window tab-bar {
|
||||||
|
flex: 0 0 64dp;
|
||||||
|
height: 64dp;
|
||||||
|
background-color: rgba(217, 217, 217, 10%);
|
||||||
|
font-family: "Fira Sans Condensed";
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18dp;
|
||||||
|
border-bottom: 2dp #92875B;
|
||||||
|
}
|
||||||
|
|
||||||
|
window tab-bar tab {
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
window content {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
window content pane {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
flex: 1 1 0;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 24dp;
|
||||||
|
padding-bottom: 0dp;
|
||||||
|
gap: 8dp;
|
||||||
|
overflow: hidden auto;
|
||||||
|
font-size: 20dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
window content pane:not(:last-of-type) {
|
||||||
|
border-right: 1dp #92875B;
|
||||||
|
}
|
||||||
|
|
||||||
|
window content pane > * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
window content pane > spacer {
|
||||||
|
display: block;
|
||||||
|
/* Completes the 24dp bottom inset after the pane's 8dp gap. */
|
||||||
|
flex: 0 0 16dp;
|
||||||
|
height: 16dp;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbarvertical {
|
||||||
|
width: 8dp;
|
||||||
|
margin: 4dp 4dp 4dp 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbarvertical sliderarrowdec,
|
||||||
|
scrollbarvertical sliderarrowinc {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbarvertical slidertrack {
|
||||||
|
width: 8dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbarvertical sliderbar {
|
||||||
|
width: 8dp;
|
||||||
|
min-height: 24dp;
|
||||||
|
background-color: rgba(224, 219, 200, 45%);
|
||||||
|
border-radius: 2dp;
|
||||||
|
transition: background-color 0.2s cubic-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbarvertical sliderbar:hover,
|
||||||
|
scrollbarvertical sliderbar:active {
|
||||||
|
background-color: rgba(194, 164, 45, 80%);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbarhorizontal {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbarhorizontal sliderarrowdec,
|
||||||
|
scrollbarhorizontal sliderarrowinc {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbarhorizontal slidertrack,
|
||||||
|
scrollbarhorizontal sliderbar {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-heading {
|
||||||
|
font-family: "Fira Sans Condensed";
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 22dp;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-heading:not(:first-of-type) {
|
||||||
|
padding-top: 12dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
text-align: center;
|
||||||
|
background-color: rgba(17, 16, 10, 20%);
|
||||||
|
opacity: 0.9;
|
||||||
|
padding: 8dp 16dp;
|
||||||
|
border-radius: 14dp;
|
||||||
|
box-shadow: rgba(146, 135, 91, 25%) 0 0 0 1dp;
|
||||||
|
font-size: 20dp;
|
||||||
|
transition: background-color 0.1s linear-in-out, opacity 0.1s linear-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
focus: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:not(:disabled):hover,
|
||||||
|
button:not(:disabled):focus-visible {
|
||||||
|
background-color: rgba(204, 184, 119, 20%);
|
||||||
|
box-shadow: #C2A42D 0 0 0 2dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:not(:disabled):selected {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: rgba(204, 184, 119, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:not(:disabled):active {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: rgba(204, 184, 119, 40%);
|
||||||
|
box-shadow: #C2A42D 0 0 0 2dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
select-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8dp;
|
||||||
|
background-color: rgba(17, 16, 10, 20%);
|
||||||
|
opacity: 0.9;
|
||||||
|
padding: 8dp 16dp;
|
||||||
|
border-radius: 14dp;
|
||||||
|
box-shadow: rgba(146, 135, 91, 25%) 0 0 0 1dp;
|
||||||
|
transition: background-color 0.1s linear-in-out, opacity 0.1s linear-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
focus: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
select-button:not(:disabled):hover,
|
||||||
|
select-button:not(:disabled):focus-visible {
|
||||||
|
background-color: rgba(204, 184, 119, 20%);
|
||||||
|
box-shadow: #C2A42D 0 0 0 2dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
select-button:not(:disabled):selected {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: rgba(204, 184, 119, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
select-button:not(:disabled):active {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: rgba(204, 184, 119, 40%);
|
||||||
|
box-shadow: #C2A42D 0 0 0 2dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
select-button:disabled {
|
||||||
|
opacity: 0.35;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
select-button key {
|
||||||
|
font-family: "Fira Sans Condensed";
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18dp;
|
||||||
|
text-transform: uppercase;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
select-button value {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 20dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
select-button input {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 20dp;
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
#include "Z2AudioLib/Z2Audience.h"
|
#include "Z2AudioLib/Z2Audience.h"
|
||||||
#include "Z2AudioLib/Z2SoundInfo.h"
|
#include "Z2AudioLib/Z2SoundInfo.h"
|
||||||
|
#if TARGET_PC
|
||||||
|
#include "dusk/audio/DuskDsp.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
#endif
|
||||||
#include "Z2AudioLib/Z2Calc.h"
|
#include "Z2AudioLib/Z2Calc.h"
|
||||||
#include "Z2AudioLib/Z2Param.h"
|
#include "Z2AudioLib/Z2Param.h"
|
||||||
#include "JSystem/JAudio2/JAISound.h"
|
#include "JSystem/JAudio2/JAISound.h"
|
||||||
@@ -734,6 +738,19 @@ f32 Z2Audience::calcRelPosPan(const Vec& param_0, int camID) {
|
|||||||
|
|
||||||
f32 Z2Audience::calcRelPosDolby(const Vec& param_0, int camID) {
|
f32 Z2Audience::calcRelPosDolby(const Vec& param_0, int camID) {
|
||||||
f32 fVar1 = param_0.z + mAudioCamera[camID].getDolbyCenterZ();
|
f32 fVar1 = param_0.z + mAudioCamera[camID].getDolbyCenterZ();
|
||||||
|
#if TARGET_PC
|
||||||
|
if (dusk::audio::EnableHrtf) {
|
||||||
|
// Normalize the direction so result is purely front/back orientation,
|
||||||
|
// independent of how far away the sound is
|
||||||
|
f32 lenSq = param_0.x * param_0.x + param_0.y * param_0.y + param_0.z * param_0.z;
|
||||||
|
if (lenSq < 0.0001f) {
|
||||||
|
return 0.5f;
|
||||||
|
}
|
||||||
|
f32 zNorm = param_0.z / sqrtf(lenSq);
|
||||||
|
f32 t = (zNorm + 1.0f) * 0.5f;
|
||||||
|
return 0.5f - 0.5f * cosf(t * static_cast<f32>(M_PI));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (fVar1 > mSetting.field_0x48) {
|
if (fVar1 > mSetting.field_0x48) {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4962,13 +4962,16 @@ int daAlink_c::create() {
|
|||||||
|
|
||||||
setArcName(checkWolf());
|
setArcName(checkWolf());
|
||||||
setOriginalHeap(&mpArcHeap, 0xA2800);
|
setOriginalHeap(&mpArcHeap, 0xA2800);
|
||||||
|
JKRHEAP_NAME(mpArcHeap, "Alink ArcHeap");
|
||||||
if (dComIfG_resLoad(&mPhaseReq, mArcName, mpArcHeap) != cPhs_COMPLEATE_e) {
|
if (dComIfG_resLoad(&mPhaseReq, mArcName, mpArcHeap) != cPhs_COMPLEATE_e) {
|
||||||
return cPhs_INIT_e;
|
return cPhs_INIT_e;
|
||||||
}
|
}
|
||||||
|
|
||||||
setShieldArcName();
|
setShieldArcName();
|
||||||
setOriginalHeap(&mpShieldArcHeap, 0x7000);
|
setOriginalHeap(&mpShieldArcHeap, 0x7000);
|
||||||
if (dComIfG_resLoad(&mShieldPhaseReq, mShieldArcName, mpShieldArcHeap) != cPhs_COMPLEATE_e) {
|
JKRHEAP_NAME(mpShieldArcHeap, "Alink ShieldArcHeap");
|
||||||
|
if (dComIfG_resLoad(&mShieldPhaseReq, mShieldArcName, mpShieldArcHeap) != cPhs_COMPLEATE_e)
|
||||||
|
{
|
||||||
return cPhs_INIT_e;
|
return cPhs_INIT_e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
#include "d/actor/d_a_horse.h"
|
#include "d/actor/d_a_horse.h"
|
||||||
#include "d/actor/d_a_crod.h"
|
#include "d/actor/d_a_crod.h"
|
||||||
#include "d/d_msg_object.h"
|
#include "d/d_msg_object.h"
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
#include "d/actor/d_a_obj_carry.h"
|
||||||
|
#include "dusk/achievements.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
#include "d/d_s_menu.h"
|
#include "d/d_s_menu.h"
|
||||||
@@ -678,6 +682,15 @@ BOOL daAlink_c::checkDamageAction() {
|
|||||||
|
|
||||||
setDamagePoint(dmg, at_mtrl == dCcD_MTRL_FIRE || at_mtrl == dCcD_MTRL_ICE, TRUE, 0);
|
setDamagePoint(dmg, at_mtrl == dCcD_MTRL_FIRE || at_mtrl == dCcD_MTRL_ICE, TRUE, 0);
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
if (tghit_ac_name == fpcNm_Obj_Carry_e) {
|
||||||
|
auto* carry = static_cast<daObjCarry_c*>(tghit_ac);
|
||||||
|
if (carry->prm_chk_type_ironball() && carry->checkCannon()) {
|
||||||
|
dusk::AchievementSystem::get().signal("iron_ball_hit_player");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (armor_no_dmg && at_mtrl != dCcD_MTRL_ELECTRIC && at_mtrl != dCcD_MTRL_ICE) {
|
if (armor_no_dmg && at_mtrl != dCcD_MTRL_ELECTRIC && at_mtrl != dCcD_MTRL_ICE) {
|
||||||
setGuardSe(var_r29);
|
setGuardSe(var_r29);
|
||||||
} else if (at_mtrl == dCcD_MTRL_FIRE) {
|
} else if (at_mtrl == dCcD_MTRL_FIRE) {
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void daAlink_c::hsChainShape_c::draw() {
|
void daAlink_c::hsChainShape_c::draw() {
|
||||||
|
if (dusk::getSettings().game.superClawshot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static const int dummy = 0;
|
static const int dummy = 0;
|
||||||
|
|
||||||
daAlink_c* alink = (daAlink_c*)getUserArea();
|
daAlink_c* alink = (daAlink_c*)getUserArea();
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ void daAlink_c::setOriginalHeap(JKRExpHeap** i_ppheap, u32 i_size) {
|
|||||||
JKRHeap* parent = mDoExt_getGameHeap();
|
JKRHeap* parent = mDoExt_getGameHeap();
|
||||||
|
|
||||||
JKRExpHeap* heap = JKRExpHeap::create(size + (var_r29 + var_r28), parent, true);
|
JKRExpHeap* heap = JKRExpHeap::create(size + (var_r29 + var_r28), parent, true);
|
||||||
JKRHEAP_NAME(heap, "Alink original");
|
|
||||||
*i_ppheap = heap;
|
*i_ppheap = heap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,11 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
#include <f_ap/f_ap_game.h>
|
||||||
|
#include <dusk/autosave.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
char* daDoor20_c::getStopBmdName() {
|
char* daDoor20_c::getStopBmdName() {
|
||||||
switch (door_param2_c::getKind(this)) {
|
switch (door_param2_c::getKind(this)) {
|
||||||
case 3:
|
case 3:
|
||||||
@@ -196,6 +201,7 @@ void daDoor20_c::setEventPrm() {
|
|||||||
} else {
|
} else {
|
||||||
roomNo = FRoomNo;
|
roomNo = FRoomNo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dComIfGp_roomControl_checkStatusFlag(roomNo, 1)) {
|
if (dComIfGp_roomControl_checkStatusFlag(roomNo, 1)) {
|
||||||
if (door_param2_c::getKind(this) == 9) {
|
if (door_param2_c::getKind(this) == 9) {
|
||||||
if (daPy_py_c::checkNowWolf()) {
|
if (daPy_py_c::checkNowWolf()) {
|
||||||
@@ -564,6 +570,11 @@ int daDoor20_c::openEnd(int param_1) {
|
|||||||
openEnd_1();
|
openEnd_1();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
triggerAutoSave();
|
||||||
|
#endif
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -282,6 +282,11 @@ static void e_th_spin_B(e_th_class* i_this) {
|
|||||||
i_this->current.pos += spC;
|
i_this->current.pos += spC;
|
||||||
|
|
||||||
f32 speed_target;
|
f32 speed_target;
|
||||||
|
|
||||||
|
#if AVOID_UB
|
||||||
|
speed_target = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
f32 anm_frame = i_this->mpModelMorf->getFrame();
|
f32 anm_frame = i_this->mpModelMorf->getFrame();
|
||||||
|
|
||||||
switch (i_this->mMode) {
|
switch (i_this->mMode) {
|
||||||
|
|||||||
@@ -463,6 +463,23 @@ int daMidna_c::createHeap() {
|
|||||||
JKRReadIdxResource(mBckHeap[0].getBuffer(), mBckHeap[0].getBufferSize(), 0x1DC, dComIfGp_getAnmArchive());
|
JKRReadIdxResource(mBckHeap[0].getBuffer(), mBckHeap[0].getBufferSize(), 0x1DC, dComIfGp_getAnmArchive());
|
||||||
J3DAnmTransform* md_anm = (J3DAnmTransform*)J3DAnmLoaderDataBase::load(mBckHeap[0].getBuffer());
|
J3DAnmTransform* md_anm = (J3DAnmTransform*)J3DAnmLoaderDataBase::load(mBckHeap[0].getBuffer());
|
||||||
modelData = (J3DModelData*)dComIfG_getObjectRes(l_arcName, 14);
|
modelData = (J3DModelData*)dComIfG_getObjectRes(l_arcName, 14);
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
J3DTexture* tex = modelData->getTexture();
|
||||||
|
JUTNameTab* nametable = modelData->getTextureName();
|
||||||
|
if (tex != NULL && nametable != NULL) {
|
||||||
|
for (u16 i = 0; i < tex->getNum(); i++) {
|
||||||
|
const char* name = nametable->getName(i);
|
||||||
|
if (name != NULL && strcmp(name, "midona_eye") == 0) {
|
||||||
|
ResTIMG* timg = tex->getResTIMG(i);
|
||||||
|
timg->mipmapEnabled = false;
|
||||||
|
tex->loadGXTexObj(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
JUT_ASSERT(852, modelData != NULL);
|
JUT_ASSERT(852, modelData != NULL);
|
||||||
mpMorf = JKR_NEW mDoExt_McaMorfSO(modelData, &mMorfCB, NULL, md_anm, J3DFrameCtrl::EMode_LOOP, 1.0f, 0, -1, NULL, 0, 0x11000284);
|
mpMorf = JKR_NEW mDoExt_McaMorfSO(modelData, &mMorfCB, NULL, md_anm, J3DFrameCtrl::EMode_LOOP, 1.0f, 0, -1, NULL, 0, 0x11000284);
|
||||||
if (mpMorf == NULL || mpMorf->getModel() == NULL) {
|
if (mpMorf == NULL || mpMorf->getModel() == NULL) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ dMirror_packet_c::dMirror_packet_c() {
|
|||||||
void dMirror_packet_c::reset() {
|
void dMirror_packet_c::reset() {
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
mbReset = true;
|
mbReset = true;
|
||||||
|
mbHadEntry = false;
|
||||||
#else
|
#else
|
||||||
mModelCount = 0;
|
mModelCount = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -84,11 +85,21 @@ void dMirror_packet_c::calcMinMax() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dMirror_packet_c::entryModel(J3DModel* i_model) {
|
int dMirror_packet_c::entryModel(J3DModel* i_model) {
|
||||||
|
#if TARGET_PC
|
||||||
|
if (mbReset) {
|
||||||
|
mModelCount = 0;
|
||||||
|
mbReset = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mModelCount >= 0x40) {
|
if (mModelCount >= 0x40) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mModels[mModelCount++] = i_model;
|
mModels[mModelCount++] = i_model;
|
||||||
|
#if TARGET_PC
|
||||||
|
mbHadEntry = true;
|
||||||
|
#endif
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,13 +603,6 @@ int daMirror_c::execute() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_PC
|
|
||||||
if (mPacket.mbReset) {
|
|
||||||
mPacket.mModelCount = 0;
|
|
||||||
mPacket.mbReset = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
daPy_py_c* player = daPy_getLinkPlayerActorClass();
|
daPy_py_c* player = daPy_getLinkPlayerActorClass();
|
||||||
JUT_ASSERT(0, player != NULL);
|
JUT_ASSERT(0, player != NULL);
|
||||||
|
|
||||||
@@ -624,6 +628,12 @@ int daMirror_c::draw() {
|
|||||||
mDoExt_modelUpdateDL(mpModel);
|
mDoExt_modelUpdateDL(mpModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
if (mPacket.mbReset && !mPacket.mbHadEntry) {
|
||||||
|
mPacket.mModelCount = 0;
|
||||||
|
}
|
||||||
|
mPacket.mbHadEntry = true;
|
||||||
|
#endif
|
||||||
dComIfGd_getOpaListBG()->entryImm(&mPacket, 0);
|
dComIfGd_getOpaListBG()->entryImm(&mPacket, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -956,7 +956,7 @@ static void npc_ne_tame(npc_ne_class* i_this) {
|
|||||||
i_this->mpMorf->setPlaySpeed(i_this->mAnmSpeed);
|
i_this->mpMorf->setPlaySpeed(i_this->mAnmSpeed);
|
||||||
|
|
||||||
/* dSv_event_flag_c::F_0470 - Fishing Pond - Reserved for fishing */
|
/* dSv_event_flag_c::F_0470 - Fishing Pond - Reserved for fishing */
|
||||||
if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) {
|
if (IF_DUSK(dusk::getSettings().game.no2ndFishForCat) || dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) {
|
||||||
if (fpcEx_Search(s_fish_sub, _this) != NULL) {
|
if (fpcEx_Search(s_fish_sub, _this) != NULL) {
|
||||||
i_this->mAction = npc_ne_class::ACT_HOME;
|
i_this->mAction = npc_ne_class::ACT_HOME;
|
||||||
i_this->mMode = 10;
|
i_this->mMode = 10;
|
||||||
@@ -2948,8 +2948,7 @@ static int daNpc_Ne_Execute(npc_ne_class* i_this) {
|
|||||||
|
|
||||||
if (i_this->mWantsFish && (i_this->mCounter & 0xf) == 0) {
|
if (i_this->mWantsFish && (i_this->mCounter & 0xf) == 0) {
|
||||||
/* dSv_event_flag_c::F_0470 - Fishing Pond - Reserved for fishing */
|
/* dSv_event_flag_c::F_0470 - Fishing Pond - Reserved for fishing */
|
||||||
if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])
|
if ((IF_DUSK(dusk::getSettings().game.no2ndFishForCat) || dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) && i_this->mDistToTarget < 1500.0f) {
|
||||||
&& i_this->mDistToTarget < 1500.0f) {
|
|
||||||
if (fopAcM_SearchByName(fpcNm_MG_ROD_e) != NULL) {
|
if (fopAcM_SearchByName(fpcNm_MG_ROD_e) != NULL) {
|
||||||
i_this->mNoFollow = false;
|
i_this->mNoFollow = false;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -62,6 +62,16 @@ void daObj_Balloon_c::saveBestScore() {
|
|||||||
dComIfGp_setMessageCountNumber(m_balloon_score);
|
dComIfGp_setMessageCountNumber(m_balloon_score);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
static void minigameReset() {
|
||||||
|
// !@bug d_a_obj_balloon.rel unload used to zero these file-statics; with static linking they dangle across scenes.
|
||||||
|
m_combo_type = 0xFFFFFFFF;
|
||||||
|
m_combo_count = 0;
|
||||||
|
m_combo_next_score = 0;
|
||||||
|
m_balloon_score = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static u8 hio_set;
|
static u8 hio_set;
|
||||||
|
|
||||||
static daObj_Balloon_HIO_c l_HIO;
|
static daObj_Balloon_HIO_c l_HIO;
|
||||||
@@ -205,13 +215,6 @@ int daObj_Balloon_c::_delete() {
|
|||||||
Z2GetAudioMgr()->seStop(Z2SE_OBJ_WATERMILL_ROUND, 0);
|
Z2GetAudioMgr()->seStop(Z2SE_OBJ_WATERMILL_ROUND, 0);
|
||||||
if (mHIOInit) {
|
if (mHIOInit) {
|
||||||
hio_set = false;
|
hio_set = false;
|
||||||
#ifdef TARGET_PC
|
|
||||||
// !@bug d_a_obj_balloon.rel unload used to zero these file-statics; with static linking they dangle across scenes.
|
|
||||||
m_combo_type = 0xFFFFFFFF;
|
|
||||||
m_combo_count = 0;
|
|
||||||
m_combo_next_score = 0;
|
|
||||||
m_balloon_score = 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -253,6 +256,7 @@ int daObj_Balloon_c::create() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hio_set) {
|
if (!hio_set) {
|
||||||
|
IF_DUSK(minigameReset());
|
||||||
mHIOInit = true;
|
mHIOInit = true;
|
||||||
hio_set = true;
|
hio_set = true;
|
||||||
l_HIO.field_0x04 = -1;
|
l_HIO.field_0x04 = -1;
|
||||||
|
|||||||
+65
-40
@@ -794,16 +794,15 @@ void dCamera_c::updatePad() {
|
|||||||
|
|
||||||
if (mTriggerLeftLast > mCamSetup.ManualEndVal()) {
|
if (mTriggerLeftLast > mCamSetup.ManualEndVal()) {
|
||||||
if (mLockLActive == 0) {
|
if (mLockLActive == 0) {
|
||||||
|
#if TARGET_PC
|
||||||
|
mCamParam.mManualMode = 0;
|
||||||
|
#endif
|
||||||
mLockLJustActivated = 1;
|
mLockLJustActivated = 1;
|
||||||
} else {
|
} else {
|
||||||
mLockLJustActivated = 0;
|
mLockLJustActivated = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLockLActive = 1;
|
mLockLActive = 1;
|
||||||
|
|
||||||
#if TARGET_PC
|
|
||||||
mCamParam.mManualMode = 0;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
mLockLJustActivated = 0;
|
mLockLJustActivated = 0;
|
||||||
mLockLActive = 0;
|
mLockLActive = 0;
|
||||||
@@ -1176,14 +1175,14 @@ bool dCamera_c::Run() {
|
|||||||
clrFlag(0x200000);
|
clrFlag(0x200000);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sp0F = (this->*engine_tbl[mCamParam.Algorythmn(mCamStyle)])(mCamStyle);
|
|
||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
if (mCamParam.Algorythmn(mCamStyle) != 1) {
|
if (mCamParam.Algorythmn(mCamStyle) != 1) {
|
||||||
mCamParam.mManualMode = 0;
|
mCamParam.mManualMode = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
sp0F = (this->*engine_tbl[mCamParam.Algorythmn(mCamStyle)])(mCamStyle);
|
||||||
|
|
||||||
field_0x170++;
|
field_0x170++;
|
||||||
field_0x160++;
|
field_0x160++;
|
||||||
mCurCamStyleTimer++;
|
mCurCamStyleTimer++;
|
||||||
@@ -1488,7 +1487,7 @@ void dCamera_c::CalcTrimSize() {
|
|||||||
mTrimHeight += -mTrimHeight * 0.25f;
|
mTrimHeight += -mTrimHeight * 0.25f;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
#if WIDESCREEN_SUPPORT
|
#if !TARGET_PC && WIDESCREEN_SUPPORT
|
||||||
if (mDoGph_gInf_c::isWide() && mDoGph_gInf_c::isWideZoom()) {
|
if (mDoGph_gInf_c::isWide() && mDoGph_gInf_c::isWideZoom()) {
|
||||||
mTrimHeight += (16.0f - mTrimHeight) * 0.25f;
|
mTrimHeight += (16.0f - mTrimHeight) * 0.25f;
|
||||||
break;
|
break;
|
||||||
@@ -3096,10 +3095,6 @@ bool dCamera_c::bumpCheck(u32 i_flags) {
|
|||||||
field_0x968 *= mMonitor.field_0xc / 5.0f;
|
field_0x968 *= mMonitor.field_0xc / 5.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_PC
|
|
||||||
if (!dusk::getSettings().game.freeCamera || !mCamParam.mManualMode) {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
f32 tmp = field_0x96c * (mIsWolf == 1 ? 30.0f : 30.0f);
|
f32 tmp = field_0x96c * (mIsWolf == 1 ? 30.0f : 30.0f);
|
||||||
center += vec3.norm() * (tmp * globe.V().Sin());
|
center += vec3.norm() * (tmp * globe.V().Sin());
|
||||||
cSGlobe globe2(vec2 - center);
|
cSGlobe globe2(vec2 - center);
|
||||||
@@ -3113,10 +3108,6 @@ bool dCamera_c::bumpCheck(u32 i_flags) {
|
|||||||
vec = lin_chk1.GetCross();
|
vec = lin_chk1.GetCross();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_PC
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (mCamSetup.CheckFlag(0x8000)) {
|
if (mCamSetup.CheckFlag(0x8000)) {
|
||||||
dDbVw_Report(20, 235, " U");
|
dDbVw_Report(20, 235, " U");
|
||||||
@@ -4208,6 +4199,11 @@ bool dCamera_c::chaseCamera(s32 param_0) {
|
|||||||
chase->field_0x8 -= chase->field_0xc;
|
chase->field_0x8 -= chase->field_0xc;
|
||||||
chase->field_0x8c = 0;
|
chase->field_0x8c = 0;
|
||||||
chase->field_0x90 = false;
|
chase->field_0x90 = false;
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
freeCamera();
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4631,10 +4627,6 @@ bool dCamera_c::chaseCamera(s32 param_0) {
|
|||||||
sp110 = mViewCache.mDirection.R();
|
sp110 = mViewCache.mDirection.R();
|
||||||
mViewCache.mDirection.R(mViewCache.mDirection.R() + (fVar55 - mViewCache.mDirection.R()) * chase->field_0x74);
|
mViewCache.mDirection.R(mViewCache.mDirection.R() + (fVar55 - mViewCache.mDirection.R()) * chase->field_0x74);
|
||||||
|
|
||||||
#if TARGET_PC
|
|
||||||
freeCamera();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
chase->field_0x64 = mViewCache.mCenter + mViewCache.mDirection.Xyz();
|
chase->field_0x64 = mViewCache.mCenter + mViewCache.mDirection.Xyz();
|
||||||
mViewCache.mEye = chase->field_0x64;
|
mViewCache.mEye = chase->field_0x64;
|
||||||
|
|
||||||
@@ -4649,6 +4641,11 @@ bool dCamera_c::chaseCamera(s32 param_0) {
|
|||||||
if (chase->field_0x1c != 0) {
|
if (chase->field_0x1c != 0) {
|
||||||
chase->field_0x1c--;
|
chase->field_0x1c--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
freeCamera();
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7096,10 +7093,12 @@ bool dCamera_c::subjectCamera(s32 param_0) {
|
|||||||
cXyz sp1E0(val0, val2, val1);
|
cXyz sp1E0(val0, val2, val1);
|
||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
f32 aspect = mDoGph_gInf_c::getAspect();
|
if (sp13) {
|
||||||
f32 baseAspect = FB_WIDTH / FB_HEIGHT;
|
f32 aspect = mDoGph_gInf_c::getAspect();
|
||||||
if (aspect > baseAspect) {
|
f32 baseAspect = FB_WIDTH / FB_HEIGHT;
|
||||||
sp1E0.z += (aspect - baseAspect) * 4;
|
if (aspect > baseAspect) {
|
||||||
|
sp1E0.z += (aspect - baseAspect) * 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -7477,35 +7476,48 @@ bool dCamera_c::test2Camera(s32 param_0) {
|
|||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
bool dCamera_c::freeCamera() {
|
bool dCamera_c::freeCamera() {
|
||||||
if (!dusk::getSettings().game.freeCamera) {
|
if (dusk::getSettings().game.freeCamera && mGear == 1) {
|
||||||
|
mGear = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dusk::getSettings().game.freeCamera || mCamStyle == 70)
|
||||||
|
{
|
||||||
mCamParam.mManualMode = 0;
|
mCamParam.mManualMode = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mCamParam.mManualMode) {
|
||||||
|
mCamParam.freeXAngle = mViewCache.mDirection.mAzimuth.Degree();
|
||||||
|
mCamParam.freeYAngle = mViewCache.mDirection.mInclination.Degree();
|
||||||
|
}
|
||||||
|
|
||||||
cXyz camMovement = {mPadInfo.mCStick.mLastPosX, mPadInfo.mCStick.mLastPosY, 0.0f};
|
cXyz camMovement = {mPadInfo.mCStick.mLastPosX, mPadInfo.mCStick.mLastPosY, 0.0f};
|
||||||
f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY);
|
f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY);
|
||||||
|
|
||||||
if (mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY != 0) {
|
if (mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY != 0) {
|
||||||
if (!mCamParam.mManualMode) {
|
mCamParam.mManualMode = 1;
|
||||||
mCamParam.mManualMode = 1;
|
|
||||||
mCamParam.freeXAngle = mViewCache.mDirection.mAzimuth.Degree();
|
|
||||||
mCamParam.freeYAngle = mViewCache.mDirection.mInclination.Degree();
|
|
||||||
}
|
|
||||||
|
|
||||||
camMovement = camMovement.normalize();
|
camMovement = camMovement.normalize();
|
||||||
camMovement.y *= dusk::getSettings().game.invertCameraYAxis ? 1.0f : -1.0f;
|
camMovement.y *= dusk::getSettings().game.invertCameraYAxis ? 1.0f : -1.0f;
|
||||||
mCamParam.freeXAngle += camMovement.x * magnitude * dusk::getSettings().game.freeCameraSensitivity * 4.0f;
|
mCamParam.freeXAngle += camMovement.x * magnitude * dusk::getSettings().game.freeCameraSensitivity * 5.0f;
|
||||||
mCamParam.freeYAngle += camMovement.y * magnitude * dusk::getSettings().game.freeCameraSensitivity * 4.0f;
|
mCamParam.freeYAngle += camMovement.y * magnitude * dusk::getSettings().game.freeCameraSensitivity * 5.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCamParam.mManualMode) {
|
fopAc_ac_c* player = dComIfGp_getPlayer(0);
|
||||||
mCamParam.freeYAngle = std::clamp(mCamParam.freeYAngle, -35.0f, 60.0f);
|
if (!mCamParam.mManualMode || player == nullptr) {
|
||||||
mViewCache.mDirection.mAzimuth = cSAngle(mCamParam.freeXAngle);
|
return false;
|
||||||
mViewCache.mDirection.mInclination = cSAngle(mCamParam.freeYAngle);
|
|
||||||
mViewCache.mDirection.mRadius = std::clamp((mCamParam.freeYAngle + 35.0f) * 10.0f, 300.0f, 10000.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mCamParam.mManualMode;
|
f32 minYAngle = -30.0f;
|
||||||
|
f32 maxAngle = 50.0f;
|
||||||
|
|
||||||
|
mCamParam.freeYAngle = std::clamp(mCamParam.freeYAngle, minYAngle, maxAngle);
|
||||||
|
mViewCache.mDirection.mAzimuth = cSAngle(mCamParam.freeXAngle);
|
||||||
|
mViewCache.mDirection.mInclination = cSAngle(mCamParam.freeYAngle);
|
||||||
|
|
||||||
|
cXyz finalEye = mViewCache.mCenter + mViewCache.mDirection.Xyz();
|
||||||
|
mViewCache.mEye = finalEye;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -11148,12 +11160,25 @@ static int camera_draw(camera_process_class* i_this) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int trim_height = body->TrimHeight();
|
|
||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
|
auto trim_height = body->TrimHeight();
|
||||||
|
|
||||||
|
if (mDoGph_gInf_c::isWideZoom()) {
|
||||||
|
const auto target_ar = FB_WIDTH / (FB_HEIGHT - trim_height * 2.0f);
|
||||||
|
const auto current_ar = mDoGph_gInf_c::m_safeWidthF / mDoGph_gInf_c::m_safeHeightF;
|
||||||
|
|
||||||
|
if (current_ar < target_ar) {
|
||||||
|
trim_height = FB_HEIGHT / 2.0f * (1.0f - current_ar / target_ar);
|
||||||
|
} else {
|
||||||
|
trim_height = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trim_height *= viewport->height / FB_HEIGHT;
|
trim_height *= viewport->height / FB_HEIGHT;
|
||||||
window->setScissor(0.0f, trim_height, viewport->width, viewport->height - trim_height * 2.0f);
|
window->setScissor(0.0f, trim_height, viewport->width, viewport->height - trim_height * 2.0f);
|
||||||
#else
|
#else
|
||||||
|
int trim_height = body->TrimHeight();
|
||||||
|
|
||||||
window->setScissor(0.0f, trim_height, FB_WIDTH, FB_HEIGHT - trim_height * 2.0f);
|
window->setScissor(0.0f, trim_height, FB_WIDTH, FB_HEIGHT - trim_height * 2.0f);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+45
-9
@@ -22,6 +22,10 @@
|
|||||||
#include "dusk/frame_interpolation.h"
|
#include "dusk/frame_interpolation.h"
|
||||||
#include "dusk/gx_helper.h"
|
#include "dusk/gx_helper.h"
|
||||||
#include "dusk/logging.h"
|
#include "dusk/logging.h"
|
||||||
|
|
||||||
|
static const void* getInterpKey(const void* base, int idx) {
|
||||||
|
return reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(base) ^ idx);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class dDlst_2Dm_c {
|
class dDlst_2Dm_c {
|
||||||
@@ -1062,7 +1066,15 @@ void dDlst_shadowReal_c::reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dDlst_shadowReal_c::imageDraw(Mtx param_0) {
|
void dDlst_shadowReal_c::imageDraw(Mtx param_0) {
|
||||||
GXSetProjection(mRenderProjMtx, GX_ORTHOGRAPHIC);
|
#ifdef TARGET_PC
|
||||||
|
Mtx render_proj_mtx;
|
||||||
|
if (dusk::frame_interp::lookup_replacement(getInterpKey(mpModels[0], 2), render_proj_mtx)) {
|
||||||
|
GXSetProjection(render_proj_mtx, GX_ORTHOGRAPHIC);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
GXSetProjection(mRenderProjMtx, GX_ORTHOGRAPHIC);
|
||||||
|
}
|
||||||
JUT_ASSERT(1916, mModelNum);
|
JUT_ASSERT(1916, mModelNum);
|
||||||
J3DModelData* model_data;
|
J3DModelData* model_data;
|
||||||
J3DModel** models = mpModels;
|
J3DModel** models = mpModels;
|
||||||
@@ -1075,7 +1087,15 @@ void dDlst_shadowReal_c::imageDraw(Mtx param_0) {
|
|||||||
for (u16 j = 0; j < model_data->getShapeNum(); j++) {
|
for (u16 j = 0; j < model_data->getShapeNum(); j++) {
|
||||||
if (!model_data->getShapeNodePointer(j)->checkFlag(1)) {
|
if (!model_data->getShapeNodePointer(j)->checkFlag(1)) {
|
||||||
shape_pkt = (*models)->getShapePacket(j);
|
shape_pkt = (*models)->getShapePacket(j);
|
||||||
shape_pkt->setBaseMtxPtr(&mViewMtx);
|
#ifdef TARGET_PC
|
||||||
|
Mtx view_mtx;
|
||||||
|
if (dusk::frame_interp::lookup_replacement(getInterpKey(mpModels[0], 1), view_mtx)) {
|
||||||
|
shape_pkt->setBaseMtxPtr(&view_mtx);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
shape_pkt->setBaseMtxPtr(&mViewMtx);
|
||||||
|
}
|
||||||
shape_pkt->drawFast();
|
shape_pkt->drawFast();
|
||||||
shape_pkt->setBaseMtxPtr((Mtx*)param_0);
|
shape_pkt->setBaseMtxPtr((Mtx*)param_0);
|
||||||
}
|
}
|
||||||
@@ -1096,7 +1116,18 @@ void dDlst_shadowReal_c::draw() {
|
|||||||
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
|
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
|
||||||
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
|
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
|
||||||
GXSetCurrentMtx(GX_PNMTX0);
|
GXSetCurrentMtx(GX_PNMTX0);
|
||||||
GXLoadTexMtxImm(mReceiverProjMtx, GX_TEXMTX0, GX_MTX3x4);
|
#ifdef TARGET_PC
|
||||||
|
Mtx view_mtx, recv_proj_mtx;
|
||||||
|
const auto have_view_mtx = dusk::frame_interp::lookup_replacement(getInterpKey(mpModels[0], 1), view_mtx);
|
||||||
|
const auto have_recv_proj_mtx = dusk::frame_interp::lookup_replacement(getInterpKey(mpModels[0], 3), recv_proj_mtx);
|
||||||
|
if (have_view_mtx && have_recv_proj_mtx) {
|
||||||
|
cMtx_concat(recv_proj_mtx, view_mtx, recv_proj_mtx);
|
||||||
|
GXLoadTexMtxImm(recv_proj_mtx, GX_TEXMTX0, GX_MTX3x4);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
GXLoadTexMtxImm(mReceiverProjMtx, GX_TEXMTX0, GX_MTX3x4);
|
||||||
|
}
|
||||||
mShadowRealPoly.draw();
|
mShadowRealPoly.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1253,6 +1284,13 @@ u8 dDlst_shadowReal_c::setShadowRealMtx(cXyz* param_0, cXyz* param_1, f32 param_
|
|||||||
cMtx_lookAt(mViewMtx, &local_64, param_1, 0);
|
cMtx_lookAt(mViewMtx, &local_64, param_1, 0);
|
||||||
C_MTXOrtho(mRenderProjMtx, param_2, -param_2, -param_2, param_2, 1.0f, 10000.0f);
|
C_MTXOrtho(mRenderProjMtx, param_2, -param_2, -param_2, param_2, 1.0f, 10000.0f);
|
||||||
C_MTXLightOrtho(mReceiverProjMtx, param_2, -param_2, -param_2, param_2, 0.5f, -0.5f, 0.5f, 0.5f);
|
C_MTXLightOrtho(mReceiverProjMtx, param_2, -param_2, -param_2, param_2, 0.5f, -0.5f, 0.5f, 0.5f);
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
const auto keybase = mpModels[0];
|
||||||
|
dusk::frame_interp::record_final_mtx(mViewMtx, getInterpKey(keybase, 1));
|
||||||
|
dusk::frame_interp::record_final_mtx(mRenderProjMtx, getInterpKey(keybase, 2));
|
||||||
|
dusk::frame_interp::record_final_mtx(mReceiverProjMtx, getInterpKey(keybase, 3));
|
||||||
|
#endif
|
||||||
cMtx_concat(mReceiverProjMtx, mViewMtx, mReceiverProjMtx);
|
cMtx_concat(mReceiverProjMtx, mViewMtx, mReceiverProjMtx);
|
||||||
return r29;
|
return r29;
|
||||||
}
|
}
|
||||||
@@ -1277,6 +1315,10 @@ u32 dDlst_shadowReal_c::set(u32 i_key, J3DModel* i_model, cXyz* param_2, f32 par
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
// provide a stable key for interpolation
|
||||||
|
mpModels[0] = i_model;
|
||||||
|
#endif
|
||||||
field_0x1 = setShadowRealMtx(&sp60, param_2, param_3, param_4, param_7, param_5);
|
field_0x1 = setShadowRealMtx(&sp60, param_2, param_3, param_4, param_7, param_5);
|
||||||
|
|
||||||
if (!field_0x1) {
|
if (!field_0x1) {
|
||||||
@@ -1370,12 +1412,6 @@ void dDlst_shadowSimple_c::draw() {
|
|||||||
GXCallDisplayList(l_shadowVolumeDL, 0x40);
|
GXCallDisplayList(l_shadowVolumeDL, 0x40);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_PC
|
|
||||||
static const void* getInterpKey(const void* base, int idx) {
|
|
||||||
return reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(base) ^ idx);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void dDlst_shadowSimple_c::set(cXyz* param_0, f32 param_1, f32 param_2, cXyz* param_3,
|
void dDlst_shadowSimple_c::set(cXyz* param_0, f32 param_1, f32 param_2, cXyz* param_3,
|
||||||
s16 param_4, f32 param_5, TGXTexObj* param_6) {
|
s16 param_4, f32 param_5, TGXTexObj* param_6) {
|
||||||
if (param_5 < 0.0f) {
|
if (param_5 < 0.0f) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#ifdef TARGET_PC
|
#ifdef TARGET_PC
|
||||||
constexpr u16 kMapResolutionMultiplier = 4;
|
constexpr u16 kMapResolutionMultiplier = 4;
|
||||||
|
constexpr u16 kMapCircleSize = 16 * kMapResolutionMultiplier;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void dMpath_n::dTexObjAggregate_c::create() {
|
void dMpath_n::dTexObjAggregate_c::create() {
|
||||||
@@ -32,6 +33,48 @@ void dMpath_n::dTexObjAggregate_c::create() {
|
|||||||
JUT_ASSERT(74, image->magFilter == GX_NEAR);
|
JUT_ASSERT(74, image->magFilter == GX_NEAR);
|
||||||
mDoLib_setResTimgObj(image, mp_texObj[lp1], 0, NULL);
|
mDoLib_setResTimgObj(image, mp_texObj[lp1], 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
auto hqCircle = JKR_NEW TGXTexObj();
|
||||||
|
|
||||||
|
static bool hqCircleDrawn = false;
|
||||||
|
static u8 hqCircleData[kMapCircleSize * kMapCircleSize];
|
||||||
|
|
||||||
|
if (!hqCircleDrawn) {
|
||||||
|
const auto center = kMapCircleSize / 2.0f;
|
||||||
|
const auto radiusSq = center * center;
|
||||||
|
const auto blocksAcross = kMapCircleSize >> 3;
|
||||||
|
const auto totalPixels = sizeof(hqCircleData);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < totalPixels; 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;
|
||||||
|
|
||||||
|
const auto dx = (x + 0.5f) - center;
|
||||||
|
const auto dy = (y + 0.5f) - center;
|
||||||
|
|
||||||
|
// the original texture is in I4 format and uses 1 to indicate if inside the circle
|
||||||
|
// so we scale to I8 range: 255 / 15 = 17
|
||||||
|
hqCircleData[i] = (dx * dx + dy * dy < radiusSq) ? 17 : 0;
|
||||||
|
}
|
||||||
|
hqCircleDrawn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GXInitTexObj(hqCircle, hqCircleData, kMapCircleSize, kMapCircleSize, GX_TF_I8, GX_CLAMP,
|
||||||
|
GX_CLAMP, GX_FALSE);
|
||||||
|
GXInitTexObjLOD(hqCircle, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
|
||||||
|
mp_texObj[6] = hqCircle;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void dMpath_n::dTexObjAggregate_c::remove() {
|
void dMpath_n::dTexObjAggregate_c::remove() {
|
||||||
|
|||||||
@@ -856,7 +856,46 @@ void dMenu_DmapBg_c::decGoldFrameAlphaRate() {
|
|||||||
setGoldFrameAlphaRate(rate);
|
setGoldFrameAlphaRate(rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dMenu_DmapBg_c::dMapBgWide() {
|
||||||
|
// Scale Base HUD
|
||||||
|
mBaseScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
|
||||||
|
mBaseScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
|
||||||
|
|
||||||
|
// Boss Key, Compass & Map icons
|
||||||
|
mBaseScreen->search(MULTI_CHAR('key_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
mBaseScreen->search(MULTI_CHAR('con_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
mBaseScreen->search(MULTI_CHAR('map_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
|
||||||
|
// Text Header
|
||||||
|
mBaseScreen->search(MULTI_CHAR('t_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
mBaseScreen->search(MULTI_CHAR('f_t_00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
|
||||||
|
// C Button
|
||||||
|
mBaseScreen->search(MULTI_CHAR('c_btn2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
|
||||||
|
// Scale Buttons HUD
|
||||||
|
mButtonScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
|
||||||
|
mButtonScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
mButtonScreen->search(MULTI_CHAR('cont_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
|
||||||
|
// C Button
|
||||||
|
mButtonScreen->search(MULTI_CHAR('c_btn'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
mButtonScreen->search(MULTI_CHAR('c_text_s'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
mButtonScreen->search(MULTI_CHAR('c_text'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
mButtonScreen->search(MULTI_CHAR('f_text_s'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
mButtonScreen->search(MULTI_CHAR('f_text'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
|
||||||
|
// Decorations
|
||||||
|
mButtonScreen->search(MULTI_CHAR('kazari_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
void dMenu_DmapBg_c::draw() {
|
void dMenu_DmapBg_c::draw() {
|
||||||
|
#if TARGET_PC
|
||||||
|
dMapBgWide();
|
||||||
|
#endif
|
||||||
|
|
||||||
u32 scissor_left;
|
u32 scissor_left;
|
||||||
u32 scissor_top;
|
u32 scissor_top;
|
||||||
u32 scissor_width;
|
u32 scissor_width;
|
||||||
|
|||||||
+34
-1
@@ -20,6 +20,15 @@
|
|||||||
#include "dusk/frame_interpolation.h"
|
#include "dusk/frame_interpolation.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void dMenu_Fmap2DBack_c::fMapBackWide() {
|
||||||
|
mpBaseScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
|
||||||
|
mpBaseScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
|
||||||
|
mpBackScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
|
||||||
|
mpBackScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
dMenu_Fmap2DBack_c::dMenu_Fmap2DBack_c() {
|
dMenu_Fmap2DBack_c::dMenu_Fmap2DBack_c() {
|
||||||
dMeter2Info_setMapDrugFlag(0);
|
dMeter2Info_setMapDrugFlag(0);
|
||||||
|
|
||||||
@@ -267,6 +276,10 @@ dMenu_Fmap2DBack_c::~dMenu_Fmap2DBack_c() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dMenu_Fmap2DBack_c::draw() {
|
void dMenu_Fmap2DBack_c::draw() {
|
||||||
|
#if TARGET_PC
|
||||||
|
fMapBackWide();
|
||||||
|
#endif
|
||||||
|
|
||||||
calcBlink();
|
calcBlink();
|
||||||
|
|
||||||
J2DGrafContext* grafPort = dComIfGp_getCurrentGrafPort();
|
J2DGrafContext* grafPort = dComIfGp_getCurrentGrafPort();
|
||||||
@@ -1199,7 +1212,7 @@ f32 dMenu_Fmap2DBack_c::getMapScissorAreaSizeX() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f32 dMenu_Fmap2DBack_c::getMapScissorAreaSizeRealX() {
|
f32 dMenu_Fmap2DBack_c::getMapScissorAreaSizeRealX() {
|
||||||
#if PLATFORM_GCN && !TARGET_PC
|
#if PLATFORM_GCN
|
||||||
return getMapScissorAreaSizeX();
|
return getMapScissorAreaSizeX();
|
||||||
#else
|
#else
|
||||||
return getMapScissorAreaSizeX() * mDoGph_gInf_c::getScale();
|
return getMapScissorAreaSizeX() * mDoGph_gInf_c::getScale();
|
||||||
@@ -1407,6 +1420,11 @@ void dMenu_Fmap2DBack_c::stageTextureDraw() {
|
|||||||
mpSpotTexture->setAlpha(mAlphaRate * 255.0f * field_0xfa8 * mSpotTextureFadeAlpha);
|
mpSpotTexture->setAlpha(mAlphaRate * 255.0f * field_0xfa8 * mSpotTextureFadeAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
JUTPalette* pPalette = mpSpotTexture->getTexture(0)->getPalette();
|
||||||
|
pPalette->dataUploaded();
|
||||||
|
#endif
|
||||||
|
|
||||||
mpSpotTexture->draw(mTransX + getMapScissorAreaLX(), mTransZ + getMapScissorAreaLY(),
|
mpSpotTexture->draw(mTransX + getMapScissorAreaLX(), mTransZ + getMapScissorAreaLY(),
|
||||||
getMapScissorAreaSizeRealX(), getMapScissorAreaSizeRealY(), false, false,
|
getMapScissorAreaSizeRealX(), getMapScissorAreaSizeRealY(), false, false,
|
||||||
false);
|
false);
|
||||||
@@ -2179,6 +2197,17 @@ void dMenu_Fmap2DBack_c::setArrowPosAxis(f32 i_posX, f32 i_posZ) {
|
|||||||
control_ypos = 0.0f;
|
control_ypos = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void dMenu_Fmap2DTop_c::fMapTopWide() {
|
||||||
|
mpTitleScreen->search(MULTI_CHAR('spot0_n'))->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
|
||||||
|
mpTitleScreen->search(MULTI_CHAR('spot2_n'))->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
|
||||||
|
mpTitleScreen->search(MULTI_CHAR('name_n'))->translate(mDoGph_gInf_c::ScaleHUDXLeft(-243.0f), -169.0f);
|
||||||
|
mpTitleScreen->search(MULTI_CHAR('sub_n_n'))->translate(mDoGph_gInf_c::ScaleHUDXLeft(-80.0f), -154.0f);
|
||||||
|
mpTitleScreen->search(MULTI_CHAR('btn_i_n'))->translate(mDoGph_gInf_c::ScaleHUDXLeft(-241.0f), 177.0f);
|
||||||
|
mpTitleScreen->search(MULTI_CHAR('cont_n'))->translate(mDoGph_gInf_c::ScaleHUDXRight(515.0f), 83.0f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) {
|
dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) {
|
||||||
mpHeap = i_heap;
|
mpHeap = i_heap;
|
||||||
mTransX = 0.0f;
|
mTransX = 0.0f;
|
||||||
@@ -2572,6 +2601,10 @@ void dMenu_Fmap2DTop_c::setAllAlphaRate(f32 i_rate, bool i_init) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dMenu_Fmap2DTop_c::draw() {
|
void dMenu_Fmap2DTop_c::draw() {
|
||||||
|
#if TARGET_PC
|
||||||
|
fMapTopWide();
|
||||||
|
#endif
|
||||||
|
|
||||||
u32 scissor_left, scissor_top, scissor_width, scissor_height;
|
u32 scissor_left, scissor_top, scissor_width, scissor_height;
|
||||||
J2DOrthoGraph* ctx = static_cast<J2DOrthoGraph*>(dComIfGp_getCurrentGrafPort());
|
J2DOrthoGraph* ctx = static_cast<J2DOrthoGraph*>(dComIfGp_getCurrentGrafPort());
|
||||||
ctx->setup2D();
|
ctx->setup2D();
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
#include "d/d_msg_scrn_arrow.h"
|
#include "d/d_msg_scrn_arrow.h"
|
||||||
#include "d/d_lib.h"
|
#include "d/d_lib.h"
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
#include "dusk/achievements.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if VERSION == VERSION_GCN_JPN
|
#if VERSION == VERSION_GCN_JPN
|
||||||
#define D_MENU_LETTER_LINE_MAX 9
|
#define D_MENU_LETTER_LINE_MAX 9
|
||||||
#else
|
#else
|
||||||
@@ -514,6 +518,10 @@ void dMenu_Letter_c::read_open_init() {
|
|||||||
setAButtonString(0);
|
setAButtonString(0);
|
||||||
setBButtonString(0);
|
setBButtonString(0);
|
||||||
mpBlackTex->setAlpha(0);
|
mpBlackTex->setAlpha(0);
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
dusk::AchievementSystem::get().signal("open_letter");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void dMenu_Letter_c::read_open_move() {
|
void dMenu_Letter_c::read_open_move() {
|
||||||
|
|||||||
+6
-6
@@ -316,6 +316,12 @@ int dMeter2_c::_execute() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dMeter2_c::_draw() {
|
int dMeter2_c::_draw() {
|
||||||
|
#if TARGET_PC
|
||||||
|
if (dusk::getSettings().game.disableMainHUD) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mpMap != NULL) {
|
if (mpMap != NULL) {
|
||||||
mpMap->_draw();
|
mpMap->_draw();
|
||||||
}
|
}
|
||||||
@@ -424,12 +430,6 @@ void dMeter2_c::setLifeZero() {
|
|||||||
void dMeter2_c::checkStatus() {
|
void dMeter2_c::checkStatus() {
|
||||||
mStatus = 0;
|
mStatus = 0;
|
||||||
|
|
||||||
#if TARGET_PC
|
|
||||||
if (dusk::getSettings().game.disableMainHUD) {
|
|
||||||
mStatus |= 0xF0000000;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
field_0x12c = field_0x128;
|
field_0x12c = field_0x128;
|
||||||
|
|
||||||
field_0x128 = daPy_py_c::checkNowWolf();
|
field_0x128 = daPy_py_c::checkNowWolf();
|
||||||
|
|||||||
@@ -2306,6 +2306,10 @@ void dMeter_drawHIO_c::updateOnWide() {
|
|||||||
// River Canoe Minigame
|
// River Canoe Minigame
|
||||||
g_drawHIO.mMiniGame.mCounterPosX[1] = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMiniGame.mCounterPosX[1]);
|
g_drawHIO.mMiniGame.mCounterPosX[1] = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMiniGame.mCounterPosX[1]);
|
||||||
g_drawHIO.mMiniGame.mIconPosX[1] = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMiniGame.mIconPosX[1]);
|
g_drawHIO.mMiniGame.mIconPosX[1] = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMiniGame.mIconPosX[1]);
|
||||||
|
|
||||||
|
// Bulblin Count in Hidden Village
|
||||||
|
g_drawHIO.mMiniGame.mCounterPosX[2] = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMiniGame.mCounterPosX[2]);
|
||||||
|
g_drawHIO.mMiniGame.mIconPosX[2] = mDoGph_gInf_c::ScaleHUDXRight(g_drawHIO.mMiniGame.mIconPosX[2]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1987,13 +1987,6 @@ bool jmessage_tSequenceProcessor::do_isReady() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if TARGET_PC
|
|
||||||
if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) {
|
|
||||||
field_0xb2 = 1;
|
|
||||||
pReference->setSendTimer(0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (dComIfGp_checkMesgBgm()) {
|
if (dComIfGp_checkMesgBgm()) {
|
||||||
bool isItemMusicPlaying = true;
|
bool isItemMusicPlaying = true;
|
||||||
if (mDoAud_checkPlayingSubBgmFlag() != Z2BGM_ITEM_GET &&
|
if (mDoAud_checkPlayingSubBgmFlag() != Z2BGM_ITEM_GET &&
|
||||||
@@ -2066,7 +2059,7 @@ bool jmessage_tSequenceProcessor::do_isReady() {
|
|||||||
case 0:
|
case 0:
|
||||||
case 5:
|
case 5:
|
||||||
case 6:
|
case 6:
|
||||||
if (mDoCPd_c::getTrigA(PAD_1) || field_0xb2 != 0) {
|
if (mDoCPd_c::getTrigA(PAD_1) || field_0xb2 != 0 IF_DUSK(|| (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)))) {
|
||||||
field_0xa4 = 0;
|
field_0xa4 = 0;
|
||||||
pReference->onBatchFlag();
|
pReference->onBatchFlag();
|
||||||
pReference->setCharCnt(D_MSG_CLASS_CHAR_CNT_MAX);
|
pReference->setCharCnt(D_MSG_CLASS_CHAR_CNT_MAX);
|
||||||
|
|||||||
+38
-1
@@ -32,6 +32,9 @@
|
|||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
#include "dusk/settings.h"
|
#include "dusk/settings.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void dMsgObject_addFundRaising(s16 param_0);
|
static void dMsgObject_addFundRaising(s16 param_0);
|
||||||
@@ -1594,7 +1597,7 @@ u8 dMsgObject_c::isSend() {
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||)
|
if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0) && !isShopItemMessage()) ||)
|
||||||
mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) {
|
mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@@ -1866,6 +1869,40 @@ bool dMsgObject_c::isTalkMessage() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
bool dMsgObject_c::isShopItemMessage() {
|
||||||
|
|
||||||
|
// Probably a better way to do this than just listing every message id, but this works for now
|
||||||
|
// Note: Keep contents sorted so we can use binary search
|
||||||
|
const auto shopMsgIds = std::to_array<std::vector<s16>>({
|
||||||
|
{},
|
||||||
|
// zel_01.bmg - Seras Shop
|
||||||
|
{7001, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7013, 7014, 7022, 7023, 7028, 7029,
|
||||||
|
7044, 7045, 7053},
|
||||||
|
// zel_02.bmg - Kakariko Shops
|
||||||
|
{5251, 5253, 5254, 5256, 5258, 5259, 5653, 5654, 5656, 5660, 5661, 5664, 5665, 5697, 5698,
|
||||||
|
5699, 5803, 5804, 5806, 5810, 5811, 5812, 5814, 5821, 5823, 5824, 5987, 5988, 5989, 5990,
|
||||||
|
5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999},
|
||||||
|
// zel_03.bmg - Death Mountain Shop
|
||||||
|
{5303, 5304, 5306, 5310, 5311, 5314, 5315, 5322, 5323, 5324, 5496, 5497, 5498, 5499},
|
||||||
|
// zel_04.bmg - Castle Town Shops
|
||||||
|
{5407, 5408, 5409, 5410, 5411, 5412, 5413, 5414, 5415, 5416, 5417, 5418, 5419, 5420, 5431,
|
||||||
|
5432, 5433, 5434, 5435, 5436, 5437, 5438, 5439, 5440, 5441, 5444, 5449, 5450, 5451, 5452,
|
||||||
|
5462},
|
||||||
|
// zel_05.bmg - Oocca Shop
|
||||||
|
{9428, 9429, 9430, 9431, 9432, 9437, 9443, 9448, 9449, 9451, 9459}
|
||||||
|
});
|
||||||
|
|
||||||
|
u16 id = mMessageID;
|
||||||
|
s16 group = dMsgObject_getGroupID();
|
||||||
|
if (group < shopMsgIds.size()) {
|
||||||
|
return std::ranges::binary_search(shopMsgIds[group], id);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const char* dMsgObject_c::getSmellName() {
|
const char* dMsgObject_c::getSmellName() {
|
||||||
JMSMesgInfo_c* info_header_p = (JMSMesgInfo_c*)((char*)mpMsgRes + 0x20);
|
JMSMesgInfo_c* info_header_p = (JMSMesgInfo_c*)((char*)mpMsgRes + 0x20);
|
||||||
char* data_ptr = (char*)info_header_p + info_header_p->header.size;
|
char* data_ptr = (char*)info_header_p + info_header_p->header.size;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
#include "dusk/memory.h"
|
#include "dusk/memory.h"
|
||||||
|
#include <dusk/autosave.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -700,6 +701,10 @@ static u8 lbl_8074CAE4;
|
|||||||
static u32 l_sceneChangeStartTick;
|
static u32 l_sceneChangeStartTick;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
static BOOL autoSaved;
|
||||||
|
#endif
|
||||||
|
|
||||||
static int dScnPly_Execute(dScnPly_c* i_this) {
|
static int dScnPly_Execute(dScnPly_c* i_this) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
fapGm_HIO_c::startCpuTimer();
|
fapGm_HIO_c::startCpuTimer();
|
||||||
@@ -742,6 +747,15 @@ static int dScnPly_Execute(dScnPly_c* i_this) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
if (!dComIfGp_event_runCheck() && !fopOvlpM_IsPeek() && !dComIfG_resetToOpening(i_this) &&
|
||||||
|
!dComIfGp_isEnableNextStage() && autoSaved == FALSE)
|
||||||
|
{
|
||||||
|
triggerAutoSave();
|
||||||
|
autoSaved = TRUE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
dKy_itudemo_se();
|
dKy_itudemo_se();
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -1593,6 +1607,11 @@ static int dScnPly_Create(scene_class* i_this) {
|
|||||||
|
|
||||||
dScnPly_c* a_this = (dScnPly_c*)i_this;
|
dScnPly_c* a_this = (dScnPly_c*)i_this;
|
||||||
int phase_state = dComLbG_PhaseHandler(&a_this->field_0x1c4, l_method, a_this);
|
int phase_state = dComLbG_PhaseHandler(&a_this->field_0x1c4, l_method, a_this);
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
autoSaved = FALSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
return phase_state;
|
return phase_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,11 @@
|
|||||||
#include "lingcod/lingcod.h"
|
#include "lingcod/lingcod.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
#include "dusk/settings.h"
|
#include "dusk/settings.h"
|
||||||
|
#include <f_ap/f_ap_game.h>
|
||||||
|
#include <dusk/autosave.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static u8 dSv_item_rename(u8 i_itemNo) {
|
static u8 dSv_item_rename(u8 i_itemNo) {
|
||||||
switch (i_itemNo) {
|
switch (i_itemNo) {
|
||||||
@@ -345,6 +349,10 @@ void dSv_player_item_c::setItem(int i_slotNo, u8 i_itemNo) {
|
|||||||
dComIfGp_setSelectItem(i);
|
dComIfGp_setSelectItem(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
triggerAutoSave();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 dSv_player_item_c::getItem(int i_slotNo, bool i_checkCombo) const {
|
u8 dSv_player_item_c::getItem(int i_slotNo, bool i_checkCombo) const {
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#include "aurora/lib/logging.hpp"
|
||||||
|
#include "os_report.h"
|
||||||
|
|
||||||
|
aurora::Module Log("dusk::osReport");
|
||||||
|
|
||||||
|
bool dusk::OSReportReallyForceEnable = false;
|
||||||
|
|
||||||
|
u8 __OSReport_disable;
|
||||||
|
|
||||||
|
void OSReportDisable() {
|
||||||
|
__OSReport_disable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSReportEnable() {
|
||||||
|
__OSReport_disable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkEnabled() {
|
||||||
|
return !__OSReport_disable || dusk::OSReportReallyForceEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string FormatToString(const char* msg, va_list list) {
|
||||||
|
int ret = vsnprintf(nullptr, 0, msg, list);
|
||||||
|
std::string buf(ret, '\0');
|
||||||
|
vsnprintf(buf.data(), buf.size(), msg, list);
|
||||||
|
buf.pop_back();
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSReport_Error(const char* fmt, ...) {
|
||||||
|
if (!checkEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
const auto str = FormatToString(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
Log.error("{}", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSReport_FatalError(const char* fmt, ...) {
|
||||||
|
if (!checkEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
const auto str = FormatToString(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
Log.fatal("{}", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSReport_Warning(const char* fmt, ...) {
|
||||||
|
if (!checkEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
const auto str = FormatToString(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
Log.warn("{}", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSReport_System(const char* fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
OSVAttention(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSVAttention(const char* fmt, va_list args) {
|
||||||
|
if (!checkEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto str = FormatToString(fmt, args);
|
||||||
|
|
||||||
|
Log.info("{}", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSAttention(const char* fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
OSVAttention(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
+127
-8
@@ -8,6 +8,7 @@
|
|||||||
#include "d/actor/d_a_player.h"
|
#include "d/actor/d_a_player.h"
|
||||||
#include "d/d_demo.h"
|
#include "d/d_demo.h"
|
||||||
#include "f_pc/f_pc_name.h"
|
#include "f_pc/f_pc_name.h"
|
||||||
|
#include "f_op/f_op_actor_mng.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <algorithm>
|
#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",
|
"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",
|
"back_in_time",
|
||||||
@@ -267,18 +335,13 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
|||||||
false, 0, 0, false
|
false, 0, 0, false
|
||||||
},
|
},
|
||||||
[](Achievement& a, json&) {
|
[](Achievement& a, json&) {
|
||||||
static int titleNoDemoFrames = 0;
|
|
||||||
if (fopAcM_SearchByName(fpcNm_TITLE_e) == nullptr) {
|
if (fopAcM_SearchByName(fpcNm_TITLE_e) == nullptr) {
|
||||||
titleNoDemoFrames = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
|
const auto* player = static_cast<const daPy_py_c*>(daPy_getPlayerActorClass());
|
||||||
if (link != nullptr && dDemo_c::getMode() == 0) {
|
|
||||||
if (++titleNoDemoFrames >= 60) {
|
if (player != nullptr && player->mDemo.getDemoMode() == 1) {
|
||||||
a.progress = 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();
|
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) {
|
void AchievementSystem::processEntry(Entry& e) {
|
||||||
if (e.achievement.unlocked) {
|
if (e.achievement.unlocked) {
|
||||||
return;
|
return;
|
||||||
@@ -458,6 +576,7 @@ void AchievementSystem::tick() {
|
|||||||
for (auto& e : m_entries) {
|
for (auto& e : m_entries) {
|
||||||
processEntry(e);
|
processEntry(e);
|
||||||
}
|
}
|
||||||
|
m_signals.clear();
|
||||||
if (m_dirty) {
|
if (m_dirty) {
|
||||||
save();
|
save();
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
|
|||||||
@@ -48,6 +48,20 @@ f32 dusk::audio::MasterVolume = 1.0f;
|
|||||||
f32 dusk::audio::PrevMasterVolume = 1.0f;
|
f32 dusk::audio::PrevMasterVolume = 1.0f;
|
||||||
bool dusk::audio::EnableReverb = true;
|
bool dusk::audio::EnableReverb = true;
|
||||||
bool dusk::audio::DumpAudio = false;
|
bool dusk::audio::DumpAudio = false;
|
||||||
|
bool dusk::audio::EnableHrtf = false;
|
||||||
|
f32 dusk::audio::HrtfGain = 0.5f;
|
||||||
|
|
||||||
|
|
||||||
|
// 3dB at 5kHz.
|
||||||
|
static constexpr f32 HRTF_LP_K = 0.75f;
|
||||||
|
static constexpr f32 HRTF_ALLPASS_G = 0.3f;
|
||||||
|
// Front never drops below (1 - HRTF_EXTRACT_MAX).
|
||||||
|
static constexpr f32 HRTF_EXTRACT_MAX = 0.6f;
|
||||||
|
|
||||||
|
static f32 sHrtfLp1 = 0.0f;
|
||||||
|
static f32 sHrtfLp2 = 0.0f;
|
||||||
|
static f32 sHrtfApIn1 = 0.0f;
|
||||||
|
static f32 sHrtfApOut1 = 0.0f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that a DSP channel's format is actually something we know how to play.
|
* Validate that a DSP channel's format is actually something we know how to play.
|
||||||
@@ -283,6 +297,9 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
|
|||||||
DspSubframe reverbInputR = {};
|
DspSubframe reverbInputR = {};
|
||||||
bool anyReverbInput = false;
|
bool anyReverbInput = false;
|
||||||
|
|
||||||
|
DspSubframe surroundBus = {};
|
||||||
|
bool anySurroundInput = false;
|
||||||
|
|
||||||
for (int i = 0; i < channels.size(); i++) {
|
for (int i = 0; i < channels.size(); i++) {
|
||||||
auto& channel = channels[i];
|
auto& channel = channels[i];
|
||||||
auto& channelAux = ChannelAux[i];
|
auto& channelAux = ChannelAux[i];
|
||||||
@@ -324,6 +341,21 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (EnableHrtf && channel.mAutoMixerBeenSet) {
|
||||||
|
f32 dolby = (channel.mAutoMixerPanDolby & 0xFF) / 127.0f;
|
||||||
|
if (dolby > 0.0f) {
|
||||||
|
anySurroundInput = true;
|
||||||
|
f32 extract = dolby * HRTF_EXTRACT_MAX;
|
||||||
|
f32 frontScale = 1.0f - extract;
|
||||||
|
for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) {
|
||||||
|
f32 mono = (channelSubframe.channels[0][j] + channelSubframe.channels[1][j]) * 0.5f;
|
||||||
|
surroundBus[j] += mono * extract;
|
||||||
|
channelSubframe.channels[0][j] *= frontScale;
|
||||||
|
channelSubframe.channels[1][j] *= frontScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (DumpAudio && sChannelDumpFiles[i]) {
|
if (DumpAudio && sChannelDumpFiles[i]) {
|
||||||
f32 interleaved[DSP_SUBFRAME_SIZE * 2];
|
f32 interleaved[DSP_SUBFRAME_SIZE * 2];
|
||||||
for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) {
|
for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) {
|
||||||
@@ -349,6 +381,28 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
|
|||||||
ReverbHasTail = wetEnergy >= REVERB_ENERGY_EPSILON;
|
ReverbHasTail = wetEnergy >= REVERB_ENERGY_EPSILON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (EnableHrtf && anySurroundInput) {
|
||||||
|
// Two-pole LPF: -12 dB/oct above 3 kHz
|
||||||
|
for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) {
|
||||||
|
sHrtfLp1 = (1.0f - HRTF_LP_K) * sHrtfLp1 + HRTF_LP_K * surroundBus[j];
|
||||||
|
sHrtfLp2 = (1.0f - HRTF_LP_K) * sHrtfLp2 + HRTF_LP_K * sHrtfLp1;
|
||||||
|
surroundBus[j] = sHrtfLp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mix into L and R
|
||||||
|
// L gets the filtered signal directly; R gets it allpass for mild decorrelation
|
||||||
|
for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) {
|
||||||
|
f32 s = surroundBus[j];
|
||||||
|
|
||||||
|
subframe.channels[0][j] += s * HrtfGain;
|
||||||
|
|
||||||
|
f32 r = -HRTF_ALLPASS_G * s + sHrtfApIn1 + HRTF_ALLPASS_G * sHrtfApOut1;
|
||||||
|
sHrtfApIn1 = s;
|
||||||
|
sHrtfApOut1 = r;
|
||||||
|
subframe.channels[1][j] += r * HrtfGain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& channel : subframe.channels) {
|
for (auto& channel : subframe.channels) {
|
||||||
ApplyVolume(channel, channel, PrevMasterVolume, MasterVolume);
|
ApplyVolume(channel, channel, PrevMasterVolume, MasterVolume);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,4 +133,6 @@ namespace dusk::audio {
|
|||||||
extern f32 PrevMasterVolume;
|
extern f32 PrevMasterVolume;
|
||||||
extern bool EnableReverb;
|
extern bool EnableReverb;
|
||||||
extern bool DumpAudio;
|
extern bool DumpAudio;
|
||||||
|
extern bool EnableHrtf;
|
||||||
|
extern f32 HrtfGain;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#include "dusk/autosave.h"
|
||||||
|
#include "imgui/ImGuiConsole.hpp"
|
||||||
|
|
||||||
|
u8 mSaveBuffer[QUEST_LOG_SIZE * 3];
|
||||||
|
u8 mAutoSaveProc = 0;
|
||||||
|
int autoSaveWriteState = 0;
|
||||||
|
|
||||||
|
typedef void (*AutoSaveFuncs)();
|
||||||
|
static AutoSaveFuncs AutoSaveFuncsProc[] = {
|
||||||
|
noAutoSave, enterAutoSave, autoSaving, waitingForWrite, endAutoSave,
|
||||||
|
};
|
||||||
|
|
||||||
|
void noAutoSave() {}
|
||||||
|
|
||||||
|
void triggerAutoSave() {
|
||||||
|
if (dusk::getSettings().game.autoSave && mAutoSaveProc == 0 &&
|
||||||
|
strcmp(dComIfGp_getStartStageName(), "F_SP102") != 0)
|
||||||
|
{
|
||||||
|
mAutoSaveProc = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateAutoSave() {
|
||||||
|
(AutoSaveFuncsProc[mAutoSaveProc])();
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeAutoSave() {
|
||||||
|
int stageNo = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo());
|
||||||
|
|
||||||
|
dComIfGs_putSave(stageNo);
|
||||||
|
dComIfGs_setMemoryToCard(mSaveBuffer, dComIfGs_getDataNum());
|
||||||
|
mDoMemCdRWm_SetCheckSumGameData(mSaveBuffer, dComIfGs_getDataNum());
|
||||||
|
|
||||||
|
u8* save = mSaveBuffer;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
mDoMemCdRWm_TestCheckSumGameData(save);
|
||||||
|
save += QUEST_LOG_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mDoMemCd_control.save(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void autoSaving() {
|
||||||
|
int cardState = g_mDoMemCd_control.LoadSync(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
||||||
|
if (cardState != 0) {
|
||||||
|
if (cardState == 2) {
|
||||||
|
mAutoSaveProc = 1;
|
||||||
|
} else if (cardState == 1) {
|
||||||
|
writeAutoSave();
|
||||||
|
mAutoSaveProc = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enterAutoSave() {
|
||||||
|
u32 cardStatus = g_mDoMemCd_control.getStatus(0);
|
||||||
|
|
||||||
|
if (cardStatus != 14) {
|
||||||
|
switch (cardStatus) {
|
||||||
|
case 2:
|
||||||
|
g_mDoMemCd_control.load();
|
||||||
|
mAutoSaveProc = 2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mAutoSaveProc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitingForWrite() {
|
||||||
|
autoSaveWriteState = g_mDoMemCd_control.SaveSync();
|
||||||
|
|
||||||
|
if (autoSaveWriteState == 2) {
|
||||||
|
mAutoSaveProc = 0;
|
||||||
|
} else if (autoSaveWriteState == 1) {
|
||||||
|
mAutoSaveProc = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void endAutoSave() {
|
||||||
|
dusk::g_imguiConsole.AddToast("Saving...", 2.0f);
|
||||||
|
mAutoSaveProc = 0;
|
||||||
|
}
|
||||||
@@ -76,8 +76,8 @@ void ImGuiAchievements::draw(bool& open) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetNextWindowSizeConstraints(ImVec2(640, 200), ImVec2(800, 900));
|
ImGui::SetNextWindowSizeConstraints(ImVec2(800, 200), ImVec2(1280, 900));
|
||||||
ImGui::SetNextWindowSize(ImVec2(640, 480), ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowSize(ImVec2(800, 480), ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
if (!ImGui::Begin(
|
if (!ImGui::Begin(
|
||||||
"Achievements", &open,
|
"Achievements", &open,
|
||||||
@@ -111,6 +111,7 @@ void ImGuiAchievements::draw(bool& open) {
|
|||||||
{AchievementCategory::Collection, "Collection", ImVec4(0.3f, 0.85f, 0.4f, 1.0f)},
|
{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::Challenge, "Challenge", ImVec4(1.0f, 0.65f, 0.15f, 1.0f)},
|
||||||
{AchievementCategory::Minigame, "Minigame", ImVec4(0.5f, 0.85f, 1.0f, 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)},
|
{AchievementCategory::Glitched, "Glitched", ImVec4(0.75f, 0.4f, 1.0f, 1.0f)},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -131,7 +132,7 @@ void ImGuiAchievements::draw(bool& open) {
|
|||||||
continue;
|
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);
|
ImGui::PushStyleColor(ImGuiCol_Text, catInfo.color);
|
||||||
const bool tabOpen = ImGui::BeginTabItem(tabLabel.c_str());
|
const bool tabOpen = ImGui::BeginTabItem(tabLabel.c_str());
|
||||||
@@ -152,6 +153,7 @@ void ImGuiAchievements::draw(bool& open) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ImGui::PushID(a.key);
|
ImGui::PushID(a.key);
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
|
||||||
ImGui::PushStyleColor(
|
ImGui::PushStyleColor(
|
||||||
ImGuiCol_Text,
|
ImGuiCol_Text,
|
||||||
@@ -190,6 +192,21 @@ void ImGuiAchievements::draw(bool& open) {
|
|||||||
ImGui::PopStyleColor();
|
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::Spacing();
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "ImGuiConsole.hpp"
|
#include "ImGuiConsole.hpp"
|
||||||
#include "ImGuiMenuTools.hpp"
|
#include "ImGuiMenuTools.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
#include "JSystem/JAudio2/JAISeq.h"
|
||||||
#include "JSystem/JAudio2/JAISeMgr.h"
|
#include "JSystem/JAudio2/JAISeMgr.h"
|
||||||
#include "JSystem/JAudio2/JAISeqMgr.h"
|
#include "JSystem/JAudio2/JAISeqMgr.h"
|
||||||
#include "JSystem/JAudio2/JAIStreamMgr.h"
|
#include "JSystem/JAudio2/JAIStreamMgr.h"
|
||||||
@@ -15,6 +17,24 @@ static std::array<u32, DSP_CHANNELS> lastResetCounts = {};
|
|||||||
|
|
||||||
static bool sortUpdateCount = true;
|
static bool sortUpdateCount = true;
|
||||||
|
|
||||||
|
static void DrawDirectionGauge(float pan, float dolby) {
|
||||||
|
constexpr float R = 20.0f;
|
||||||
|
constexpr float SIZE = R * 2.0f + 4.0f;
|
||||||
|
|
||||||
|
ImVec2 origin = ImGui::GetCursorScreenPos();
|
||||||
|
ImGui::Dummy(ImVec2(SIZE, SIZE));
|
||||||
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
ImVec2 c = ImVec2(origin.x + SIZE * 0.5f, origin.y + SIZE * 0.5f);
|
||||||
|
|
||||||
|
dl->AddCircle(c, R, IM_COL32(90, 90, 90, 255), 32);
|
||||||
|
|
||||||
|
float dx = (pan - 0.5f) * 2.0f;
|
||||||
|
float dy = dolby * 2.0f - 1.0f;
|
||||||
|
float len = sqrtf(dx * dx + dy * dy);
|
||||||
|
if (len > 1.0f) { dx /= len; dy /= len; }
|
||||||
|
dl->AddLine(c, ImVec2(c.x + dx * R, c.y + dy * R), IM_COL32(255, 200, 50, 255), 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
static void DisplayDspChannel(int i) {
|
static void DisplayDspChannel(int i) {
|
||||||
using namespace dusk::audio;
|
using namespace dusk::audio;
|
||||||
|
|
||||||
@@ -52,8 +72,10 @@ static void DisplayDspChannel(int i) {
|
|||||||
auto fxMix = (channel.mAutoMixerFxMix >> 8) / 127.5f;
|
auto fxMix = (channel.mAutoMixerFxMix >> 8) / 127.5f;
|
||||||
auto volume = VolumeFromU16(channel.mAutoMixerVolume);
|
auto volume = VolumeFromU16(channel.mAutoMixerVolume);
|
||||||
auto pitch = channel.mPitch / 4096.0f;
|
auto pitch = channel.mPitch / 4096.0f;
|
||||||
|
DrawDirectionGauge(pan, dolby);
|
||||||
|
ImGui::SameLine();
|
||||||
ImGui::Text(
|
ImGui::Text(
|
||||||
"Auto mixer active (pan: %f, dolby: %f, fx: %f, volume: %f, pitch %f)",
|
"pan: %.2f dolby: %.2f\nfx: %.2f vol: %.2f pitch: %.2f",
|
||||||
pan, dolby, fxMix, volume, pitch);
|
pan, dolby, fxMix, volume, pitch);
|
||||||
} else {
|
} else {
|
||||||
ImGui::Text(
|
ImGui::Text(
|
||||||
@@ -183,6 +205,10 @@ static void ShowAllJAISes() {
|
|||||||
if (ImGui::Button("Pause All")) {
|
if (ImGui::Button("Pause All")) {
|
||||||
category->pause(true);
|
category->pause(true);
|
||||||
}
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Resume All")) {
|
||||||
|
category->pause(false);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto seLink = category->getSeList()->getFirst(); seLink != nullptr; seLink = seLink->getNext()) {
|
for (auto seLink = category->getSeList()->getFirst(); seLink != nullptr; seLink = seLink->getNext()) {
|
||||||
const auto se = seLink->getObject();
|
const auto se = seLink->getObject();
|
||||||
@@ -196,6 +222,33 @@ static void ShowAllJAISes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ShowSeqTracks(JAISeq& seq) {
|
||||||
|
JASTrack& root = seq.inner_.outputTrack;
|
||||||
|
|
||||||
|
for (int group = 0; group < 2; group++) {
|
||||||
|
JASTrack* groupTrack = root.getChild(group);
|
||||||
|
if (groupTrack == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < JASTrack::MAX_CHILDREN; j++) {
|
||||||
|
JASTrack* track = groupTrack->getChild(j);
|
||||||
|
if (track == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int trackIdx = group * 16 + j;
|
||||||
|
char label[64];
|
||||||
|
snprintf(label, sizeof(label), "Track %d (bank %hu, prog %hu)##%p",
|
||||||
|
trackIdx, track->getBankNumber(), track->getProgNumber(), track);
|
||||||
|
bool muted = track->mFlags.mute;
|
||||||
|
if (ImGui::Checkbox(label, &muted)) {
|
||||||
|
track->mute(muted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ShowAllJAISeqs() {
|
static void ShowAllJAISeqs() {
|
||||||
auto& mgr = *JAISeqMgr::getInstance();
|
auto& mgr = *JAISeqMgr::getInstance();
|
||||||
|
|
||||||
@@ -206,6 +259,26 @@ static void ShowAllJAISeqs() {
|
|||||||
if (ImGui::Button("Unpause")) {
|
if (ImGui::Button("Unpause")) {
|
||||||
mgr.pause(false);
|
mgr.pause(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Active sequences: %d", mgr.getNumActiveSeqs());
|
||||||
|
|
||||||
|
auto* seqList = mgr.getSeqList();
|
||||||
|
for (auto* link = seqList->getFirst(); link != nullptr; link = link->getNext()) {
|
||||||
|
JAISeq* seq = link->getObject();
|
||||||
|
if (seq == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "%p", seq);
|
||||||
|
|
||||||
|
if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) {
|
||||||
|
ImGui::Text("Seq [%p]", seq);
|
||||||
|
ShowSeqTracks(*seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dusk::ImGuiMenuTools::ShowAudioDebug() {
|
void dusk::ImGuiMenuTools::ShowAudioDebug() {
|
||||||
|
|||||||
@@ -56,21 +56,6 @@ ImGuiWindow* FindDragScrollWindow(ImGuiWindow* window) {
|
|||||||
}
|
}
|
||||||
return nullptr;
|
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
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
@@ -339,28 +324,18 @@ namespace dusk {
|
|||||||
ImGuiMenuGame::ToggleFullscreen();
|
ImGuiMenuGame::ToggleFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dusk::IsGameLaunched) {
|
// if (!dusk::IsGameLaunched) {
|
||||||
m_preLaunchWindow.draw();
|
// m_preLaunchWindow.draw();
|
||||||
}
|
// }
|
||||||
|
|
||||||
m_isHidden = !getSettings().backend.duskMenuOpen;
|
m_isHidden = !getSettings().backend.duskMenuOpen;
|
||||||
if (dusk::IsGameLaunched) {
|
if (ImGui::GetIO().KeyShift && ImGui::IsKeyPressed(ImGuiKey_F1)) {
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_F1)) {
|
m_isHidden = !m_isHidden;
|
||||||
m_isHidden = !m_isHidden;
|
|
||||||
}
|
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack)) {
|
|
||||||
m_isHidden = !m_isHidden;
|
|
||||||
m_focusMenuBar = !m_isHidden;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
bool showMenu = !m_isHidden;
|
||||||
bool showMenu = !dusk::IsGameLaunched || !m_isHidden;
|
if (getSettings().backend.duskMenuOpen != showMenu) {
|
||||||
if (dusk::IsGameLaunched) {
|
getSettings().backend.duskMenuOpen.setValue(showMenu);
|
||||||
const bool menuOpen = !m_isHidden;
|
Save();
|
||||||
if (getSettings().backend.duskMenuOpen != menuOpen) {
|
|
||||||
getSettings().backend.duskMenuOpen.setValue(menuOpen);
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The menu bar renders with ImGuiCol_WindowBg behind it. We just want ImGuiCol_MenuBarBg,
|
// The menu bar renders with ImGuiCol_WindowBg behind it. We just want ImGuiCol_MenuBarBg,
|
||||||
@@ -368,10 +343,6 @@ namespace dusk {
|
|||||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||||
if (showMenu && ImGui::BeginMainMenuBar()) {
|
if (showMenu && ImGui::BeginMainMenuBar()) {
|
||||||
m_menuGame.draw();
|
m_menuGame.draw();
|
||||||
if (m_focusMenuBar) {
|
|
||||||
FocusLastMenuBarItem();
|
|
||||||
m_focusMenuBar = false;
|
|
||||||
}
|
|
||||||
m_menuTools.draw();
|
m_menuTools.draw();
|
||||||
|
|
||||||
const auto fpsLabel =
|
const auto fpsLabel =
|
||||||
@@ -394,10 +365,10 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
|
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
|
||||||
m_toasts.emplace_back(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ?
|
AddToast(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ?
|
||||||
"Tap to toggle menu"s :
|
"Tap to toggle menu"s :
|
||||||
"Press F1 or Minus/Back to toggle menu"s,
|
"Press F1 to toggle menu"s,
|
||||||
2.5f);
|
4.f);
|
||||||
m_isLaunchInitialized = true;
|
m_isLaunchInitialized = true;
|
||||||
if (getSettings().game.liveSplitEnabled) {
|
if (getSettings().game.liveSplitEnabled) {
|
||||||
dusk::speedrun::connectLiveSplit();
|
dusk::speedrun::connectLiveSplit();
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ private:
|
|||||||
float mouseHideTimer = 0.0f;
|
float mouseHideTimer = 0.0f;
|
||||||
|
|
||||||
bool m_isHidden = true;
|
bool m_isHidden = true;
|
||||||
bool m_focusMenuBar = false;
|
|
||||||
bool m_isLaunchInitialized = false;
|
bool m_isLaunchInitialized = false;
|
||||||
bool m_touchTapActive = false;
|
bool m_touchTapActive = false;
|
||||||
bool m_touchTapMoved = false;
|
bool m_touchTapMoved = false;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,7 @@ static void ApplyPresetHD() {
|
|||||||
s.game.biggerWallets.setValue(true);
|
s.game.biggerWallets.setValue(true);
|
||||||
s.game.invertCameraXAxis.setValue(true);
|
s.game.invertCameraXAxis.setValue(true);
|
||||||
s.game.freeCamera.setValue(true);
|
s.game.freeCamera.setValue(true);
|
||||||
|
s.game.no2ndFishForCat.setValue(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyPresetDusk() {
|
static void ApplyPresetDusk() {
|
||||||
@@ -43,6 +44,7 @@ static void ApplyPresetDusk() {
|
|||||||
s.game.enableFrameInterpolation.setValue(true);
|
s.game.enableFrameInterpolation.setValue(true);
|
||||||
s.game.sunsSong.setValue(true);
|
s.game.sunsSong.setValue(true);
|
||||||
s.game.bloomMode.setValue(BloomMode::Dusk);
|
s.game.bloomMode.setValue(BloomMode::Dusk);
|
||||||
|
s.game.autoSave.setValue(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|||||||
@@ -180,9 +180,9 @@ namespace dusk {
|
|||||||
void ShowHeapDetailed(JKRHeap* heap, OpenHeapData& data, bool& open) {
|
void ShowHeapDetailed(JKRHeap* heap, OpenHeapData& data, bool& open) {
|
||||||
char title[128];
|
char title[128];
|
||||||
const char* name = data.Safe ? heap->getName() : "INVALID";
|
const char* name = data.Safe ? heap->getName() : "INVALID";
|
||||||
snprintf(title, sizeof(title), "Heap %s##%p", heap->getName(), static_cast<const void*>(heap));
|
snprintf(title, sizeof(title), "Heap %s##%p", name, static_cast<const void*>(heap));
|
||||||
|
|
||||||
if (!ImGui::Begin(name, &open)) {
|
if (!ImGui::Begin(title, &open)) {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ namespace dusk {
|
|||||||
|
|
||||||
heap->lock();
|
heap->lock();
|
||||||
|
|
||||||
ImGui::Text("Name: %s", heap->getName());
|
ImGui::Text("Name: %s", name);
|
||||||
const auto size = BytesToString(heap->getSize());
|
const auto size = BytesToString(heap->getSize());
|
||||||
const auto freeSize = BytesToString(heap->getFreeSize());
|
const auto freeSize = BytesToString(heap->getFreeSize());
|
||||||
ImGui::Text("Size: %08X (%s), free: %08X (%s)", heap->getSize(), size.c_str(), heap->getFreeSize(), freeSize.c_str());
|
ImGui::Text("Size: %08X (%s), free: %08X (%s)", heap->getSize(), size.c_str(), heap->getFreeSize(), freeSize.c_str());
|
||||||
|
|||||||
@@ -3,40 +3,13 @@
|
|||||||
|
|
||||||
#include "ImGuiEngine.hpp"
|
#include "ImGuiEngine.hpp"
|
||||||
#include "ImGuiConsole.hpp"
|
#include "ImGuiConsole.hpp"
|
||||||
#include "ImGuiMenuGame.hpp"
|
|
||||||
#include "ImGuiConfig.hpp"
|
#include "ImGuiConfig.hpp"
|
||||||
|
|
||||||
#include "JSystem/JUtility/JUTGamePad.h"
|
|
||||||
#include "dusk/audio/DuskAudioSystem.h"
|
|
||||||
#include "dusk/audio/DuskDsp.hpp"
|
|
||||||
#include "dusk/main.h"
|
#include "dusk/main.h"
|
||||||
#include "dusk/hotkeys.h"
|
#include "dusk/hotkeys.h"
|
||||||
#include "dusk/settings.h"
|
|
||||||
#include "dusk/livesplit.h"
|
|
||||||
#include "m_Do/m_Do_controller_pad.h"
|
|
||||||
#include "m_Do/m_Do_graphic.h"
|
|
||||||
|
|
||||||
#include <aurora/gfx.h>
|
|
||||||
#include <SDL3/SDL_gamepad.h>
|
|
||||||
|
|
||||||
#include "m_Do/m_Do_main.h"
|
#include "m_Do/m_Do_main.h"
|
||||||
|
|
||||||
namespace {
|
#include <SDL3/SDL_gamepad.h>
|
||||||
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 {
|
|
||||||
extern bool enableLodBias;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
void ImGuiMenuGame::ToggleFullscreen() {
|
void ImGuiMenuGame::ToggleFullscreen() {
|
||||||
@@ -49,470 +22,17 @@ namespace dusk {
|
|||||||
|
|
||||||
void ImGuiMenuGame::draw() {
|
void ImGuiMenuGame::draw() {
|
||||||
if (ImGui::BeginMenu("Settings")) {
|
if (ImGui::BeginMenu("Settings")) {
|
||||||
drawAudioMenu();
|
// TODO: Remove this once Controller Config exists in RmlUi
|
||||||
drawCheatsMenu();
|
|
||||||
drawGameplayMenu();
|
|
||||||
drawGraphicsMenu();
|
|
||||||
drawInputMenu();
|
|
||||||
drawInterfaceMenu();
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
if (ImGui::MenuItem("Reset", hotkeys::DO_RESET)) {
|
|
||||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsMobile && ImGui::MenuItem("Exit")) {
|
|
||||||
dusk::IsRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiMenuGame::drawGraphicsMenu() {
|
|
||||||
if (ImGui::BeginMenu("Graphics")) {
|
|
||||||
ImGui::SeparatorText("Display");
|
|
||||||
|
|
||||||
if (!IsMobile) {
|
|
||||||
if (ImGui::MenuItem("Toggle Fullscreen", hotkeys::TOGGLE_FULLSCREEN)) {
|
|
||||||
ToggleFullscreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("Restore Default Window Size")) {
|
|
||||||
getSettings().video.enableFullscreen.setValue(false);
|
|
||||||
VISetWindowFullscreen(false);
|
|
||||||
VISetWindowSize(FB_WIDTH * 2, FB_HEIGHT * 2);
|
|
||||||
VICenterWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
bool vsync = getSettings().video.enableVsync;
|
|
||||||
if (ImGui::Checkbox("Enable VSync", &vsync)) {
|
|
||||||
getSettings().video.enableVsync.setValue(vsync);
|
|
||||||
aurora_enable_vsync(vsync);
|
|
||||||
config::Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lockAspect = getSettings().video.lockAspectRatio;
|
|
||||||
if (ImGui::Checkbox("Force 4:3 Aspect Ratio", &lockAspect)) {
|
|
||||||
getSettings().video.lockAspectRatio.setValue(lockAspect);
|
|
||||||
|
|
||||||
if (lockAspect) {
|
|
||||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT);
|
|
||||||
} else {
|
|
||||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_STRETCH);
|
|
||||||
}
|
|
||||||
|
|
||||||
config::Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Resolution");
|
|
||||||
|
|
||||||
u32 internalResolutionWidth = 0;
|
|
||||||
u32 internalResolutionHeight = 0;
|
|
||||||
AuroraGetRenderSize(&internalResolutionWidth, &internalResolutionHeight);
|
|
||||||
ImGui::TextDisabled("Current internal resolution: %ux%u", internalResolutionWidth,
|
|
||||||
internalResolutionHeight);
|
|
||||||
|
|
||||||
int scale = std::clamp(getSettings().game.internalResolutionScale.getValue(), 0,
|
|
||||||
kInternalResolutionScaleMax);
|
|
||||||
if (ImGui::SliderInt("Internal Resolution", &scale, 0, kInternalResolutionScaleMax,
|
|
||||||
scale == 0 ? "Auto" : "%dx"))
|
|
||||||
{
|
|
||||||
getSettings().game.internalResolutionScale.setValue(scale);
|
|
||||||
VISetFrameBufferScale(static_cast<float>(scale));
|
|
||||||
config::Save();
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Auto renders at the native window resolution.\n"
|
|
||||||
"Higher values scale the game's internal framebuffer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d");
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Improves the shadow resolution, making them higher quality.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Post-Processing");
|
|
||||||
|
|
||||||
constexpr const char* bloomModeNames[] = {"Off", "Classic", "Dusk"};
|
|
||||||
int bloomMode = static_cast<int>(getSettings().game.bloomMode.getValue());
|
|
||||||
if (ImGui::BeginCombo("Bloom", bloomModeNames[bloomMode])) {
|
|
||||||
for (int i = 0; i < IM_ARRAYSIZE(bloomModeNames); i++) {
|
|
||||||
const bool selected = bloomMode == i;
|
|
||||||
if (ImGui::Selectable(bloomModeNames[i], selected)) {
|
|
||||||
getSettings().game.bloomMode.setValue(static_cast<BloomMode>(i));
|
|
||||||
config::Save();
|
|
||||||
}
|
|
||||||
if (selected) {
|
|
||||||
ImGui::SetItemDefaultFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bloomOff = bloomMode == static_cast<int>(BloomMode::Off);
|
|
||||||
if (bloomOff) ImGui::BeginDisabled();
|
|
||||||
float mult = getSettings().game.bloomMultiplier.getValue();
|
|
||||||
if (ImGui::SliderFloat("Bloom Brightness", &mult, 0.0f, 1.0f, "%.2f")) {
|
|
||||||
getSettings().game.bloomMultiplier.setValue(mult);
|
|
||||||
config::Save();
|
|
||||||
}
|
|
||||||
if (bloomOff) ImGui::EndDisabled();
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Rendering");
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Unlock Framerate", getSettings().game.enableFrameInterpolation);
|
|
||||||
const bool frameInterpolationHovered = ImGui::IsItemHovered();
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Checkbox("Enable LOD Bias", &aurora::gx::enableLodBias);
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Enable Depth of Field", getSettings().game.enableDepthOfField);
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Enable Mini-Map Shadows", getSettings().game.enableMapBackground);
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiMenuGame::drawGameplayMenu() {
|
|
||||||
if (ImGui::BeginMenu("Gameplay")) {
|
|
||||||
ImGui::SeparatorText("General");
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Mirrors the world horizontally, matching the Wii version of the game.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Disable Main HUD", getSettings().game.disableMainHUD);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Disables the main HUD of the game.\n"
|
|
||||||
"Useful for recording or a more immersive experience!");
|
|
||||||
}
|
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Difficulty");
|
|
||||||
|
|
||||||
ImGui::BeginDisabled(getSettings().game.speedrunMode);
|
|
||||||
config::ImGuiSliderInt("Damage Multiplier", getSettings().game.damageMultiplier, 1, 8, "x%d");
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Any hit will instantly kill you.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("No Heart Drops", getSettings().game.noHeartDrops);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Hearts will never drop from enemies,\n"
|
|
||||||
"pots and various other places.");
|
|
||||||
}
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Quality of Life");
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Bigger Wallets", getSettings().game.biggerWallets);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Wallet sizes are like in the HD version. (500, 1000, 2000)");
|
|
||||||
}
|
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Faster Climbing", getSettings().game.fastClimbing);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version.");
|
|
||||||
}
|
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Hold B for Instant Text", getSettings().game.instantText);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Make text scroll immediately by holding B.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Prevents Link from playing a struggle animation\n"
|
|
||||||
"when grabbing ledges or climbing on vines.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Skip TV Settings Screen", getSettings().game.hideTvSettingsScreen);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Skip the TV calibration screen shown when loading a save.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Skip Warning Screen", getSettings().game.skipWarningScreen);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Skip the warning screen shown when starting the game.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Transform instantly by pressing R and Y simultaneously.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Speedrunning");
|
|
||||||
if (config::ImGuiCheckbox("Speedrun Mode", getSettings().game.speedrunMode)) {
|
|
||||||
resetForSpeedrunMode();
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Enables Speedrunning options, while restricting certain gameplay modifiers.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::BeginDisabled(!getSettings().game.speedrunMode);
|
|
||||||
bool prevLiveSplit = getSettings().game.liveSplitEnabled;
|
|
||||||
config::ImGuiCheckbox("LiveSplit Connection", getSettings().game.liveSplitEnabled);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Connect to LiveSplit server on localhost:16834.");
|
|
||||||
}
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
|
|
||||||
if ((bool)getSettings().game.liveSplitEnabled != prevLiveSplit) {
|
|
||||||
if (getSettings().game.liveSplitEnabled) {
|
|
||||||
dusk::speedrun::connectLiveSplit();
|
|
||||||
} else {
|
|
||||||
dusk::speedrun::disconnectLiveSplit();
|
|
||||||
DuskToast("LiveSplit disconnected", 3.f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiMenuGame::drawCheatsMenu() {
|
|
||||||
if (ImGui::BeginMenu("Cheats")) {
|
|
||||||
ImGui::BeginDisabled(getSettings().game.speedrunMode);
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Resources");
|
|
||||||
config::ImGuiCheckbox("Infinite Hearts", getSettings().game.infiniteHearts);
|
|
||||||
config::ImGuiCheckbox("Infinite Arrows", getSettings().game.infiniteArrows);
|
|
||||||
config::ImGuiCheckbox("Infinite Bombs", getSettings().game.infiniteBombs);
|
|
||||||
config::ImGuiCheckbox("Infinite Oil", getSettings().game.infiniteOil);
|
|
||||||
config::ImGuiCheckbox("Infinite Oxygen", getSettings().game.infiniteOxygen);
|
|
||||||
config::ImGuiCheckbox("Infinite Rupees", getSettings().game.infiniteRupees);
|
|
||||||
config::ImGuiCheckbox("No Item Timer", getSettings().game.enableIndefiniteItemDrops);
|
|
||||||
ImGui::SetItemTooltip("Item drops such as Rupees, Hearts, etc. will never disappear after they drop.");
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Abilities");
|
|
||||||
config::ImGuiCheckbox("Moon Jump (R+A)", getSettings().game.moonJump);
|
|
||||||
config::ImGuiCheckbox("Super Clawshot", getSettings().game.superClawshot);
|
|
||||||
config::ImGuiCheckbox("Always Greatspin", getSettings().game.alwaysGreatspin);
|
|
||||||
config::ImGuiCheckbox("Fast Iron Boots", getSettings().game.enableFastIronBoots);
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Can Transform Anywhere", getSettings().game.canTransformAnywhere);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Allows you to transform even if NPCs are looking.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Fast Spinner", getSettings().game.fastSpinner);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Speeds up Spinner movement when holding R.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Free Magic Armor", getSettings().game.freeMagicArmor);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Makes the magic armor work without rupees.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiMenuGame::drawAudioMenu() {
|
|
||||||
if (ImGui::BeginMenu("Audio")) {
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Volume");
|
|
||||||
|
|
||||||
ImGui::Text("Master Volume");
|
|
||||||
if (config::ImGuiSliderInt("##masterVolume", getSettings().audio.masterVolume, 0, 100)) {
|
|
||||||
dusk::audio::SetMasterVolume(getSettings().audio.masterVolume / 100.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// TODO: Implement additional settings
|
|
||||||
ImGui::Text("Main Music Volume");
|
|
||||||
ImGui::SliderFloat("##mainMusicVolume", &getSettings().audio.mainMusicVolume, 0, 100);
|
|
||||||
|
|
||||||
ImGui::Text("Sub Music Volume");
|
|
||||||
ImGui::SliderFloat("##subMusicVolume", &getSettings().audio.subMusicVolume, 0, 100);
|
|
||||||
|
|
||||||
ImGui::Text("Sound Effects Volume");
|
|
||||||
ImGui::SliderFloat("##soundEffectsVolume", &getSettings().audio.soundEffectsVolume, 0, 100);
|
|
||||||
|
|
||||||
ImGui::Text("Fanfare Volume");
|
|
||||||
ImGui::SliderFloat("##fanfareVolume", &getSettings().audio.fanfareVolume, 0, 100);
|
|
||||||
|
|
||||||
Z2AudioMgr* audioMgr = Z2AudioMgr::getInterface();
|
|
||||||
if (audioMgr != nullptr) {
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Effects");
|
|
||||||
|
|
||||||
if (config::ImGuiCheckbox("Enable Reverb", getSettings().audio.enableReverb)) {
|
|
||||||
dusk::audio::SetEnableReverb(getSettings().audio.enableReverb);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Tweaks");
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("No Low HP Sound", getSettings().game.noLowHpSound);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
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::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiMenuGame::drawInputMenu() {
|
|
||||||
if (ImGui::BeginMenu("Input")) {
|
|
||||||
ImGui::SeparatorText("Controller");
|
|
||||||
|
|
||||||
if (ImGui::Button("Configure Controller")){
|
if (ImGui::Button("Configure Controller")){
|
||||||
m_showControllerConfig = !m_showControllerConfig;
|
m_showControllerConfig = !m_showControllerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SeparatorText("Camera");
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Free Camera", getSettings().game.freeCamera);
|
|
||||||
|
|
||||||
if (getSettings().game.freeCamera) {
|
|
||||||
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
|
|
||||||
config::ImGuiCheckbox("Invert Camera Y Axis", getSettings().game.invertCameraYAxis);
|
|
||||||
config::ImGuiSliderFloat("Free Camera Sensitivity", getSettings().game.freeCameraSensitivity, 0.5f, 2.0f, "%.1f");
|
|
||||||
} else {
|
|
||||||
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Gyro");
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Gyro Aim", getSettings().game.enableGyroAim);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Enables the gyroscope on supported controllers\n"
|
|
||||||
"while in look mode (C-Up) and while aiming the\n"
|
|
||||||
"Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s),\n"
|
|
||||||
"Ball and Chain, and Dominion Rod.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Gyro Rollgoal", getSettings().game.enableGyroRollgoal);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Enables the gyroscope on supported controllers to\n"
|
|
||||||
"tilt the Rollgoal table in Hena's Cabin.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getSettings().game.enableGyroAim || getSettings().game.enableGyroRollgoal) {
|
|
||||||
config::ImGuiSliderFloat("Gyro Pitch Sensitivity", getSettings().game.gyroSensitivityY, 0.25f, 4.0f, "%.2f");
|
|
||||||
config::ImGuiSliderFloat("Gyro Yaw Sensitivity", getSettings().game.gyroSensitivityX, 0.25f, 4.0f, "%.2f");
|
|
||||||
|
|
||||||
if (getSettings().game.enableGyroRollgoal) {
|
|
||||||
config::ImGuiSliderFloat("Rollgoal Sensitivity", getSettings().game.gyroSensitivityRollgoal, 0.25f, 4.0f, "%.2f");
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Additional multiplier for scaling how strongly\n"
|
|
||||||
"the gyroscope affects the Rollgoal table.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiSliderFloat("Gyro Deadband", getSettings().game.gyroDeadband, 0.0f, 0.5f, "%.3f");
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Angular rates below this magnitude are treated as zero,\n"
|
|
||||||
"reducing drift and jitter when the controller is still.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiSliderFloat("Gyro Smoothing", getSettings().game.gyroSmoothing, 0.0f, 1.0f, "%.2f");
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Low values track raw gyro input more closely,\n"
|
|
||||||
"while higher values smooth out input over time.");
|
|
||||||
}
|
|
||||||
|
|
||||||
config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroInvertPitch);
|
|
||||||
config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroInvertYaw);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Tools");
|
|
||||||
|
|
||||||
ImGui::BeginDisabled(getSettings().game.speedrunMode);
|
|
||||||
config::ImGuiCheckbox("Turbo Key", getSettings().game.enableTurboKeybind);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Hold TAB to increase game speed by up to 4x.");
|
|
||||||
}
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
|
|
||||||
ImGui::Checkbox("Show Input Viewer", &m_showInputViewer);
|
ImGui::Checkbox("Show Input Viewer", &m_showInputViewer);
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiMenuGame::drawInterfaceMenu() {
|
|
||||||
if (ImGui::BeginMenu("Interface")) {
|
|
||||||
config::ImGuiCheckbox("Achievement Notifications", getSettings().game.enableAchievementNotifications);
|
|
||||||
config::ImGuiCheckbox("Skip Pre-Launch UI", getSettings().backend.skipPreLaunchUI);
|
|
||||||
config::ImGuiCheckbox("Show Pipeline Compilation", getSettings().backend.showPipelineCompilation);
|
|
||||||
#if DUSK_ENABLE_SENTRY_NATIVE
|
|
||||||
config::ImGuiCheckbox("Enable Crash Reporting", getSettings().backend.enableCrashReporting);
|
|
||||||
#endif
|
|
||||||
if (!IsMobile) {
|
|
||||||
config::ImGuiCheckbox("Pause on Focus Lost", getSettings().game.pauseOnFocusLost);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drawVirtualStick(const char* id, const ImVec2& stick) {
|
static void drawVirtualStick(const char* id, const ImVec2& stick) {
|
||||||
float scale = ImGuiScale();
|
float scale = ImGuiScale();
|
||||||
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 45 * scale, ImGui::GetCursorPos().y + 10));
|
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 45 * scale, ImGui::GetCursorPos().y + 10));
|
||||||
@@ -651,90 +171,39 @@ namespace dusk {
|
|||||||
|
|
||||||
void ImGuiMenuGame::windowControllerConfig() {
|
void ImGuiMenuGame::windowControllerConfig() {
|
||||||
if (!m_showControllerConfig) {
|
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;
|
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 pending for a button mapping, check to set new input
|
||||||
if (m_controllerConfig.m_pendingButtonMapping != nullptr &&
|
if (m_controllerConfig.m_pendingButtonMapping != nullptr) {
|
||||||
!m_controllerConfig.m_waitForInputRelease)
|
|
||||||
{
|
|
||||||
s32 nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort);
|
s32 nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort);
|
||||||
if (nativeButton != -1) {
|
if (nativeButton != -1) {
|
||||||
const int suppressPort = m_controllerConfig.m_pendingPort;
|
|
||||||
m_controllerConfig.m_pendingButtonMapping->nativeButton = nativeButton;
|
m_controllerConfig.m_pendingButtonMapping->nativeButton = nativeButton;
|
||||||
m_controllerConfig.m_pendingButtonMapping = nullptr;
|
m_controllerConfig.m_pendingButtonMapping = nullptr;
|
||||||
m_controllerConfig.m_pendingPort = -1;
|
m_controllerConfig.m_pendingPort = -1;
|
||||||
m_controllerConfig.m_isReading = false;
|
PADBlockInput(false);
|
||||||
m_controllerConfig.m_waitForInputRelease = false;
|
|
||||||
m_controllerConfig.m_suppressRemapActivationUntilRelease = true;
|
|
||||||
m_controllerConfig.m_suppressRemapActivationPort = suppressPort;
|
|
||||||
suppressRemapActivationThisFrame = true;
|
|
||||||
PADBlockInput(true);
|
|
||||||
PADSerializeMappings();
|
PADSerializeMappings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if pending for an axis mapping, check to set new input
|
// if pending for an axis mapping, check to set new input
|
||||||
if (m_controllerConfig.m_pendingAxisMapping != nullptr &&
|
if (m_controllerConfig.m_pendingAxisMapping != nullptr) {
|
||||||
!m_controllerConfig.m_waitForInputRelease)
|
|
||||||
{
|
|
||||||
auto nativeAxis = PADGetNativeAxisPulled(m_controllerConfig.m_pendingPort);
|
auto nativeAxis = PADGetNativeAxisPulled(m_controllerConfig.m_pendingPort);
|
||||||
if (nativeAxis.nativeAxis != -1) {
|
if (nativeAxis.nativeAxis != -1) {
|
||||||
const int suppressPort = m_controllerConfig.m_pendingPort;
|
|
||||||
m_controllerConfig.m_pendingAxisMapping->nativeAxis = nativeAxis;
|
m_controllerConfig.m_pendingAxisMapping->nativeAxis = nativeAxis;
|
||||||
m_controllerConfig.m_pendingAxisMapping->nativeButton = -1;
|
m_controllerConfig.m_pendingAxisMapping->nativeButton = -1;
|
||||||
m_controllerConfig.m_pendingAxisMapping = nullptr;
|
m_controllerConfig.m_pendingAxisMapping = nullptr;
|
||||||
m_controllerConfig.m_pendingPort = -1;
|
m_controllerConfig.m_pendingPort = -1;
|
||||||
m_controllerConfig.m_isReading = false;
|
PADBlockInput(false);
|
||||||
m_controllerConfig.m_waitForInputRelease = false;
|
|
||||||
m_controllerConfig.m_suppressRemapActivationUntilRelease = true;
|
|
||||||
m_controllerConfig.m_suppressRemapActivationPort = suppressPort;
|
|
||||||
suppressRemapActivationThisFrame = true;
|
|
||||||
PADBlockInput(true);
|
|
||||||
PADSerializeMappings();
|
PADSerializeMappings();
|
||||||
} else {
|
} else {
|
||||||
auto nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort);
|
auto nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort);
|
||||||
if (nativeButton != -1) {
|
if (nativeButton != -1) {
|
||||||
const int suppressPort = m_controllerConfig.m_pendingPort;
|
|
||||||
m_controllerConfig.m_pendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE};
|
m_controllerConfig.m_pendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE};
|
||||||
m_controllerConfig.m_pendingAxisMapping->nativeButton = nativeButton;
|
m_controllerConfig.m_pendingAxisMapping->nativeButton = nativeButton;
|
||||||
m_controllerConfig.m_pendingAxisMapping = nullptr;
|
m_controllerConfig.m_pendingAxisMapping = nullptr;
|
||||||
m_controllerConfig.m_pendingPort = -1;
|
m_controllerConfig.m_pendingPort = -1;
|
||||||
m_controllerConfig.m_isReading = false;
|
PADBlockInput(false);
|
||||||
m_controllerConfig.m_waitForInputRelease = false;
|
|
||||||
m_controllerConfig.m_suppressRemapActivationUntilRelease = true;
|
|
||||||
m_controllerConfig.m_suppressRemapActivationPort = suppressPort;
|
|
||||||
suppressRemapActivationThisFrame = true;
|
|
||||||
PADBlockInput(true);
|
|
||||||
PADSerializeMappings();
|
PADSerializeMappings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -770,10 +239,6 @@ namespace dusk {
|
|||||||
m_controllerConfig.m_pendingButtonMapping = nullptr;
|
m_controllerConfig.m_pendingButtonMapping = nullptr;
|
||||||
m_controllerConfig.m_pendingAxisMapping = nullptr;
|
m_controllerConfig.m_pendingAxisMapping = nullptr;
|
||||||
m_controllerConfig.m_pendingPort = -1;
|
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);
|
PADBlockInput(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,7 +315,7 @@ namespace dusk {
|
|||||||
|
|
||||||
std::string dispName;
|
std::string dispName;
|
||||||
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingButtonMapping == &btnMappingList[i]) {
|
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 {
|
} else {
|
||||||
const char* nativeName = GetNameForGamepadButton(gamepad, btnMappingList[i].nativeButton);
|
const char* nativeName = GetNameForGamepadButton(gamepad, btnMappingList[i].nativeButton);
|
||||||
if (nativeName == nullptr) {
|
if (nativeName == nullptr) {
|
||||||
@@ -861,11 +326,10 @@ namespace dusk {
|
|||||||
bool pressed = ImGui::Button(dispName.c_str(),
|
bool pressed = ImGui::Button(dispName.c_str(),
|
||||||
btnSize);
|
btnSize);
|
||||||
|
|
||||||
if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) {
|
if (pressed) {
|
||||||
m_controllerConfig.m_isReading = true;
|
m_controllerConfig.m_isReading = true;
|
||||||
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
|
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
|
||||||
m_controllerConfig.m_pendingButtonMapping = &btnMappingList[i];
|
m_controllerConfig.m_pendingButtonMapping = &btnMappingList[i];
|
||||||
m_controllerConfig.m_waitForInputRelease = true;
|
|
||||||
PADBlockInput(true);
|
PADBlockInput(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -895,18 +359,17 @@ namespace dusk {
|
|||||||
|
|
||||||
std::string dispName;
|
std::string dispName;
|
||||||
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[trigger]) {
|
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 {
|
} else {
|
||||||
dispName = fmt::format("{0}##-{1}", PADGetNativeAxisName(axisMappingList[trigger].nativeAxis), trigger);
|
dispName = fmt::format("{0}##-{1}", PADGetNativeAxisName(axisMappingList[trigger].nativeAxis), trigger);
|
||||||
}
|
}
|
||||||
bool pressed = ImGui::Button(dispName.c_str(),
|
bool pressed = ImGui::Button(dispName.c_str(),
|
||||||
btnSize);
|
btnSize);
|
||||||
|
|
||||||
if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) {
|
if (pressed) {
|
||||||
m_controllerConfig.m_isReading = true;
|
m_controllerConfig.m_isReading = true;
|
||||||
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
|
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
|
||||||
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[trigger];
|
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[trigger];
|
||||||
m_controllerConfig.m_waitForInputRelease = true;
|
|
||||||
PADBlockInput(true);
|
PADBlockInput(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -963,7 +426,7 @@ namespace dusk {
|
|||||||
|
|
||||||
std::string dispName;
|
std::string dispName;
|
||||||
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) {
|
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 {
|
} else {
|
||||||
if (axisMappingList[axis].nativeAxis.nativeAxis != -1) {
|
if (axisMappingList[axis].nativeAxis.nativeAxis != -1) {
|
||||||
const char* signStr;
|
const char* signStr;
|
||||||
@@ -982,11 +445,10 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
bool pressed = ImGui::Button(dispName.c_str(), btnSize);
|
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_isReading = true;
|
||||||
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
|
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
|
||||||
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis];
|
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis];
|
||||||
m_controllerConfig.m_waitForInputRelease = true;
|
|
||||||
PADBlockInput(true);
|
PADBlockInput(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1027,7 +489,7 @@ namespace dusk {
|
|||||||
|
|
||||||
std::string dispName;
|
std::string dispName;
|
||||||
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) {
|
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 {
|
} else {
|
||||||
if (axisMappingList[axis].nativeAxis.nativeAxis != -1) {
|
if (axisMappingList[axis].nativeAxis.nativeAxis != -1) {
|
||||||
const char* signStr;
|
const char* signStr;
|
||||||
@@ -1046,11 +508,10 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), btnSize);
|
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_isReading = true;
|
||||||
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
|
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
|
||||||
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis];
|
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis];
|
||||||
m_controllerConfig.m_waitForInputRelease = true;
|
|
||||||
PADBlockInput(true);
|
PADBlockInput(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1122,6 +583,7 @@ namespace dusk {
|
|||||||
getSettings().game.damageMultiplier.setValue(1);
|
getSettings().game.damageMultiplier.setValue(1);
|
||||||
getSettings().game.instantDeath.setValue(false);
|
getSettings().game.instantDeath.setValue(false);
|
||||||
getSettings().game.noHeartDrops.setValue(false);
|
getSettings().game.noHeartDrops.setValue(false);
|
||||||
|
getSettings().game.hyperEnemies.setValue(false);
|
||||||
|
|
||||||
getSettings().game.infiniteHearts.setValue(false);
|
getSettings().game.infiniteHearts.setValue(false);
|
||||||
getSettings().game.infiniteArrows.setValue(false);
|
getSettings().game.infiniteArrows.setValue(false);
|
||||||
|
|||||||
@@ -55,22 +55,12 @@ namespace dusk {
|
|||||||
static void resetForSpeedrunMode();
|
static void resetForSpeedrunMode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void drawAudioMenu();
|
|
||||||
void drawInputMenu();
|
|
||||||
void drawGraphicsMenu();
|
|
||||||
void drawGameplayMenu();
|
|
||||||
void drawCheatsMenu();
|
|
||||||
void drawInterfaceMenu();
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int m_selectedPort = 0;
|
int m_selectedPort = 0;
|
||||||
bool m_isReading = false;
|
bool m_isReading = false;
|
||||||
PADButtonMapping* m_pendingButtonMapping = nullptr;
|
PADButtonMapping* m_pendingButtonMapping = nullptr;
|
||||||
PADAxisMapping* m_pendingAxisMapping = nullptr;
|
PADAxisMapping* m_pendingAxisMapping = nullptr;
|
||||||
int m_pendingPort = -1;
|
int m_pendingPort = -1;
|
||||||
bool m_waitForInputRelease = false;
|
|
||||||
bool m_suppressRemapActivationUntilRelease = false;
|
|
||||||
int m_suppressRemapActivationPort = -1;
|
|
||||||
bool m_isRumbling = false;
|
bool m_isRumbling = false;
|
||||||
} m_controllerConfig;
|
} m_controllerConfig;
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ static void OpenDataFolder() {
|
|||||||
#define DUSK_CAN_OPEN_DATA_FOLDER 0
|
#define DUSK_CAN_OPEN_DATA_FOLDER 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace aurora::gx {
|
||||||
|
extern bool enableLodBias;
|
||||||
|
}
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
ImGuiMenuTools::ImGuiMenuTools() {}
|
ImGuiMenuTools::ImGuiMenuTools() {}
|
||||||
|
|
||||||
@@ -91,6 +95,7 @@ namespace dusk {
|
|||||||
getSettings().game.disableWaterRefraction.setValue(disableWaterRefraction);
|
getSettings().game.disableWaterRefraction.setValue(disableWaterRefraction);
|
||||||
config::Save();
|
config::Save();
|
||||||
}
|
}
|
||||||
|
ImGui::Checkbox("Enable LOD Bias", &aurora::gx::enableLodBias);
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "d/actor/d_a_player.h"
|
#include "d/actor/d_a_player.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <bit>
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
enum ItemType {
|
enum ItemType {
|
||||||
@@ -1296,7 +1297,32 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void genMembitFlags(const char* id, dSv_memBit_c& membit) {
|
static void genCommonAreaFlags(dSv_memBit_c& membit) {
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f);
|
||||||
|
|
||||||
|
genDungeonItemCheckbox(membit, "Got Map", dSv_memBit_c::MAP);
|
||||||
|
ImGui::SameLine(230.0f);
|
||||||
|
genDungeonItemCheckbox(membit, "Got Compass", dSv_memBit_c::COMPASS);
|
||||||
|
|
||||||
|
genDungeonItemCheckbox(membit, "Got Boss Key", dSv_memBit_c::BOSS_KEY);
|
||||||
|
ImGui::SameLine(230.0f);
|
||||||
|
genDungeonItemCheckbox(membit, "Saw Boss Demo", dSv_memBit_c::STAGE_BOSS_DEMO);
|
||||||
|
|
||||||
|
genDungeonItemCheckbox(membit, "Got Heart Container", dSv_memBit_c::STAGE_LIFE);
|
||||||
|
ImGui::SameLine(230.0f);
|
||||||
|
genDungeonItemCheckbox(membit, "Defeated Boss", dSv_memBit_c::STAGE_BOSS_ENEMY);
|
||||||
|
|
||||||
|
genDungeonItemCheckbox(membit, "Defeated Miniboss", dSv_memBit_c::STAGE_BOSS_ENEMY_2);
|
||||||
|
ImGui::SameLine(230.0f);
|
||||||
|
genDungeonItemCheckbox(membit, "Got Ooccoo", dSv_memBit_c::OOCCOO_NOTE);
|
||||||
|
|
||||||
|
int keyTemp = membit.getKeyNum();
|
||||||
|
if (ImGui::SliderInt("Keys", &keyTemp, 0, 5)) {
|
||||||
|
membit.setKeyNum(keyTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void genMembitFlags(const char* id, dSv_memBit_c& membit) {
|
||||||
ImGuiBeginGroupPanel("Chest", { 100, 100 });
|
ImGuiBeginGroupPanel("Chest", { 100, 100 });
|
||||||
for (int j = 0; j < 2; j++) {
|
for (int j = 0; j < 2; j++) {
|
||||||
drawFlagList(fmt::format("##_tbox{}", j).c_str(), membit.mTbox[j]);
|
drawFlagList(fmt::format("##_tbox{}", j).c_str(), membit.mTbox[j]);
|
||||||
@@ -1322,29 +1348,10 @@ namespace dusk {
|
|||||||
drawFlagList(fmt::format("##_item{}", j).c_str(), membit.mItem[j]);
|
drawFlagList(fmt::format("##_item{}", j).c_str(), membit.mItem[j]);
|
||||||
}
|
}
|
||||||
ImGuiEndGroupPanel();
|
ImGuiEndGroupPanel();
|
||||||
|
ImVec2 post_item_custor = ImGui::GetCursorPos();
|
||||||
|
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f);
|
ImGui::SetCursorPos({post_item_custor.x, post_switch_cursor.y});
|
||||||
|
// genCommonAreaFlags(membit);
|
||||||
genDungeonItemCheckbox(membit, "Got Map", dSv_memBit_c::MAP);
|
|
||||||
ImGui::SameLine(230.0f);
|
|
||||||
genDungeonItemCheckbox(membit, "Got Compass", dSv_memBit_c::COMPASS);
|
|
||||||
|
|
||||||
genDungeonItemCheckbox(membit, "Got Boss Key", dSv_memBit_c::BOSS_KEY);
|
|
||||||
ImGui::SameLine(230.0f);
|
|
||||||
genDungeonItemCheckbox(membit, "Saw Boss Demo", dSv_memBit_c::STAGE_BOSS_DEMO);
|
|
||||||
|
|
||||||
genDungeonItemCheckbox(membit, "Got Heart Container", dSv_memBit_c::STAGE_LIFE);
|
|
||||||
ImGui::SameLine(230.0f);
|
|
||||||
genDungeonItemCheckbox(membit, "Defeated Boss", dSv_memBit_c::STAGE_BOSS_ENEMY);
|
|
||||||
|
|
||||||
genDungeonItemCheckbox(membit, "Defeated Miniboss", dSv_memBit_c::STAGE_BOSS_ENEMY_2);
|
|
||||||
ImGui::SameLine(230.0f);
|
|
||||||
genDungeonItemCheckbox(membit, "Got Ooccoo", dSv_memBit_c::OOCCOO_NOTE);
|
|
||||||
|
|
||||||
int keyTemp = membit.getKeyNum();
|
|
||||||
if (ImGui::SliderInt("Keys", &keyTemp, 0, 5)) {
|
|
||||||
membit.setKeyNum(keyTemp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -1392,74 +1399,326 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiSaveEditor::drawFlagsTab() {
|
|
||||||
if (ImGui::TreeNode("Current Region Flags")) {
|
|
||||||
dSv_memBit_c& membit = g_dComIfG_gameInfo.info.mMemory.mBit;
|
|
||||||
genMembitFlags("##TempSceneFlags", membit);
|
|
||||||
|
|
||||||
stage_stag_info_class* pstag = dComIfGp_getStageStagInfo();
|
static void genAreaFlagTable(uint8_t areaIndex, dSv_memBit_c& membit) {
|
||||||
if (pstag != nullptr) {
|
constexpr auto makeMask = [](uint8_t size) -> uint16_t { return (1 << size) - 1; };
|
||||||
int stageNo = dStage_stagInfo_GetSaveTbl(pstag);
|
constexpr auto getByteIndexFromFlag = [](uint16_t f) -> uint8_t { return f >> 8; };
|
||||||
if (ImGui::Button("Save##SaveTempFlags")) {
|
constexpr auto getBitMaskFromFlag = [](uint16_t f) -> uint8_t { return f & 0xff; };
|
||||||
dComIfGs_putSave(stageNo);
|
constexpr auto getValueSize = [getBitMaskFromFlag](uint16_t f) -> uint8_t {
|
||||||
|
return std::popcount(getBitMaskFromFlag(f));
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto makeEventFlag = [](uint8_t byteIndex, uint8_t bitIndices) -> uint16_t {
|
||||||
|
return (byteIndex << 8) | bitIndices;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto eventFlagToAreaFlag = [&](uint16_t areaFlag) -> int {
|
||||||
|
auto byteInd = getByteIndexFromFlag(areaFlag);
|
||||||
|
constexpr size_t areaIndexSize = 5;
|
||||||
|
// if we're looking at 0x580, that would be byte 5, and check if 0x80 is set on that byte
|
||||||
|
// the event flags are structured differently than area flags
|
||||||
|
// B is byte index, b is the flag mask to check
|
||||||
|
// event flags are BBBBBBBB bbbbbbbb
|
||||||
|
// for area flags, they check bitIndex, not mask, i is index
|
||||||
|
// also area uses u32 index, not byte index
|
||||||
|
// area flags are BBBiiiii
|
||||||
|
// so we need to convert from bit mask to index
|
||||||
|
// also our byte index has to become a u32 index
|
||||||
|
|
||||||
|
// dividing byte index by sizeof(u32) gets us the u32 index
|
||||||
|
// but in big endian, the first byte is the highest order byte of the u32
|
||||||
|
// so we skip 24 bytes for the first byte, 16 for the second, etc
|
||||||
|
// essentially (3 - (x % 4)), reversing the modulus, 0=3, 1=2
|
||||||
|
auto bitsToSkip = 8 * ((sizeof(u32) - 1) - (byteInd % sizeof(u32)));
|
||||||
|
return ((byteInd / sizeof(u32)) << areaIndexSize) | ((std::countr_zero(areaFlag) + bitsToSkip) & makeMask(areaIndexSize));
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint8_t validTbox = sizeof(membit.mTbox);
|
||||||
|
constexpr uint8_t validSwitch = validTbox + sizeof(membit.mSwitch);
|
||||||
|
constexpr uint8_t validItem = validSwitch + sizeof(membit.mItem);
|
||||||
|
constexpr uint16_t tboxConvert = 0;
|
||||||
|
constexpr uint16_t switchConvert = sizeof(membit.mTbox) << 8;
|
||||||
|
constexpr uint16_t itemConvert = switchConvert + (sizeof(membit.mItem) << 8);
|
||||||
|
|
||||||
|
const auto LoadFlag = [&](uint16_t flag) -> bool {
|
||||||
|
const auto byteIndex = getByteIndexFromFlag(flag);
|
||||||
|
|
||||||
|
if (byteIndex < validTbox) {
|
||||||
|
return membit.isTbox(eventFlagToAreaFlag(flag - tboxConvert));
|
||||||
|
} else if (byteIndex < validSwitch) {
|
||||||
|
return membit.isSwitch(eventFlagToAreaFlag(flag - switchConvert));
|
||||||
|
} else if (byteIndex < validItem) {
|
||||||
|
return membit.isItem(eventFlagToAreaFlag(flag - itemConvert));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto SetFlag = [&](uint16_t flag, bool set) -> void {
|
||||||
|
const auto byteIndex = getByteIndexFromFlag(flag);
|
||||||
|
if (set) {
|
||||||
|
if (byteIndex < validTbox) {
|
||||||
|
membit.onTbox(eventFlagToAreaFlag(flag - tboxConvert));
|
||||||
|
} else if (byteIndex < validSwitch) {
|
||||||
|
membit.onSwitch(eventFlagToAreaFlag(flag - switchConvert));
|
||||||
|
} else if (byteIndex < validItem) {
|
||||||
|
membit.onItem(eventFlagToAreaFlag(flag - itemConvert));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
ImGui::SameLine();
|
if (byteIndex < validTbox) {
|
||||||
|
membit.offTbox(eventFlagToAreaFlag(flag - tboxConvert));
|
||||||
if (ImGui::Button("Load##LoadSaveFlags")) {
|
} else if (byteIndex < validSwitch) {
|
||||||
dComIfGs_getSave(stageNo);
|
membit.offSwitch(eventFlagToAreaFlag(flag - switchConvert));
|
||||||
|
} else if (byteIndex < validItem) {
|
||||||
|
membit.offItem(eventFlagToAreaFlag(flag - itemConvert));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto LoadMultiByteFlag = [&](uint16_t flag) -> uint8_t {
|
||||||
|
const auto bitInds = getBitMaskFromFlag(flag);
|
||||||
|
const auto byteIndex = getByteIndexFromFlag(flag);
|
||||||
|
|
||||||
|
const uint16_t startingMask = std::bit_floor(bitInds);
|
||||||
|
uint8_t val = 0;
|
||||||
|
for (uint16_t bitIndexMask = startingMask; (bitInds & bitIndexMask) != 0;
|
||||||
|
bitIndexMask >>= 1)
|
||||||
|
{
|
||||||
|
val <<= 1;
|
||||||
|
if (LoadFlag(makeEventFlag(byteIndex, bitInds & bitIndexMask))) {
|
||||||
|
val |= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto SetMultiByteFlag = [&](uint16_t flag, uint8_t val) -> void {
|
||||||
|
const auto bitInds = getBitMaskFromFlag(flag);
|
||||||
|
const auto byteIndex = getByteIndexFromFlag(flag);
|
||||||
|
|
||||||
|
const uint16_t startingMask = std::bit_floor(bitInds);
|
||||||
|
uint16_t valueMask = 1 << (getValueSize(flag) - 1);
|
||||||
|
|
||||||
|
for (uint16_t bitIndexMask = startingMask; (bitInds & bitIndexMask) != 0;
|
||||||
|
bitIndexMask >>= 1, valueMask >>= 1)
|
||||||
|
{
|
||||||
|
SetFlag(makeEventFlag(byteIndex, bitInds & bitIndexMask), (val & valueMask) != 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto LoadSpreadMultiByte = [&](uint16_t high, uint16_t low) -> uint8_t {
|
||||||
|
if (low == AREA_FLAG_NONE)
|
||||||
|
return LoadMultiByteFlag(high);
|
||||||
|
return (LoadMultiByteFlag(high) << getValueSize(low)) | LoadMultiByteFlag(low);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto SetSpreadMultiByte = [&](uint16_t high, uint16_t low, uint8_t value) -> void {
|
||||||
|
if (low == AREA_FLAG_NONE)
|
||||||
|
return SetMultiByteFlag(high, value);
|
||||||
|
const auto lowerSize = getValueSize(low);
|
||||||
|
SetMultiByteFlag(high, value >> lowerSize);
|
||||||
|
SetMultiByteFlag(low, value & makeMask(lowerSize));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto iter = imguiAreaFlagLookup.find(areaIndex);
|
||||||
|
if (iter == imguiAreaFlagLookup.end()) return;
|
||||||
|
|
||||||
|
auto& areaFlags = iter->second;
|
||||||
|
|
||||||
|
static ImGuiTextFilter filter;
|
||||||
|
filter.Draw(); // Search bar
|
||||||
|
|
||||||
|
ImVec2 flagTableSize = {700, 400};
|
||||||
|
if (ImGui::BeginTable("Area Flags", 3,
|
||||||
|
ImGuiTableFlags_ScrollY | ImGuiTableFlags_ScrollX |
|
||||||
|
ImGuiTableFlags_Sortable,
|
||||||
|
flagTableSize))
|
||||||
|
{
|
||||||
|
ImGui::TableSetupScrollFreeze(0, 1);
|
||||||
|
constexpr int COLUMN_FLAG = 0, COLUMN_BIT = 1, COLUMN_DESC = 2;
|
||||||
|
ImGui::TableSetupColumn("Flag");
|
||||||
|
ImGui::TableSetupColumn("Byte:Bit");
|
||||||
|
ImGui::TableSetupColumn("Description");
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
|
// if we're sorting by whether the flag is set or not,
|
||||||
|
// we want to re-sort whenever a flag updates, which means every frame cuz we don't
|
||||||
|
// know when it changes. otherwise only re-sort when the sort is dirty
|
||||||
|
if (auto* sort = ImGui::TableGetSortSpecs();
|
||||||
|
sort != nullptr && sort->SpecsCount > 0 &&
|
||||||
|
(sort->SpecsDirty || sort->Specs[0].ColumnIndex == COLUMN_FLAG))
|
||||||
|
{
|
||||||
|
const auto column = sort->Specs[0].ColumnIndex;
|
||||||
|
const auto direction = sort->Specs[0].SortDirection;
|
||||||
|
|
||||||
|
// if we're sorting by flags, do special sort, regular sort is bad for sorting
|
||||||
|
// bools it can swap values that are the same, and that causes constant
|
||||||
|
// reordering
|
||||||
|
if (column == COLUMN_FLAG) {
|
||||||
|
if (direction == ImGuiSortDirection_Ascending) {
|
||||||
|
sortByFlags(std::begin(areaFlags.bitFlags), std::end(areaFlags.bitFlags),
|
||||||
|
LoadFlag);
|
||||||
|
} else {
|
||||||
|
sortByFlags(std::rbegin(areaFlags.bitFlags), std::rend(areaFlags.bitFlags),
|
||||||
|
LoadFlag);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto cmp = [column](const EventAreaFlags& l,
|
||||||
|
const EventAreaFlags& r) -> bool {
|
||||||
|
switch (column) {
|
||||||
|
case COLUMN_DESC:
|
||||||
|
return l.description < r.description;
|
||||||
|
case COLUMN_BIT:
|
||||||
|
return l.flagID < r.flagID;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (direction == ImGuiSortDirection_Ascending) {
|
||||||
|
std::sort(std::begin(areaFlags.bitFlags), std::end(areaFlags.bitFlags),
|
||||||
|
cmp);
|
||||||
|
} else {
|
||||||
|
std::sort(std::rbegin(areaFlags.bitFlags), std::rend(areaFlags.bitFlags),
|
||||||
|
cmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort->SpecsDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& e : areaFlags.bitFlags) {
|
||||||
|
std::string formattedBitLocation =
|
||||||
|
fmt::format("{0:02X}:{1:02X}", e.byteIndex, e.bitIndex);
|
||||||
|
|
||||||
|
if (!filter.PassFilter(e.description.c_str()) &&
|
||||||
|
!filter.PassFilter(formattedBitLocation.c_str()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
bool flag = LoadFlag(e.flagID);
|
||||||
|
if (ImGui::Checkbox(fmt::format("##_unused_area_flag_{}", e.flagID).c_str(), &flag)) {
|
||||||
|
SetFlag(e.flagID, flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextUnformatted(formattedBitLocation.c_str());
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextUnformatted(e.description.c_str());
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& multiByteFlag : areaFlags.multibyteFlags) {
|
||||||
|
auto flagValue = LoadSpreadMultiByte(multiByteFlag.highOrderflag, multiByteFlag.lowOrderflag);
|
||||||
|
|
||||||
|
const char* currentVal = "UNKNOWN";
|
||||||
|
|
||||||
|
auto enumValIter = multiByteFlag.enumValues.find(flagValue);
|
||||||
|
if (enumValIter != multiByteFlag.enumValues.end()) {
|
||||||
|
currentVal = enumValIter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginCombo(multiByteFlag.name, currentVal)) {
|
||||||
|
for (const auto& [val, name] : multiByteFlag.enumValues) {
|
||||||
|
if (ImGui::Selectable(name)) {
|
||||||
|
SetSpreadMultiByte(multiByteFlag.highOrderflag, multiByteFlag.lowOrderflag, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
genCommonAreaFlags(membit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawCurrentRegionFlags()
|
||||||
|
{
|
||||||
|
dSv_memBit_c& membit = g_dComIfG_gameInfo.info.mMemory.mBit;
|
||||||
|
auto* stageData = dComIfGp_getStageStagInfo();
|
||||||
|
if (!stageData)
|
||||||
|
return;
|
||||||
|
uint8_t stageIndex = dStage_stagInfo_GetSaveTbl(stageData);
|
||||||
|
|
||||||
|
genAreaFlagTable(stageIndex, membit);
|
||||||
|
|
||||||
|
if (ImGui::TreeNode("Flag Matrix")) {
|
||||||
|
genMembitFlags("##TempSceneFlags", membit);
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
stage_stag_info_class* pstag = dComIfGp_getStageStagInfo();
|
||||||
|
if (pstag != nullptr) {
|
||||||
|
int stageNo = dStage_stagInfo_GetSaveTbl(pstag);
|
||||||
|
if (ImGui::Button("Save##SaveTempFlags")) {
|
||||||
|
dComIfGs_putSave(stageNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button("Load##LoadSaveFlags")) {
|
||||||
|
dComIfGs_getSave(stageNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiSaveEditor::drawFlagsTab() {
|
||||||
|
if (ImGui::TreeNode("Current Region Flags")) {
|
||||||
|
drawCurrentRegionFlags();
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::TreeNode("Region Saved Flags")) {
|
if (ImGui::TreeNode("Region Saved Flags")) {
|
||||||
static std::array<const char*, 27> regionNames = {
|
static const std::map<uint8_t, const char*> regionNames = {
|
||||||
"Ordon",
|
{ 0x00, "Ordon" },
|
||||||
"Hyrule Sewers",
|
{ 0x01, "Hyrule Sewers" },
|
||||||
"Faron",
|
{ 0x02, "Faron" },
|
||||||
"Eldin",
|
{ 0x03, "Eldin" },
|
||||||
"Lanayru",
|
{ 0x04, "Lanayru" },
|
||||||
"Reserved",
|
{ 0x06, "Hyrule Field" },
|
||||||
"Hyrule Field",
|
{ 0x07, "Sacred Grove" },
|
||||||
"Sacred Grove",
|
{ 0x08, "Snowpeak" },
|
||||||
"Snowpeak",
|
{ 0x09, "Castle Town" },
|
||||||
"Castle Town",
|
{ 0x0A, "Gerudo Desert" },
|
||||||
"Gerudo Desert",
|
{ 0x0B, "Fishing Pond" },
|
||||||
"Fishing Pond",
|
{ 0x10, "Forest Temple" },
|
||||||
"Reserved",
|
{ 0x11, "Goron Mines" },
|
||||||
"Reserved",
|
{ 0x12, "Lakebed Temple" },
|
||||||
"Reserved",
|
{ 0x13, "Arbiter's Grounds" },
|
||||||
"Reserved",
|
{ 0x14, "Snowpeak Ruins" },
|
||||||
"Forest Temple",
|
{ 0x15, "Temple of Time" },
|
||||||
"Goron Mines",
|
{ 0x16, "City in the Sky" },
|
||||||
"Lakebed Temple",
|
{ 0x17, "Palace of Twilight" },
|
||||||
"Arbiter's Grounds",
|
{ 0x18, "Hyrule Castle" },
|
||||||
"Snowpeak Ruins",
|
{ 0x19, "Caves" },
|
||||||
"Temple of Time",
|
{ 0x1A, "Lake Hylia Long Cave"},
|
||||||
"City in the Sky",
|
{ 0x1B, "Grottos" }
|
||||||
"Palace of Twilight",
|
|
||||||
"Hyrule Castle",
|
|
||||||
"Caves",
|
|
||||||
"Grottos",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ImGui::BeginCombo("Region", regionNames[m_selectedRegion])) {
|
if (m_selectedRegion.name == nullptr)
|
||||||
for (int i = 0; i < regionNames.size(); i++) {
|
{
|
||||||
if (strcmp(regionNames[i], "Reserved") == 0) continue;
|
const auto& firstRegion = *regionNames.find(0);
|
||||||
|
m_selectedRegion = { firstRegion.first, firstRegion.second };
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui::Selectable(regionNames[i])) {
|
if (ImGui::BeginCombo("Region", m_selectedRegion.name)) {
|
||||||
m_selectedRegion = i;
|
for (const auto& [id, name] : regionNames) {
|
||||||
|
if (ImGui::Selectable(name)) {
|
||||||
|
m_selectedRegion = {id, name};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
dSv_memBit_c* membit = &dComIfGs_getSaveData()->mSave[m_selectedRegion].mBit;
|
dSv_memBit_c& membit = dComIfGs_getSaveData()->mSave[m_selectedRegion.id].mBit;
|
||||||
if (membit != nullptr) {
|
|
||||||
genMembitFlags("##SaveSceneFlags", *membit);
|
genAreaFlagTable(m_selectedRegion.id, membit);
|
||||||
|
|
||||||
|
if (ImGui::TreeNode("Flag Matrix")) {
|
||||||
|
genMembitFlags("##SaveSceneFlags", membit);
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
@@ -1530,7 +1789,9 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& e : duskImguiEventFlags) {
|
for (const auto& e : duskImguiEventFlags) {
|
||||||
if (!filter.PassFilter((e.location + "\n" + e.description + "\n" + e.flagName).c_str()))
|
if (!filter.PassFilter(e.location.c_str()) &&
|
||||||
|
!filter.PassFilter(e.description.c_str()) &&
|
||||||
|
!filter.PassFilter(e.flagName.c_str()))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ namespace dusk {
|
|||||||
void drawConfigTab();
|
void drawConfigTab();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_selectedRegion = 0;
|
struct {
|
||||||
|
uint8_t id;
|
||||||
|
const char* name;
|
||||||
|
} m_selectedRegion = {0, nullptr};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ ValidationError validate(const char* path) {
|
|||||||
NodHandleWrapper disc;
|
NodHandleWrapper disc;
|
||||||
|
|
||||||
const auto sdlStream = SDL_IOFromFile(path, "rb");
|
const auto sdlStream = SDL_IOFromFile(path, "rb");
|
||||||
|
if (sdlStream == nullptr) {
|
||||||
|
return ValidationError::IOError;
|
||||||
|
}
|
||||||
|
|
||||||
const NodDiscStream nod_stream {
|
const NodDiscStream nod_stream {
|
||||||
.user_data = sdlStream,
|
.user_data = sdlStream,
|
||||||
.read_at = StreamReadAt,
|
.read_at = StreamReadAt,
|
||||||
|
|||||||
+48
-21
@@ -1,5 +1,6 @@
|
|||||||
#include "dusk/logging.h"
|
#include "dusk/logging.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -32,9 +33,33 @@ static constexpr std::string_view StubFragments[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::mutex g_logMutex;
|
// On macOS, std::mutex becomes poisoned when its dtor is run.
|
||||||
FILE* g_logFile = nullptr;
|
// We use this to check if the LogState is destroyed before attempting to acquire it.
|
||||||
std::string g_logFilePath;
|
std::atomic g_logStateAlive(true);
|
||||||
|
|
||||||
|
struct LogState {
|
||||||
|
std::mutex mutex;
|
||||||
|
FILE* file = nullptr;
|
||||||
|
std::string filePath;
|
||||||
|
|
||||||
|
~LogState() {
|
||||||
|
CloseFile();
|
||||||
|
g_logStateAlive.store(false, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseFile() {
|
||||||
|
if (!g_logStateAlive.load(std::memory_order_acquire)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
if (file != nullptr) {
|
||||||
|
std::fflush(file);
|
||||||
|
std::fclose(file);
|
||||||
|
file = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LogState g_logState;
|
||||||
|
|
||||||
const char* LogLevelString(AuroraLogLevel level) {
|
const char* LogLevelString(AuroraLogLevel level) {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
@@ -152,10 +177,10 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
|
|||||||
FILE* out = LogStreamForLevel(level);
|
FILE* out = LogStreamForLevel(level);
|
||||||
WriteLogLine(out, levelStr, module, message, len);
|
WriteLogLine(out, levelStr, module, message, len);
|
||||||
|
|
||||||
{
|
if (g_logStateAlive.load(std::memory_order_acquire)) {
|
||||||
std::lock_guard lock(g_logMutex);
|
std::lock_guard lock(g_logState.mutex);
|
||||||
if (g_logFile != nullptr) {
|
if (g_logState.file != nullptr) {
|
||||||
WriteLogLine(g_logFile, levelStr, module, message, len);
|
WriteLogLine(g_logState.file, levelStr, module, message, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,8 +194,11 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
|
|||||||
aurora::Module DuskLog("dusk");
|
aurora::Module DuskLog("dusk");
|
||||||
|
|
||||||
void dusk::InitializeFileLogging(const std::filesystem::path& configDir, AuroraLogLevel logLevel) {
|
void dusk::InitializeFileLogging(const std::filesystem::path& configDir, AuroraLogLevel logLevel) {
|
||||||
std::lock_guard lock(g_logMutex);
|
if (!g_logStateAlive.load(std::memory_order_acquire)) {
|
||||||
if (g_logFile != nullptr || configDir.empty()) {
|
return;
|
||||||
|
}
|
||||||
|
std::lock_guard lock(g_logState.mutex);
|
||||||
|
if (g_logState.file != nullptr || configDir.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,31 +212,30 @@ void dusk::InitializeFileLogging(const std::filesystem::path& configDir, AuroraL
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::filesystem::path logPath = logsDir / MakeTimestampedLogName();
|
const std::filesystem::path logPath = logsDir / MakeTimestampedLogName();
|
||||||
g_logFile = std::fopen(logPath.string().c_str(), "wb");
|
g_logState.file = std::fopen(logPath.string().c_str(), "wb");
|
||||||
if (g_logFile == nullptr) {
|
if (g_logState.file == nullptr) {
|
||||||
std::fprintf(stderr, "[WARNING | dusk] Failed to open log file '%s'\n",
|
std::fprintf(stderr, "[WARNING | dusk] Failed to open log file '%s'\n",
|
||||||
logPath.string().c_str());
|
logPath.string().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_logFilePath = logPath.string();
|
g_logState.filePath = logPath.string();
|
||||||
aurora::g_config.logCallback = &aurora_log_callback;
|
aurora::g_config.logCallback = &aurora_log_callback;
|
||||||
aurora::g_config.logLevel = logLevel;
|
aurora::g_config.logLevel = logLevel;
|
||||||
WriteLogLine(g_logFile, "INFO", "dusk", "File logging initialized", 24);
|
WriteLogLine(g_logState.file, "INFO", "dusk", "File logging initialized", 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dusk::ShutdownFileLogging() {
|
void dusk::ShutdownFileLogging() {
|
||||||
std::lock_guard lock(g_logMutex);
|
if (!g_logStateAlive.load(std::memory_order_acquire)) {
|
||||||
if (g_logFile == nullptr) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
g_logState.CloseFile();
|
||||||
std::fflush(g_logFile);
|
|
||||||
std::fclose(g_logFile);
|
|
||||||
g_logFile = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* dusk::GetLogFilePath() {
|
const char* dusk::GetLogFilePath() {
|
||||||
std::lock_guard lock(g_logMutex);
|
if (!g_logStateAlive.load(std::memory_order_acquire)) {
|
||||||
return g_logFilePath.empty() ? nullptr : g_logFilePath.c_str();
|
return nullptr;
|
||||||
|
}
|
||||||
|
std::lock_guard lock(g_logState.mutex);
|
||||||
|
return g_logState.filePath.empty() ? nullptr : g_logState.filePath.c_str();
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -17,6 +17,7 @@ UserSettings g_userSettings = {
|
|||||||
.soundEffectsVolume {"audio.soundEffectsVolume", 100},
|
.soundEffectsVolume {"audio.soundEffectsVolume", 100},
|
||||||
.fanfareVolume {"audio.fanfareVolume", 100},
|
.fanfareVolume {"audio.fanfareVolume", 100},
|
||||||
.enableReverb {"audio.enableReverb", true},
|
.enableReverb {"audio.enableReverb", true},
|
||||||
|
.enableHrtf {"audio.enableHrtf", false},
|
||||||
},
|
},
|
||||||
|
|
||||||
.game = {
|
.game = {
|
||||||
@@ -31,14 +32,17 @@ UserSettings g_userSettings = {
|
|||||||
.disableRupeeCutscenes {"game.disableRupeeCutscenes", false},
|
.disableRupeeCutscenes {"game.disableRupeeCutscenes", false},
|
||||||
.noSwordRecoil {"game.noSwordRecoil", false},
|
.noSwordRecoil {"game.noSwordRecoil", false},
|
||||||
.damageMultiplier {"game.damageMultiplier", 1},
|
.damageMultiplier {"game.damageMultiplier", 1},
|
||||||
.noHeartDrops{"game.noHeartDrops", false},
|
.hyperEnemies {"game.hyperEnemies", false},
|
||||||
|
.noHeartDrops {"game.noHeartDrops", false},
|
||||||
.instantDeath {"game.instantDeath", false},
|
.instantDeath {"game.instantDeath", false},
|
||||||
.fastClimbing {"game.fastClimbing", false},
|
.fastClimbing {"game.fastClimbing", false},
|
||||||
.noMissClimbing {"game.noMissClimbing", false},
|
.noMissClimbing {"game.noMissClimbing", false},
|
||||||
.fastTears {"game.fastTears", false},
|
.fastTears {"game.fastTears", false},
|
||||||
|
.no2ndFishForCat {"game.no2ndFishForCat", false},
|
||||||
.instantSaves {"game.instantSaves", false},
|
.instantSaves {"game.instantSaves", false},
|
||||||
.instantText {"game.instantText", false},
|
.instantText {"game.instantText", false},
|
||||||
.sunsSong {"game.sunsSong", false},
|
.sunsSong {"game.sunsSong", false},
|
||||||
|
.autoSave {"game.autoSave", false},
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
.enableMirrorMode {"game.enableMirrorMode", false},
|
.enableMirrorMode {"game.enableMirrorMode", false},
|
||||||
@@ -51,7 +55,7 @@ UserSettings g_userSettings = {
|
|||||||
.bloomMode {"game.bloomMode", BloomMode::Classic},
|
.bloomMode {"game.bloomMode", BloomMode::Classic},
|
||||||
.bloomMultiplier {"game.bloomMultiplier", 1.0f},
|
.bloomMultiplier {"game.bloomMultiplier", 1.0f},
|
||||||
.disableWaterRefraction {"game.disableWaterRefraction", false},
|
.disableWaterRefraction {"game.disableWaterRefraction", false},
|
||||||
.enableFrameInterpolation = {"game.enableFrameInterpolation", false},
|
.enableFrameInterpolation {"game.enableFrameInterpolation", false},
|
||||||
.internalResolutionScale {"game.internalResolutionScale", 0},
|
.internalResolutionScale {"game.internalResolutionScale", 0},
|
||||||
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
|
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
|
||||||
.enableDepthOfField {"game.enableDepthOfField", true},
|
.enableDepthOfField {"game.enableDepthOfField", true},
|
||||||
@@ -132,6 +136,7 @@ void registerSettings() {
|
|||||||
Register(g_userSettings.audio.soundEffectsVolume);
|
Register(g_userSettings.audio.soundEffectsVolume);
|
||||||
Register(g_userSettings.audio.fanfareVolume);
|
Register(g_userSettings.audio.fanfareVolume);
|
||||||
Register(g_userSettings.audio.enableReverb);
|
Register(g_userSettings.audio.enableReverb);
|
||||||
|
Register(g_userSettings.audio.enableHrtf);
|
||||||
|
|
||||||
// Game
|
// Game
|
||||||
Register(g_userSettings.game.language);
|
Register(g_userSettings.game.language);
|
||||||
@@ -143,13 +148,16 @@ void registerSettings() {
|
|||||||
Register(g_userSettings.game.disableRupeeCutscenes);
|
Register(g_userSettings.game.disableRupeeCutscenes);
|
||||||
Register(g_userSettings.game.noSwordRecoil);
|
Register(g_userSettings.game.noSwordRecoil);
|
||||||
Register(g_userSettings.game.damageMultiplier);
|
Register(g_userSettings.game.damageMultiplier);
|
||||||
|
Register(g_userSettings.game.hyperEnemies);
|
||||||
Register(g_userSettings.game.noHeartDrops);
|
Register(g_userSettings.game.noHeartDrops);
|
||||||
Register(g_userSettings.game.instantDeath);
|
Register(g_userSettings.game.instantDeath);
|
||||||
Register(g_userSettings.game.fastClimbing);
|
Register(g_userSettings.game.fastClimbing);
|
||||||
Register(g_userSettings.game.fastTears);
|
Register(g_userSettings.game.fastTears);
|
||||||
|
Register(g_userSettings.game.no2ndFishForCat);
|
||||||
Register(g_userSettings.game.instantSaves);
|
Register(g_userSettings.game.instantSaves);
|
||||||
Register(g_userSettings.game.instantText);
|
Register(g_userSettings.game.instantText);
|
||||||
Register(g_userSettings.game.sunsSong);
|
Register(g_userSettings.game.sunsSong);
|
||||||
|
Register(g_userSettings.game.autoSave);
|
||||||
Register(g_userSettings.game.enableMirrorMode);
|
Register(g_userSettings.game.enableMirrorMode);
|
||||||
Register(g_userSettings.game.invertCameraXAxis);
|
Register(g_userSettings.game.invertCameraXAxis);
|
||||||
Register(g_userSettings.game.invertCameraYAxis);
|
Register(g_userSettings.game.invertCameraYAxis);
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include "bool_button.hpp"
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
BoolButton::BoolButton(Rml::Element* parent, Props props)
|
||||||
|
: BaseControlledSelectButton(parent, {std::move(props.key)}),
|
||||||
|
mGetValue(std::move(props.getValue)), mSetValue(std::move(props.setValue)),
|
||||||
|
mIsDisabled(std::move(props.isDisabled)) {}
|
||||||
|
|
||||||
|
bool BoolButton::disabled() const {
|
||||||
|
if (mIsDisabled) {
|
||||||
|
return mIsDisabled();
|
||||||
|
}
|
||||||
|
return BaseControlledSelectButton::disabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::String BoolButton::format_value() {
|
||||||
|
return mGetValue() ? "On" : "Off";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BoolButton::handle_nav_command(NavCommand cmd) {
|
||||||
|
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left || cmd == NavCommand::Right) {
|
||||||
|
mSetValue(!mGetValue());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "select_button.hpp"
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
class BoolButton : public BaseControlledSelectButton {
|
||||||
|
public:
|
||||||
|
struct Props {
|
||||||
|
Rml::String key;
|
||||||
|
std::function<bool()> getValue;
|
||||||
|
std::function<void(bool)> setValue;
|
||||||
|
std::function<bool()> isDisabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
BoolButton(Rml::Element* parent, Props props);
|
||||||
|
|
||||||
|
bool disabled() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Rml::String format_value() override;
|
||||||
|
bool handle_nav_command(NavCommand cmd) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<int()> mGetValue;
|
||||||
|
std::function<void(int)> mSetValue;
|
||||||
|
std::function<bool()> mIsDisabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#include "button.hpp"
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Rml::Element* createRoot(Rml::Element* parent, const Rml::String& tagName) {
|
||||||
|
auto* doc = parent->GetOwnerDocument();
|
||||||
|
auto elem = doc->CreateElement(tagName);
|
||||||
|
return parent->AppendChild(std::move(elem));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Button::Button(Rml::Element* parent, Props props, const Rml::String& tagName)
|
||||||
|
: FluentComponent(createRoot(parent, tagName)) {
|
||||||
|
update_props(std::move(props));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::set_text(const Rml::String& text) {
|
||||||
|
if (mProps.text != text) {
|
||||||
|
mRoot->SetInnerRML(escape(text));
|
||||||
|
mProps.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button& Button::on_pressed(ButtonCallback callback) {
|
||||||
|
if (!callback) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
// TODO: convert this to a FluentComponent method?
|
||||||
|
on_nav_command([callback = std::move(callback)](Rml::Event&, NavCommand cmd) {
|
||||||
|
if (cmd == NavCommand::Confirm) {
|
||||||
|
callback();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::update_props(Props props) {
|
||||||
|
set_text(props.text);
|
||||||
|
mProps = std::move(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlledButton::update() {
|
||||||
|
if (mIsSelected) {
|
||||||
|
set_selected(mIsSelected());
|
||||||
|
}
|
||||||
|
Button::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlledButton::selected() const {
|
||||||
|
if (mIsSelected) {
|
||||||
|
return mIsSelected();
|
||||||
|
}
|
||||||
|
return Button::selected();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "component.hpp"
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
using ButtonCallback = std::function<void()>;
|
||||||
|
|
||||||
|
class Button : public FluentComponent<Button> {
|
||||||
|
public:
|
||||||
|
struct Props {
|
||||||
|
Rml::String text;
|
||||||
|
};
|
||||||
|
|
||||||
|
Button(Rml::Element* parent, Props props, const Rml::String& tagName = "button");
|
||||||
|
Button(Rml::Element* parent, Rml::String text, const Rml::String& tagName = "button")
|
||||||
|
: Button(parent, Props{std::move(text)}, tagName) {}
|
||||||
|
|
||||||
|
void set_text(const Rml::String& text);
|
||||||
|
Button& on_pressed(ButtonCallback callback);
|
||||||
|
|
||||||
|
const Rml::String& get_text() const { return mProps.text; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void update_props(Props props);
|
||||||
|
|
||||||
|
Props mProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ControlledButton : public Button {
|
||||||
|
public:
|
||||||
|
struct Props {
|
||||||
|
Rml::String text;
|
||||||
|
std::function<bool()> isSelected;
|
||||||
|
};
|
||||||
|
|
||||||
|
ControlledButton(Rml::Element* parent, Props props, const Rml::String& tagName = "button")
|
||||||
|
: Button(parent, {std::move(props.text)}, tagName),
|
||||||
|
mIsSelected(std::move(props.isSelected)) {}
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
bool selected() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<bool()> mIsSelected;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#include "component.hpp"
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
Component::Component(Rml::Element* root) : mRoot(root) {}
|
||||||
|
|
||||||
|
Component::~Component() = default;
|
||||||
|
|
||||||
|
void Component::update() {
|
||||||
|
for (const auto& child : mChildren) {
|
||||||
|
child->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Component::focus() {
|
||||||
|
if (disabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Can we focus self?
|
||||||
|
if (mRoot->Focus(true)) {
|
||||||
|
mRoot->ScrollIntoView(Rml::ScrollIntoViewOptions{
|
||||||
|
Rml::ScrollAlignment::Center,
|
||||||
|
Rml::ScrollAlignment::Center,
|
||||||
|
Rml::ScrollBehavior::Smooth,
|
||||||
|
Rml::ScrollParentage::Closest,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Otherwise, try to focus a child
|
||||||
|
for (const auto& child : mChildren) {
|
||||||
|
if (child->focus()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::set_selected(bool value) {
|
||||||
|
// Subclasses may override selected() to return a dynamic value, but
|
||||||
|
// we're only interested in if the pseudoclass is set or not, so we
|
||||||
|
// use Component::selected() directly rather than selected().
|
||||||
|
if (Component::selected() == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mRoot->SetPseudoClass("selected", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::set_disabled(bool value) {
|
||||||
|
if (Component::disabled() == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
mRoot->SetAttribute("disabled", "");
|
||||||
|
mRoot->SetPseudoClass("disabled", true);
|
||||||
|
mRoot->Blur();
|
||||||
|
} else {
|
||||||
|
mRoot->RemoveAttribute("disabled");
|
||||||
|
mRoot->SetPseudoClass("disabled", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Element* Component::append(Rml::Element* parent, const Rml::String& tag) {
|
||||||
|
if (parent == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto* doc = parent->GetOwnerDocument();
|
||||||
|
if (doc == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return parent->AppendChild(doc->CreateElement(tag));
|
||||||
|
}
|
||||||
|
void Component::listen(Rml::Element* element, Rml::EventId event,
|
||||||
|
ScopedEventListener::Callback callback, bool capture) {
|
||||||
|
if (element == nullptr) {
|
||||||
|
element = mRoot;
|
||||||
|
}
|
||||||
|
mListeners.emplace_back(
|
||||||
|
std::make_unique<ScopedEventListener>(element, event, std::move(callback), capture));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Component::contains(Rml::Element* element) const {
|
||||||
|
for (const auto* node = element; node != nullptr; node = node->GetParentNode()) {
|
||||||
|
if (node == mRoot) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::clear_children() {
|
||||||
|
mChildren.clear();
|
||||||
|
while (mRoot->GetNumChildren() > 0) {
|
||||||
|
mRoot->RemoveChild(mRoot->GetFirstChild());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "event.hpp"
|
||||||
|
#include "ui.hpp"
|
||||||
|
|
||||||
|
#include <RmlUi/Core.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Rml {
|
||||||
|
class Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
class Component {
|
||||||
|
public:
|
||||||
|
Component() = default;
|
||||||
|
explicit Component(Rml::Element* root);
|
||||||
|
virtual ~Component();
|
||||||
|
|
||||||
|
Component(const Component&) = delete;
|
||||||
|
Component& operator=(const Component&) = delete;
|
||||||
|
|
||||||
|
virtual void update();
|
||||||
|
virtual bool focus();
|
||||||
|
|
||||||
|
virtual bool selected() const { return mRoot->IsPseudoClassSet("selected"); }
|
||||||
|
virtual void set_selected(bool selected);
|
||||||
|
virtual bool disabled() const { return mRoot->IsPseudoClassSet("disabled"); }
|
||||||
|
virtual void set_disabled(bool disabled);
|
||||||
|
|
||||||
|
void listen(Rml::Element* element, Rml::EventId event, ScopedEventListener::Callback callback,
|
||||||
|
bool capture = false);
|
||||||
|
bool contains(Rml::Element* element) const;
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
requires std::is_base_of_v<Component, T> T& add_child(Args&&... args) {
|
||||||
|
auto child = std::make_unique<T>(mRoot, std::forward<Args>(args)...);
|
||||||
|
T& ref = *child;
|
||||||
|
mChildren.emplace_back(std::move(child));
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Element* root() const { return mRoot; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static Rml::Element* append(Rml::Element* parent, const Rml::String& tag);
|
||||||
|
void clear_children();
|
||||||
|
|
||||||
|
Rml::Element* mRoot = nullptr;
|
||||||
|
std::vector<std::unique_ptr<Component> > mChildren;
|
||||||
|
std::vector<std::unique_ptr<ScopedEventListener> > mListeners;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Derived>
|
||||||
|
class FluentComponent : public Component {
|
||||||
|
public:
|
||||||
|
using Component::Component;
|
||||||
|
|
||||||
|
Derived& listen(
|
||||||
|
Rml::EventId event, ScopedEventListener::Callback callback, bool capture = false) {
|
||||||
|
Component::listen(mRoot, event, std::move(callback), capture);
|
||||||
|
return static_cast<Derived&>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Derived& on_focus(ScopedEventListener::Callback callback) {
|
||||||
|
return listen(
|
||||||
|
Rml::EventId::Focus, [this, callback = std::move(callback)](Rml::Event& event) {
|
||||||
|
if (!disabled()) {
|
||||||
|
callback(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Derived& on_nav_command(std::function<bool(Rml::Event&, NavCommand)> callback) {
|
||||||
|
listen(Rml::EventId::Click, [this, callback](Rml::Event& event) {
|
||||||
|
if (!disabled() && callback(event, NavCommand::Confirm)) {
|
||||||
|
event.StopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
listen(Rml::EventId::Keydown, [this, callback = std::move(callback)](Rml::Event& event) {
|
||||||
|
if (disabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto cmd = map_nav_event(event);
|
||||||
|
if (cmd != NavCommand::None && callback(event, cmd)) {
|
||||||
|
event.StopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return static_cast<Derived&>(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#include "document.hpp"
|
||||||
|
|
||||||
|
#include "aurora/rmlui.hpp"
|
||||||
|
#include "ui.hpp"
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Rml::ElementDocument* load_document(const Rml::String& source) {
|
||||||
|
auto* context = aurora::rmlui::get_context();
|
||||||
|
if (context == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return context->LoadDocumentFromMemory(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Document::Document(const Rml::String& source) : mDocument(load_document(source)) {
|
||||||
|
// Block events while hidden (except for Menu command)
|
||||||
|
listen(
|
||||||
|
Rml::EventId::Keydown,
|
||||||
|
[this](Rml::Event& event) {
|
||||||
|
const auto cmd = map_nav_event(event);
|
||||||
|
if (cmd != NavCommand::Menu && !visible()) {
|
||||||
|
event.StopImmediatePropagation();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
const auto blockUnlessVisible = [this](Rml::Event& event) {
|
||||||
|
if (!visible()) {
|
||||||
|
event.StopImmediatePropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
listen(Rml::EventId::Mouseover, blockUnlessVisible, true);
|
||||||
|
listen(Rml::EventId::Click, blockUnlessVisible, true);
|
||||||
|
listen(Rml::EventId::Scroll, blockUnlessVisible, true);
|
||||||
|
|
||||||
|
listen(Rml::EventId::Keydown, [this](Rml::Event& event) {
|
||||||
|
const auto cmd = map_nav_event(event);
|
||||||
|
if (cmd != NavCommand::None && handle_nav_command(event, cmd)) {
|
||||||
|
event.StopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Document::~Document() {
|
||||||
|
mListeners.clear();
|
||||||
|
if (mDocument != nullptr) {
|
||||||
|
mDocument->Close();
|
||||||
|
mDocument = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::show() {
|
||||||
|
if (mDocument != nullptr) {
|
||||||
|
// Attempt to preserve the previously focused element
|
||||||
|
mDocument->Show(Rml::ModalFlag::None, Rml::FocusFlag::Keep, Rml::ScrollFlag::None);
|
||||||
|
// If nothing is focused, let the document decide the initial focus
|
||||||
|
auto* leaf = mDocument->GetFocusLeafNode();
|
||||||
|
if (leaf == nullptr || leaf == mDocument) {
|
||||||
|
focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::hide(bool close) {
|
||||||
|
if (mDocument != nullptr) {
|
||||||
|
mDocument->Hide();
|
||||||
|
}
|
||||||
|
if (close) {
|
||||||
|
mClosed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::update() {}
|
||||||
|
|
||||||
|
bool Document::focus() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::listen(Rml::Element* element, Rml::EventId event,
|
||||||
|
ScopedEventListener::Callback callback, bool capture) {
|
||||||
|
if (element == nullptr) {
|
||||||
|
element = mDocument;
|
||||||
|
}
|
||||||
|
if (element == nullptr || !callback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mListeners.emplace_back(
|
||||||
|
std::make_unique<ScopedEventListener>(element, event, std::move(callback), capture));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Document::visible() const {
|
||||||
|
if (mDocument == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return *mDocument->GetProperty(Rml::PropertyId::Visibility) == Rml::Style::Visibility::Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Document::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||||
|
if (cmd == NavCommand::Menu) {
|
||||||
|
toggle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "component.hpp"
|
||||||
|
#include "ui.hpp"
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
class Document {
|
||||||
|
public:
|
||||||
|
Document(const Rml::String& source);
|
||||||
|
virtual ~Document();
|
||||||
|
|
||||||
|
Document(const Document&) = delete;
|
||||||
|
Document& operator=(const Document&) = delete;
|
||||||
|
|
||||||
|
virtual void show();
|
||||||
|
virtual void hide(bool close);
|
||||||
|
virtual void update();
|
||||||
|
virtual bool focus();
|
||||||
|
virtual bool visible() const;
|
||||||
|
|
||||||
|
void listen(Rml::Element* element, Rml::EventId event, ScopedEventListener::Callback callback,
|
||||||
|
bool capture = false);
|
||||||
|
void listen(Rml::EventId event, ScopedEventListener::Callback callback, bool capture = false) {
|
||||||
|
listen(mDocument, event, std::move(callback), capture);
|
||||||
|
}
|
||||||
|
void toggle() {
|
||||||
|
if (visible()) {
|
||||||
|
hide(false);
|
||||||
|
} else {
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void pop() {
|
||||||
|
hide(true);
|
||||||
|
show_top_document();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pending_close() const { return mPendingClose; }
|
||||||
|
bool closed() const { return mClosed; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool handle_nav_command(Rml::Event& event, NavCommand cmd);
|
||||||
|
|
||||||
|
Rml::ElementDocument* mDocument;
|
||||||
|
std::vector<std::unique_ptr<ScopedEventListener> > mListeners;
|
||||||
|
bool mPendingClose = false;
|
||||||
|
bool mClosed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "window.hpp"
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
class EditorWindow : public Window {
|
||||||
|
public:
|
||||||
|
EditorWindow();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#include "event.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
ScopedEventListener::ScopedEventListener(
|
||||||
|
Rml::Element* element, Rml::EventId event, Callback callback, bool capture)
|
||||||
|
: mElement(element), mEvent(event), mCapture(capture), mCallback(std::move(callback)) {
|
||||||
|
mElement->AddEventListener(mEvent, this, mCapture);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedEventListener::~ScopedEventListener() {
|
||||||
|
if (mElement != nullptr) {
|
||||||
|
mElement->RemoveEventListener(mEvent, this, mCapture);
|
||||||
|
mElement = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopedEventListener::ProcessEvent(Rml::Event& event) {
|
||||||
|
if (mCallback) {
|
||||||
|
mCallback(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopedEventListener::OnDetach(Rml::Element* element) {
|
||||||
|
if (element == mElement) {
|
||||||
|
mElement = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <RmlUi/Core.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
class ScopedEventListener final : public Rml::EventListener {
|
||||||
|
public:
|
||||||
|
using Callback = std::function<void(Rml::Event&)>;
|
||||||
|
|
||||||
|
ScopedEventListener(
|
||||||
|
Rml::Element* element, Rml::EventId event, Callback callback, bool capture = false);
|
||||||
|
~ScopedEventListener() override;
|
||||||
|
|
||||||
|
ScopedEventListener(const ScopedEventListener&) = delete;
|
||||||
|
ScopedEventListener& operator=(const ScopedEventListener&) = delete;
|
||||||
|
ScopedEventListener(ScopedEventListener&&) = delete;
|
||||||
|
ScopedEventListener& operator=(ScopedEventListener&&) = delete;
|
||||||
|
|
||||||
|
void ProcessEvent(Rml::Event& event) override;
|
||||||
|
void OnDetach(Rml::Element* element) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Rml::Element* mElement = nullptr;
|
||||||
|
Rml::EventId mEvent = Rml::EventId::Invalid;
|
||||||
|
bool mCapture = false;
|
||||||
|
Callback mCallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,610 @@
|
|||||||
|
#include "input.hpp"
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
|
||||||
|
#include <RmlUi/Core.h>
|
||||||
|
#include <SDL3/SDL_gamepad.h>
|
||||||
|
#include <SDL3/SDL_timer.h>
|
||||||
|
#include <aurora/rmlui.hpp>
|
||||||
|
#include <dolphin/pad.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr double kGamepadRepeatInitialDelay = 0.32;
|
||||||
|
constexpr double kGamepadRepeatStartInterval = 0.12;
|
||||||
|
constexpr double kGamepadRepeatMinInterval = 0.045;
|
||||||
|
constexpr double kGamepadRepeatRampDuration = 1.0;
|
||||||
|
constexpr double kGamepadMenuChordGraceDuration = 0.12;
|
||||||
|
constexpr Sint16 kGamepadAxisPressThreshold = 16384;
|
||||||
|
constexpr Sint16 kGamepadAxisReleaseThreshold = 12000;
|
||||||
|
constexpr int kGamepadAxisDirectionCount = SDL_GAMEPAD_AXIS_COUNT * 2;
|
||||||
|
|
||||||
|
struct GamepadRepeatState {
|
||||||
|
Rml::Input::KeyIdentifier key = Rml::Input::KI_UNKNOWN;
|
||||||
|
double pressedAt = 0.0;
|
||||||
|
double nextRepeatAt = 0.0;
|
||||||
|
bool held = false;
|
||||||
|
bool repeatable = false;
|
||||||
|
bool pending = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool sPadInputBlocked = false;
|
||||||
|
std::array<GamepadRepeatState, SDL_GAMEPAD_BUTTON_COUNT> sGamepadButtonRepeats;
|
||||||
|
std::array<GamepadRepeatState, kGamepadAxisDirectionCount> sGamepadAxisRepeats;
|
||||||
|
std::array<u32, PAD_MAX_CONTROLLERS> sPadHoldMasks;
|
||||||
|
std::array<bool, PAD_MAX_CONTROLLERS> sMenuChordConsumed;
|
||||||
|
|
||||||
|
double now_seconds() noexcept {
|
||||||
|
return static_cast<double>(SDL_GetTicksNS()) / 1000000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_menu_chord_part(PADButton button) noexcept {
|
||||||
|
return button == PAD_TRIGGER_R || button == PAD_BUTTON_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_menu_chord_part_held(u32 port) noexcept {
|
||||||
|
if (port >= sPadHoldMasks.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 held = sPadHoldMasks[port];
|
||||||
|
return (held & (PAD_TRIGGER_R | PAD_BUTTON_START)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_block_pad_for_menu_chord() noexcept {
|
||||||
|
for (u32 port = 0; port < sPadHoldMasks.size(); ++port) {
|
||||||
|
if (sMenuChordConsumed[port] && has_menu_chord_part_held(port)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PADButton pad_button_from_axis(PADAxis axis) noexcept {
|
||||||
|
switch (axis) {
|
||||||
|
case PAD_AXIS_TRIGGER_R:
|
||||||
|
return PAD_TRIGGER_R;
|
||||||
|
case PAD_AXIS_TRIGGER_L:
|
||||||
|
return PAD_TRIGGER_L;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_pad_button_held(u32 port, PADButton button, bool held) noexcept {
|
||||||
|
if (port >= sPadHoldMasks.size() || button == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (held) {
|
||||||
|
sPadHoldMasks[port] |= button;
|
||||||
|
} else {
|
||||||
|
sPadHoldMasks[port] &= ~button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_menu_chord(u32 port) noexcept {
|
||||||
|
if (port >= sPadHoldMasks.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 held = sPadHoldMasks[port];
|
||||||
|
return (held & PAD_TRIGGER_R) != 0 && (held & PAD_BUTTON_START) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool any_menu_chord() noexcept {
|
||||||
|
return std::any_of(sPadHoldMasks.begin(), sPadHoldMasks.end(),
|
||||||
|
[](u32 held) { return (held & PAD_TRIGGER_R) != 0 && (held & PAD_BUTTON_START) != 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Input::KeyIdentifier map_pad_button(PADButton button) noexcept {
|
||||||
|
switch (button) {
|
||||||
|
case PAD_BUTTON_UP:
|
||||||
|
return Rml::Input::KI_UP;
|
||||||
|
case PAD_BUTTON_DOWN:
|
||||||
|
return Rml::Input::KI_DOWN;
|
||||||
|
case PAD_BUTTON_LEFT:
|
||||||
|
return Rml::Input::KI_LEFT;
|
||||||
|
case PAD_BUTTON_RIGHT:
|
||||||
|
return Rml::Input::KI_RIGHT;
|
||||||
|
case PAD_BUTTON_B:
|
||||||
|
return Rml::Input::KI_ESCAPE;
|
||||||
|
case PAD_BUTTON_A:
|
||||||
|
return Rml::Input::KI_RETURN;
|
||||||
|
case PAD_TRIGGER_R:
|
||||||
|
return Rml::Input::KI_NEXT;
|
||||||
|
case PAD_TRIGGER_L:
|
||||||
|
return Rml::Input::KI_PRIOR;
|
||||||
|
default:
|
||||||
|
return Rml::Input::KI_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Input::KeyIdentifier map_pad_axis(PADAxis axis) noexcept {
|
||||||
|
switch (axis) {
|
||||||
|
case PAD_AXIS_LEFT_X_POS:
|
||||||
|
return Rml::Input::KI_RIGHT;
|
||||||
|
case PAD_AXIS_LEFT_X_NEG:
|
||||||
|
return Rml::Input::KI_LEFT;
|
||||||
|
case PAD_AXIS_LEFT_Y_POS:
|
||||||
|
return Rml::Input::KI_UP;
|
||||||
|
case PAD_AXIS_LEFT_Y_NEG:
|
||||||
|
return Rml::Input::KI_DOWN;
|
||||||
|
case PAD_AXIS_TRIGGER_R:
|
||||||
|
return Rml::Input::KI_NEXT;
|
||||||
|
case PAD_AXIS_TRIGGER_L:
|
||||||
|
return Rml::Input::KI_PRIOR;
|
||||||
|
default:
|
||||||
|
return Rml::Input::KI_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Input::KeyIdentifier map_raw_gamepad_button(SDL_GamepadButton button) noexcept {
|
||||||
|
switch (button) {
|
||||||
|
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||||
|
return Rml::Input::KI_UP;
|
||||||
|
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||||
|
return Rml::Input::KI_DOWN;
|
||||||
|
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||||
|
return Rml::Input::KI_LEFT;
|
||||||
|
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||||
|
return Rml::Input::KI_RIGHT;
|
||||||
|
case SDL_GAMEPAD_BUTTON_EAST:
|
||||||
|
return Rml::Input::KI_ESCAPE;
|
||||||
|
case SDL_GAMEPAD_BUTTON_SOUTH:
|
||||||
|
return Rml::Input::KI_RETURN;
|
||||||
|
case SDL_GAMEPAD_BUTTON_BACK:
|
||||||
|
return Rml::Input::KI_F1;
|
||||||
|
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||||
|
return Rml::Input::KI_NEXT;
|
||||||
|
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||||
|
return Rml::Input::KI_PRIOR;
|
||||||
|
default:
|
||||||
|
return Rml::Input::KI_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Input::KeyIdentifier map_raw_button_alias(SDL_GamepadButton button) noexcept {
|
||||||
|
switch (button) {
|
||||||
|
case SDL_GAMEPAD_BUTTON_BACK:
|
||||||
|
return Rml::Input::KI_F1;
|
||||||
|
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||||
|
return Rml::Input::KI_NEXT;
|
||||||
|
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||||
|
return Rml::Input::KI_PRIOR;
|
||||||
|
default:
|
||||||
|
return Rml::Input::KI_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Input::KeyIdentifier map_raw_gamepad_axis(SDL_GamepadAxis axis, PADAxisSign sign) noexcept {
|
||||||
|
switch (axis) {
|
||||||
|
case SDL_GAMEPAD_AXIS_LEFTX:
|
||||||
|
return sign == AXIS_SIGN_POSITIVE ? Rml::Input::KI_RIGHT : Rml::Input::KI_LEFT;
|
||||||
|
case SDL_GAMEPAD_AXIS_LEFTY:
|
||||||
|
return sign == AXIS_SIGN_NEGATIVE ? Rml::Input::KI_UP : Rml::Input::KI_DOWN;
|
||||||
|
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER:
|
||||||
|
return sign == AXIS_SIGN_POSITIVE ? Rml::Input::KI_NEXT : Rml::Input::KI_UNKNOWN;
|
||||||
|
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER:
|
||||||
|
return sign == AXIS_SIGN_POSITIVE ? Rml::Input::KI_PRIOR : Rml::Input::KI_UNKNOWN;
|
||||||
|
default:
|
||||||
|
return Rml::Input::KI_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_event_port(SDL_JoystickID instance, u32& port) noexcept {
|
||||||
|
for (u32 candidate = 0; candidate < PAD_MAX_CONTROLLERS; ++candidate) {
|
||||||
|
const s32 index = PADGetIndexForPort(candidate);
|
||||||
|
if (index < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Gamepad* gamepad = PADGetSDLGamepadForIndex(static_cast<u32>(index));
|
||||||
|
if (gamepad == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Joystick* joystick = SDL_GetGamepadJoystick(gamepad);
|
||||||
|
if (joystick != nullptr && SDL_GetJoystickID(joystick) == instance) {
|
||||||
|
port = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_mapped_pad_button(u32 port, SDL_GamepadButton nativeButton, PADButton& button) noexcept {
|
||||||
|
u32 buttonCount = 0;
|
||||||
|
PADButtonMapping* buttons = PADGetButtonMappings(port, &buttonCount);
|
||||||
|
if (buttons != nullptr) {
|
||||||
|
for (u32 i = 0; i < buttonCount; ++i) {
|
||||||
|
if (buttons[i].nativeButton == static_cast<u32>(nativeButton)) {
|
||||||
|
button = buttons[i].padButton;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 axisCount = 0;
|
||||||
|
PADAxisMapping* axes = PADGetAxisMappings(port, &axisCount);
|
||||||
|
if (axes != nullptr) {
|
||||||
|
for (u32 i = 0; i < axisCount; ++i) {
|
||||||
|
if (axes[i].nativeButton == nativeButton) {
|
||||||
|
button = pad_button_from_axis(axes[i].padAxis);
|
||||||
|
return button != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_mapped_pad_axis(
|
||||||
|
u32 port, SDL_GamepadAxis nativeAxis, PADAxisSign sign, PADAxis& axis) noexcept {
|
||||||
|
u32 buttonCount = 0;
|
||||||
|
PADGetButtonMappings(port, &buttonCount);
|
||||||
|
|
||||||
|
u32 axisCount = 0;
|
||||||
|
PADAxisMapping* axes = PADGetAxisMappings(port, &axisCount);
|
||||||
|
if (axes == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; i < axisCount; ++i) {
|
||||||
|
const PADSignedNativeAxis mappedAxis = axes[i].nativeAxis;
|
||||||
|
if (mappedAxis.nativeAxis == nativeAxis && mappedAxis.sign == sign) {
|
||||||
|
axis = axes[i].padAxis;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_event_pad_button(
|
||||||
|
const SDL_GamepadButtonEvent& event, u32& port, PADButton& button) noexcept {
|
||||||
|
return find_event_port(event.which, port) &&
|
||||||
|
find_mapped_pad_button(port, static_cast<SDL_GamepadButton>(event.button), button);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Input::KeyIdentifier map_gamepad_button(const SDL_GamepadButtonEvent& event) noexcept {
|
||||||
|
const auto nativeButton = static_cast<SDL_GamepadButton>(event.button);
|
||||||
|
if (nativeButton == SDL_GAMEPAD_BUTTON_BACK) {
|
||||||
|
return Rml::Input::KI_F1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 port = 0;
|
||||||
|
if (!find_event_port(event.which, port)) {
|
||||||
|
return map_raw_gamepad_button(nativeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
PADButton button = 0;
|
||||||
|
if (find_mapped_pad_button(port, nativeButton, button)) {
|
||||||
|
const auto key = map_pad_button(button);
|
||||||
|
return key == Rml::Input::KI_UNKNOWN ? map_raw_button_alias(nativeButton) : key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map_raw_button_alias(nativeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Input::KeyIdentifier map_gamepad_axis(
|
||||||
|
const SDL_GamepadAxisEvent& event, PADAxisSign sign) noexcept {
|
||||||
|
u32 port = 0;
|
||||||
|
if (!find_event_port(event.which, port)) {
|
||||||
|
return map_raw_gamepad_axis(static_cast<SDL_GamepadAxis>(event.axis), sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
PADAxis axis = 0;
|
||||||
|
if (find_mapped_pad_axis(port, static_cast<SDL_GamepadAxis>(event.axis), sign, axis)) {
|
||||||
|
return map_pad_axis(axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Rml::Input::KI_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_repeatable_key(Rml::Input::KeyIdentifier key) noexcept {
|
||||||
|
switch (key) {
|
||||||
|
case Rml::Input::KI_UP:
|
||||||
|
case Rml::Input::KI_DOWN:
|
||||||
|
case Rml::Input::KI_LEFT:
|
||||||
|
case Rml::Input::KI_RIGHT:
|
||||||
|
case Rml::Input::KI_NEXT:
|
||||||
|
case Rml::Input::KI_PRIOR:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double repeat_interval(double heldFor) noexcept {
|
||||||
|
const double ramp = std::clamp(heldFor / kGamepadRepeatRampDuration, 0.0, 1.0);
|
||||||
|
return kGamepadRepeatStartInterval +
|
||||||
|
(kGamepadRepeatMinInterval - kGamepadRepeatStartInterval) * ramp;
|
||||||
|
}
|
||||||
|
|
||||||
|
GamepadRepeatState* button_repeat_state(SDL_GamepadButton button) noexcept {
|
||||||
|
const auto index = static_cast<int>(button);
|
||||||
|
if (index < 0 || index >= static_cast<int>(sGamepadButtonRepeats.size())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &sGamepadButtonRepeats[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
GamepadRepeatState* axis_repeat_state(SDL_GamepadAxis axis, PADAxisSign sign) noexcept {
|
||||||
|
const auto axisIndex = static_cast<int>(axis);
|
||||||
|
if (axisIndex < 0 || axisIndex >= SDL_GAMEPAD_AXIS_COUNT) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int directionOffset = sign == AXIS_SIGN_POSITIVE ? 0 : 1;
|
||||||
|
return &sGamepadAxisRepeats[axisIndex * 2 + directionOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_gamepad_repeats() noexcept {
|
||||||
|
for (auto& repeat : sGamepadButtonRepeats) {
|
||||||
|
repeat = {};
|
||||||
|
}
|
||||||
|
for (auto& repeat : sGamepadAxisRepeats) {
|
||||||
|
repeat = {};
|
||||||
|
}
|
||||||
|
sPadHoldMasks.fill(0);
|
||||||
|
sMenuChordConsumed.fill(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_gamepad_key(GamepadRepeatState& repeat, Rml::Input::KeyIdentifier key) noexcept {
|
||||||
|
if (repeat.held) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double now = now_seconds();
|
||||||
|
repeat.key = key;
|
||||||
|
repeat.pressedAt = now;
|
||||||
|
repeat.held = true;
|
||||||
|
repeat.repeatable = is_repeatable_key(key);
|
||||||
|
repeat.nextRepeatAt = repeat.repeatable ? now + kGamepadRepeatInitialDelay : 0.0;
|
||||||
|
repeat.pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_pending_gamepad_key(GamepadRepeatState& repeat, Rml::Input::KeyIdentifier key) noexcept {
|
||||||
|
if (repeat.held) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double now = now_seconds();
|
||||||
|
repeat.key = key;
|
||||||
|
repeat.pressedAt = now;
|
||||||
|
repeat.held = true;
|
||||||
|
repeat.repeatable = is_repeatable_key(key);
|
||||||
|
repeat.nextRepeatAt = 0.0;
|
||||||
|
repeat.pending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void consume_menu_chord(u32 port, Rml::Context& context) noexcept {
|
||||||
|
if (port < sMenuChordConsumed.size()) {
|
||||||
|
sMenuChordConsumed[port] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cancel_next = [&context](GamepadRepeatState& repeat) {
|
||||||
|
if (!repeat.held || repeat.key != Rml::Input::KI_NEXT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!repeat.pending) {
|
||||||
|
context.ProcessKeyUp(repeat.key, 0);
|
||||||
|
}
|
||||||
|
repeat = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& repeat : sGamepadButtonRepeats) {
|
||||||
|
cancel_next(repeat);
|
||||||
|
}
|
||||||
|
for (auto& repeat : sGamepadAxisRepeats) {
|
||||||
|
cancel_next(repeat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_menu_chord_release(u32 port) noexcept {
|
||||||
|
if (port >= sMenuChordConsumed.size() || has_menu_chord_part_held(port)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sMenuChordConsumed[port] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_defer_menu_chord_part(PADButton button, Rml::Input::KeyIdentifier key) noexcept {
|
||||||
|
return button == PAD_TRIGGER_R && key == Rml::Input::KI_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_axis_direction(
|
||||||
|
Rml::Context& context, const SDL_GamepadAxisEvent& event, PADAxisSign sign) noexcept {
|
||||||
|
GamepadRepeatState* repeat = axis_repeat_state(static_cast<SDL_GamepadAxis>(event.axis), sign);
|
||||||
|
if (repeat == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool active = sign == AXIS_SIGN_POSITIVE ? event.value >= kGamepadAxisPressThreshold :
|
||||||
|
event.value <= -kGamepadAxisPressThreshold;
|
||||||
|
const bool released = sign == AXIS_SIGN_POSITIVE ? event.value <= kGamepadAxisReleaseThreshold :
|
||||||
|
event.value >= -kGamepadAxisReleaseThreshold;
|
||||||
|
|
||||||
|
u32 port = 0;
|
||||||
|
PADAxis padAxis = 0;
|
||||||
|
const bool hasPadAxis =
|
||||||
|
find_event_port(event.which, port) &&
|
||||||
|
find_mapped_pad_axis(port, static_cast<SDL_GamepadAxis>(event.axis), sign, padAxis);
|
||||||
|
const PADButton heldPadButton = hasPadAxis ? pad_button_from_axis(padAxis) : 0;
|
||||||
|
|
||||||
|
if (repeat->held) {
|
||||||
|
if (released) {
|
||||||
|
if (!repeat->pending) {
|
||||||
|
context.ProcessKeyUp(repeat->key, 0);
|
||||||
|
}
|
||||||
|
set_pad_button_held(port, heldPadButton, false);
|
||||||
|
*repeat = {};
|
||||||
|
update_menu_chord_release(port);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_pad_button_held(port, heldPadButton, true);
|
||||||
|
const bool chorded = heldPadButton == PAD_TRIGGER_R && is_menu_chord(port);
|
||||||
|
if (chorded) {
|
||||||
|
consume_menu_chord(port, context);
|
||||||
|
}
|
||||||
|
const auto key = chorded ? Rml::Input::KI_F1 : map_gamepad_axis(event, sign);
|
||||||
|
if (key == Rml::Input::KI_UNKNOWN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chorded && should_defer_menu_chord_part(heldPadButton, key)) {
|
||||||
|
begin_pending_gamepad_key(*repeat, key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
begin_gamepad_key(*repeat, key);
|
||||||
|
context.ProcessMouseLeave();
|
||||||
|
context.ProcessKeyDown(key, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void sync_input_block() noexcept {
|
||||||
|
const bool shouldBlock = any_document_visible() || should_block_pad_for_menu_chord();
|
||||||
|
if (sPadInputBlocked == shouldBlock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PADBlockInput(shouldBlock);
|
||||||
|
sPadInputBlocked = shouldBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void release_input_block() noexcept {
|
||||||
|
if (!sPadInputBlocked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PADBlockInput(false);
|
||||||
|
sPadInputBlocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_input_state() noexcept {
|
||||||
|
clear_gamepad_repeats();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_event(const SDL_Event& event) noexcept {
|
||||||
|
if (event.type == SDL_EVENT_GAMEPAD_REMOVED || event.type == SDL_EVENT_WINDOW_FOCUS_LOST) {
|
||||||
|
reset_input_state();
|
||||||
|
sync_input_block();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.type != SDL_EVENT_GAMEPAD_BUTTON_DOWN && event.type != SDL_EVENT_GAMEPAD_BUTTON_UP &&
|
||||||
|
event.type != SDL_EVENT_GAMEPAD_AXIS_MOTION)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* context = aurora::rmlui::get_context();
|
||||||
|
if (context == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
|
||||||
|
process_axis_direction(*context, event.gaxis, AXIS_SIGN_POSITIVE);
|
||||||
|
process_axis_direction(*context, event.gaxis, AXIS_SIGN_NEGATIVE);
|
||||||
|
sync_input_block();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* repeat = button_repeat_state(static_cast<SDL_GamepadButton>(event.gbutton.button));
|
||||||
|
u32 port = 0;
|
||||||
|
PADButton button = 0;
|
||||||
|
const bool hasPadButton = find_event_pad_button(event.gbutton, port, button);
|
||||||
|
if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
|
||||||
|
set_pad_button_held(port, button, true);
|
||||||
|
const bool chorded = hasPadButton && is_menu_chord_part(button) && is_menu_chord(port);
|
||||||
|
if (chorded) {
|
||||||
|
consume_menu_chord(port, *context);
|
||||||
|
}
|
||||||
|
const auto key = chorded ? Rml::Input::KI_F1 : map_gamepad_button(event.gbutton);
|
||||||
|
if (key != Rml::Input::KI_UNKNOWN) {
|
||||||
|
bool deferred = false;
|
||||||
|
if (repeat != nullptr) {
|
||||||
|
if (!chorded && should_defer_menu_chord_part(button, key)) {
|
||||||
|
begin_pending_gamepad_key(*repeat, key);
|
||||||
|
deferred = true;
|
||||||
|
} else {
|
||||||
|
begin_gamepad_key(*repeat, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!deferred) {
|
||||||
|
context->ProcessMouseLeave();
|
||||||
|
context->ProcessKeyDown(key, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto key = repeat != nullptr && repeat->held ? repeat->key : Rml::Input::KI_UNKNOWN;
|
||||||
|
const bool wasPending = repeat != nullptr && repeat->pending;
|
||||||
|
set_pad_button_held(port, button, false);
|
||||||
|
update_menu_chord_release(port);
|
||||||
|
if (key != Rml::Input::KI_UNKNOWN) {
|
||||||
|
if (repeat != nullptr) {
|
||||||
|
*repeat = {};
|
||||||
|
}
|
||||||
|
if (!wasPending) {
|
||||||
|
context->ProcessKeyUp(key, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync_input_block();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_input() noexcept {
|
||||||
|
auto* context = aurora::rmlui::get_context();
|
||||||
|
if (context != nullptr) {
|
||||||
|
const double now = now_seconds();
|
||||||
|
auto process_repeats = [context, now](auto& repeats) {
|
||||||
|
for (auto& repeat : repeats) {
|
||||||
|
if (!repeat.held) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeat.pending) {
|
||||||
|
if (now < repeat.pressedAt + kGamepadMenuChordGraceDuration) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat.pending = false;
|
||||||
|
repeat.pressedAt = now;
|
||||||
|
repeat.nextRepeatAt =
|
||||||
|
repeat.repeatable ? now + kGamepadRepeatInitialDelay : 0.0;
|
||||||
|
context->ProcessMouseLeave();
|
||||||
|
context->ProcessKeyDown(repeat.key, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!repeat.repeatable || now < repeat.nextRepeatAt ||
|
||||||
|
(repeat.key == Rml::Input::KI_NEXT && any_menu_chord()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->ProcessKeyDown(repeat.key, 0);
|
||||||
|
const double heldFor = now - repeat.pressedAt;
|
||||||
|
repeat.nextRepeatAt = now + repeat_interval(heldFor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
process_repeats(sGamepadButtonRepeats);
|
||||||
|
process_repeats(sGamepadAxisRepeats);
|
||||||
|
} else {
|
||||||
|
reset_input_state();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
void update_input() noexcept;
|
||||||
|
void reset_input_state() noexcept;
|
||||||
|
void sync_input_block() noexcept;
|
||||||
|
void release_input_block() noexcept;
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
enum class NavCommand {
|
||||||
|
None,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Next, // R1
|
||||||
|
Previous, // L1
|
||||||
|
Confirm, // A
|
||||||
|
Cancel, // B
|
||||||
|
Menu, // Back/Minus, or R + Start
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#include "number_button.hpp"
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
NumberButton::NumberButton(Rml::Element* parent, Props props)
|
||||||
|
: BaseStringButton(parent, {.key = std::move(props.key), .type = "number"}),
|
||||||
|
mGetValue(std::move(props.getValue)), mSetValue(std::move(props.setValue)),
|
||||||
|
mIsDisabled(std::move(props.isDisabled)), mMin(props.min), mMax(props.max), mStep(props.step),
|
||||||
|
mPrefix(std::move(props.prefix)), mSuffix(std::move(props.suffix)) {}
|
||||||
|
|
||||||
|
bool NumberButton::disabled() const {
|
||||||
|
if (mIsDisabled) {
|
||||||
|
return mIsDisabled();
|
||||||
|
}
|
||||||
|
return BaseStringButton::disabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::String NumberButton::format_value() {
|
||||||
|
return fmt::format("{}{}{}", mPrefix, mGetValue(), mSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::String NumberButton::input_value() {
|
||||||
|
return fmt::to_string(mGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NumberButton::set_value(Rml::String value) {
|
||||||
|
if (!mSetValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parsedValue = 0;
|
||||||
|
const char* begin = value.data();
|
||||||
|
const char* end = begin + value.size();
|
||||||
|
const auto result = std::from_chars(begin, end, parsedValue);
|
||||||
|
if (result.ec != std::errc() || result.ptr != end) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSetValue(std::clamp(parsedValue, mMin, mMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NumberButton::handle_nav_command(NavCommand cmd) {
|
||||||
|
if (cmd == NavCommand::Left) {
|
||||||
|
mSetValue(std::clamp(mGetValue() - mStep, mMin, mMax));
|
||||||
|
return true;
|
||||||
|
} else if (cmd == NavCommand::Right) {
|
||||||
|
mSetValue(std::clamp(mGetValue() + mStep, mMin, mMax));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return BaseStringButton::handle_nav_command(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "string_button.hpp"
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
class NumberButton : public BaseStringButton {
|
||||||
|
public:
|
||||||
|
struct Props {
|
||||||
|
Rml::String key;
|
||||||
|
std::function<int()> getValue;
|
||||||
|
std::function<void(int)> setValue;
|
||||||
|
std::function<bool()> isDisabled;
|
||||||
|
int min = 0;
|
||||||
|
int max = INT_MAX;
|
||||||
|
int step = 1;
|
||||||
|
Rml::String prefix;
|
||||||
|
Rml::String suffix;
|
||||||
|
};
|
||||||
|
|
||||||
|
NumberButton(Rml::Element* parent, Props props);
|
||||||
|
|
||||||
|
bool disabled() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Rml::String format_value() override;
|
||||||
|
Rml::String input_value() override;
|
||||||
|
void set_value(Rml::String value) override;
|
||||||
|
bool handle_nav_command(NavCommand cmd) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<int()> mGetValue;
|
||||||
|
std::function<void(int)> mSetValue;
|
||||||
|
std::function<bool()> mIsDisabled;
|
||||||
|
int mMin;
|
||||||
|
int mMax;
|
||||||
|
int mStep;
|
||||||
|
Rml::String mPrefix;
|
||||||
|
Rml::String mSuffix;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
#include "overlay.hpp"
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXAurora.h>
|
||||||
|
#include <dolphin/vi.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "dusk/config.hpp"
|
||||||
|
#include "dusk/settings.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const Rml::String kDocumentSource = R"RML(
|
||||||
|
<rml>
|
||||||
|
<head>
|
||||||
|
<link type="text/rcss" href="res/rml/overlay.rcss" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root" class="overlay-root">
|
||||||
|
<div class="overlay">
|
||||||
|
<div class="header">
|
||||||
|
<div id="title"></div>
|
||||||
|
<div id="carousel-container" class="carousel-container"></div>
|
||||||
|
</div>
|
||||||
|
<div id="description" class="description"></div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div id="footer" class="footer"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</rml>
|
||||||
|
)RML";
|
||||||
|
|
||||||
|
int get_value(GraphicsOption option) {
|
||||||
|
switch (option) {
|
||||||
|
case GraphicsOption::InternalResolution:
|
||||||
|
return getSettings().game.internalResolutionScale.getValue();
|
||||||
|
case GraphicsOption::ShadowResolution:
|
||||||
|
return getSettings().game.shadowResolutionMultiplier.getValue();
|
||||||
|
case GraphicsOption::BloomMode:
|
||||||
|
return static_cast<int>(getSettings().game.bloomMode.getValue());
|
||||||
|
case GraphicsOption::BloomMultiplier:
|
||||||
|
return std::clamp(
|
||||||
|
static_cast<int>(getSettings().game.bloomMultiplier.getValue() * 100.0f + 0.5f), 0,
|
||||||
|
100);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_value(GraphicsOption option, int value) {
|
||||||
|
switch (option) {
|
||||||
|
case GraphicsOption::InternalResolution:
|
||||||
|
getSettings().game.internalResolutionScale.setValue(value);
|
||||||
|
VISetFrameBufferScale(static_cast<float>(value));
|
||||||
|
break;
|
||||||
|
case GraphicsOption::ShadowResolution:
|
||||||
|
getSettings().game.shadowResolutionMultiplier.setValue(value);
|
||||||
|
break;
|
||||||
|
case GraphicsOption::BloomMode:
|
||||||
|
getSettings().game.bloomMode.setValue(static_cast<BloomMode>(std::clamp(
|
||||||
|
value, static_cast<int>(BloomMode::Off), static_cast<int>(BloomMode::Dusk))));
|
||||||
|
break;
|
||||||
|
case GraphicsOption::BloomMultiplier:
|
||||||
|
getSettings().game.bloomMultiplier.setValue(std::clamp(value, 0, 100) / 100.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
config::Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Element* create_stepped_carousel_root(Rml::Element* parent) {
|
||||||
|
auto* doc = parent->GetOwnerDocument();
|
||||||
|
auto root = doc->CreateElement("div");
|
||||||
|
root->SetClass("stepped-carousel", true);
|
||||||
|
root->SetAttribute("tabindex", "0");
|
||||||
|
return parent->AppendChild(std::move(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Element* create_stepped_carousel_arrow(
|
||||||
|
Rml::Element* parent, const Rml::String& className, const Rml::String& label) {
|
||||||
|
auto* doc = parent->GetOwnerDocument();
|
||||||
|
auto button = doc->CreateElement("button");
|
||||||
|
button->SetClass("stepped-carousel-arrow", true);
|
||||||
|
button->SetClass(className, true);
|
||||||
|
button->SetInnerRML(escape(label));
|
||||||
|
return parent->AppendChild(std::move(button));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SteppedCarousel::SteppedCarousel(Rml::Element* parent, Props props)
|
||||||
|
: Component(create_stepped_carousel_root(parent)), mProps(std::move(props)) {
|
||||||
|
Rml::Element* prevElem = create_stepped_carousel_arrow(mRoot, "prev", "<");
|
||||||
|
mValueElem = append(mRoot, "div");
|
||||||
|
mValueElem->SetClass("stepped-carousel-value", true);
|
||||||
|
Rml::Element* nextElem = create_stepped_carousel_arrow(mRoot, "next", ">");
|
||||||
|
|
||||||
|
listen(prevElem, Rml::EventId::Click,
|
||||||
|
[this](Rml::Event&) { handle_nav_command(NavCommand::Left); });
|
||||||
|
listen(nextElem, Rml::EventId::Click,
|
||||||
|
[this](Rml::Event&) { handle_nav_command(NavCommand::Right); });
|
||||||
|
listen(mRoot, Rml::EventId::Keydown, [this](Rml::Event& event) {
|
||||||
|
const auto cmd = map_nav_event(event);
|
||||||
|
if (cmd != NavCommand::None && handle_nav_command(cmd)) {
|
||||||
|
event.StopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SteppedCarousel::focus() {
|
||||||
|
return Component::focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SteppedCarousel::update() {
|
||||||
|
if (mValueElem == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int value = std::clamp(mProps.getValue ? mProps.getValue() : 0, mProps.min, mProps.max);
|
||||||
|
if (mProps.formatValue) {
|
||||||
|
mValueElem->SetInnerRML(mProps.formatValue(value));
|
||||||
|
} else {
|
||||||
|
mValueElem->SetInnerRML(std::to_string(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SteppedCarousel::handle_nav_command(NavCommand cmd) {
|
||||||
|
if (cmd == NavCommand::Left) {
|
||||||
|
const int value = mProps.getValue ? mProps.getValue() : 0;
|
||||||
|
apply(std::clamp(value - mProps.step, mProps.min, mProps.max));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (cmd == NavCommand::Right) {
|
||||||
|
const int value = mProps.getValue ? mProps.getValue() : 0;
|
||||||
|
apply(std::clamp(value + mProps.step, mProps.min, mProps.max));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SteppedCarousel::apply(int value) {
|
||||||
|
const int nextValue = std::clamp(value, mProps.min, mProps.max);
|
||||||
|
const int currentValue =
|
||||||
|
std::clamp(mProps.getValue ? mProps.getValue() : 0, mProps.min, mProps.max);
|
||||||
|
if (nextValue == currentValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mProps.onChange) {
|
||||||
|
mProps.onChange(nextValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::String format_graphics_setting_value(GraphicsOption option, int value) {
|
||||||
|
switch (option) {
|
||||||
|
case GraphicsOption::InternalResolution:
|
||||||
|
if (value <= 0) {
|
||||||
|
return "Auto";
|
||||||
|
} else {
|
||||||
|
u32 width = 0;
|
||||||
|
u32 height = 0;
|
||||||
|
AuroraGetRenderSize(&width, &height);
|
||||||
|
return fmt::format("{}x ({}x{})", value, width, height);
|
||||||
|
}
|
||||||
|
case GraphicsOption::ShadowResolution:
|
||||||
|
return fmt::format("{}x", value);
|
||||||
|
case GraphicsOption::BloomMode:
|
||||||
|
switch (static_cast<BloomMode>(value)) {
|
||||||
|
case BloomMode::Off:
|
||||||
|
return "Off";
|
||||||
|
case BloomMode::Classic:
|
||||||
|
return "Classic";
|
||||||
|
case BloomMode::Dusk:
|
||||||
|
return "Dusk";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GraphicsOption::BloomMultiplier:
|
||||||
|
return fmt::format("{}%", value);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Overlay::Overlay(OverlayProps props)
|
||||||
|
: Document(kDocumentSource), mOption(props.option), mValueMin(props.valueMin),
|
||||||
|
mValueMax(props.valueMax), mDefaultValue(props.defaultValue) {
|
||||||
|
if (mDocument == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* title = mDocument->GetElementById("title")) {
|
||||||
|
title->SetInnerRML(escape(props.title));
|
||||||
|
}
|
||||||
|
if (auto* description = mDocument->GetElementById("description")) {
|
||||||
|
description->SetInnerRML(escape(props.helpText));
|
||||||
|
}
|
||||||
|
if (auto* carouselParent = mDocument->GetElementById("carousel-container")) {
|
||||||
|
add_component<SteppedCarousel>(carouselParent,
|
||||||
|
SteppedCarousel::Props{
|
||||||
|
.min = mValueMin,
|
||||||
|
.max = mValueMax,
|
||||||
|
.step = 1,
|
||||||
|
.getValue = [this] { return get_value(mOption); },
|
||||||
|
.onChange = [this](int value) { set_value(mOption, value); },
|
||||||
|
.formatValue =
|
||||||
|
[this](int value) { return format_graphics_setting_value(mOption, value); },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* footer = mDocument->GetElementById("footer")) {
|
||||||
|
auto& returnButton = add_component<Button>(footer, "\xE2\x86\x90 Return", "footer-button")
|
||||||
|
.on_pressed([this] { pop(); });
|
||||||
|
returnButton.root()->SetClass("return", true);
|
||||||
|
auto& resetButton =
|
||||||
|
add_component<Button>(footer, "Reset to default", "footer-button").on_pressed([this] {
|
||||||
|
reset_default();
|
||||||
|
});
|
||||||
|
resetButton.root()->SetClass("reset", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide document after transition completion
|
||||||
|
mRoot = mDocument->GetElementById("root");
|
||||||
|
listen(mRoot, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||||
|
if (event.GetTargetElement() == mRoot && !mRoot->HasAttribute("open") &&
|
||||||
|
Document::visible())
|
||||||
|
{
|
||||||
|
Document::hide(mPendingClose);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::show() {
|
||||||
|
Document::show();
|
||||||
|
mRoot->SetAttribute("open", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::hide(bool close) {
|
||||||
|
mRoot->RemoveAttribute("open");
|
||||||
|
if (close) {
|
||||||
|
mPendingClose = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::update() {
|
||||||
|
for (const auto& component : mComponents) {
|
||||||
|
component->update();
|
||||||
|
}
|
||||||
|
Document::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Overlay::focus() {
|
||||||
|
for (const auto& component : mComponents) {
|
||||||
|
if (component->focus()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Overlay::visible() const {
|
||||||
|
return mRoot->HasAttribute("open");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Overlay::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||||
|
if (cmd == NavCommand::Cancel) {
|
||||||
|
pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Document::handle_nav_command(event, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::reset_default() {
|
||||||
|
set_value(mOption, mDefaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "button.hpp"
|
||||||
|
#include "component.hpp"
|
||||||
|
#include "document.hpp"
|
||||||
|
#include "ui.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace dusk::ui {
|
||||||
|
|
||||||
|
class SteppedCarousel : public Component {
|
||||||
|
public:
|
||||||
|
struct Props {
|
||||||
|
int min = 0;
|
||||||
|
int max = 0;
|
||||||
|
int step = 1;
|
||||||
|
std::function<int()> getValue;
|
||||||
|
std::function<void(int)> onChange;
|
||||||
|
std::function<Rml::String(int)> formatValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
SteppedCarousel(Rml::Element* parent, Props props);
|
||||||
|
|
||||||
|
bool focus() override;
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool handle_nav_command(NavCommand cmd);
|
||||||
|
void apply(int value);
|
||||||
|
|
||||||
|
Props mProps;
|
||||||
|
Rml::Element* mValueElem = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GraphicsOption {
|
||||||
|
InternalResolution,
|
||||||
|
ShadowResolution,
|
||||||
|
BloomMode,
|
||||||
|
BloomMultiplier,
|
||||||
|
};
|
||||||
|
|
||||||
|
Rml::String format_graphics_setting_value(GraphicsOption option, int value);
|
||||||
|
|
||||||
|
struct OverlayProps {
|
||||||
|
GraphicsOption option;
|
||||||
|
Rml::String title;
|
||||||
|
Rml::String helpText;
|
||||||
|
int valueMin = 0;
|
||||||
|
int valueMax = 0;
|
||||||
|
int defaultValue = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Overlay : public Document {
|
||||||
|
public:
|
||||||
|
explicit Overlay(OverlayProps props);
|
||||||
|
|
||||||
|
void show() override;
|
||||||
|
void hide(bool close) override;
|
||||||
|
void update() override;
|
||||||
|
bool focus() override;
|
||||||
|
bool visible() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
requires std::is_base_of_v<Component, T> T& add_component(Args&&... args) {
|
||||||
|
auto child = std::make_unique<T>(std::forward<Args>(args)...);
|
||||||
|
T& ref = *child;
|
||||||
|
mComponents.emplace_back(std::move(child));
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_default();
|
||||||
|
|
||||||
|
GraphicsOption mOption;
|
||||||
|
int mValueMin = 0;
|
||||||
|
int mValueMax = 0;
|
||||||
|
int mDefaultValue = 0;
|
||||||
|
std::vector<std::unique_ptr<Component> > mComponents;
|
||||||
|
Rml::Element* mRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user