Implement Modern Menu UI (#66)

* Implement Modern Menu UI

* Implement missing menu options

* Add build.c to gitignore
This commit is contained in:
Caladius
2026-01-08 19:41:39 -06:00
committed by GitHub
parent 07e3f781d9
commit 27f55e1e11
91 changed files with 7724 additions and 2093 deletions
+3 -3
View File
@@ -533,9 +533,9 @@ void audio_init() {
init_sequence_players();
gAudioLoadLock = AUDIO_LOCK_NOT_LOADING;
audio_set_player_volume(SEQ_PLAYER_LEVEL, CVarGetFloat("gMainMusicVolume", 1.0f));
audio_set_player_volume(SEQ_PLAYER_ENV, CVarGetFloat("gEnvironmentVolume", 1.0f));
audio_set_player_volume(SEQ_PLAYER_SFX, CVarGetFloat("gSFXMusicVolume", 1.0f));
audio_set_player_volume(SEQ_PLAYER_LEVEL, CVarGetFloat("gSettings.Volume.MainMusic", 1.0f));
audio_set_player_volume(SEQ_PLAYER_ENV, CVarGetFloat("gSettings.Volume.Environment", 1.0f));
audio_set_player_volume(SEQ_PLAYER_SFX, CVarGetFloat("gSettings.Volume.SFX", 1.0f));
// Should probably contain the sizes of the data banks, but those aren't
// easily accessible from here.
+2 -1
View File
@@ -1512,7 +1512,8 @@ void note_set_vel_pan_reverb(struct Note* note, f32 velocity, f32 pan, u8 reverb
velocity = 0;
}
float master_vol = CVarGetFloat("gGameMasterVolume", 1.0f);
float master_vol = CVarGetFloat("gSettings.Volume.Master", 1.0f);
volLeft *= master_vol;
volRight *= master_vol;
+15 -15
View File
@@ -52,7 +52,7 @@ s8 sDebugLvSelectCheckFlag = FALSE;
// #region [PU] Replace variable with CVar so we have better control from the UI and it can be persisted
// s8 sDebugPage = DEBUG_PAGE_MIN;
#define sDebugPage CVarGetInteger("gDeveloper.DebugInfoPage", DEBUG_PAGE_MIN)
#define sDebugPage CVarGetInteger("gDeveloperTools.DebugInfoPage", DEBUG_PAGE_MIN)
// #endregion
s8 sNoExtraDebug = FALSE;
s8 sDebugStringArrPrinted = FALSE;
@@ -135,31 +135,29 @@ void set_text_array_x_y(s32 xOffset, s32 yOffset) {
* current debug mode as well as the printer array (down to up vs up to down).
*/
void print_debug_bottom_up(const char* str, s32 number) {
if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloper.DrawDebugInfo", 0)) {
if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloperTools.DrawDebugInfo", 0)) {
print_text_array_info(gDebugPrintState2, str, number);
}
}
void print_debug_top_down_objectinfo(const char* str, s32 number) {
if ((gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloper.DrawDebugInfo", 0)) &&
sDebugPage == DEBUG_PAGE_OBJECTINFO) {
if ((gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloperTools.DrawDebugInfo", 0)) && sDebugPage == DEBUG_PAGE_OBJECTINFO) {
print_text_array_info(gDebugPrintState1, str, number);
}
}
void print_debug_top_down_mapinfo(const char* str, s32 number) {
if (sNoExtraDebug && !CVarGetInteger("gDeveloper.DrawDebugInfo",
0)) { // how come this is the only instance of the sNoExtraDebug check?
if (sNoExtraDebug && !CVarGetInteger("gDeveloperTools.DrawDebugInfo", 0)) { // how come this is the only instance of the sNoExtraDebug check?
return;
}
if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloper.DrawDebugInfo", 0)) {
if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloperTools.DrawDebugInfo", 0)) {
print_text_array_info(gDebugPrintState1, str, number);
}
}
void print_debug_top_down_normal(const char* str, s32 number) {
if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloper.DrawDebugInfo", 0)) {
if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloperTools.DrawDebugInfo", 0)) {
print_text_array_info(gDebugPrintState1, str, number);
}
}
@@ -333,18 +331,20 @@ UNUSED static void check_debug_button_seq(void) {
* control sDebugPage's range. (unused)
*/
UNUSED static void try_change_debug_page(void) {
if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloper.DrawDebugInfo", 0)) {
if ((gPlayer1Controller->buttonPressed & L_JPAD) && (gPlayer1Controller->buttonDown & (L_TRIG | R_TRIG))) {
CVarSetInteger("gDeveloper.DebugInfoPage", sDebugPage + 1);
if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT || CVarGetInteger("gDeveloperTools.DrawDebugInfo", 0)) {
if ((gPlayer1Controller->buttonPressed & L_JPAD)
&& (gPlayer1Controller->buttonDown & (L_TRIG | R_TRIG))) {
CVarSetInteger("gDeveloperTools.DebugInfoPage", sDebugPage + 1);
}
if ((gPlayer1Controller->buttonPressed & R_JPAD) && (gPlayer1Controller->buttonDown & (L_TRIG | R_TRIG))) {
CVarSetInteger("gDeveloper.DebugInfoPage", sDebugPage - 1);
if ((gPlayer1Controller->buttonPressed & R_JPAD)
&& (gPlayer1Controller->buttonDown & (L_TRIG | R_TRIG))) {
CVarSetInteger("gDeveloperTools.DebugInfoPage", sDebugPage - 1);
}
if (sDebugPage >= (DEBUG_PAGE_MAX + 1)) {
CVarSetInteger("gDeveloper.DebugInfoPage", DEBUG_PAGE_MIN);
CVarSetInteger("gDeveloperTools.DebugInfoPage", DEBUG_PAGE_MIN);
}
if (sDebugPage < DEBUG_PAGE_MIN) {
CVarSetInteger("gDeveloper.DebugInfoPage", DEBUG_PAGE_MAX);
CVarSetInteger("gDeveloperTools.DebugInfoPage", DEBUG_PAGE_MAX);
}
}
}
+1 -1
View File
@@ -765,7 +765,7 @@ u32 interact_water_ring(struct MarioState* m, UNUSED u32 interactType, struct Ob
u32 interact_star_or_key(struct MarioState* m, UNUSED u32 interactType, struct Object* o) {
u32 starIndex;
u32 starGrabAction = ACT_STAR_DANCE_EXIT;
u32 noExit = (o->oInteractionSubtype & INT_SUBTYPE_NO_EXIT) != 0 || CVarGetInteger("gStarNoExit", 0);
u32 noExit = (o->oInteractionSubtype & INT_SUBTYPE_NO_EXIT) != 0 || CVarGetInteger("gEnhancements.StarNoExit", 0);
u32 grandStar = (o->oInteractionSubtype & INT_SUBTYPE_GRAND_STAR) != 0;
if (m->health >= 0x100) {
+5 -5
View File
@@ -442,8 +442,8 @@ void init_mario_after_warp(void) {
play_cap_music(SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP));
}
if (CVarGetInteger("gFixKoopaRaceMusic", 0) == 1 && gCurrLevelNum == LEVEL_BOB &&
get_current_background_music() != SEQUENCE_ARGS(4, SEQ_LEVEL_SLIDE) && sTimerRunning) {
if (CVarGetInteger("gEnhancements.FixKoopaRaceMusic", 0) == 1 && gCurrLevelNum == LEVEL_BOB
&& get_current_background_music() != SEQUENCE_ARGS(4, SEQ_LEVEL_SLIDE) && sTimerRunning) {
play_music(SEQ_PLAYER_LEVEL, SEQUENCE_ARGS(4, SEQ_LEVEL_SLIDE), 0);
}
@@ -568,7 +568,7 @@ s16 music_changed_through_warp(s16 arg) {
struct ObjectWarpNode* warpNode = area_get_warp_node(arg);
s16 levelNum = warpNode->node.destLevel & 0x7F;
if (CVarGetInteger("gFixKoopaRaceMusic", 0) == 1) {
if(CVarGetInteger("gEnhancements.FixKoopaRaceMusic", 0) == 1) {
s16 destArea = warpNode->node.destArea;
s16 val4 = TRUE;
s16 sp2C;
@@ -1184,7 +1184,7 @@ s32 init_level(void) {
if (gMarioState->action != ACT_UNINITIALIZED) {
if (save_file_exists(gCurrSaveFileNum - 1)) {
set_mario_action(gMarioState, ACT_IDLE, 0);
} else if (CVarGetInteger("gDisablePeachCutscene", 0) == 0) {
} else if (CVarGetInteger("gEnhancements.DisablePeachCutscene", 0) == 0) {
set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0);
val4 = TRUE;
}
@@ -1250,7 +1250,7 @@ s32 lvl_init_from_save_file(UNUSED s16 arg0, s32 levelNum) {
#endif
sWarpDest.type = WARP_TYPE_NOT_WARPING;
sDelayedWarpOp = WARP_OP_NONE;
gNeverEnteredCastle = !save_file_exists(gCurrSaveFileNum - 1) || CVarGetInteger("gDisablePeachCutscene", 0) == 0;
gNeverEnteredCastle = !save_file_exists(gCurrSaveFileNum - 1) || CVarGetInteger("gEnhancements.DisablePeachCutscene", 0) == 0;
gCurrLevelNum = levelNum;
gCurrCourseNum = COURSE_NONE;
+1 -1
View File
@@ -67,7 +67,7 @@ extern s8 D_8032C650;
extern s8 gShowProfiler;
extern s8 gShowDebugText;
#define gDebugLevelSelect (CVarGetInteger("gEnableDebugMode", 0) == 1)
#define gDebugLevelSelect (CVarGetInteger("gDeveloperTools.DebugMode", 0) == 1)
void exec_display_list(struct SPTask *spTask);
+1 -1
View File
@@ -287,7 +287,7 @@ static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail* node) {
Mtx* mtx = gMatStackFixed[gMatStackIndex];
s16 distanceFromCam = -GET_HIGH_S16_OF_32(mtx->m[1][3]); // z-component of the translation column
#endif
if (CVarGetInteger("gDisableLOD", 0) == 1) {
if(CVarGetInteger("gEnhancements.DisableLOD", 0) == 1) {
distanceFromCam = 0;
}
+6 -6
View File
@@ -119,14 +119,14 @@ void bhv_act_selector_init(void) {
s32 selectorModelIDs[10];
u8 stars = save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(gCurrCourseNum));
if (CVarGetInteger("gSelectAllStars", 0)) {
if (CVarGetInteger("gEnhancements.SelectAllStars", 0)) {
sStarSelectState = false;
} else {
sStarSelectState = true;
}
sVisibleStars = 0;
while (i != sObtainedStars || (CVarGetInteger("gSelectAllStars", 0) && sVisibleStars != LEVEL_STARS_MAX)) {
while (i != sObtainedStars || (CVarGetInteger("gEnhancements.SelectAllStars", 0) && sVisibleStars != LEVEL_STARS_MAX)) {
if (stars & (1 << sVisibleStars)) { // Star has been collected
selectorModelIDs[sVisibleStars] = MODEL_STAR;
i++;
@@ -184,7 +184,7 @@ void bhv_act_selector_loop(void) {
u8 starIndexCounter;
u8 stars = save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(gCurrCourseNum));
if (!sStarSelectState && !CVarGetInteger("gSelectAllStars", 0)) {
if (!sStarSelectState && !CVarGetInteger("gEnhancements.SelectAllStars", 0)) {
// If the option is toggled after stars have been drawn, then we want to set the star index to a valid index
starIndexCounter = 0;
for (i = 0; i <= sSelectableStarIndex; i++) {
@@ -195,12 +195,12 @@ void bhv_act_selector_loop(void) {
}
sSelectableStarIndex = starIndexCounter - 1;
sStarSelectState = true;
} else if (sStarSelectState && CVarGetInteger("gSelectAllStars", 0)) {
} else if (sStarSelectState && CVarGetInteger("gEnhancements.SelectAllStars", 0)) {
sSelectableStarIndex = sSelectedActIndex;
sStarSelectState = false;
}
if (sObtainedStars != LEVEL_STARS_MAX && !CVarGetInteger("gSelectAllStars", 0)) {
if (sObtainedStars != LEVEL_STARS_MAX && !CVarGetInteger("gEnhancements.SelectAllStars", 0)) {
// Sometimes, stars are not selectable even if they appear on the screen.
// This code filters selectable and non-selectable stars.
sSelectedActIndex = 0;
@@ -429,7 +429,7 @@ s32 lvl_update_obj_and_load_act_button_actions(UNUSED s32 arg, UNUSED s32 unused
queue_rumble_data(60, 70);
func_sh_8024C89C(1);
#endif
if (sInitSelectedActNum >= sSelectedActIndex + 1 || CVarGetInteger("gSelectAllStars", 0)) {
if (sInitSelectedActNum >= sSelectedActIndex + 1 || CVarGetInteger("gEnhancements.SelectAllStars", 0)) {
sLoadedActNum = sSelectedActIndex + 1;
} else {
sLoadedActNum = sInitSelectedActNum;
+7 -11
View File
@@ -114,22 +114,18 @@ s16 intro_level_select(void) {
--gCurrLevelNum, stageChanged = TRUE;
direction = -1;
}
if (gPlayer1Controller->buttonPressed & U_JPAD ||
(CVarGetInteger("gDeveloper.BetterLevelSelect", 0) && gPlayer1Controller->buttonPressed & U_CBUTTONS)) {
if (gPlayer1Controller->buttonPressed & U_JPAD || (CVarGetInteger("gDeveloperTools.BetterLevelSelect", 0) && gPlayer1Controller->buttonPressed & U_CBUTTONS)) {
--gCurrLevelNum, stageChanged = TRUE;
direction = -1;
}
if (gPlayer1Controller->buttonPressed & D_JPAD ||
(CVarGetInteger("gDeveloper.BetterLevelSelect", 0) && gPlayer1Controller->buttonPressed & D_CBUTTONS)) {
if (gPlayer1Controller->buttonPressed & D_JPAD || (CVarGetInteger("gDeveloperTools.BetterLevelSelect", 0) && gPlayer1Controller->buttonPressed & D_CBUTTONS)) {
++gCurrLevelNum, stageChanged = TRUE;
}
if (gPlayer1Controller->buttonPressed & L_JPAD ||
(CVarGetInteger("gDeveloper.BetterLevelSelect", 0) && gPlayer1Controller->buttonPressed & L_CBUTTONS)) {
if (gPlayer1Controller->buttonPressed & L_JPAD || (CVarGetInteger("gDeveloperTools.BetterLevelSelect", 0) && gPlayer1Controller->buttonPressed & L_CBUTTONS)) {
gCurrLevelNum -= 10, stageChanged = TRUE;
direction = -1;
}
if (gPlayer1Controller->buttonPressed & R_JPAD ||
(CVarGetInteger("gDeveloper.BetterLevelSelect", 0) && gPlayer1Controller->buttonPressed & R_CBUTTONS)) {
if (gPlayer1Controller->buttonPressed & R_JPAD || (CVarGetInteger("gDeveloperTools.BetterLevelSelect", 0) && gPlayer1Controller->buttonPressed & R_CBUTTONS)) {
gCurrLevelNum += 10, stageChanged = TRUE;
}
@@ -147,7 +143,7 @@ s16 intro_level_select(void) {
}
// This block will ensure that the level select menu skips over the levels that are not in the game.
if (CVarGetInteger("gDeveloper.BetterLevelSelect", 0) && (stageChanged || gCurrLevelNum == 1)) {
if (CVarGetInteger("gDeveloperTools.BetterLevelSelect", 0) && (stageChanged || gCurrLevelNum == 1)) {
while (sBetterLevelSelectStageNames[gCurrLevelNum - 1][0] == '\0') {
gCurrLevelNum += direction;
if (gCurrLevelNum > LEVEL_MAX) {
@@ -165,7 +161,7 @@ s16 intro_level_select(void) {
print_text_centered(160, 80, "SELECT STAGE");
print_text_centered(160, 30, "PRESS START BUTTON");
if (CVarGetInteger("gDeveloper.BetterLevelSelect", 0)) {
if (CVarGetInteger("gDeveloperTools.BetterLevelSelect", 0)) {
print_text_fmt_int(240, 80, "%2d", gCurrLevelNum);
print_text_centered(160, 60, sBetterLevelSelectStageNames[gCurrLevelNum - 1]);
} else {
@@ -180,7 +176,7 @@ s16 intro_level_select(void) {
// ... the level select quit combo is being pressed, which uses START. If this
// is the case, quit the menu instead.
if (gPlayer1Controller->buttonDown == QUIT_LEVEL_SELECT_COMBO) {
// CVarSetInteger("gEnableDebugMode", 0);
// CVarSetInteger("gDeveloperTools.DebugMode", 0);
return -1;
}
play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
+68 -3
View File
@@ -1,5 +1,5 @@
#include "Engine.h"
#include "ui/ImguiUI.h"
#include "ui/GhostshipGui.hpp"
#include "GameExtractor.h"
#include "port/importer/AnimationFactory.h"
#include "port/importer/AudioBankFactory.h"
@@ -26,6 +26,7 @@
#endif
#include <fast/resource/ResourceType.h>
#include <ship/window/gui/Fonts.h>
#include <fast/resource/factory/DisplayListFactory.h>
#include <fast/resource/factory/TextureFactory.h>
#include <fast/resource/factory/MatrixFactory.h>
@@ -44,8 +45,14 @@
#include <port/switch/SwitchImpl.h>
#endif
const float imguiScaleOptionToValue[4] = { 0.75f, 1.0f, 1.5f, 2.0f };
const uint32_t defaultImGuiScale = 1;
int32_t previousImGuiScaleIndex = -1;
float previousImGuiScale = defaultImGuiScale;
namespace fs = std::filesystem;
extern "C" {
#include "sm64.h"
#include "audio/external.h"
@@ -193,6 +200,14 @@ GameEngine::GameEngine() : dictionary(nullptr) {
static_cast<uint32_t>(Ship::ResourceType::Blob), 0);
prevAltAssets = CVarGetInteger("gEnhancements.Mods.AlternateAssets", 0);
context->GetResourceManager()->SetAltAssetsEnabled(prevAltAssets);
fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf");
fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf");
fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf");
fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf");
fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf");
fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf");
ImGui::GetIO().FontDefault = fontMono;
}
bool GameEngine::GenAssetFile(bool exitOnFail) {
@@ -260,9 +275,59 @@ int GameEngine::ShowYesNoBox(const char* title, const char* box) {
return ret;
}
void GameEngine::Create() {
ImFont *GameEngine::CreateFontWithSize(float size, std::string fontPath) {
auto mImGuiIo = &ImGui::GetIO();
ImFont *font;
if (fontPath == "") {
ImFontConfig fontCfg = ImFontConfig();
fontCfg.OversampleH = fontCfg.OversampleV = 1;
fontCfg.PixelSnapH = true;
fontCfg.SizePixels = size;
font = mImGuiIo->Fonts->AddFontDefault(&fontCfg);
} else {
auto initData = std::make_shared<Ship::ResourceInitData>();
ImFontConfig config;
config.FontDataOwnedByAtlas = false;
initData->Format = RESOURCE_FORMAT_BINARY;
initData->Type = static_cast<uint32_t>(RESOURCE_TYPE_FONT);
initData->ResourceVersion = 0;
initData->Path = fontPath;
std::shared_ptr<Ship::Font> fontData = std::static_pointer_cast<Ship::Font>(
Ship::Context::GetInstance()->GetResourceManager()->LoadResource(fontPath, false,
initData));
font = mImGuiIo->Fonts->AddFontFromMemoryTTF(fontData->Data, fontData->DataSize, size, &config);
}
// FontAwesome fonts need to have their sizes reduced by 2.0f/3.0f in order to align correctly
float iconFontSize = size * 2.0f / 3.0f;
static const ImWchar sIconsRanges[] = { ICON_MIN_FA, ICON_MAX_16_FA, 0 };
ImFontConfig iconsConfig;
iconsConfig.MergeMode = true;
iconsConfig.PixelSnapH = true;
iconsConfig.GlyphMinAdvanceX = iconFontSize;
mImGuiIo->Fonts->AddFontFromMemoryCompressedBase85TTF(fontawesome_compressed_data_base85,
iconFontSize, &iconsConfig, sIconsRanges);
return font;
}
void GameEngine::ScaleImGui() {
int32_t imGuiScaleIndex = CVarGetInteger("gSettings.ImGuiScale", defaultImGuiScale);
if (imGuiScaleIndex == previousImGuiScaleIndex) {
return;
}
float scale = imguiScaleOptionToValue[imGuiScaleIndex];
float newScale = scale / previousImGuiScale;
ImGui::GetStyle().ScaleAllSizes(newScale);
ImGui::GetIO().FontGlobalScale = scale;
previousImGuiScale = scale;
previousImGuiScaleIndex = imGuiScaleIndex;
}
void GameEngine::Create(){
const auto instance = Instance = new GameEngine();
GameUI::SetupGuiElements();
GhostshipGui::SetupGuiElements();
instance->AudioInit();
instance->LoadDictionary();
instance->LoadPlayerAnims();
+10
View File
@@ -31,6 +31,13 @@ class GameEngine {
public:
static GameEngine* Instance;
ImFont *fontStandard;
ImFont *fontStandardLarger;
ImFont *fontStandardLargest;
ImFont *fontMono;
ImFont *fontMonoLarger;
ImFont *fontMonoLargest;
std::shared_ptr<Ship::Context> context;
std::vector<Animation*> animationsTable;
@@ -55,6 +62,8 @@ class GameEngine {
static void ProcessGfxCommands(Gfx* commands);
static uint8_t GetBankIdByName(const std::string& name);
static int ShowYesNoBox(const char* title, const char* box);
static ImFont *CreateFontWithSize(float size, std::string fontPath);
static void ScaleImGui();
static void ShowMessage(const char* title, const char* message, SDL_MessageBoxFlags type = SDL_MESSAGEBOX_ERROR);
void LoadDictionary();
void LoadPlayerAnims();
@@ -85,6 +94,7 @@ struct DialogEntry* GameEngine_LoadDialog(uint32_t dialogId);
uint8_t* GameEngine_LoadTranslation(const char* key);
int GameEngine_OTRSigCheck(const char* imgData);
struct Animation* GameEngine_LoadAnimation(uint32_t animId);
#ifdef __cplusplus
}
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef SHIP_INIT_HPP
#define SHIP_INIT_HPP
#ifdef __cplusplus
#include <vector>
#include <set>
#include <unordered_map>
#include <string>
#include <functional>
struct ShipInit {
static std::unordered_map<std::string, std::vector<std::function<void()>>>& GetAll() {
static std::unordered_map<std::string, std::vector<std::function<void()>>> shipInitFuncs;
return shipInitFuncs;
}
static void InitAll() {
ShipInit::Init("*");
}
static void Init(const std::string& path) {
auto& shipInitFuncs = ShipInit::GetAll();
for (const auto& initFunc : shipInitFuncs[path]) {
initFunc();
}
}
};
struct RegisterShipInitFunc {
RegisterShipInitFunc(std::function<void()> initFunc, const std::set<std::string>& updatePaths = {}) {
auto& shipInitFuncs = ShipInit::GetAll();
shipInitFuncs["*"].push_back(initFunc);
for (const auto& path : updatePaths) {
shipInitFuncs[path].push_back(initFunc);
}
}
};
#endif // __cplusplus
#endif // SHIP_INIT_HPP
+15
View File
@@ -0,0 +1,15 @@
#include <libultraship/libultra.h>
// Lighthouse Version information
const char gBuildVersion[] = "@PROJECT_BUILD_NAME@ (@CMAKE_PROJECT_VERSION_MAJOR@.@CMAKE_PROJECT_VERSION_MINOR@.@CMAKE_PROJECT_VERSION_PATCH@)";
const u16 gBuildVersionMajor = @CMAKE_PROJECT_VERSION_MAJOR@;
const u16 gBuildVersionMinor = @CMAKE_PROJECT_VERSION_MINOR@;
const u16 gBuildVersionPatch = @CMAKE_PROJECT_VERSION_PATCH@;
const char gGitBranch[] = "@CMAKE_PROJECT_GIT_BRANCH@";
const char gGitCommitHash[] = "@CMAKE_PROJECT_GIT_COMMIT_HASH@";
const char gGitCommitTag[] = "@CMAKE_PROJECT_GIT_COMMIT_TAG@";
const char gBuildTeam[] = "@PROJECT_TEAM@";
const char gBuildDate[] = __DATE__ " " __TIME__;
const char gBuildMakeOption[] = "";
+24
View File
@@ -0,0 +1,24 @@
#ifndef BUILD_H
#define BUILD_H
#include <libultraship/libultra.h>
#ifdef __cplusplus
extern "C" {
#endif
// Ghostship Version information
extern char gBuildVersion[];
extern u16 gBuildVersionMajor;
extern u16 gBuildVersionMinor;
extern u16 gBuildVersionPatch;
extern char gGitBranch[];
extern char gGitCommitHash[];
extern char gGitCommitTag[];
extern char gBuildTeam[];
extern char gBuildDate[];
extern char gBuildMakeOption[];
#ifdef __cplusplus
}
#endif
#endif
+5 -5
View File
@@ -18,8 +18,8 @@ static const Mtx matrix_patch_fullscreen = { { { 2.0f / SCREEN_WIDTH, 0.0f, 0.0f
{ -1.0f, -1.0f, -1.0f, 1.0f } } };
void OnLivesChange(IEvent* event) {
PlayerLivesChange* ev = (PlayerLivesChange*)event;
if (CVarGetInteger("gInfiniteLives", 0) == 0 || ev->lives > 0) {
PlayerLivesChange* ev = (PlayerLivesChange*) event;
if (CVarGetInteger("gCheats.InfiniteLives", 0) == 0 || ev->lives > 0) {
return;
}
@@ -27,8 +27,8 @@ void OnLivesChange(IEvent* event) {
}
void OnHealthChange(IEvent* event) {
PlayerHealthChange* ev = (PlayerHealthChange*)event;
if (CVarGetInteger("gInfiniteHealth", 0) == 0 || ev->health > 0) {
PlayerHealthChange* ev = (PlayerHealthChange*) event;
if (CVarGetInteger("gCheats.InfiniteHealth", 0) == 0 || ev->health > 0) {
return;
}
@@ -36,7 +36,7 @@ void OnHealthChange(IEvent* event) {
}
void OnRenderPauseCourseOptions(IEvent* event) {
if (CVarGetInteger("gPauseExitWhenever", 0) == 0) {
if (CVarGetInteger("gCheats.PauseExitWhenever", 0) == 0) {
return;
}
+85
View File
@@ -0,0 +1,85 @@
#include "GhostshipGui.hpp"
#include <spdlog/spdlog.h>
#include <imgui.h>
#include <imgui_internal.h>
#include "UIWidgets.hpp"
#ifdef __APPLE__
#include <fast/backends/gfx_metal.h>
#endif
#ifdef __SWITCH__
#include <port/switch/SwitchImpl.h>
#endif
#include "Notification.h"
#include "GhostshipMenu.h"
#include "GhostshipInputEditorWindow.h"
#include "SaveEditor.h"
namespace GhostshipGui {
// MARK: - Delegates
std::shared_ptr<Ship::GuiWindow> mInputEditorWindow;
std::shared_ptr<GhostshipMenu> mGhostshipMenu;
std::shared_ptr<SaveEditorWindow> mSaveEditorWindow;
std::shared_ptr<Notification::Window> mNotificationWindow;
std::shared_ptr<InputViewer> mInputViewer;
std::shared_ptr<InputViewerSettingsWindow> mInputViewerSettings;
std::shared_ptr<GhostshipModalWindow> mModalWindow;
UIWidgets::Colors GetMenuThemeColor() {
return mGhostshipMenu->GetMenuThemeColor();
}
void SetupGuiElements() {
auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui();
auto& style = ImGui::GetStyle();
style.FramePadding = ImVec2(4.0f, 6.0f);
style.ItemSpacing = ImVec2(8.0f, 6.0f);
style.Colors[ImGuiCol_MenuBarBg] = UIWidgets::ColorValues.at(UIWidgets::Colors::DarkGray);
mGhostshipMenu = std::make_shared<GhostshipMenu>(CVAR_WINDOW("Menu"), "Settings Menu");
gui->SetMenu(mGhostshipMenu);
mSaveEditorWindow = std::make_shared<SaveEditorWindow>(CVAR_WINDOW("SaveEditor"), "Save Editor");
gui->AddGuiWindow(mSaveEditorWindow);
mInputEditorWindow = std::make_shared<GhostshipInputEditorWindow>(
CVAR_WINDOW("ControllerConfiguration"), "Configure Controller");
gui->AddGuiWindow(mInputEditorWindow);
mNotificationWindow = std::make_shared<Notification::Window>(CVAR_WINDOW("Notifications"), "Notifications Window");
gui->AddGuiWindow(mNotificationWindow);
mNotificationWindow->Show();
mInputViewer = std::make_shared<InputViewer>(CVAR_WINDOW("InputViewer"), "Input Viewer");
gui->AddGuiWindow(mInputViewer);
mInputViewerSettings = std::make_shared<InputViewerSettingsWindow>(CVAR_WINDOW("InputViewerSettings"),
"Input Viewer Settings", ImVec2(500, 525));
gui->AddGuiWindow(mInputViewerSettings);
mModalWindow = std::make_shared<GhostshipModalWindow>(CVAR_WINDOW("ModalWindow"), "Modal Window");
gui->AddGuiWindow(mModalWindow);
mModalWindow->Show();
}
void Destroy() {
auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui();
gui->RemoveAllGuiWindows();
mGhostshipMenu = nullptr;
mModalWindow = nullptr;
mSaveEditorWindow = nullptr;
mInputEditorWindow = nullptr;
mNotificationWindow = nullptr;
mInputViewer = nullptr;
mInputViewerSettings = nullptr;
}
void RegisterPopup(std::string title, std::string message, std::string button1, std::string button2,
std::function<void()> button1callback, std::function<void()> button2callback) {
mModalWindow->RegisterPopup(title, message, button1, button2, button1callback, button2callback);
}
} // namespace GhostshipGui
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include "UIWidgets.hpp"
#include "InputViewer.h"
#include "GhostshipModals.h"
namespace GhostshipGui {
void SetupHooks();
void SetupGuiElements();
void Draw();
void Destroy();
UIWidgets::Colors GetMenuThemeColor();
} // namespace GhostshipGui
#define THEME_COLOR GhostshipGui::GetMenuThemeColor()
File diff suppressed because it is too large Load Diff
+104
View File
@@ -0,0 +1,104 @@
#pragma once
#include <unordered_map>
#include <string>
#include <vector>
#include <set>
#include <list>
#include <memory>
#include "stdint.h"
#include <ship/window/gui/GuiWindow.h>
#include <ship/controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h>
#include <ship/controller/physicaldevice/PhysicalDeviceType.h>
#include <imgui.h>
#include <libultraship/libultra/controller.h>
namespace Ship {
class ControllerRumbleMapping;
}
typedef CONTROLLERBUTTONS_T N64ButtonMask;
typedef struct {
const char* label;
const char* cVarName;
N64ButtonMask defaultBtn;
} CustomButtonMap;
class GhostshipInputEditorWindow : public Ship::GuiWindow {
public:
using GuiWindow::GuiWindow;
~GhostshipInputEditorWindow();
void DrawButton(const char* label, int32_t n64Btn, int32_t currentPort, int32_t* btnReading);
void DrawInputChip(const char* buttonName, ImVec4 color);
void DrawAnalogPreview(const char* label, ImVec2 stick, float deadzone = 0, bool gyro = false);
void DrawControllerSchema();
bool TestingRumble();
void DrawFullContents();
void DrawPortTabContents(uint8_t portIndex);
protected:
void InitElement() override;
void DrawElement() override;
void UpdateElement() override;
private:
void DrawStickDirectionLine(const char* axisDirectionName, uint8_t port, uint8_t stick, Ship::Direction direction,
ImVec4 color);
void DrawButtonLine(const char* buttonName, uint8_t port, N64ButtonMask bitmask, ImVec4 color);
void DrawButtonLineEditMappingButton(uint8_t port, N64ButtonMask bitmask, std::string id);
void DrawButtonLineAddMappingButton(uint8_t port, N64ButtonMask bitmask);
void DrawStickDirectionLineEditMappingButton(uint8_t port, uint8_t stick, Ship::Direction direction,
std::string id);
void DrawStickDirectionLineAddMappingButton(uint8_t port, uint8_t stick, Ship::Direction direction);
void DrawStickSection(uint8_t port, uint8_t stick, ImVec4 color);
void DrawRumbleSection(uint8_t port);
void DrawRemoveRumbleMappingButton(uint8_t port, std::string id);
void DrawAddRumbleMappingButton(uint8_t port);
void DrawLEDSection(uint8_t port);
void DrawRemoveLEDMappingButton(uint8_t port, std::string id);
void DrawAddLEDMappingButton(uint8_t port);
void DrawGyroSection(uint8_t port);
void DrawRemoveGyroMappingButton(uint8_t port, std::string id);
void DrawAddGyroMappingButton(uint8_t port);
// Used together for an incomplete linked hash map implementation in order to
// map button masks to their names and original mapping on N64
std::list<std::pair<N64ButtonMask, const char*>> buttons;
std::unordered_map<N64ButtonMask, decltype(buttons)::iterator> buttonNames;
void addButtonName(N64ButtonMask mask, const char* name);
void DrawMapping(CustomButtonMap& mapping, float labelWidth, N64ButtonMask excludedButtons);
int32_t mGameInputBlockTimer;
int32_t mMappingInputBlockTimer;
int32_t mRumbleTimer;
std::shared_ptr<Ship::ControllerRumbleMapping> mRumbleMappingToTest;
// mBitmaskToMappingIds[port][bitmask] = { id0, id1, ... }
std::unordered_map<uint8_t, std::unordered_map<N64ButtonMask, std::vector<std::string>>> mBitmaskToMappingIds;
// mStickDirectionToMappingIds[port][stick][direction] = { id0, id1, ... }
std::unordered_map<uint8_t,
std::unordered_map<uint8_t, std::unordered_map<Ship::Direction, std::vector<std::string>>>>
mStickDirectionToMappingIds;
void UpdateBitmaskToMappingIds(uint8_t port);
void UpdateStickDirectionToMappingIds(uint8_t port);
void GetButtonColorsForDeviceType(Ship::PhysicalDeviceType lusIndex, ImVec4& buttonColor,
ImVec4& buttonHoveredColor);
void DrawPortTab(uint8_t portIndex);
std::set<N64ButtonMask> mButtonsBitmasks;
std::set<N64ButtonMask> mDpadBitmasks;
bool mInputEditorPopupOpen;
void DrawSetDefaultsButton(uint8_t portIndex);
void DrawClearAllButton(uint8_t portIndex);
void OffsetMappingPopup();
void DrawDeviceToggles(uint8_t portIndex);
};
+215
View File
@@ -0,0 +1,215 @@
#include "GhostshipMenu.h"
#include "GhostshipInputEditorWindow.h"
#include <ship/window/gui/GuiMenuBar.h>
#include <ship/window/gui/GuiElement.h>
#include <variant>
#include <ship/utils/StringHelper.h>
#include <spdlog/fmt/fmt.h>
#include <tuple>
static const std::unordered_map<int32_t, const char*> menuThemeOptions = {
{ UIWidgets::Colors::Red, "Red" },
{ UIWidgets::Colors::DarkRed, "Dark Red" },
{ UIWidgets::Colors::Orange, "Orange" },
{ UIWidgets::Colors::Green, "Green" },
{ UIWidgets::Colors::DarkGreen, "Dark Green" },
{ UIWidgets::Colors::LightBlue, "Light Blue" },
{ UIWidgets::Colors::Blue, "Blue" },
{ UIWidgets::Colors::DarkBlue, "Dark Blue" },
{ UIWidgets::Colors::Indigo, "Indigo" },
{ UIWidgets::Colors::Violet, "Violet" },
{ UIWidgets::Colors::Purple, "Purple" },
{ UIWidgets::Colors::Brown, "Brown" },
{ UIWidgets::Colors::Gray, "Gray" },
{ UIWidgets::Colors::DarkGray, "Dark Gray" },
};
static const std::vector<const char*> textureFilteringOptions = {
"Three-Point", // Fast::FILTER_THREE_POINT,
"Linear", // Fast::FILTER_LINEAR
"None", // Fast::FILTER_NONE
};
#ifdef _DEBUG
DebugLogOption defaultLogLevel = DEBUG_LOG_TRACE;
#else
DebugLogOption defaultLogLevel = DEBUG_LOG_INFO;
#endif
static const std::vector<const char*> logLevels = {
"Trace", // DEBUG_LOG_TRACE
"Debug", // DEBUG_LOG_DEBUG
"Info", // DEBUG_LOG_INFO
"Warn", // DEBUG_LOG_WARN
"Error", // DEBUG_LOG_ERROR
"Critical", // DEBUG_LOG_CRITICAL
"Off", // DEBUG_LOG_OFF
};
extern std::unordered_map<s16, const char*> warpPointSceneList;
namespace GhostshipGui {
extern std::shared_ptr<GhostshipMenu> mGhostshipMenu;
using namespace UIWidgets;
void GhostshipMenu::AddSidebarEntry(std::string sectionName, std::string sidebarName, uint32_t columnCount) {
assert(!sectionName.empty());
assert(!sidebarName.empty());
menuEntries.at(sectionName).sidebars.emplace(sidebarName, SidebarEntry{ .columnCount = columnCount });
menuEntries.at(sectionName).sidebarOrder.push_back(sidebarName);
}
WidgetInfo& GhostshipMenu::AddWidget(WidgetPath& pathInfo, std::string widgetName, WidgetType widgetType) {
assert(!widgetName.empty()); // Must be unique
assert(menuEntries.contains(pathInfo.sectionName)); // Section/header must already exist
assert(menuEntries.at(pathInfo.sectionName).sidebars.contains(pathInfo.sidebarName)); // Sidebar must already exist
std::unordered_map<std::string, SidebarEntry>& sidebar = menuEntries.at(pathInfo.sectionName).sidebars;
uint8_t column = pathInfo.column;
if (sidebar.contains(pathInfo.sidebarName)) {
while (sidebar.at(pathInfo.sidebarName).columnWidgets.size() < column + 1) {
sidebar.at(pathInfo.sidebarName).columnWidgets.push_back({});
}
}
SidebarEntry& entry = sidebar.at(pathInfo.sidebarName);
entry.columnWidgets.at(column).push_back({ .name = widgetName, .type = widgetType });
WidgetInfo& widget = entry.columnWidgets.at(column).back();
switch (widgetType) {
case WIDGET_CHECKBOX:
case WIDGET_CVAR_CHECKBOX:
widget.options = std::make_shared<CheckboxOptions>();
break;
case WIDGET_SLIDER_FLOAT:
case WIDGET_CVAR_SLIDER_FLOAT:
widget.options = std::make_shared<FloatSliderOptions>();
break;
case WIDGET_SLIDER_INT:
case WIDGET_CVAR_SLIDER_INT:
widget.options = std::make_shared<IntSliderOptions>();
break;
case WIDGET_COMBOBOX:
case WIDGET_CVAR_COMBOBOX:
case WIDGET_AUDIO_BACKEND:
case WIDGET_VIDEO_BACKEND:
widget.options = std::make_shared<ComboboxOptions>();
break;
case WIDGET_BUTTON:
widget.options = std::make_shared<ButtonOptions>();
break;
case WIDGET_WINDOW_BUTTON:
widget.options = std::make_shared<WindowButtonOptions>();
break;
case WIDGET_CVAR_COLOR_PICKER:
case WIDGET_COLOR_PICKER:
widget.options = std::make_shared<ColorPickerOptions>();
break;
case WIDGET_SEPARATOR_TEXT:
case WIDGET_TEXT:
widget.options = std::make_shared<TextOptions>();
break;
case WIDGET_SEARCH:
case WIDGET_SEPARATOR:
default:
widget.options = std::make_shared<WidgetOptions>();
}
return widget;
}
GhostshipMenu::GhostshipMenu(const std::string& consoleVariable, const std::string& name)
: Menu(consoleVariable, name, 0, UIWidgets::Colors::LightBlue) {
}
void GhostshipMenu::InitElement() {
Ship::Menu::InitElement();
AddMenuSettings();
AddMenuEnhancements();
AddMenuDevTools();
if (CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0)) {
InsertSidebarSearch();
}
for (auto& initFunc : MenuInit::GetInitFuncs()) {
initFunc();
}
disabledMap = {
{ DISABLE_FOR_NO_VSYNC,
{ [](disabledInfo& info) -> bool {
return !Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync();
},
"Disabling VSync not supported" } },
{ DISABLE_FOR_NO_WINDOWED_FULLSCREEN,
{ [](disabledInfo& info) -> bool {
return !Ship::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen();
},
"Windowed Fullscreen not supported" } },
{ DISABLE_FOR_NO_MULTI_VIEWPORT,
{ [](disabledInfo& info) -> bool {
return !Ship::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports();
},
"Multi-viewports not supported" } },
{ DISABLE_FOR_NOT_DIRECTX,
{ [](disabledInfo& info) -> bool {
return Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() !=
Ship::WindowBackend::FAST3D_DXGI_DX11;
},
"Available Only on DirectX" } },
{ DISABLE_FOR_DIRECTX,
{ [](disabledInfo& info) -> bool {
return Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() ==
Ship::WindowBackend::FAST3D_DXGI_DX11;
},
"Not Available on DirectX" } },
{ DISABLE_FOR_MATCH_REFRESH_RATE_ON,
{ [](disabledInfo& info) -> bool { return CVarGetInteger(CVAR_SETTING("MatchRefreshRate"), 0); },
"Match Refresh Rate is Enabled" } },
{ DISABLE_FOR_ADVANCED_RESOLUTION_ON,
{ [](disabledInfo& info) -> bool { return CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); },
"Advanced Resolution Enabled" } },
{ DISABLE_FOR_VERTICAL_RES_TOGGLE_ON,
{ [](disabledInfo& info) -> bool {
return CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0);
},
"Vertical Resolution Toggle Enabled" } },
{ DISABLE_FOR_LOW_RES_MODE_ON,
{ [](disabledInfo& info) -> bool { return CVarGetInteger(CVAR_LOW_RES_MODE, 0); }, "N64 Mode Enabled" } },
{ DISABLE_FOR_NULL_PLAY_STATE,
{ [](disabledInfo& info) -> bool { return /*gPlayState == NULL*/ "TODO"; }, "Save Not Loaded" } },
{ DISABLE_FOR_DEBUG_MODE_OFF,
{ [](disabledInfo& info) -> bool { return !CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0); },
"Debug Mode is Disabled" } },
{ DISABLE_FOR_FRAME_ADVANCE_OFF,
{ [](disabledInfo& info) -> bool {
return /*!(gPlayState != nullptr && gPlayState->frameAdvCtx.enabled)*/ "TODO";
},
"Frame Advance is Disabled" } },
{ DISABLE_FOR_ADVANCED_RESOLUTION_OFF,
{ [](disabledInfo& info) -> bool { return !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); },
"Advanced Resolution is Disabled" } },
{ DISABLE_FOR_VERTICAL_RESOLUTION_OFF,
{ [](disabledInfo& info) -> bool {
return !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0);
},
"Vertical Resolution Toggle is Off" } },
{ DISABLE_FOR_BOOT_TO_DEBUG_WARP_SCREEN_ON,
{ [](disabledInfo& info) -> bool {
return CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0) &&
CVarGetInteger(CVAR_DEVELOPER_TOOLS("BootToDebugWarpScreen"), 0);
},
"\"Boot To Debug Warp Screen\" Enabled (see Dev Tools -> General)" } },
};
}
void GhostshipMenu::UpdateElement() {
Ship::Menu::UpdateElement();
}
void GhostshipMenu::Draw() {
Ship::Menu::Draw();
}
void GhostshipMenu::DrawElement() {
Ship::Menu::DrawElement();
}
} // namespace GhostshipGui

Some files were not shown because too many files have changed in this diff Show More