Compare commits

..

13 Commits

Author SHA1 Message Date
Luke Street fbe7b57132 Refactor command console UI lifecycle 2026-08-20 17:49:00 -06:00
Luke Street d3f10221b8 Merge remote-tracking branch 'origin/main' into better-tools
# Conflicts:
#	files.cmake
#	src/dusk/game_clock.cpp
#	src/dusk/imgui/ImGuiConsole.cpp
#	src/dusk/settings.cpp
#	src/dusk/settings.h
#	src/m_Do/m_Do_main.cpp
2026-08-20 17:21:22 -06:00
madeline 75fd5ad1c4 pending clos 2026-08-08 19:47:51 -07:00
madeline bf4c727d04 Merge branch 'main' of https://github.com/TakaRikka/dusk into better-tools 2026-08-08 19:36:21 -07:00
madeline ea1f50fe27 speedrun mode reset returns you to file screen 2026-08-08 19:21:47 -07:00
madeline 99180d02f7 Merge branch 'better-tools' of https://github.com/TakaRikka/dusk into better-tools 2026-08-08 19:21:07 -07:00
madeline 5b64e4fd2c trigger viewer, more commands 2026-08-08 19:20:46 -07:00
Luke Street 692c04e936 Merge branch 'main' into better-tools 2026-06-07 22:37:14 -06:00
Luke Street e17d0f21fc Console focus & closing fixes 2026-06-02 00:40:49 -06:00
madeline 91e77b1051 eight (8) spaces 2026-06-01 23:18:56 -07:00
madeline 0b1a2c10b6 spaces 2026-06-01 23:13:49 -07:00
madeline e855e6471f Merge branch 'main' of https://github.com/TakaRikka/dusk into better-tools 2026-06-01 23:05:09 -07:00
madeline 848f635798 better tools 2026-06-01 22:56:14 -07:00
135 changed files with 2584 additions and 5662 deletions
+1 -2
View File
@@ -425,7 +425,7 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "^(AppleClang|Clang)$")
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64") if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=2,1>) set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=2,1>)
else () else ()
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=10,4>) set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=10,5>)
endif () endif ()
endif () endif ()
@@ -520,7 +520,6 @@ if (DUSK_ENABLE_CODE_MODS AND CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR
add_subdirectory(mods/shadow_mod) add_subdirectory(mods/shadow_mod)
add_subdirectory(mods/window_demo) add_subdirectory(mods/window_demo)
add_subdirectory(mods/flow_demo) add_subdirectory(mods/flow_demo)
add_subdirectory(mods/basic_cosmetics_mod)
endif () endif ()
if (APPLE) if (APPLE)
+9 -35
View File
@@ -474,8 +474,8 @@ per-window RCSS. A non-`MOD_OK` result from `build`/`update` fails your mod, as
callback. callback.
**Controls:** `pane_add_control` adds an input row described by a `UiControlDesc`: `UI_CONTROL_BUTTON`, **Controls:** `pane_add_control` adds an input row described by a `UiControlDesc`: `UI_CONTROL_BUTTON`,
`UI_CONTROL_GROUP`, `UI_CONTROL_TOGGLE`, `UI_CONTROL_NUMBER`, `UI_CONTROL_STRING`, `UI_CONTROL_SELECT`, or `UI_CONTROL_TOGGLE`, `UI_CONTROL_NUMBER`, `UI_CONTROL_STRING`, or `UI_CONTROL_SELECT`. Values bind with callbacks or
`UI_CONTROL_COLOR`. Values bind with callbacks or directly to a config var. directly to a config var.
```cpp ```cpp
UiControlDesc control = UI_CONTROL_DESC_INIT; UiControlDesc control = UI_CONTROL_DESC_INIT;
@@ -488,13 +488,9 @@ svc_ui->pane_add_control(mod_ctx, leftPane, &control, nullptr);
``` ```
`UI_BINDING_CONFIG_VAR` wires persistence, change notifications, and the modified indicator automatically. The var `UI_BINDING_CONFIG_VAR` wires persistence, change notifications, and the modified indicator automatically. The var
type must match the control: `TOGGLE` = bool, `NUMBER` and `SELECT` = int, `STRING` and `COLOR` = string. Float vars type must match the control: `TOGGLE` = bool, `NUMBER` and `SELECT` = int, `STRING` = string. Float vars are not
are not bindable; use callbacks and convert. `help_rml` and `SELECT` option lists render in a help pane, so `SELECT` bindable; use callbacks and convert. `help_rml` and `SELECT` option lists render in a help pane, so `SELECT` controls
controls are only available inside window tabs. are only available inside window tabs.
`pane_add_group` adds a category button to a window tab's left pane. Focusing the button clears the paired right pane
and calls the group's build callback with that pane, which is useful for organizing related controls without adding
more tabs.
**Windows:** `window_push` pushes a tabbed two-pane window onto the document stack and shows it. Each tab's `build` **Windows:** `window_push` pushes a tabbed two-pane window onto the document stack and shows it. Each tab's `build`
receives the window handle plus fresh left and right pane handles on every activation. The optional per-tab `update` receives the window handle plus fresh left and right pane handles on every activation. The optional per-tab `update`
@@ -516,32 +512,10 @@ svc_ui->window_push(mod_ctx, &desc, &window);
``` ```
**Dialogs:** `dialog_push` shows a modal dialog. `variant` picks the style, `icon` optionally overrides the variant's **Dialogs:** `dialog_push` shows a modal dialog. `variant` picks the style, `icon` optionally overrides the variant's
default icon, and actions become buttons. The optional `build` callback allows you to add controls to a pane between default icon, and actions become buttons. After an action's `on_pressed` returns, the dialog closes unless the action
the body and actions. It uses the same text, progress, and control builders as panels. sets `keep_open`. A `keep_open` action can close it later (or immediately) with `dialog_close`. Cancel fires
`on_dismiss` if present and always closes. `dialog_set_body`, `dialog_set_icon`, and `dialog_add_action` mutate a live
```cpp dialog.
ModResult build_dialog(ModContext*, UiElementHandle pane, void*, ModError*) {
UiControlDesc input = UI_CONTROL_DESC_INIT;
input.kind = UI_CONTROL_STRING;
input.label = "Name";
input.get = get_name;
input.set = set_name;
return svc_ui->pane_add_control(mod_ctx, pane, &input, nullptr);
}
UiDialogAction action = {"Save", save, nullptr, false};
UiDialogDesc dialog = UI_DIALOG_DESC_INIT;
dialog.title = "New Preset";
dialog.body_rml = "Choose a name for the preset.";
dialog.actions = &action;
dialog.action_count = 1;
dialog.build = build_dialog;
svc_ui->dialog_push(mod_ctx, &dialog, nullptr);
```
After an action's `on_pressed`, the dialog closes unless the action sets `keep_open`. It can then be closed later
(or immediately) with `dialog_close`. Cancel fires `on_dismiss` and always closes. `dialog_set_body`, `dialog_set_icon`,
and `dialog_add_action` mutate a live dialog.
**Toasts:** `push_toast` enqueues a notification. Titles and bodies accept RML. The optional `type` is applied as an **Toasts:** `push_toast` enqueues a notification. Titles and bodies accept RML. The optional `type` is applied as an
RCSS class; `warning` uses the built-in warning appearance, and mods can define their own types. A duration of 0 uses RCSS class; `warning` uses the built-in warning appearance, and mods can define their own types. A duration of 0 uses
+12 -13
View File
@@ -244,7 +244,7 @@ set(DOLZEL_FILES
src/CaptureScreen.cpp src/CaptureScreen.cpp
) )
if(DEBUG) if(DEBUG)
list(APPEND DOLZEL_FILES src/d/d_event_debug.cpp) list(APPEND DOLZEL_FILES src/d/d_event_debug.cpp)
endif(DEBUG) endif(DEBUG)
set(Z2AUDIOLIB_FILES set(Z2AUDIOLIB_FILES
@@ -1404,10 +1404,10 @@ set(REL_FILES
) )
set(DOLPHIN_FILES set(DOLPHIN_FILES
libs/dolphin/src/gf/GFGeometry.cpp libs/dolphin/src/gf/GFGeometry.cpp
libs/dolphin/src/gf/GFLight.cpp libs/dolphin/src/gf/GFLight.cpp
libs/dolphin/src/gf/GFPixel.cpp libs/dolphin/src/gf/GFPixel.cpp
libs/dolphin/src/gf/GFTev.cpp libs/dolphin/src/gf/GFTev.cpp
) )
set(DUSK_FILES set(DUSK_FILES
@@ -1433,11 +1433,16 @@ set(DUSK_FILES
src/dusk/dvd_asset.hpp src/dusk/dvd_asset.hpp
src/dusk/extras.c src/dusk/extras.c
src/dusk/frame_interpolation.cpp src/dusk/frame_interpolation.cpp
src/dusk/commands.cpp
src/dusk/commands.hpp
src/dusk/game_clock.cpp src/dusk/game_clock.cpp
src/dusk/game_mode.cpp src/dusk/game_mode.cpp
src/dusk/gamepad_color.cpp src/dusk/gamepad_color.cpp
src/dusk/globals.cpp src/dusk/globals.cpp
src/dusk/gyro.cpp src/dusk/gyro.cpp
src/dusk/game_combos.cpp
src/dusk/d_trigger_view.cpp
#src/dusk/m_Do_ext_dusk.cpp
src/dusk/hq_minimap.cpp src/dusk/hq_minimap.cpp
src/dusk/imgui/ImGuiActorSpawner.cpp src/dusk/imgui/ImGuiActorSpawner.cpp
src/dusk/imgui/ImGuiBloomWindow.cpp src/dusk/imgui/ImGuiBloomWindow.cpp
@@ -1521,12 +1526,12 @@ set(DUSK_FILES
src/dusk/touch_camera.cpp src/dusk/touch_camera.cpp
src/dusk/ui/achievements.cpp src/dusk/ui/achievements.cpp
src/dusk/ui/achievements.hpp src/dusk/ui/achievements.hpp
src/dusk/ui/command_console.cpp
src/dusk/ui/command_console.hpp
src/dusk/ui/bool_button.cpp src/dusk/ui/bool_button.cpp
src/dusk/ui/bool_button.hpp src/dusk/ui/bool_button.hpp
src/dusk/ui/button.cpp src/dusk/ui/button.cpp
src/dusk/ui/button.hpp src/dusk/ui/button.hpp
src/dusk/ui/color_input.cpp
src/dusk/ui/color_input.hpp
src/dusk/ui/component.cpp src/dusk/ui/component.cpp
src/dusk/ui/component.hpp src/dusk/ui/component.hpp
src/dusk/ui/controller_config.cpp src/dusk/ui/controller_config.cpp
@@ -1540,8 +1545,6 @@ set(DUSK_FILES
src/dusk/ui/event.hpp src/dusk/ui/event.hpp
src/dusk/ui/graphics_tuner.cpp src/dusk/ui/graphics_tuner.cpp
src/dusk/ui/graphics_tuner.hpp src/dusk/ui/graphics_tuner.hpp
src/dusk/ui/group_button.cpp
src/dusk/ui/group_button.hpp
src/dusk/ui/icon_provider.cpp src/dusk/ui/icon_provider.cpp
src/dusk/ui/icon_provider.hpp src/dusk/ui/icon_provider.hpp
src/dusk/ui/input.cpp src/dusk/ui/input.cpp
@@ -1559,16 +1562,12 @@ set(DUSK_FILES
src/dusk/ui/mods_window.cpp src/dusk/ui/mods_window.cpp
src/dusk/ui/mods_window.hpp src/dusk/ui/mods_window.hpp
src/dusk/ui/nav_types.hpp src/dusk/ui/nav_types.hpp
src/dusk/ui/nav_group.cpp
src/dusk/ui/nav_group.hpp
src/dusk/ui/number_button.cpp src/dusk/ui/number_button.cpp
src/dusk/ui/number_button.hpp src/dusk/ui/number_button.hpp
src/dusk/ui/overlay.cpp src/dusk/ui/overlay.cpp
src/dusk/ui/overlay.hpp src/dusk/ui/overlay.hpp
src/dusk/ui/pane.cpp src/dusk/ui/pane.cpp
src/dusk/ui/pane.hpp src/dusk/ui/pane.hpp
src/dusk/ui/popover.cpp
src/dusk/ui/popover.hpp
src/dusk/ui/prelaunch.cpp src/dusk/ui/prelaunch.cpp
src/dusk/ui/prelaunch.hpp src/dusk/ui/prelaunch.hpp
src/dusk/ui/preset.cpp src/dusk/ui/preset.cpp
+1 -1
View File
@@ -97,7 +97,7 @@ public:
/* 0xDEC */ u8 field_0xdec[0xdf9 - 0xdec]; /* 0xDEC */ u8 field_0xdec[0xdf9 - 0xdec];
/* 0xDF9 */ u8 field_0xdf9; /* 0xDF9 */ u8 field_0xdf9;
#if TARGET_PC #if TARGET_PC
bool mItemCheckHandled; bool mItemCheckOverridden;
#endif #endif
}; };
+1 -1
View File
@@ -423,7 +423,7 @@ public:
static DUSK_GAME_DATA daMidna_texData_s const m_texDataTable[21]; static DUSK_GAME_DATA daMidna_texData_s const m_texDataTable[21];
static DUSK_GAME_DATA daMidna_anmData_s const m_anmDataTable[53]; static DUSK_GAME_DATA daMidna_anmData_s const m_anmDataTable[53];
// private: private:
/* 0x568 */ request_of_phase_process_class mPhase; /* 0x568 */ request_of_phase_process_class mPhase;
/* 0x570 */ J3DModel* mpModel; /* 0x570 */ J3DModel* mpModel;
/* 0x574 */ J3DModel* mpShadowModel; /* 0x574 */ J3DModel* mpShadowModel;
-5
View File
@@ -1,15 +1,10 @@
#ifndef D_A_D_A_ITEMBASE_STATIC_H #ifndef D_A_D_A_ITEMBASE_STATIC_H
#define D_A_D_A_ITEMBASE_STATIC_H #define D_A_D_A_ITEMBASE_STATIC_H
#include "dolphin/types.h"
class fopAc_ac_c; class fopAc_ac_c;
int CheckFieldItemCreateHeap(fopAc_ac_c* actor); int CheckFieldItemCreateHeap(fopAc_ac_c* actor);
int CheckItemCreateHeap(fopAc_ac_c* i_this); int CheckItemCreateHeap(fopAc_ac_c* i_this);
#if TARGET_PC
const char* dItem_fieldModelArc(u8 itemNo);
#endif
#endif /* D_A_D_A_ITEMBASE_STATIC_H */ #endif /* D_A_D_A_ITEMBASE_STATIC_H */
+1 -1
View File
@@ -37,7 +37,7 @@ public:
CPaneMgrAlpha* getNoDatBase() { return mNoDatBase; } CPaneMgrAlpha* getNoDatBase() { return mNoDatBase; }
void draw() { _draw(); } void draw() { _draw(); }
// private: private:
/* 0x04 */ JKRArchive* mArchive; /* 0x04 */ JKRArchive* mArchive;
/* 0x08 */ dDlst_FileInfo_c mFileInfo; /* 0x08 */ dDlst_FileInfo_c mFileInfo;
/* 0x1C */ u8 field_0x1c[4]; /* 0x1C */ u8 field_0x1c[4];
+33
View File
@@ -16,6 +16,39 @@
class dFile_info_c; class dFile_info_c;
class J2DPicture; class J2DPicture;
#if TARGET_PC
static PaneCache mSelDtPanes[] = {
{MULTI_CHAR('tate_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n2'), 0.0f, 0.0f, false},
{MULTI_CHAR('gray_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('b_base'), 0.0f, 0.0f, false},
{MULTI_CHAR('b_base1'), 0.0f, 0.0f, false},
};
static PaneCache fileSelPanes[] = {
{MULTI_CHAR('w_uzu00'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu01'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu02'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu03'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu04'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu05'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu06'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu07'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu08'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu09'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msg'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msE'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msR'), 0.0f, 0.0f, false},
{MULTI_CHAR('er_for0'), 0.0f, 0.0f, false},
{MULTI_CHAR('er_for1'), 0.0f, 0.0f, false},
};
#endif
class dDlst_FileSel_c : public dDlst_base_c { class dDlst_FileSel_c : public dDlst_base_c {
public: public:
void draw(); void draw();
-5
View File
@@ -142,12 +142,7 @@ enum {
/* 0x2F */ dItemNo_WEAR_KOKIRI_e, /* 0x2F */ dItemNo_WEAR_KOKIRI_e,
/* 0x30 */ dItemNo_ARMOR_e, /* 0x30 */ dItemNo_ARMOR_e,
/* 0x31 */ dItemNo_WEAR_ZORA_e, /* 0x31 */ dItemNo_WEAR_ZORA_e,
#if TARGET_PC
/* 0x32 */ dItemNo_MAGIC_LV1_e, /* 0x32 */ dItemNo_MAGIC_LV1_e,
dItemNo_SHADOW_CRYSTAL_e = dItemNo_MAGIC_LV1_e,
#else
/* 0x32 */ dItemNo_MAGIC_LV1_e,
#endif
/* 0x33 */ dItemNo_DUNGEON_EXIT_2_e, /* 0x33 */ dItemNo_DUNGEON_EXIT_2_e,
/* 0x34 */ dItemNo_WALLET_LV1_e, /* 0x34 */ dItemNo_WALLET_LV1_e,
/* 0x35 */ dItemNo_WALLET_LV2_e, /* 0x35 */ dItemNo_WALLET_LV2_e,
+1 -1
View File
@@ -30,7 +30,7 @@ public:
void drawSelf() { mpKanteraIcon->draw(); } void drawSelf() { mpKanteraIcon->draw(); }
// private: private:
/* 0x04 */ dDlst_KanteraIcon_c* mpKanteraIcon; /* 0x04 */ dDlst_KanteraIcon_c* mpKanteraIcon;
/* 0x08 */ CPaneMgr* mpParent; /* 0x08 */ CPaneMgr* mpParent;
/* 0x0C */ CPaneMgr* mpGauge; /* 0x0C */ CPaneMgr* mpGauge;
+44 -1
View File
@@ -15,6 +15,49 @@ class dMenu_Fishing_c;
class dMenu_Skill_c; class dMenu_Skill_c;
class dMenu_Insect_c; class dMenu_Insect_c;
class dSelect_cursor_c; class dSelect_cursor_c;
#if TARGET_PC
struct PaneCache {
u64 tag;
f32 origTransX;
f32 origTransY;
bool cached;
};
static PaneCache mpScreenPanes[] = {
{MULTI_CHAR('sa_tex_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('op_tex_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('heart_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('wolf_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_0_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_1_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_2_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('fish_3_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('lett_4_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('maki_5_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n2'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('kabu_6n'), 0.0f, 0.0f, false},
{MULTI_CHAR('t_t00'), 0.0f, 0.0f, false},
{MULTI_CHAR('f_t00'), 0.0f, 0.0f, false},
{MULTI_CHAR('itemn_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('infotxtn'), 0.0f, 0.0f, false},
{MULTI_CHAR('sa_op_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('title_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('menu_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('center_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('info_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('lavel_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('modelbgn'), 0.0f, 0.0f, false},
};
#endif
class dMenu_Collect2D_c; class dMenu_Collect2D_c;
class dMenu_Collect2DTop_c : public dDlst_base_c { class dMenu_Collect2DTop_c : public dDlst_base_c {
public: public:
@@ -129,7 +172,7 @@ public:
J2DPicture* getBlackTex() { return mpBlackTex; } J2DPicture* getBlackTex() { return mpBlackTex; }
u8 getSubWindowOpenCheck() { return mSubWindowOpenCheck; } u8 getSubWindowOpenCheck() { return mSubWindowOpenCheck; }
// private: private:
/* 0x004 */ JKRExpHeap* mpHeap; /* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRExpHeap* mpSubHeap; /* 0x008 */ JKRExpHeap* mpSubHeap;
/* 0x00C */ void* field_0xc; /* 0x00C */ void* field_0xc;
+1 -1
View File
@@ -43,7 +43,7 @@ public:
u8 getStatus() { return mStatus; } u8 getStatus() { return mStatus; }
// private: private:
/* 0x004 */ JKRExpHeap* mpHeap; /* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive; /* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick; /* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -270,7 +270,7 @@ public:
static DUSK_GAME_DATA dMenu_Fmap_c* MyClass; static DUSK_GAME_DATA dMenu_Fmap_c* MyClass;
// private: private:
/* 0x004 */ JKRExpHeap* mpHeap; /* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRExpHeap* mpTalkHeap; /* 0x008 */ JKRExpHeap* mpTalkHeap;
/* 0x00C */ STControl* mpStick; /* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -60,7 +60,7 @@ public:
u8 getStatus() { return mStatus; } u8 getStatus() { return mStatus; }
// private: private:
/* 0x04 */ JKRExpHeap* mpHeap; /* 0x04 */ JKRExpHeap* mpHeap;
/* 0x08 */ JKRArchive* mpArchive; /* 0x08 */ JKRArchive* mpArchive;
/* 0x0C */ STControl* mpStick; /* 0x0C */ STControl* mpStick;
+1 -1
View File
@@ -64,7 +64,7 @@ public:
u8 getStatus() { return mStatus; } u8 getStatus() { return mStatus; }
// private: private:
/* 0x004 */ JKRExpHeap* mpHeap; /* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive; /* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick; /* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -117,7 +117,7 @@ public:
return ((u8)mUseFlag & (u8)i_flag) != 0 ? true : false; return ((u8)mUseFlag & (u8)i_flag) != 0 ? true : false;
} }
// private: private:
/* 0x004 */ J2DScreen* mpBackScreen; /* 0x004 */ J2DScreen* mpBackScreen;
/* 0x008 */ J2DScreen* mpScreen; /* 0x008 */ J2DScreen* mpScreen;
/* 0x00C */ J2DScreen* mpClipScreen; /* 0x00C */ J2DScreen* mpClipScreen;
+1 -1
View File
@@ -84,7 +84,7 @@ public:
void drawFlag0() { mDrawFlag = 0; } void drawFlag0() { mDrawFlag = 0; }
void setStatus(u8 i_status) { mStatus = i_status; } void setStatus(u8 i_status) { mStatus = i_status; }
// private: private:
/* 0x004 */ JKRExpHeap* mpHeap; /* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ STControl* mpStick; /* 0x008 */ STControl* mpStick;
/* 0x00C */ CSTControl* mpCStick; /* 0x00C */ CSTControl* mpCStick;
+1 -1
View File
@@ -278,7 +278,7 @@ public:
u8 getEndStatus() { return mEndStatus; } u8 getEndStatus() { return mEndStatus; }
void setUseType(u8 type) { mUseType = type; } void setUseType(u8 type) { mUseType = type; }
// private: private:
/* 0x0004 */ JKRArchive* mpArchive; /* 0x0004 */ JKRArchive* mpArchive;
/* 0x0008 */ mDoDvdThd_mountArchive_c* mpMount; /* 0x0008 */ mDoDvdThd_mountArchive_c* mpMount;
/* 0x000C */ STControl* stick; /* 0x000C */ STControl* stick;
+1 -1
View File
@@ -58,7 +58,7 @@ public:
u8 getStatus() { return mStatus; } u8 getStatus() { return mStatus; }
// private: private:
/* 0x004 */ JKRExpHeap* mpHeap; /* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive; /* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick; /* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -154,7 +154,7 @@ public:
} }
#endif #endif
// private: private:
/* 0x004 */ item_params mItemParams[4]; /* 0x004 */ item_params mItemParams[4];
/* 0x074 */ JKRExpHeap* heap; /* 0x074 */ JKRExpHeap* heap;
/* 0x078 */ J2DScreen* mpScreen; /* 0x078 */ J2DScreen* mpScreen;
+2 -2
View File
@@ -25,7 +25,7 @@ public:
void setTextBoxPtr(J2DTextBox* p_textBox) { mpTextBoxPtr = p_textBox; } void setTextBoxPtr(J2DTextBox* p_textBox) { mpTextBoxPtr = p_textBox; }
void resetType() { mType = 0x47; } void resetType() { mType = 0x47; }
// private: private:
/* 0x04 */ J2DTextBox* mpTextBoxPtr; /* 0x04 */ J2DTextBox* mpTextBoxPtr;
/* 0x08 */ f32 mPosX; /* 0x08 */ f32 mPosX;
/* 0x0C */ f32 mPosY; /* 0x0C */ f32 mPosY;
@@ -53,7 +53,7 @@ public:
void setRupeeColor(u8 color) { mRupeeColor = color; } void setRupeeColor(u8 color) { mRupeeColor = color; }
// private: private:
/* 0x004 */ COutFontSet_c* mpOfs[35]; /* 0x004 */ COutFontSet_c* mpOfs[35];
/* 0x090 */ J2DPicture* mpPane[70]; /* 0x090 */ J2DPicture* mpPane[70];
/* 0x1A8 */ f32 mAlphaRatio; /* 0x1A8 */ f32 mAlphaRatio;
+28
View File
@@ -12,6 +12,34 @@ class J2DTextBox;
class JUTFont; class JUTFont;
class STControl; class STControl;
#if TARGET_PC
struct PaneCache {
u64 tag;
f32 origTransX;
f32 origTransY;
bool cached;
};
static PaneCache l_tagName[] = {
{MULTI_CHAR('m_00_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_3'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_01_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_2'), 0.0f, 0.0f, false},
{MULTI_CHAR('m03_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_1'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_05_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_0'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_07_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_4'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_09_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_3'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_10_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_2'), 0.0f, 0.0f, false},
{MULTI_CHAR('m12_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_0'), 0.0f, 0.0f, false},
};
static PaneCache l_nameTagName[] = {
{MULTI_CHAR('name_00'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_01'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_02'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_03'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_04'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_05'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_06'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_07'), 0.0f, 0.0f, false},
};
static PaneCache l_nameCurTagName[] = {
{MULTI_CHAR('s__n_00'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_01'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_02'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_03'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_04'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_05'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_06'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_07'), 0.0f, 0.0f, false},
};
#endif
class dNm_HIO_c { class dNm_HIO_c {
public: public:
dNm_HIO_c(); dNm_HIO_c();
+3
View File
@@ -1351,6 +1351,9 @@ enum dStage_SaveTbl {
const char* dStage_getName2(s16, s8); const char* dStage_getName2(s16, s8);
dStage_objectNameInf* dStage_searchName(const char*); dStage_objectNameInf* dStage_searchName(const char*);
#if TARGET_PC
dStage_objectNameInf* dStage_searchNameCI(const char*);
#endif
static int dStage_stageKeepTresureInit(dStage_dt_c*, void*, int, void*); static int dStage_stageKeepTresureInit(dStage_dt_c*, void*, int, void*);
static int dStage_filiInfo2Init(dStage_dt_c*, void*, int, void*); static int dStage_filiInfo2Init(dStage_dt_c*, void*, int, void*);
static int dStage_mapPathInitCommonLayer(dStage_dt_c*, void*, int, void*); static int dStage_mapPathInitCommonLayer(dStage_dt_c*, void*, int, void*);
+1 -7
View File
@@ -259,19 +259,13 @@ using std::isnan;
#if TARGET_PC && defined(DUSK_BUILDING_GAME) #if TARGET_PC && defined(DUSK_BUILDING_GAME)
#include "dusk/mods/item.hpp" #include "dusk/mods/item.hpp"
#define DUSK_ITEM_CHECK(name, item_no, giver) \ #define DUSK_ITEM_CHECK(name, item_no, giver) \
(item_no) = ::dusk::mods::item_check_commit(name, (item_no), giver).itemNo
#define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) \
(::dusk::mods::item_check_commit(name, (item_no), giver).itemNo)
#define DUSK_ITEM_CHECK_PREVIEW(name, item_no, giver) \
(item_no) = ::dusk::mods::item_check(name, (item_no), giver) (item_no) = ::dusk::mods::item_check(name, (item_no), giver)
#define DUSK_ITEM_CHECK_PREVIEW_EXPR(name, item_no, giver) \ #define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) \
(::dusk::mods::item_check(name, (item_no), giver)) (::dusk::mods::item_check(name, (item_no), giver))
#define DUSK_GIVE_TAG(name) IF_DUSK_ARG(::dusk::mods::item_give_tag(name)) #define DUSK_GIVE_TAG(name) IF_DUSK_ARG(::dusk::mods::item_give_tag(name))
#else #else
#define DUSK_ITEM_CHECK(name, item_no, giver) #define DUSK_ITEM_CHECK(name, item_no, giver)
#define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) (item_no) #define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) (item_no)
#define DUSK_ITEM_CHECK_PREVIEW(name, item_no, giver)
#define DUSK_ITEM_CHECK_PREVIEW_EXPR(name, item_no, giver) (item_no)
#define DUSK_GIVE_TAG(name) #define DUSK_GIVE_TAG(name)
#endif #endif
+1 -1
View File
@@ -68,7 +68,7 @@ public:
JKRMemArchive* getArchive() const { return mArchive; } JKRMemArchive* getArchive() const { return mArchive; }
JKRHeap* getHeap() const { return mHeap; } JKRHeap* getHeap() const { return mHeap; }
// private: private:
/* 0x14 */ u8 mMountDirection; /* 0x14 */ u8 mMountDirection;
/* 0x18 */ s32 mEntryNumber; /* 0x18 */ s32 mEntryNumber;
/* 0x1C */ JKRMemArchive* mArchive; /* 0x1C */ JKRMemArchive* mArchive;
@@ -9,7 +9,6 @@
class J3DModelData; class J3DModelData;
class J3DMaterialTable; class J3DMaterialTable;
struct J3DModelHierarchy; struct J3DModelHierarchy;
class J3DVertexData;
#if TARGET_PC #if TARGET_PC
#define OFFSET_PTR_V0 BE(u32) #define OFFSET_PTR_V0 BE(u32)
-35
View File
@@ -1,35 +0,0 @@
cmake_minimum_required(VERSION 3.25)
project(basic_cosmetics_mod CXX)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(DUSK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE PATH "Path to dusk source root")
option(DUSK_MOD_USE_FULL_TREE "Use full build instead of the minimal mod SDK" OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (DUSK_MOD_USE_FULL_TREE)
add_subdirectory("${DUSK_DIR}" dusk EXCLUDE_FROM_ALL)
else ()
add_subdirectory("${DUSK_DIR}/sdk" dusk-sdk EXCLUDE_FROM_ALL)
endif ()
endif ()
include(FetchContent)
message(STATUS "basic_cosmetics_mod: Fetching xxhash")
FetchContent_Declare(
xxhash
GIT_REPOSITORY https://github.com/Cyan4973/xxHash.git
GIT_TAG v0.8.3
)
FetchContent_MakeAvailable(xxhash)
set(COSMETIC_SOURCES src/color_utils.cpp src/midna_hair_color.cpp src/texture_utils.cpp src/hooks.cpp)
add_mod(basic_cosmetics_mod
FEATURES game fmt
SOURCES src/mod.cpp ${COSMETIC_SOURCES}
MOD_JSON mod.json
RES_DIR res
BUNDLE
)
target_link_libraries(basic_cosmetics_mod PRIVATE xxHash::xxhash)
-7
View File
@@ -1,7 +0,0 @@
{
"id": "dev.twilitrealm.basic_cosmetics",
"name": "Basic Cosmetics",
"version": "1.0.0",
"author": "Twilit Realm",
"description": "Change Basic Cosmetic Colors"
}
@@ -1,88 +0,0 @@
#include "color_utils.hpp"
uint8_t desaturate_rgb_565(uint16_t rgb565Val) {
const uint32_t r = (rgb565Val & 0xf800) >> 11;
const uint32_t g = (rgb565Val & 0x7e0) >> 5;
const uint32_t b = rgb565Val & 0x1f;
// Here we are doing a quicker (0.22 * r + 0.72 * g + 0.06 * b) which
// uses multiplies and shifts rather than division.
const uint32_t combined = 30480413 * r + 49085341 * g + 8312839 * b;
uint8_t shifted = (combined >> 24) & 0xff;
// Check if should round up shifted value.
if (shifted < 0xff && combined & 0x00800000) {
shifted += 1;
}
return shifted;
}
uint16_t blend_overlay_rgb_565(uint8_t grayVal, GXColor color) {
uint32_t rTimes255, gTimes255, bTimes255;
if (grayVal <= 0x7f) {
const uint32_t grayTimesTwo = 2 * grayVal;
rTimes255 = grayTimesTwo * color.r;
gTimes255 = grayTimesTwo * color.g;
bTimes255 = grayTimesTwo * color.b;
} else {
const uint32_t multiplier = 2 * (255 - grayVal);
rTimes255 = 255 * 255 - multiplier * (255 - color.r);
gTimes255 = 255 * 255 - multiplier * (255 - color.g);
bTimes255 = 255 * 255 - multiplier * (255 - color.b);
}
// Divide each by 255
const uint32_t r = (rTimes255 + 1 + (rTimes255 >> 8)) >> 8;
const uint32_t g = (gTimes255 + 1 + (gTimes255 >> 8)) >> 8;
const uint32_t b = (bTimes255 + 1 + (bTimes255 >> 8)) >> 8;
return ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3);
}
// Helper function to perform overlay blending on a single 8-bit color channel
uint8_t blend_overlay_channel(uint8_t base, uint8_t blend) {
if (base < 128) {
return static_cast<uint8_t>((2 * base * blend) / 255);
}
return static_cast<uint8_t>(255 - (2 * (255 - base) * (255 - blend)) / 255);
}
bool is_valid_hex_color_str(std::string_view hexStr) {
return hexStr.find_first_not_of("0123456789ABCDEFabcdef") == std::string_view::npos &&
hexStr.length() == 6;
}
GXColor hex_color_str_to_gx_color(const std::string& hexColorStr) {
u8 r = std::stoi(hexColorStr.substr(0, 2), nullptr, 16);
u8 g = std::stoi(hexColorStr.substr(2, 2), nullptr, 16);
u8 b = std::stoi(hexColorStr.substr(4, 2), nullptr, 16);
return GXColor{r, g, b};
}
static f32 rainbowPhaseAngle = 0.f;
GXColor get_rainbow_rgb(f32 amplitude) {
f32 phase_rad = rainbowPhaseAngle * M_PI / 180.0f;
u8 r_val = (u8)(amplitude * (sinf(phase_rad) + 1.0f) + 0.5f);
u8 g_val = (u8)(amplitude * (sinf(phase_rad + 2.0f * M_PI / 3.0f) + 1.0f) + 0.5f);
u8 b_val = (u8)(amplitude * (sinf(phase_rad + 4.0f * M_PI / 3.0f) + 1.0f));
GXColor rgbColor;
rgbColor.r = r_val;
rgbColor.g = g_val;
rgbColor.b = b_val;
rgbColor.a = 0xff;
return rgbColor;
}
void update_rainbow_rgb(f32 increment) {
rainbowPhaseAngle += increment;
if (rainbowPhaseAngle >= 360.0f) {
rainbowPhaseAngle -= 360.0f;
}
}
@@ -1,30 +0,0 @@
#pragma once
/**
* File originally copied from console TPR with permission from isaac
* https://github.com/zsrtp/libtp_rel/blob/master/include/util/color_utils.h
*/
#include <gx.h>
#include <cstdint>
#include <string>
// Desaturates an RGB565 color to a u8 gray value (0xFF being white and 0x00
// being black).
uint8_t desaturate_rgb_565(uint16_t rgb565Val);
// Performs an "Overlay" blend of a u8 gray value and a pointer to a u8
// array of {r,g,b}. Returns the result as an RGB565.
uint16_t blend_overlay_rgb_565(uint8_t grayVal, GXColor color);
// Perform overlay blending on a single 8-bit color channel
uint8_t blend_overlay_channel(uint8_t base, uint8_t blend);
bool is_valid_hex_color_str(std::string_view hexStr);
GXColor hex_color_str_to_gx_color(const std::string& hexColorStr);
GXColor get_rainbow_rgb(f32 amplitude);
void update_rainbow_rgb(f32 increment);
-731
View File
@@ -1,731 +0,0 @@
#include "hooks.hpp"
#include "color_utils.hpp"
#include "midna_hair_color.hpp"
#include "mod.hpp"
#include "types.h"
#include "mods/svc/hook.hpp"
#include "mods/svc/log.hpp"
#include "SSystem/SComponent/c_phase.h"
#include "d/actor/d_a_alink.h"
#include "d/actor/d_a_midna.h"
#include "d/d_a_item_static.h"
#include "d/d_bright_check.h"
#include "d/d_file_sel_info.h"
#include "d/d_file_select.h"
#include "d/d_kankyo.h"
#include "d/d_kantera_icon_meter.h"
#include "d/d_menu_collect.h"
#include "d/d_menu_fishing.h"
#include "d/d_menu_fmap2D.h"
#include "d/d_menu_insect.h"
#include "d/d_menu_letter.h"
#include "d/d_menu_option.h"
#include "d/d_menu_ring.h"
#include "d/d_menu_save.h"
#include "d/d_menu_skill.h"
#include "d/d_meter2.h"
#include "d/d_meter2_draw.h"
#include "d/d_meter2_info.h"
#include "d/d_meter_button.h"
#include "d/d_meter_hakusha.h"
#include "d/d_msg_object.h"
#include "d/d_msg_out_font.h"
#include "d/d_msg_scrn_base.h"
#include "d/d_pane_class.h"
// Lantern ambience color
DEFINE_HOOK(&dKy_WolfEyeLight_set, WolfEyeLightSet);
void wolf_eye_light_set_post(ModContext*, void*, void*, void*) {
auto maybeLanternColor = get_config_var_color(get_cvars().lanternGlowColor, true);
if (maybeLanternColor.has_value()) {
auto lanternColor = maybeLanternColor.value();
dScnKy_env_light_c* kankyo = dKy_getEnvlight();
kankyo->field_0x0c18[0].mColor.r = lanternColor.r;
kankyo->field_0x0c18[0].mColor.g = lanternColor.g;
kankyo->field_0x0c18[0].mColor.b = lanternColor.b;
}
}
// Lantern Sphere color
DEFINE_HOOK(&daAlink_c::preKandelaarDraw, PreKandelaarDraw);
void pre_kandelaar_draw_post(ModContext*, void*, void*, void*) {
auto maybeLanternColor = get_config_var_color(get_cvars().lanternGlowColor, true);
if (maybeLanternColor.has_value()) {
auto lanternColor = maybeLanternColor.value();
J3DMaterial* mat_p = daAlink_getAlinkActorClass()
->mpKanteraGlowModel->getModelData()
->getMaterialNodePointer(0);
J3DGXColorS10 color;
color.r = lanternColor.r;
color.g = lanternColor.g;
color.b = lanternColor.b;
color.a = 255;
mat_p->setTevColor(1, &color);
color.r = lanternColor.r;
color.g = lanternColor.g;
color.b = lanternColor.b;
mat_p->setTevColor(2, &color);
}
}
// Main Lantern Meter Color
DEFINE_HOOK(&CPaneMgr::setBlackWhite, CPaneMgrSetBlackWhite);
HookAction cpane_mgr_set_black_white_pre(ModContext*, void* args, void*, void*) {
// Check for magic meter
auto pane = mods::arg<CPaneMgr*>(args, 0);
if (dMeter2Info_getMeterClass() == NULL ||
pane != dMeter2Info_getMeterClass()->getMeterDrawPtr()->mpMagicMeter)
{
return HOOK_CONTINUE;
}
// If magic meter, check to see we're setting lantern colors
auto& black = mods::arg_ref<JUtility::TColor>(args, 1);
auto& white = mods::arg_ref<JUtility::TColor>(args, 2);
if (black != JUtility::TColor(255, 255, 140, 255) &&
white != JUtility::TColor(230, 170, 0, 255))
{
return HOOK_CONTINUE;
}
auto maybeLanternColor = get_config_var_color(get_cvars().lanternGlowColor, true);
if (maybeLanternColor.has_value()) {
auto lanternColor = maybeLanternColor.value();
black = JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255);
white = JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255);
}
return HOOK_CONTINUE;
}
// Lantern Icon Meter Color
DEFINE_HOOK(&dKantera_icon_c::setNowGauge, KanteraIconSetNowGauge);
void kantera_icon_set_now_gauge_post(ModContext*, void* args, void*, void*) {
auto maybeLanternColor = get_config_var_color(get_cvars().lanternGlowColor, true);
if (maybeLanternColor.has_value()) {
auto lanternColor = maybeLanternColor.value();
auto kanteraIcon = mods::arg<dKantera_icon_c*>(args, 0);
kanteraIcon->mpGauge->setBlackWhite(
JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255),
JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255));
}
}
// Light Sword Effect Color
DEFINE_HOOK(&daAlink_c::setLightningSwordEffect, SetLightningSwordEffect);
void set_lightning_sword_effect_post(ModContext*, void* args, void*, void*) {
auto maybeGlowColor = get_config_var_color(get_cvars().lightSwordGlowColor, true);
if (maybeGlowColor.has_value()) {
auto glowColor = maybeGlowColor.value();
auto link = mods::arg<daAlink_c*>(args, 0);
// Check copied from inside the hooked function
if (link->mEquipItem == 0x103 && link->checkNoResetFlg3(daPy_py_c::FLG3_UNK_100000)) {
for (size_t i = 0; i < 3; i++) {
auto emitter = dComIfGp_particle_getEmitter(link->field_0x327c[i]);
if (emitter != NULL) {
emitter->setGlobalEnvColor(glowColor.r, glowColor.g, glowColor.b);
emitter->setGlobalPrmColor(glowColor.r, glowColor.g, glowColor.b);
}
}
}
}
}
void recolor_ui_button(ConfigVarHandle option, u64 tag, J2DScreen* screen) {
auto buttonColorStr = get_str_option(option, "");
if (is_valid_hex_color_str(buttonColorStr)) {
auto color = hex_color_str_to_gx_color(buttonColorStr);
auto element = static_cast<J2DPicture*>(screen->search(tag));
if (element != nullptr) {
element->setBlackWhite(
JUtility::TColor(0, 0, 0, 0), JUtility::TColor(color.r, color.g, color.b, 0xFF));
}
}
}
// Main gameplay UI. Top left hearts and top right buttons
DEFINE_HOOK(&dMeter2Draw_c::init, dMeter2Init);
void d_meter_2_init_post(ModContext*, void* args, void*, void*) {
auto dMeter2Draw = mods::arg<dMeter2Draw_c*>(args, 0);
auto screen = dMeter2Draw->getMainScreenPtr();
// Heart tags on main UI
static constexpr std::array kHeartTags = {MULTI_CHAR('hear_00'), MULTI_CHAR('hear_01'),
MULTI_CHAR('hear_02'), MULTI_CHAR('hear_03'), MULTI_CHAR('hear_04'), MULTI_CHAR('hear_05'),
MULTI_CHAR('hear_06'), MULTI_CHAR('hear_07'), MULTI_CHAR('hear_08'), MULTI_CHAR('hear_09'),
MULTI_CHAR('hear_10'), MULTI_CHAR('hear_11'), MULTI_CHAR('hear_12'), MULTI_CHAR('hear_13'),
MULTI_CHAR('hear_14'), MULTI_CHAR('hear_15'), MULTI_CHAR('hear_16'), MULTI_CHAR('hear_17'),
MULTI_CHAR('hear_18'), MULTI_CHAR('hear_19'), MULTI_CHAR('bigh_00'), MULTI_CHAR('bigh_01'),
MULTI_CHAR('bigh_02'), MULTI_CHAR('bigh_03')};
auto maybeHeartColor = get_config_var_color(get_cvars().heartColor);
if (maybeHeartColor.has_value()) {
auto heartColor = maybeHeartColor.value();
for (auto tag : kHeartTags) {
auto element = static_cast<J2DPicture*>(screen->search(tag));
if (element != nullptr) {
element->setBlackWhite(heartColor, JUtility::TColor(200, 200, 200, 255));
}
}
}
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
recolor_ui_button(get_cvars().xButtonColor, MULTI_CHAR('x_btn'), screen);
recolor_ui_button(get_cvars().yButtonColor, MULTI_CHAR('y_btn'), screen);
recolor_ui_button(get_cvars().zButtonColor, MULTI_CHAR('zbtn'), screen);
}
// Hearts on the file select screen
DEFINE_HOOK(&dFile_info_c::setHeartCnt, FileInfoSetHeartCount);
void file_info_set_heart_count_post(ModContext*, void* args, void*, void*) {
auto fileInfo = mods::arg<dFile_info_c*>(args, 0);
// Heart tags on file select
static constexpr std::array kFileSelectHeartTags{
MULTI_CHAR('hear_20'),
MULTI_CHAR('hear_21'),
MULTI_CHAR('hear_22'),
MULTI_CHAR('hear_23'),
MULTI_CHAR('hear_24'),
MULTI_CHAR('hear_25'),
MULTI_CHAR('hear_26'),
MULTI_CHAR('hear_27'),
MULTI_CHAR('hear_28'),
MULTI_CHAR('hear_29'),
MULTI_CHAR('hear_30'),
MULTI_CHAR('hear_31'),
MULTI_CHAR('hear_32'),
MULTI_CHAR('hear_33'),
MULTI_CHAR('hear_34'),
MULTI_CHAR('hear_35'),
MULTI_CHAR('hear_36'),
MULTI_CHAR('hear_37'),
MULTI_CHAR('hear_38'),
MULTI_CHAR('hear_39'),
};
auto maybeHeartColor = get_config_var_color(get_cvars().heartColor);
if (maybeHeartColor.has_value()) {
auto heartColor = maybeHeartColor.value();
for (auto tag : kFileSelectHeartTags) {
auto element = static_cast<J2DPicture*>(fileInfo->mFileInfo.Scr->search(tag));
if (element != nullptr) {
element->setBlackWhite(heartColor, JUtility::TColor(200, 200, 200, 255));
}
}
}
}
// Buttons that appear contextually at the bottom of the screen during gameplay
// (Are X, Y ever used there?)
DEFINE_HOOK(&dMeterButton_c::screenInitButton, ScreenInitButton);
void screen_init_button_post(ModContext*, void* args, void*, void*) {
auto meterButton = mods::arg<dMeterButton_c*>(args, 0);
auto screen = meterButton->mpButtonScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn1'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
recolor_ui_button(get_cvars().xButtonColor, MULTI_CHAR('x_btn'), screen);
recolor_ui_button(get_cvars().yButtonColor, MULTI_CHAR('y_btn'), screen);
recolor_ui_button(get_cvars().zButtonColor, MULTI_CHAR('zbtn'), screen);
}
// A and B buttons when saving a file
DEFINE_HOOK(&dMenu_save_c::screenSet, MenuSaveScreenSet);
void menu_save_screen_set_post(ModContext*, void* args, void*, void*) {
auto menuSave = mods::arg<dMenu_save_c*>(args, 0);
auto screen = menuSave->mSaveSel.Scr;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('wabtn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('wbbtn'), screen);
}
// A and B buttons when selecting a file
DEFINE_HOOK(&dFile_select_c::screenSet, FileSelectScreenSet);
void file_select_screen_set_post(ModContext*, void* args, void*, void*) {
auto fileSelect = mods::arg<dFile_select_c*>(args, 0);
auto screen = fileSelect->fileSel.Scr;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('wabtn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('wbbtn'), screen);
}
// A button on brightness check screen
DEFINE_HOOK(&dBrightCheck_c::screenSet, BrightCheckScreenSet);
void bright_check_screen_set_post(ModContext*, void* args, void*, void*) {
auto brightCheck = mods::arg<dBrightCheck_c*>(args, 0);
auto screen = brightCheck->mBrightCheck.Scr;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn1'), screen);
}
// X and Y buttons on the item wheel screen
DEFINE_HOOK(&dMenu_Ring_c::_create, MenuRingCreate);
void menu_ring_create_post(ModContext*, void* args, void*, void*) {
auto menuRing = mods::arg<dMenu_Ring_c*>(args, 0);
auto screen = menuRing->mpScreen;
recolor_ui_button(get_cvars().xButtonColor, MULTI_CHAR('xbtn'), screen);
recolor_ui_button(get_cvars().yButtonColor, MULTI_CHAR('ybtn'), screen);
}
// A and B buttons on the main pause screen
DEFINE_HOOK(&dMenu_Collect2D_c::_create, MenuCollect2DCreate);
void menu_collect_2D_create_post(ModContext*, void* args, void*, void*) {
auto menuCollect2D = mods::arg<dMenu_Collect2D_c*>(args, 0);
auto screen = menuCollect2D->mpScreenIcon;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A and B buttons on the fishing journal screen
DEFINE_HOOK(&dMenu_Fishing_c::screenSetDoIcon, MenuFishingScreenSetDoIcon);
void menu_fishing_screen_set_do_icon_post(ModContext*, void* args, void*, void*) {
auto menuFishing = mods::arg<dMenu_Fishing_c*>(args, 0);
auto screen = menuFishing->mpIconScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A and B buttons on the insect collection screen
DEFINE_HOOK(&dMenu_Insect_c::screenSetDoIcon, MenuInsectScreenSetDoIcon);
void menu_insect_screen_set_do_icon_post(ModContext*, void* args, void*, void*) {
auto menuInsect = mods::arg<dMenu_Insect_c*>(args, 0);
auto screen = menuInsect->mpIconScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A and B buttons on the letters screen
DEFINE_HOOK(&dMenu_Letter_c::screenSetDoIcon, MenuLetterScreenSetDoIcon);
void menu_letter_screen_set_do_icon_post(ModContext*, void* args, void*, void*) {
auto menuLetter = mods::arg<dMenu_Letter_c*>(args, 0);
auto screen = menuLetter->mpIconScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A, B, and Z buttons on option screen
DEFINE_HOOK(&dMenu_Option_c::_create, MenuOptionCreate);
void menu_option_create_post(ModContext*, void* args, void*, void*) {
auto menuOption = mods::arg<dMenu_Option_c*>(args, 0);
auto screen = menuOption->mpScreenIcon;
auto backScreen = menuOption->mpBackScreen;
auto tvScreen = menuOption->mpTVScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
recolor_ui_button(get_cvars().zButtonColor, MULTI_CHAR('g_zbtn'), backScreen);
// A Button on option's brightness check screen
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn1'), tvScreen);
}
// A and B buttons on the hidden skills screen
DEFINE_HOOK(&dMenu_Skill_c::screenSetDoIcon, MenuSkillScreenSetDoIcon);
void menu_skill_screen_set_do_icon_post(ModContext*, void* args, void*, void*) {
auto menuSkill = mods::arg<dMenu_Skill_c*>(args, 0);
auto screen = menuSkill->mpIconScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A, B, and Z buttons on the map screen
DEFINE_HOOK(&dMenu_Fmap_c::_create, MenuFMapCreate);
void menu_fmap_create_post(ModContext*, void* args, void*, void*) {
auto menuFMap = mods::arg<dMenu_Fmap_c*>(args, 0);
auto screen = menuFMap->mpDraw2DTop->mpTitleScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn1'), screen);
recolor_ui_button(get_cvars().zButtonColor, MULTI_CHAR('zbtn'), screen);
}
// A button on the howling screen
DEFINE_HOOK(&dMsgObject_c::talkStartInit, MsgObjectTalkStartInit);
void msg_object_talk_start_init_post(ModContext*, void* args, void*, void*) {
auto msgObject = mods::arg<dMsgObject_c*>(args, 0);
// If textbox kind is howling
if (msgObject->mFukiKind == 17) {
auto screen = msgObject->mpScrnDraw->mpScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('abtn'), screen);
}
}
// A button when on Epona
DEFINE_HOOK(&dMeterHakusha_c::_create, MeterHakushaCreate);
void meter_hakusha_create_post(ModContext*, void* args, void*, void*) {
auto meterHakusha = mods::arg<dMeterHakusha_c*>(args, 0);
auto screen = meterHakusha->mpButtonScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn1'), screen);
}
// A, B, X, and Y button icons in text
DEFINE_HOOK(&COutFont_c::createPane, OutFontCreatePane);
void out_font_create_pane_post(ModContext*, void* args, void*, void*) {
auto outFont = mods::arg<COutFont_c*>(args, 0);
auto paneArr = outFont->mpPane;
auto maybeAButtonColor = get_config_var_color(get_cvars().aButtonColor);
if (maybeAButtonColor.has_value()) {
auto aButtonColor = maybeAButtonColor.value();
paneArr[0]->setBlackWhite(JUtility::TColor(255, 255, 255, 0),
JUtility::TColor(aButtonColor.r, aButtonColor.g, aButtonColor.b, 255));
}
auto maybeBButtonColor = get_config_var_color(get_cvars().bButtonColor);
if (maybeBButtonColor.has_value()) {
auto bButtonColor = maybeBButtonColor.value();
paneArr[1]->setBlackWhite(JUtility::TColor(255, 255, 255, 0),
JUtility::TColor(bButtonColor.r, bButtonColor.g, bButtonColor.b, 255));
}
// Condition copied from hooked function
auto xyBlack = JUtility::TColor(255, 255, 255, 0);
if (outFont->field_0x242 == 1) {
xyBlack = JUtility::TColor(0, 0, 0, 0);
}
auto maybeXButtonColor = get_config_var_color(get_cvars().xButtonColor);
if (maybeXButtonColor.has_value()) {
auto xButtonColor = maybeXButtonColor.value();
paneArr[5]->setBlackWhite(
xyBlack, JUtility::TColor(xButtonColor.r, xButtonColor.g, xButtonColor.b, 255));
}
auto maybeYButtonColor = get_config_var_color(get_cvars().yButtonColor);
if (maybeYButtonColor.has_value()) {
auto yButtonColor = maybeYButtonColor.value();
paneArr[6]->setBlackWhite(
xyBlack, JUtility::TColor(yButtonColor.r, yButtonColor.g, yButtonColor.b, 255));
}
}
// Heart icon in text
DEFINE_HOOK(&COutFontSet_c::drawFont, OutFontSetDrawFont);
void out_font_set_draw_font_post(ModContext*, void* args, void*, void*) {
auto outFontSet = mods::arg<COutFontSet_c*>(args, 0);
auto maybeHeartColor = get_config_var_color(get_cvars().heartColor);
if (maybeHeartColor.has_value()) {
auto heartColor = maybeHeartColor.value();
u32 heartColor_u32 = heartColor.r << 24 | heartColor.g << 16 | heartColor.b << 8 | 0xFF;
if (outFontSet->getType() == 0x1B) { // Heart icon type
outFontSet->mColor = heartColor_u32;
}
}
}
// Heart Drop/Piece of Heart/Heart Container model color
DEFINE_HOOK(&daItemBase_c::CreateItemHeap, ItemBaseCreateItemHeap);
void item_base_create_item_heap_post(ModContext*, void* args, void*, void*) {
auto itemBase = mods::arg<daItemBase_c*>(args, 0);
if (itemBase == NULL) {
return;
}
auto itemNo = itemBase->m_itemNo;
if (itemNo == dItemNo_HEART_e || itemNo == dItemNo_UTAWA_HEART_e ||
itemNo == dItemNo_KAKERA_HEART_e)
{
auto maybeHeartColor = get_config_var_color(get_cvars().heartColor);
if (maybeHeartColor.has_value()) {
auto heartColor = maybeHeartColor.value();
auto heartColorS10 = GXColorS10(heartColor.r, heartColor.g, heartColor.b, heartColor.a);
// Edit inner heart material color for Piece of Heart / Heart Container
if (itemNo == dItemNo_UTAWA_HEART_e || itemNo == dItemNo_KAKERA_HEART_e) {
*itemBase->mpModel->getModelData()->getMaterialNodePointer(3)->getTevKColor(1) =
heartColor;
*itemBase->mpModel->getModelData()->getMaterialNodePointer(3)->getTevColor(1) =
heartColorS10;
}
const u8 heartColorRGB[3] = {heartColor.r, heartColor.g, heartColor.b};
u8** cRegTable =
reinterpret_cast<u8**>(&itemBase->mpBrkAnm->getBrkAnm()->mAnmCRegDataR);
u8** kRegTable =
reinterpret_cast<u8**>(&itemBase->mpBrkAnm->getBrkAnm()->mAnmKRegDataR);
for (int i = 0; i < 3; i++) {
u8* cReg = cRegTable[i];
u8* kReg = kRegTable[i];
auto curColor = heartColorRGB[i];
// Set heart drop inner heart color
cReg[0x3] = curColor;
cReg[0xB] = curColor;
// Set heart drop outer heart color
kReg[0x3] = curColor;
kReg[0xB] = curColor;
if (itemNo == dItemNo_KAKERA_HEART_e) {
cReg[0x13] = curColor;
cReg[0x1B] = curColor;
kReg[0x13] = curColor;
kReg[0x1B] = curColor;
}
if (itemNo == dItemNo_UTAWA_HEART_e) {
cReg[0x13] = curColor;
kReg[0x13] = curColor;
kReg[0x1B] = curColor;
kReg[0x23] = curColor;
kReg[0x2B] = curColor;
}
}
}
}
}
// Midna Hair Color
DEFINE_HOOK(&daMidna_c::create, MidnaCreate);
void midna_create_post(ModContext*, void* args, void* retval, void*) {
auto step = reinterpret_cast<int*>(retval);
// Don't set colors if midna isn't done loading
if (*step != cPhs_COMPLEATE_e) {
return;
}
auto midna = mods::arg<daMidna_c*>(args, 0);
midna->field_0x6e0 = g_currentMidnaHairColors.normalColor;
if (dKy_darkworld_check()) {
midna->field_0x6e8 = g_currentMidnaHairColors.normalKColor;
midna->field_0x6ec = g_currentMidnaHairColors.normalKColor2;
} else {
midna->field_0x6e8 = g_currentMidnaHairColors.lNormalKColor;
midna->field_0x6ec = g_currentMidnaHairColors.lNormalKColor2;
}
}
// Override Midna Hair Color Part 2
DEFINE_HOOK(&daMidna_c::setBodyPartMatrix, MidnaSetBodyPartMatrix);
static GXColorS10 midnaField0x6e0{};
static GXColor midnaField0x6e8{};
static GXColor midnaField0x6ec{};
// Copy the original values before setBodyPartMatrix runs
HookAction midna_set_body_part_matrix_pre(ModContext*, void* args, void* retval, void* userdata) {
auto midna = mods::arg<daMidna_c*>(args, 0);
midnaField0x6e0 = midna->field_0x6e0;
midnaField0x6e8 = midna->field_0x6e8;
midnaField0x6ec = midna->field_0x6ec;
return HOOK_CONTINUE;
}
void midna_set_body_part_matrix_post(ModContext*, void* args, void* retval, void* userdata) {
auto midna = mods::arg<daMidna_c*>(args, 0);
if (midna->mpHairhandBmd != NULL) {
// Restore the original values from before setBodyPartMatrix ran (this undoes the chase)
midna->field_0x6e0 = midnaField0x6e0;
midna->field_0x6e8 = midnaField0x6e8;
midna->field_0x6ec = midnaField0x6ec;
// Statement copied from inside function to determine colors
bool bigColors =
midna->checkStateFlg0(daMidna_c::FLG0_UNK_10000000) ||
midna->mBckHeap[2].getIdx() == daMidna_c::m_anmDataTable[daMidna_c::ANM_HAIR].mResID ||
midna->mBckHeap[2].getIdx() ==
daMidna_c::m_anmDataTable[daMidna_c::ANM_S_TAKES].mResID ||
midna->mBckHeap[2].getIdx() ==
daMidna_c::m_anmDataTable[daMidna_c::ANM_S_WAITS].mResID ||
midna->mBckHeap[2].getIdx() ==
daMidna_c::m_anmDataTable[daMidna_c::ANM_S_PACKAWAY].mResID ||
midna->mBckHeap[2].getIdx() ==
daMidna_c::m_anmDataTable[daMidna_c::ANM_GRABST].mResID ||
midna->checkEndResetStateFlg0(daMidna_c::ERFLG0_UNK_40) ||
dComIfGp_checkPlayerStatus1(0, 0x800000);
GXColorS10 color{};
GXColor kcolor1{};
GXColor kcolor2{};
// Set our own colors
if (bigColors) {
kcolor1 = g_currentMidnaHairColors.bigKColor;
if (dKy_darkworld_check()) {
color = g_currentMidnaHairColors.bigColor;
kcolor2 = g_currentMidnaHairColors.normalKColor2;
} else {
color = g_currentMidnaHairColors.lBigColor;
kcolor2 = g_currentMidnaHairColors.lBigKColor2;
}
} else {
color = g_currentMidnaHairColors.normalColor;
if (dKy_darkworld_check()) {
kcolor1 = g_currentMidnaHairColors.normalKColor;
kcolor2 = g_currentMidnaHairColors.normalKColor2;
} else {
kcolor1 = g_currentMidnaHairColors.lNormalKColor;
kcolor2 = g_currentMidnaHairColors.lNormalKColor2;
}
}
// Reapply the chase that happens in the function
cLib_chaseS(&midna->field_0x6e0.r, color.r, 10);
cLib_chaseS(&midna->field_0x6e0.g, color.g, 10);
cLib_chaseS(&midna->field_0x6e0.b, color.b, 10);
cLib_chaseUC(&midna->field_0x6e8.r, kcolor1.r, 10);
cLib_chaseUC(&midna->field_0x6e8.g, kcolor1.g, 10);
cLib_chaseUC(&midna->field_0x6e8.b, kcolor1.b, 10);
cLib_chaseUC(&midna->field_0x6ec.r, kcolor2.r, 10);
cLib_chaseUC(&midna->field_0x6ec.g, kcolor2.g, 10);
cLib_chaseUC(&midna->field_0x6ec.b, kcolor2.b, 10);
}
}
// Midna Charge Ring Color
DEFINE_HOOK(&daAlink_c::setWolfLockDomeModel, SetWolfLockDomeModel);
void wolf_lock_dome_model_post(ModContext*, void*, void*, void*) {
auto domeRingColorStr = get_str_option(get_cvars().midnaChargeRingColor, "");
if (is_valid_hex_color_str(domeRingColorStr)) {
auto domeRingColor = hex_color_str_to_gx_color(domeRingColorStr);
const u8 domeWave1RGBA[3] = {domeRingColor.r, domeRingColor.g, domeRingColor.b};
const u8 domeWave2RGBA[3] = {domeRingColor.r, domeRingColor.g, domeRingColor.b};
u8** chromaRegisterTable =
reinterpret_cast<u8**>(&daAlink_getAlinkActorClass()->field_0x0724->mAnmCRegDataR);
for (int i = 0; i < 3; i++) {
u8* currentTable = chromaRegisterTable[i];
const u8 currentWave1Color = domeWave1RGBA[i];
const u8 currentWave2Color = domeWave2RGBA[i];
const u8 currentBaseColor = (currentWave1Color + currentWave2Color) / 2;
currentTable[0x3] = currentBaseColor; // Set Alpha for the ring base
currentTable[0x13] = currentWave1Color; // Set Alpha for ring wave 1
currentTable[0x23] = currentWave2Color; // Set Alpha for ring wave 2
currentTable[0xB] = currentBaseColor; // Set Alpha for darkworld ring base
currentTable[0x1B] = currentWave1Color; // Set Alpha for darkworld ring wave 1
currentTable[0x2B] = currentWave2Color; // Set Alpha for darkworld ring wave 2
}
}
}
#define ADD_POST_HOOK(defined_hook, function, original) \
result = mods::hook::add_post<defined_hook>(function); \
if (result != MOD_OK) { \
mods::log::debug( \
"failed to add post hook to" #original ", Result {}", static_cast<int>(result)); \
return result; \
}
#define ADD_PRE_HOOK(defined_hook, function, original) \
result = mods::hook::add_pre<defined_hook>(function); \
if (result != MOD_OK) { \
mods::log::debug( \
"failed to add pre hook to" #original ", Result {}", static_cast<int>(result)); \
return result; \
}
ModResult add_all_hooks() {
ModResult result{};
// Hooks for lantern glow
ADD_POST_HOOK(WolfEyeLightSet, wolf_eye_light_set_post, dKy_WolfEyeLight_set)
ADD_POST_HOOK(PreKandelaarDraw, pre_kandelaar_draw_post, daAlink_c::preKandelaarDraw)
// Hooks for lantern meter color
ADD_PRE_HOOK(CPaneMgrSetBlackWhite, cpane_mgr_set_black_white_pre, CPaneMgr::setBlackWhite)
ADD_POST_HOOK(
KanteraIconSetNowGauge, kantera_icon_set_now_gauge_post, dKantera_icon_c::setNowGauge)
// Hook for midna charge ring
ADD_POST_HOOK(SetWolfLockDomeModel, wolf_lock_dome_model_post, daAlink_c::setWolfLockDomeModel)
// Hook for light sword glow
ADD_POST_HOOK(SetLightningSwordEffect, set_lightning_sword_effect_post,
daAlink_c::setLightningSwordEffect)
// Hooks for Midna Hair Color
ADD_POST_HOOK(MidnaCreate, midna_create_post, daMidna_c::create)
ADD_PRE_HOOK(
MidnaSetBodyPartMatrix, midna_set_body_part_matrix_pre, daMidna_c::setBodyPartMatrix)
ADD_POST_HOOK(
MidnaSetBodyPartMatrix, midna_set_body_part_matrix_post, daMidna_c::setBodyPartMatrix)
// Hooks for UI colors
ADD_POST_HOOK(dMeter2Init, d_meter_2_init_post, dMeter2Draw_c::init)
ADD_POST_HOOK(FileInfoSetHeartCount, file_info_set_heart_count_post, dFile_info_c::setHeartCnt)
ADD_POST_HOOK(ScreenInitButton, screen_init_button_post, dMeterButton_c::screenInitButton)
ADD_POST_HOOK(MenuSaveScreenSet, menu_save_screen_set_post, dMenu_save_c::screenSet)
ADD_POST_HOOK(FileSelectScreenSet, file_select_screen_set_post, dFile_select_c::screenSet)
ADD_POST_HOOK(BrightCheckScreenSet, bright_check_screen_set_post, dBrightCheck_c::screenSet)
ADD_POST_HOOK(MenuRingCreate, menu_ring_create_post, dMenu_Ring_c::_create)
ADD_POST_HOOK(MenuCollect2DCreate, menu_collect_2D_create_post, dMenu_Collect2D_c::_create)
ADD_POST_HOOK(MenuFishingScreenSetDoIcon, menu_fishing_screen_set_do_icon_post,
dMenu_Fishing_c::screenSetDoIcon)
ADD_POST_HOOK(MenuInsectScreenSetDoIcon, menu_insect_screen_set_do_icon_post,
dMenu_Insect_c::screenSetDoIcon)
ADD_POST_HOOK(MenuLetterScreenSetDoIcon, menu_letter_screen_set_do_icon_post,
dMenu_Letter_c::screenSetDoIcon)
ADD_POST_HOOK(MenuOptionCreate, menu_option_create_post, dMenu_Option_c::_create)
ADD_POST_HOOK(MenuSkillScreenSetDoIcon, menu_skill_screen_set_do_icon_post,
dMenu_Skill_c::screenSetDoIcon)
ADD_POST_HOOK(OutFontCreatePane, out_font_create_pane_post, COutFont_c::createPane)
ADD_POST_HOOK(OutFontSetDrawFont, out_font_set_draw_font_post, COutFontSet_c::drawFont)
ADD_POST_HOOK(MenuFMapCreate, menu_fmap_create_post, dMenu_Fmap_c::_create)
ADD_POST_HOOK(
MsgObjectTalkStartInit, msg_object_talk_start_init_post, dMsgObject_c::talkStartInit)
ADD_POST_HOOK(MeterHakushaCreate, meter_hakusha_create_post, dMeterHakusha_c::_create)
// Heart Model Color
ADD_POST_HOOK(
ItemBaseCreateItemHeap, item_base_create_item_heap_post, daItemBase_c::CreateItemHeap)
return MOD_OK;
}
#define UNINSTALL_HOOK(defined_hook) mods::hook::uninstall<defined_hook>(svc_hook);
ModResult remove_all_hooks() {
UNINSTALL_HOOK(WolfEyeLightSet)
UNINSTALL_HOOK(PreKandelaarDraw)
UNINSTALL_HOOK(CPaneMgrSetBlackWhite)
UNINSTALL_HOOK(KanteraIconSetNowGauge)
UNINSTALL_HOOK(SetWolfLockDomeModel)
UNINSTALL_HOOK(SetLightningSwordEffect)
UNINSTALL_HOOK(MidnaCreate)
UNINSTALL_HOOK(MidnaSetBodyPartMatrix)
UNINSTALL_HOOK(MidnaSetBodyPartMatrix)
UNINSTALL_HOOK(dMeter2Init)
UNINSTALL_HOOK(FileInfoSetHeartCount)
UNINSTALL_HOOK(ScreenInitButton)
UNINSTALL_HOOK(MenuSaveScreenSet)
UNINSTALL_HOOK(FileSelectScreenSet)
UNINSTALL_HOOK(BrightCheckScreenSet)
UNINSTALL_HOOK(MenuRingCreate)
UNINSTALL_HOOK(MenuCollect2DCreate)
UNINSTALL_HOOK(MenuFishingScreenSetDoIcon)
UNINSTALL_HOOK(MenuInsectScreenSetDoIcon)
UNINSTALL_HOOK(MenuLetterScreenSetDoIcon)
UNINSTALL_HOOK(MenuOptionCreate)
UNINSTALL_HOOK(MenuSkillScreenSetDoIcon)
UNINSTALL_HOOK(OutFontCreatePane)
UNINSTALL_HOOK(OutFontSetDrawFont)
UNINSTALL_HOOK(MenuFMapCreate)
UNINSTALL_HOOK(MsgObjectTalkStartInit)
UNINSTALL_HOOK(MeterHakushaCreate)
UNINSTALL_HOOK(ItemBaseCreateItemHeap)
return MOD_OK;
}
-7
View File
@@ -1,7 +0,0 @@
#pragma once
#include "mods/service.hpp"
ModResult add_all_hooks();
ModResult remove_all_hooks();
@@ -1,115 +0,0 @@
#include "midna_hair_color.hpp"
#include "mod.hpp"
#include "mods/svc/log.hpp"
#include <array>
namespace {
constexpr std::array<std::array<GXColor, 9>, 10> kMidnaHairColors = {{
// Default
{{
/*l_lNormalKColor*/ /*l_normalKColor*/ /*l_normalColor*/
{0xFF, 0xDC, 0x00}, {0xB4, 0x87, 0x00}, {0x50, 0x00, 0x00},
/*l_bigKColor*/ /*l_lBigColor*/ /*l_bigColor*/
{0x1E, 0x00, 0x00}, {0xFF, 0x78, 0x00}, {0xFF, 0x64, 0x78},
/*l_lNormalKColor2*//*l_normalKColor2*/ /*l_lBigKColor2*/
{0x00, 0xC3, 0xEB}, {0x00, 0xC3, 0xC3}, {0xAA, 0xFF, 0xC3}
}},
// Pink
{{
{0xF5, 0xCF, 0xF3}, {0xAD, 0x7F, 0x7F}, {0x1B, 0x00, 0x20},
{0x3C, 0x02, 0x58}, {0xE3, 0x72, 0xF2}, {0xE3, 0x5F, 0xF8},
{0xDD, 0x00, 0xEB}, {0xDD, 0x00, 0xC3}, {0xF6, 0x4C, 0xFF}
}},
// Red
{{
{0xE4, 0x65, 0x41}, {0xA1, 0x3E, 0x22}, {0x21, 0x00, 0x00},
{0x4F, 0x02, 0x01}, {0xF0, 0x3A, 0x25}, {0xF0, 0x30, 0x8C},
{0xEB, 0x00, 0x00}, {0xEB, 0x00, 0x00}, {0xFF, 0x4F, 0x3A}
}},
// Yellow
{{
{0x91, 0x83, 0x0E}, {0x66, 0x50, 0x07}, {0x0E, 0x0B, 0x00},
{0x24, 0x25, 0x02}, {0xCB, 0xB7, 0x00}, {0xCB, 0x99, 0x78},
{0xEB, 0xDE, 0x00}, {0xEB, 0xDE, 0x00}, {0xFF, 0xF8, 0xBF}
}},
// Green
{{
{0x35, 0x79, 0x53}, {0x25, 0x4A, 0x2B}, {0x00, 0x0E, 0x05},
{0x0A, 0x29, 0x10}, {0x00, 0xB6, 0x6F}, {0x00, 0x98, 0xB3},
{0x1F, 0xEB, 0x00}, {0x1F, 0xEB, 0x00}, {0x9A, 0xFF, 0x81}
}},
// Blue
{{
{0x00, 0x72, 0xFF}, {0x00, 0x46, 0x85}, {0x00, 0x08, 0x28},
{0x16, 0x1B, 0x5D}, {0x00, 0x60, 0xFF}, {0x00, 0x50, 0xFF},
{0x00, 0x48, 0xEB}, {0x00, 0x48, 0xC3}, {0x3A, 0x66, 0xFF}
}},
// Purple
{{
{0x6F, 0x34, 0xFF}, {0x4E, 0x20, 0x85}, {0x0D, 0x00, 0x34},
{0x15, 0x08, 0x79}, {0x62, 0x00, 0xFF}, {0x62, 0x00, 0xFF},
{0x7B, 0x00, 0xEB}, {0x7B, 0x00, 0xC3}, {0x94, 0x3E, 0xFF}
}},
// Brown
{{
{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x1A, 0x05, 0x00},
{0x3C, 0x19, 0x0E}, {0x3F, 0x1D, 0x0B}, {0x3F, 0x18, 0x7E},
{0x3F, 0x1D, 0x0B}, {0x3F, 0x1D, 0x09}, {0x59, 0x32, 0x1E}
}},
// White
{{
{0xF0, 0xF1, 0xF1}, {0xA9, 0x94, 0x7E}, {0x09, 0x0B, 0x0C},
{0x22, 0x24, 0x24}, {0xFF, 0xFF, 0xFF}, {0xFF, 0xD5, 0xFF},
{0xEA, 0xEA, 0xEA}, {0xEA, 0xEA, 0xC2}, {0xF3, 0xF3, 0xF3}
}},
// Black
{{
{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x0B, 0x0B, 0x0B},
{0x23, 0x23, 0x23}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x78},
{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}
}}
}};
size_t get_midna_hair_color_index(ConfigVarHandle handle) {
const auto optionIndex = get_int_option(handle, 0);
if (optionIndex < 0 || optionIndex >= static_cast<int64_t>(kMidnaHairColors.size())) {
return 0;
}
return static_cast<size_t>(optionIndex);
}
}
MidnaHairColors g_currentMidnaHairColors = {
.normalColor = {0x50, 0x00, 0x00, 0x00},
.normalKColor = {0xB4, 0x87, 0x00, 0x00},
.normalKColor2 = {0x00, 0xC3, 0xC3, 0x00},
.bigColor = {0xFF, 0x64, 0x78, 0x00},
.bigKColor = {0x1E, 0x00, 0x00, 0x00},
.lNormalKColor = {0xFF, 0xDC, 0x00, 0x00},
.lNormalKColor2 = {0x00, 0xC3, 0xEB, 0x00},
.lBigColor = {0xFF, 0x78, 0x00, 0x00},
.lBigKColor2 = {0xAA, 0xFF, 0xC3, 0x00},
};
void set_all_midna_hair_colors() {
auto& g_cvars = get_cvars();
auto hairBaseColor = get_midna_hair_color_index(g_cvars.midnaHairBaseColor);
auto hairTipsColor = get_midna_hair_color_index(g_cvars.midnaHairTipsColor);
// Colors we have to convert to GXColorS10
auto& normalColor = kMidnaHairColors.at(hairBaseColor)[2];
auto& bigColor = kMidnaHairColors.at(hairBaseColor)[5];
auto& lBigColor = kMidnaHairColors.at(hairBaseColor)[4];
g_currentMidnaHairColors.normalColor = GXColorS10{normalColor.r, normalColor.g, normalColor.b};
g_currentMidnaHairColors.normalKColor = kMidnaHairColors.at(hairBaseColor)[1];
g_currentMidnaHairColors.normalKColor2 = kMidnaHairColors.at(hairTipsColor)[7];
g_currentMidnaHairColors.bigColor = GXColorS10{bigColor.r, bigColor.g, bigColor.b};
g_currentMidnaHairColors.bigKColor = kMidnaHairColors.at(hairBaseColor)[3];
g_currentMidnaHairColors.lNormalKColor = kMidnaHairColors.at(hairBaseColor)[0];
g_currentMidnaHairColors.lNormalKColor2 = kMidnaHairColors.at(hairTipsColor)[6];
g_currentMidnaHairColors.lBigColor = GXColorS10{lBigColor.r, lBigColor.g, lBigColor.b};
g_currentMidnaHairColors.lBigKColor2 = kMidnaHairColors.at(hairTipsColor)[8];
}
@@ -1,19 +0,0 @@
#pragma once
#include <gx.h>
struct MidnaHairColors {
GXColorS10 normalColor;
GXColor normalKColor;
GXColor normalKColor2;
GXColorS10 bigColor;
GXColor bigKColor;
GXColor lNormalKColor;
GXColor lNormalKColor2;
GXColorS10 lBigColor;
GXColor lBigKColor2;
};
extern MidnaHairColors g_currentMidnaHairColors;
void set_all_midna_hair_colors();
-653
View File
@@ -1,653 +0,0 @@
#include "mod.hpp"
#include "color_utils.hpp"
#include "hooks.hpp"
#include "midna_hair_color.hpp"
#include "texture_utils.hpp"
#include "mods/service.hpp"
#include "mods/svc/config.h"
#include "mods/svc/hook.hpp"
#include "mods/svc/log.h"
#include "mods/svc/log.hpp"
#include "mods/svc/ui.h"
#include "d/d_com_inf_game.h"
#include <xxhash.h>
#include <optional>
#include <ranges>
#include <string>
DEFINE_MOD();
IMPORT_SERVICE(LogService, svc_log);
IMPORT_SERVICE(ConfigService, svc_config);
IMPORT_SERVICE(HookService, svc_hook);
IMPORT_SERVICE(TextureService, svc_texture);
IMPORT_SERVICE(UiService, svc_ui);
static cvars g_cvars;
cvars& get_cvars() {
return g_cvars;
}
std::string get_str_option(ConfigVarHandle handle, const std::string& fallback) {
std::string value{};
size_t outLength{};
svc_config->get_string(mod_ctx, handle, NULL, 0, &outLength);
value.resize(outLength);
if (handle == 0 || svc_config->get_string(
mod_ctx, handle, value.data(), value.size() + 1, &outLength) != MOD_OK)
{
return fallback;
}
return value;
}
int64_t get_int_option(ConfigVarHandle handle, int64_t fallback) {
int64_t value = fallback;
if (handle == 0 || svc_config->get_int(mod_ctx, handle, &value) != MOD_OK) {
return fallback;
}
return value;
}
// Helper for getting configVar color
std::optional<GXColor> get_config_var_color(ConfigVarHandle handle, bool allowRainbow) {
auto colorStr = get_str_option(handle, "");
// Convert to lowercase
for (auto& c : colorStr) {
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
}
if (colorStr == "rainbow" && allowRainbow) {
auto color = get_rainbow_rgb(127.5f);
color.r /= 2;
color.g /= 2;
color.b /= 2;
return color;
}
if (is_valid_hex_color_str(colorStr)) {
return hex_color_str_to_gx_color(colorStr);
}
return std::nullopt;
}
namespace {
UiWindowHandle g_cosmeticsWindow = 0;
bool g_loadedAllBaseTextures = false;
constexpr uint32_t kTextureLoadRetryFrames = 30;
uint32_t g_textureLoadRetryCountdown = 0;
constexpr const char* kOverlayPresets[] = {
"ab706e",
"6382a0",
"94749a",
"ec8644",
"b9ab00",
"ec9fc8",
"505154",
"f8f7f4",
"91723e",
};
constexpr const char* kLightPresets[] = {
"ff0000",
"f68821",
"f6f321",
"00ff00",
"0000ff",
"8000ff",
"a0a0a0",
"30d0d0",
};
constexpr const char* kRainbowLightPresets[] = {
"rainbow",
"ff0000",
"f68821",
"f6f321",
"00ff00",
"0000ff",
"8000ff",
"a0a0a0",
};
constexpr const char* kAButtonPresets[] = {
"ff0000",
"ff5000",
"ffaf00",
"0080ff",
"0000ff",
"8000ff",
"5555ff",
"ff20ff",
};
constexpr const char* kBButtonPresets[] = {
"ffff40",
"ffa0ff",
"00e87b",
"00aaff",
"6078ff",
"000000",
"00f3ff",
};
constexpr const char* kXyButtonPresets[] = {
"ff0000",
"ff8200",
"f7df00",
"70ff00",
"00bd11",
"0000ff",
"800088",
"000000",
"ff00aa",
"00ffff",
};
constexpr const char* kZButtonPresets[] = {
"ff0000",
"ff8200",
"f7df00",
"70ff00",
"00bd11",
"800088",
"000000",
"00ffff",
};
constexpr const char* kChargeRingPresets[] = {
"ff9f9f",
"ff0000",
"ffff00",
"00ff00",
"0000ff",
"ff00ff",
"331900",
"feffff",
"000000",
};
ModResult register_str_option(
const char* name, const char* defaultValue, ConfigVarHandle& outHandle, ModError* error) {
ConfigVarDesc cvarDesc = CONFIG_VAR_DESC_INIT;
cvarDesc.name = name;
cvarDesc.type = CONFIG_VAR_STRING;
cvarDesc.default_string = defaultValue;
if (svc_config->register_var(mod_ctx, &cvarDesc, &outHandle) != MOD_OK) {
return mods::set_error(error, MOD_ERROR, "failed to register cosmetics option");
}
return MOD_OK;
}
ModResult register_int_option(
const char* name, int64_t defaultValue, ConfigVarHandle& outHandle, ModError* error) {
ConfigVarDesc cvarDesc = CONFIG_VAR_DESC_INIT;
cvarDesc.name = name;
cvarDesc.type = CONFIG_VAR_INT;
cvarDesc.default_int = defaultValue;
if (svc_config->register_var(mod_ctx, &cvarDesc, &outHandle) != MOD_OK) {
return mods::set_error(error, MOD_ERROR, "failed to register cosmetics option");
}
return MOD_OK;
}
void add_control(UiElementHandle pane, const UiControlDesc& desc) {
auto result = svc_ui->pane_add_control(mod_ctx, pane, &desc, nullptr);
if (result != MOD_OK) {
mods::log::debug("pane_add_control failed {}", static_cast<int>(result));
}
}
template <size_t N>
void add_cosmetic_option(
UiElementHandle pane, ConfigVarHandle cvar, const char* name, const char* const (&presets)[N]) {
UiControlDesc control = UI_CONTROL_DESC_INIT;
control.kind = UI_CONTROL_COLOR;
control.label = name;
control.binding = UI_BINDING_CONFIG_VAR;
control.config_var = cvar;
control.color_presets = presets;
control.color_preset_count = N;
add_control(pane, control);
}
void add_midna_hair_option(UiElementHandle left, ConfigVarHandle cvar, const std::string& name) {
static const char* kMidnaHairOptions[] = {
"Default", "Pink", "Red", "Yellow", "Green", "Blue", "Purple", "Brown", "White", "Black"};
UiControlDesc control = UI_CONTROL_DESC_INIT;
control.kind = UI_CONTROL_SELECT;
control.label = name.c_str();
control.help_rml = "Choose Midna's hair color.";
control.binding = UI_BINDING_CONFIG_VAR;
control.config_var = cvar;
control.options = kMidnaHairOptions;
control.option_count = 10;
add_control(left, control);
}
void add_group(
UiElementHandle groups, UiElementHandle colors, const char* label, UiGroupBuildFn build) {
UiGroupDesc group = UI_GROUP_DESC_INIT;
group.label = label;
group.build = build;
const auto result = svc_ui->pane_add_group(mod_ctx, groups, colors, &group, nullptr);
if (result != MOD_OK) {
mods::log::debug("pane_add_group failed {}", static_cast<int>(result));
}
}
ModResult build_hero_tunic_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Hero's Tunic");
add_cosmetic_option(pane, g_cvars.herosTunicCapColor, "Cap", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.herosTunicTorsoColor, "Body", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.herosTunicSkirtColor, "Skirt", kOverlayPresets);
return MOD_OK;
}
ModResult build_zora_armor_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Zora Armor");
add_cosmetic_option(pane, g_cvars.zoraArmorCapColor, "Cap", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.zoraArmorHelmetColor, "Helmet", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.zoraArmorTorsoColor, "Torso", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.zoraArmorScalesColor, "Scales", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.zoraArmorFlippersColor, "Flippers", kOverlayPresets);
return MOD_OK;
}
ModResult build_sword_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Swords");
add_cosmetic_option(pane, g_cvars.woodenSwordColor, "Wooden Sword", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.ordonSwordBladeColor, "Ordon Blade", kLightPresets);
add_cosmetic_option(pane, g_cvars.ordonSwordHandleColor, "Ordon Handle", kLightPresets);
add_cosmetic_option(pane, g_cvars.msBladeColor, "Master Sword Blade", kLightPresets);
add_cosmetic_option(pane, g_cvars.msHandleColor, "Master Sword Handle", kLightPresets);
add_cosmetic_option(
pane, g_cvars.lightSwordGlowColor, "Light Sword Glow", kRainbowLightPresets);
return MOD_OK;
}
ModResult build_equipment_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Equipment");
add_cosmetic_option(pane, g_cvars.lanternGlowColor, "Lantern Glow", kRainbowLightPresets);
add_cosmetic_option(pane, g_cvars.boomerangColor, "Gale Boomerang", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.ironBootsColor, "Iron Boots", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.spinnerColor, "Spinner", kOverlayPresets);
return MOD_OK;
}
ModResult build_button_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Buttons");
add_cosmetic_option(pane, g_cvars.aButtonColor, "A Button", kAButtonPresets);
add_cosmetic_option(pane, g_cvars.bButtonColor, "B Button", kBButtonPresets);
add_cosmetic_option(pane, g_cvars.xButtonColor, "X Button", kXyButtonPresets);
add_cosmetic_option(pane, g_cvars.yButtonColor, "Y Button", kXyButtonPresets);
add_cosmetic_option(pane, g_cvars.zButtonColor, "Z Button", kZButtonPresets);
return MOD_OK;
}
ModResult build_hud_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "HUD");
add_cosmetic_option(pane, g_cvars.heartColor, "Hearts", kBButtonPresets);
return MOD_OK;
}
ModResult build_link_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Link");
add_cosmetic_option(pane, g_cvars.linkHairColor, "Hair", kOverlayPresets);
return MOD_OK;
}
ModResult build_midna_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Midna");
add_cosmetic_option(pane, g_cvars.midnaChargeRingColor, "Charge Ring", kChargeRingPresets);
return MOD_OK;
}
ModResult build_companion_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Companions");
add_cosmetic_option(pane, g_cvars.wolfLinkColor, "Wolf Link", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.eponaColor, "Epona", kOverlayPresets);
return MOD_OK;
}
ModResult build_equipment_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, left, "Color Groups");
add_group(left, right, "Hero's Tunic", build_hero_tunic_colors);
add_group(left, right, "Zora Armor", build_zora_armor_colors);
add_group(left, right, "Swords", build_sword_colors);
add_group(left, right, "Equipment", build_equipment_colors);
return MOD_OK;
}
ModResult build_ui_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, left, "Color Groups");
add_group(left, right, "Buttons", build_button_colors);
add_group(left, right, "HUD", build_hud_colors);
return MOD_OK;
}
ModResult build_misc_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, left, "Hair Presets");
add_midna_hair_option(left, g_cvars.midnaHairBaseColor, "Midna's Hair Base Color");
add_midna_hair_option(left, g_cvars.midnaHairTipsColor, "Midna's Hair Tips Color");
svc_ui->pane_add_section(mod_ctx, left, "Color Groups");
add_group(left, right, "Link", build_link_colors);
add_group(left, right, "Midna", build_midna_colors);
add_group(left, right, "Companions", build_companion_colors);
return MOD_OK;
}
void on_cosmetics_menu_window_closed(ModContext*, UiWindowHandle, void*) {
g_cosmeticsWindow = 0;
}
void on_open_cosmetics_menu(ModContext*, void*) {
if (g_cosmeticsWindow != 0) {
return;
}
UiTabDesc tabs[] = {UI_TAB_DESC_INIT, UI_TAB_DESC_INIT, UI_TAB_DESC_INIT};
tabs[0].title = "Equipment";
tabs[0].build = build_equipment_colors_tab;
tabs[1].title = "Interface";
tabs[1].build = build_ui_colors_tab;
tabs[2].title = "Characters";
tabs[2].build = build_misc_colors_tab;
UiWindowDesc desc = UI_WINDOW_DESC_INIT;
desc.tabs = tabs;
desc.tab_count = ARRAY_SIZE(tabs);
desc.on_closed = on_cosmetics_menu_window_closed;
if (svc_ui->window_push(mod_ctx, &desc, &g_cosmeticsWindow) != MOD_OK) {
svc_log->error(mod_ctx, "failed to open basic cosmetics window");
}
}
ModResult build_panel(ModContext*, UiElementHandle panel, void*, ModError*) {
UiControlDesc control = UI_CONTROL_DESC_INIT;
control.kind = UI_CONTROL_GROUP;
control.label = "Open Cosmetics Menu";
control.on_pressed = on_open_cosmetics_menu;
add_control(panel, control);
return MOD_OK;
}
} // namespace
void load_base_texture_data() {
// Go through each texture we can recolor and attempt to load and store
// the texture data for future recoloring
for (auto& replacements : get_texture_replacements() | std::views::values) {
for (auto& replacement : replacements) {
// If we've already loaded the texture data, don't load it again
if (replacement.loadedTextureData) {
continue;
}
// Avoid noisy resource lookups until the archive has finished loading.
auto* resInfo = dComIfG_getObjectResInfo(replacement.arc);
if (resInfo == nullptr || resInfo->getArchive() == nullptr) {
continue;
}
// Try to get the model this texture is part of. If we can't get it, try again later
auto model = static_cast<J3DModelData*>(
dComIfG_getObjectRes(replacement.arc, replacement.modelFileName));
if (model == nullptr) {
continue;
}
J3DTexture* tex = model->getTexture();
JUTNameTab* nametable = model->getTextureName();
if (tex != nullptr && nametable != nullptr) {
for (u16 i = 0; i < tex->getNum(); i++) {
const char* texName = nametable->getName(i);
if (texName != nullptr && std::strcmp(texName, replacement.textureName) == 0) {
// Once we've found the texture, set all our TextureKey and TextureData
// fields that we can set right now.
auto imageHeader = tex->getResTIMG(i);
auto& key = replacement.key;
auto& data = replacement.data;
key.kind = TEXTURE_KEY_SOURCE;
key.has_tlut = imageHeader->numColors > 0;
key.width = imageHeader->width;
key.height = imageHeader->height;
key.gx_format = imageHeader->format;
// Currently, no replaced textures have a tlut
key.tlut_hash = replacement.tlutHash;
// Calculate the size of the image data
const uint32_t mipCount =
imageHeader->mipmapEnabled ?
std::max<uint32_t>(imageHeader->mipmapCount, 1) :
1;
auto size = get_image_data_size(
imageHeader->format, imageHeader->width, imageHeader->height, mipCount);
replacement.baseTextureData.resize(size);
std::memcpy(
replacement.baseTextureData.data(), tex->getImgDataPtr(i), size);
// Source texture keys hash only the base mip level.
const auto baseMipSize = get_image_data_size(
imageHeader->format, imageHeader->width, imageHeader->height, 1);
auto textureHash =
XXH64(replacement.baseTextureData.data(), baseMipSize, 0);
replacement.key.texture_hash = textureHash;
mods::log::debug("Loaded base texture data for {}. size: {:X} hash: {:X}",
replacement.textureName, size, textureHash);
replacement.loadedTextureData = true;
data.width = imageHeader->width;
data.height = imageHeader->height;
data.mip_count = mipCount;
data.size = size;
data.gx_format = imageHeader->format;
break;
}
}
}
}
}
// If we've loaded all base textures for recoloring, then we don't need to call this function
// again
g_loadedAllBaseTextures = std::ranges::all_of(
get_texture_replacements() | std::views::values, [](auto& replacementList) {
return std::ranges::all_of(
replacementList, [](const TextureReplacementData& replacement) {
return replacement.loadedTextureData;
});
});
}
ModResult check_and_set_recolored_textures() {
for (auto& [configVar, replacements] : get_texture_replacements()) {
// If the configvar hasn't been set, don't continue
if (configVar == 0) {
continue;
}
auto maybeColor = get_config_var_color(configVar);
if (!maybeColor.has_value()) {
// An empty or invalid option means the original texture should be restored.
for (auto& replacement : replacements) {
if (replacement.handle != 0) {
auto result = svc_texture->unregister(mod_ctx, replacement.handle);
if (result != MOD_OK) {
mods::log::debug("Could not unregister replacement for {}. Result: {}",
replacement.textureName, static_cast<int>(result));
}
replacement.handle = 0;
}
replacement.curColor.reset();
}
continue;
}
auto color = maybeColor.value();
for (auto& replacement : replacements) {
// If we haven't loaded the base texture yet, don't try to recolor it
if (!replacement.loadedTextureData) {
continue;
}
// If our color hasn't changed, don't try to recolor
auto& curColor = replacement.curColor;
if (curColor == std::nullopt || curColor.value().r != color.r ||
curColor.value().g != color.g || curColor.value().b != color.b)
{
// Make a copy of the base texture data to recolor
auto newTexture = replacement.baseTextureData;
recolor_texture(replacement, color, newTexture);
TextureData newTextureData = replacement.data;
newTextureData.data = newTexture.data();
newTextureData.size = newTexture.size();
// Keep the current replacement active if registering the new one fails.
TextureReplacementHandle newHandle{};
auto result = svc_texture->register_data(
mod_ctx, &replacement.key, &newTextureData, &newHandle);
if (result != MOD_OK) {
mods::log::debug("Could not register_data for {}. Result: {}",
replacement.textureName, static_cast<int>(result));
} else {
mods::log::debug("Registered replacement for {}.", replacement.textureName,
static_cast<int>(result));
auto oldHandle = replacement.handle;
replacement.handle = newHandle;
curColor = color;
if (oldHandle != 0) {
result = svc_texture->unregister(mod_ctx, oldHandle);
if (result != MOD_OK) {
mods::log::debug(
"Could not unregister previous replacement for {}. Result: {}",
replacement.textureName, static_cast<int>(result));
}
}
}
}
}
}
return MOD_OK;
}
void unregister_all_texture_handles() {
for (auto& replacements : get_texture_replacements() | std::views::values) {
for (auto& replacement : replacements) {
if (replacement.handle != 0) {
svc_texture->unregister(mod_ctx, replacement.handle);
replacement.handle = 0;
}
replacement.curColor.reset();
}
}
}
#define REGISTER_COSMETIC_OPTION(option) \
result = register_str_option(#option, NULL, g_cvars.option, error); \
if (result != MOD_OK) { \
return result; \
}
extern "C" {
MOD_EXPORT ModResult mod_initialize(ModError* error) {
svc_log->info(mod_ctx, "basic_cosmetics_mod initialized");
ModResult result{};
REGISTER_COSMETIC_OPTION(herosTunicCapColor)
REGISTER_COSMETIC_OPTION(herosTunicTorsoColor)
REGISTER_COSMETIC_OPTION(herosTunicSkirtColor)
REGISTER_COSMETIC_OPTION(zoraArmorCapColor)
REGISTER_COSMETIC_OPTION(zoraArmorHelmetColor)
REGISTER_COSMETIC_OPTION(zoraArmorTorsoColor)
REGISTER_COSMETIC_OPTION(zoraArmorScalesColor)
REGISTER_COSMETIC_OPTION(zoraArmorFlippersColor)
REGISTER_COSMETIC_OPTION(lanternGlowColor)
REGISTER_COSMETIC_OPTION(woodenSwordColor)
REGISTER_COSMETIC_OPTION(ordonSwordBladeColor)
REGISTER_COSMETIC_OPTION(ordonSwordHandleColor)
REGISTER_COSMETIC_OPTION(msBladeColor)
REGISTER_COSMETIC_OPTION(msHandleColor)
REGISTER_COSMETIC_OPTION(lightSwordGlowColor)
REGISTER_COSMETIC_OPTION(boomerangColor)
REGISTER_COSMETIC_OPTION(ironBootsColor)
REGISTER_COSMETIC_OPTION(spinnerColor)
REGISTER_COSMETIC_OPTION(aButtonColor)
REGISTER_COSMETIC_OPTION(bButtonColor)
REGISTER_COSMETIC_OPTION(xButtonColor)
REGISTER_COSMETIC_OPTION(yButtonColor)
REGISTER_COSMETIC_OPTION(zButtonColor)
REGISTER_COSMETIC_OPTION(heartColor)
result = register_int_option("midnaHairBaseColor", 0, g_cvars.midnaHairBaseColor, error);
if (result != MOD_OK) {
return result;
}
result = register_int_option("midnaHairTipsColor", 0, g_cvars.midnaHairTipsColor, error);
if (result != MOD_OK) {
return result;
}
REGISTER_COSMETIC_OPTION(midnaChargeRingColor)
REGISTER_COSMETIC_OPTION(linkHairColor)
REGISTER_COSMETIC_OPTION(wolfLinkColor)
REGISTER_COSMETIC_OPTION(eponaColor)
UiModsPanelDesc panelDesc = UI_MODS_PANEL_DESC_INIT;
panelDesc.build = build_panel;
svc_ui->register_mods_panel(mod_ctx, &panelDesc);
// Add all our hooks
result = add_all_hooks();
if (result != MOD_OK) {
return result;
}
g_loadedAllBaseTextures = false;
g_textureLoadRetryCountdown = 0;
return MOD_OK;
}
MOD_EXPORT ModResult mod_update(ModError*) {
update_rainbow_rgb(1.0f);
set_all_midna_hair_colors();
if (!g_loadedAllBaseTextures) {
if (g_textureLoadRetryCountdown == 0) {
load_base_texture_data();
g_textureLoadRetryCountdown = kTextureLoadRetryFrames;
} else {
--g_textureLoadRetryCountdown;
}
}
check_and_set_recolored_textures();
return MOD_OK;
}
MOD_EXPORT ModResult mod_shutdown(ModError*) {
svc_log->info(mod_ctx, "basic_cosmetics_mod unloaded");
g_cosmeticsWindow = 0;
remove_all_hooks();
unregister_all_texture_handles();
get_texture_replacements().clear();
g_loadedAllBaseTextures = false;
g_textureLoadRetryCountdown = 0;
return MOD_OK;
}
}
-65
View File
@@ -1,65 +0,0 @@
#pragma once
#include "mods/svc/config.h"
#include "mods/svc/texture.h"
#include <gx.h>
#include <optional>
#include <string>
#include <vector>
struct cvars {
ConfigVarHandle herosTunicCapColor = 0;
ConfigVarHandle herosTunicTorsoColor = 0;
ConfigVarHandle herosTunicSkirtColor = 0;
ConfigVarHandle zoraArmorCapColor = 0;
ConfigVarHandle zoraArmorHelmetColor = 0;
ConfigVarHandle zoraArmorTorsoColor = 0;
ConfigVarHandle zoraArmorScalesColor = 0;
ConfigVarHandle zoraArmorFlippersColor = 0;
ConfigVarHandle lanternGlowColor = 0;
ConfigVarHandle woodenSwordColor = 0;
ConfigVarHandle ordonSwordBladeColor = 0;
ConfigVarHandle ordonSwordHandleColor = 0;
ConfigVarHandle msBladeColor = 0;
ConfigVarHandle msHandleColor = 0;
ConfigVarHandle lightSwordGlowColor = 0;
ConfigVarHandle boomerangColor = 0;
ConfigVarHandle ironBootsColor = 0;
ConfigVarHandle spinnerColor = 0;
ConfigVarHandle heartColor = 0;
ConfigVarHandle aButtonColor = 0;
ConfigVarHandle bButtonColor = 0;
ConfigVarHandle xButtonColor = 0;
ConfigVarHandle yButtonColor = 0;
ConfigVarHandle zButtonColor = 0;
ConfigVarHandle midnaHairBaseColor = 0;
ConfigVarHandle midnaHairTipsColor = 0;
ConfigVarHandle midnaChargeRingColor = 0;
ConfigVarHandle linkHairColor = 0;
ConfigVarHandle wolfLinkColor = 0;
ConfigVarHandle eponaColor = 0;
};
struct TextureReplacementData {
const char* arc{};
const char* modelFileName{};
const char* textureName{};
uint64_t textureHash{};
uint64_t tlutHash{};
TextureKey key = TEXTURE_KEY_INIT;
TextureData data = TEXTURE_DATA_INIT;
TextureReplacementHandle handle{};
std::vector<u8> baseTextureData{};
bool loadedTextureData = false;
std::optional<GXColor> curColor{};
};
cvars& get_cvars();
std::string get_str_option(ConfigVarHandle handle, const std::string& fallback);
int64_t get_int_option(ConfigVarHandle handle, int64_t fallback);
std::optional<GXColor> get_config_var_color(ConfigVarHandle handle, bool allowRainbow = false);
@@ -1,682 +0,0 @@
#include "texture_utils.hpp"
#include "color_utils.hpp"
#include "mod.hpp"
#include "mods/svc/config.h"
#include "mods/svc/log.hpp"
#include "JSystem/J3DGraphLoader/J3DModelLoader.h"
#include "JSystem/JSupport/JSupport.h"
#include "JSystem/JUtility/JUTNameTab.h"
#include "d/actor/d_a_alink.h"
static void get_gx_tile_info(uint8_t format, uint32_t& tileWidth, uint32_t& tileHeight, uint32_t& tileSize) {
switch (format) {
case GX_TF_I8:
case GX_TF_IA4:
case GX_TF_C8:
tileWidth = 8; tileHeight = 4; tileSize = 32;
break;
case GX_TF_IA8:
case GX_TF_RGB565:
case GX_TF_RGB5A3:
case GX_TF_C14X2:
tileWidth = 4; tileHeight = 4; tileSize = 32;
break;
case GX_TF_RGBA8:
tileWidth = 4; tileHeight = 4; tileSize = 64;
break;
case GX_TF_I4:
case GX_TF_C4:
case GX_TF_CMPR:
default:
tileWidth = 8; tileHeight = 8; tileSize = 32;
break;
}
}
uint32_t get_image_data_size(uint32_t format, uint32_t width, uint32_t height, uint32_t mipmapCount) {
uint32_t tileWidth, tileHeight, tileSize;
get_gx_tile_info(format, tileWidth, tileHeight, tileSize);
uint32_t totalSize = 0;
for (uint8_t i = 0; i < mipmapCount; ++i) {
// Round dimensions up to nearest tile boundary
uint32_t paddedWidth = (width + tileWidth - 1) & ~(tileWidth - 1);
uint32_t paddedHeight = (height + tileHeight - 1) & ~(tileHeight - 1);
uint32_t tilesX = paddedWidth / tileWidth;
uint32_t tilesY = paddedHeight / tileHeight;
totalSize += tilesX * tilesY * tileSize;
// Downscale dimensions for next mipmap level
width = std::max(1u, width >> 1);
height = std::max(1u, height >> 1);
}
return totalSize;
}
// When left is greater than right
// 0b00 points to the left color
// 0b01 points to the right color
// 0b10 is closer to left color
// 0b11 is closer to right color
// When left is not greater than right
// 0b00 points to the left color
// 0b01 points to the right color
// 0b10 is midway between the colors
// 0b11 is transparent
// That means when maintaining the relative order, if we have to swap the colors:
// in the case of left being greater than right:
// 0b00 will swap to 0b01
// 0b01 will swap to 0b00
// 0b10 will swap to 0b11
// 0b11 will swap to 0b10
// So the left bit stays the same, and the right bit changes
// Can do xor (^) like 0b01010101 or 0x55 for each u16
// in the case of left not being greater than right:
// 0b00 will swap to 0b01
// 0b01 will swap to 0b00
// 0b10 will stay the same
// 0b11 will stay the same
// so if the left bit is a 0, the right bit will change
uint32_t swap_index_bits(bool leftIsGreater, uint32_t bits) {
if (leftIsGreater) {
return bits ^ 0x55555555;
}
const uint32_t mask = ((bits >> 1) & 0x55555555) ^ 0x55555555;
return bits ^ mask;
}
void recolor_cmpr_texture(const TextureReplacementData& replacementData, const GXColor color, std::vector<u8>& newTextureDataOut)
{
uint16_t recolors[0x100];
for (int32_t i = 0; i < 0x100; i++) {
recolors[i] = blend_overlay_rgb_565(i, color);
}
const uint8_t mipCount = (replacementData.data.mip_count > 0) ? replacementData.data.mip_count : 1;
uint32_t mipWidth = replacementData.key.width;
uint32_t mipHeight = replacementData.key.height;
uint8_t* currentAddr = newTextureDataOut.data();
for (uint8_t mip = 0; mip < mipCount; ++mip) {
// Round dimensions up to the nearest 8x8 tile boundary
const uint32_t roundedWidth = (mipWidth + 7) & ~7;
const uint32_t roundedHeight = (mipHeight + 7) & ~7;
const uint32_t numBlocks = (roundedWidth / 8) * (roundedHeight / 8);
const uint32_t iterations = numBlocks * 4; // 4 CMPR sub-blocks per 8x8 tile
for (uint32_t i = 0; i < iterations; i++) {
auto* rgb565Ptr = reinterpret_cast<BE<uint16_t>*>(currentAddr);
auto leftRgb565 = rgb565Ptr[0];
auto rightRgb565 = rgb565Ptr[1];
const bool leftIsGreater = leftRgb565 > rightRgb565;
const uint32_t leftGrayVal = desaturate_rgb_565(leftRgb565);
const uint32_t rightGrayVal = desaturate_rgb_565(rightRgb565);
uint16_t leftNewRgb565 = recolors[leftGrayVal];
uint16_t rightNewRgb565 = recolors[rightGrayVal];
bool needsBitSwap = false;
if (leftIsGreater) {
if (leftNewRgb565 == rightNewRgb565) {
// Need to make sure that subtracting 1 does not mess
// everything up. For example, 0x1000 - 1 => 0x0fff which is
// a completely different color.
if ((leftNewRgb565 & 0x1f) == 0) {
// If left value has 0 blue, we change its blue to 1.
leftNewRgb565 += 1;
}
rightNewRgb565 = leftNewRgb565 - 1;
}
else if (leftNewRgb565 < rightNewRgb565) {
needsBitSwap = true;
}
}
else if (leftNewRgb565 > rightNewRgb565) {
needsBitSwap = true;
}
if (needsBitSwap) {
// The left and right colors are swapping so that their values
// are relative in the same way. We need to update the bits
// referencing the palette entries to handle the swap.
const uint16_t temp = leftNewRgb565;
leftNewRgb565 = rightNewRgb565;
rightNewRgb565 = temp;
auto wordPtr = reinterpret_cast<BE<uint32_t>*>(currentAddr);
const uint32_t bits = wordPtr[1];
const uint32_t newBits = swap_index_bits(leftIsGreater, bits);
wordPtr[1] = newBits;
}
rgb565Ptr[0] = leftNewRgb565;
rgb565Ptr[1] = rightNewRgb565;
currentAddr += 8;
}
// Halve dimensions for the next mipmap level
mipWidth = std::max(1u, mipWidth >> 1);
mipHeight = std::max(1u, mipHeight >> 1);
}
}
void recolor_rgb5a3_texture(const TextureReplacementData& replacementData, const GXColor color, std::vector<u8>& newTextureDataOut)
{
// Precompute lookup tables for both RGB555 (opaque) and RGB444 (translucent) modes
uint16_t recolors_rgb555[0x100];
uint16_t recolors_rgb444[0x100];
for (int32_t i = 0; i < 0x100; i++) {
const uint8_t r = blend_overlay_channel(i, color.r);
const uint8_t g = blend_overlay_channel(i, color.g);
const uint8_t b = blend_overlay_channel(i, color.b);
// Pack as RGB555: Bit 15 set to 1 + 5 bits R, G, B
recolors_rgb555[i] = 0x8000 | ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
// Pack as RGB444: 4 bits R, G, B (Bit 15 remains 0)
recolors_rgb444[i] = ((r >> 4) << 8) | ((g >> 4) << 4) | (b >> 4);
}
const uint8_t mipCount =
replacementData.data.mip_count > 0 ? replacementData.data.mip_count : 1;
uint32_t mipWidth = replacementData.key.width;
uint32_t mipHeight = replacementData.key.height;
auto* pixelPtr = reinterpret_cast<BE<uint16_t>*>(newTextureDataOut.data());
for (uint8_t mip = 0; mip < mipCount; ++mip) {
const uint32_t roundedWidth = (mipWidth + 3) & ~3;
const uint32_t roundedHeight = (mipHeight + 3) & ~3;
const uint32_t totalPixels = roundedWidth * roundedHeight;
for (uint32_t i = 0; i < totalPixels; i++) {
const uint16_t rawPixel = pixelPtr[i];
// MSB determines if pixel is opaque or translucent
if (rawPixel & 0x8000) {
// Pixel is opaque
const uint8_t r5 = (rawPixel >> 10) & 0x1F;
const uint8_t g5 = (rawPixel >> 5) & 0x1F;
const uint8_t b5 = rawPixel & 0x1F;
// Expand 5-bit to 8-bit
const uint8_t r8 = (r5 << 3) | (r5 >> 2);
const uint8_t g8 = (g5 << 3) | (g5 >> 2);
const uint8_t b8 = (b5 << 3) | (b5 >> 2);
const uint8_t grayVal = static_cast<uint8_t>((r8 * 77 + g8 * 150 + b8 * 29) >> 8);
pixelPtr[i] = recolors_rgb555[grayVal];
} else {
// Pixel is translucent
const uint16_t alpha3 = rawPixel & 0x7000;
const uint8_t r4 = (rawPixel >> 8) & 0x0F;
const uint8_t g4 = (rawPixel >> 4) & 0x0F;
const uint8_t b4 = rawPixel & 0x0F;
// Expand 4-bit to 8-bit
const uint8_t r8 = (r4 << 4) | r4;
const uint8_t g8 = (g4 << 4) | g4;
const uint8_t b8 = (b4 << 4) | b4;
const uint8_t grayVal = static_cast<uint8_t>((r8 * 77 + g8 * 150 + b8 * 29) >> 8);
// Combine original alpha with recolored RGB444
pixelPtr[i] = alpha3 | recolors_rgb444[grayVal];
}
}
pixelPtr += totalPixels;
mipWidth = std::max(1u, mipWidth >> 1);
mipHeight = std::max(1u, mipHeight >> 1);
}
}
// Function to encode a single 4x4 sub-block (16 pixels) into an 8-byte CMPR block
static void encode_cmpr_sub_block(uint8_t* dst, const uint8_t pixels[16]) {
uint8_t min_val = 255;
uint8_t max_val = 0;
for (int i = 0; i < 16; ++i) {
if (pixels[i] < min_val) min_val = pixels[i];
if (pixels[i] > max_val) max_val = pixels[i];
}
auto intensity_to_rgb565 = [](uint8_t val) -> uint16_t {
uint16_t r5 = val >> 3;
uint16_t g6 = val >> 2;
uint16_t b5 = val >> 3;
return static_cast<uint16_t>((r5 << 11) | (g6 << 5) | b5);
};
uint16_t c0_565 = intensity_to_rgb565(max_val);
uint16_t c1_565 = intensity_to_rgb565(min_val);
uint32_t indices = 0;
if (max_val > min_val) {
// Enforce c0_565 > c1_565 in unsigned 16-bit representation to use 4-color mode
if (c0_565 == c1_565) {
if ((c0_565 & 0x001F) < 0x001F) {
c0_565 += 1;
} else {
c1_565 -= 1;
}
}
// Interpolated 8-bit intensity values for quantization
const int c0 = max_val;
const int c1 = min_val;
const int c2 = (2 * max_val + min_val) / 3;
const int c3 = (max_val + 2 * min_val) / 3;
// Map each pixel to the nearest palette entry
for (int i = 0; i < 16; ++i) {
const int p = pixels[i];
const int d0 = std::abs(p - c0);
const int d1 = std::abs(p - c1);
const int d2 = std::abs(p - c2);
const int d3 = std::abs(p - c3);
uint32_t best_idx = 0;
int min_d = d0;
if (d1 < min_d) { min_d = d1; best_idx = 1; }
if (d2 < min_d) { min_d = d2; best_idx = 2; }
if (d3 < min_d) { min_d = d3; best_idx = 3; }
indices |= (best_idx << (30 - (2 * i)));
}
}
// Account for big endian data expectation
dst[0] = static_cast<uint8_t>(c0_565 >> 8);
dst[1] = static_cast<uint8_t>(c0_565 & 0xFF);
dst[2] = static_cast<uint8_t>(c1_565 >> 8);
dst[3] = static_cast<uint8_t>(c1_565 & 0xFF);
dst[4] = static_cast<uint8_t>(indices >> 24);
dst[5] = static_cast<uint8_t>((indices >> 16) & 0xFF);
dst[6] = static_cast<uint8_t>((indices >> 8) & 0xFF);
dst[7] = static_cast<uint8_t>(indices & 0xFF);
}
bool convert_i8_to_cmpr(TextureReplacementData& replacementData, std::vector<u8>& cmprOut) {
if (cmprOut.empty()) {
return false;
}
const uint8_t mipCount = (replacementData.data.mip_count > 0) ? replacementData.data.mip_count : 1;
const auto expectedInputSize = get_image_data_size(
GX_TF_I8, replacementData.key.width, replacementData.key.height, mipCount);
if (cmprOut.size() < expectedInputSize) {
return false;
}
const auto sourceData = cmprOut;
std::vector<u8> convertedData(get_image_data_size(
GX_TF_CMPR, replacementData.key.width, replacementData.key.height, mipCount));
uint32_t mipWidth = replacementData.key.width;
uint32_t mipHeight = replacementData.key.height;
const uint8_t* readPtr = sourceData.data();
uint8_t* writePtr = convertedData.data();
for (uint8_t mip = 0; mip < mipCount; ++mip) {
const uint32_t paddedWidthI8 = (mipWidth + 7) & ~7;
const uint32_t paddedHeightI8 = (mipHeight + 3) & ~3;
const uint32_t tilesX_I8 = paddedWidthI8 / 8;
const uint32_t paddedWidthCMPR = (mipWidth + 7) & ~7;
const uint32_t paddedHeightCMPR = (mipHeight + 7) & ~7;
const uint32_t blocksX_CMPR = paddedWidthCMPR / 8;
const uint32_t blocksY_CMPR = paddedHeightCMPR / 8;
for (uint32_t by = 0; by < blocksY_CMPR; ++by) {
for (uint32_t bx = 0; bx < blocksX_CMPR; ++bx) {
uint8_t subBlockPixels[4][16]{};
for (uint32_t subBlockY = 0; subBlockY < 2; ++subBlockY) {
for (uint32_t subBlockX = 0; subBlockX < 2; ++subBlockX) {
const uint32_t subBlock = subBlockY * 2 + subBlockX;
for (uint32_t row = 0; row < 4; ++row) {
for (uint32_t col = 0; col < 4; ++col) {
const uint32_t x = bx * 8 + subBlockX * 4 + col;
const uint32_t y = by * 8 + subBlockY * 4 + row;
if (x >= mipWidth || y >= mipHeight) {
continue;
}
const uint32_t tile = (y / 4) * tilesX_I8 + (x / 8);
const uint32_t tileOffset = (y % 4) * 8 + (x % 8);
subBlockPixels[subBlock][row * 4 + col] =
readPtr[tile * 32 + tileOffset];
}
}
}
}
for (const auto& subBlockPixel : subBlockPixels) {
encode_cmpr_sub_block(writePtr, subBlockPixel);
writePtr += 8;
}
}
}
const uint32_t tilesY_I8 = paddedHeightI8 / 4;
readPtr += tilesX_I8 * tilesY_I8 * 32;
mipWidth = std::max(1u, mipWidth >> 1);
mipHeight = std::max(1u, mipHeight >> 1);
}
cmprOut.swap(convertedData);
// Update format
replacementData.data.gx_format = GX_TF_CMPR;
return true;
}
void recolor_texture(TextureReplacementData& replacementData, GXColor color, std::vector<u8>& newTextureDataOut) {
switch (replacementData.key.gx_format) {
case GX_TF_CMPR:
recolor_cmpr_texture(replacementData, color, newTextureDataOut);
break;
case GX_TF_RGB5A3:
recolor_rgb5a3_texture(replacementData, color, newTextureDataOut);
break;
case GX_TF_I8:
if (convert_i8_to_cmpr(replacementData, newTextureDataOut)) {
recolor_cmpr_texture(replacementData, color, newTextureDataOut);
} else {
mods::log::debug("Could not convert {} from i8 to cmpr", replacementData.textureName);
}
break;
default:
break;
}
}
std::unordered_map<ConfigVarHandle, std::list<TextureReplacementData>>& get_texture_replacements() {
static std::unordered_map<ConfigVarHandle, std::list<TextureReplacementData>> replacements{};
if (replacements.empty()) {
replacements = {
{get_cvars().herosTunicCapColor, {
{
.arc = "Kmdl",
.modelFileName = "al_head.bmd",
.textureName = "al_cap",
}
}},
{get_cvars().herosTunicTorsoColor, {
{
.arc = "Kmdl",
.modelFileName = "al.bmd",
.textureName = "al_upbody",
}
}},
{get_cvars().herosTunicSkirtColor, {
{
.arc = "Kmdl",
.modelFileName = "al.bmd",
.textureName = "al_lowbody",
}
}},
{get_cvars().zoraArmorCapColor, {
{
.arc = "Zmdl",
.modelFileName = "zl_head.bmd",
.textureName = "zl_cap",
}
}},
{get_cvars().zoraArmorHelmetColor, {
{
.arc = "Zmdl",
.modelFileName = "zl_head.bmd",
.textureName = "zl_helmet",
}
}},
{get_cvars().zoraArmorTorsoColor, {
{
.arc = "Zmdl",
.modelFileName = "zl.bmd",
.textureName = "zl_armor",
},
{
.arc = "Zmdl",
.modelFileName = "zl.bmd",
.textureName = "zl_armL",
}
}},
{get_cvars().zoraArmorScalesColor, {
{
.arc = "Zmdl",
.modelFileName = "zl.bmd",
.textureName = "zl_body",
}
}},
{get_cvars().zoraArmorFlippersColor, {
{
.arc = "Zmdl",
.modelFileName = "zl.bmd",
.textureName = "zl_boots",
}
}},
{get_cvars().woodenSwordColor, {
{
.arc = "Bmdl", // Ordon Clothes Model
.modelFileName = "al_swb.bmd",
.textureName = "al_SWB",
},
{
.arc = "Kmdl", // Hero's Tunic Model
.modelFileName = "al_swb.bmd",
.textureName = "al_SWB",
},
{
.arc = "Zmdl", // Zora Armor Model
.modelFileName = "al_swb.bmd",
.textureName = "al_SWB",
},
{
.arc = "Mmdl", // Magic Armor Model
.modelFileName = "al_swb.bmd",
.textureName = "al_SWB",
},
{
.arc = "O_gD_SWB", // Get Item Model
.modelFileName = "o_gd_al_swb.bmd",
.textureName = "al_SWB",
}
}},
{get_cvars().ordonSwordHandleColor, {
{
.arc = "Alink",
.modelFileName = "al_swa.bmd",
.textureName = "al_SWgripA",
},
{
.arc = "O_gD_SWA", // Get Item Model
.modelFileName = "o_gd_al_swa.bmd",
.textureName = "al_SWgripA",
}
}},
{get_cvars().ordonSwordBladeColor, {
{
.arc = "Alink",
.modelFileName = "al_swa.bmd",
.textureName = "al_SWA",
}
}},
{get_cvars().msHandleColor, {
{
.arc = "Alink",
.modelFileName = "al_swm.bmd",
.textureName = "al_SWgripM",
}
}},
{get_cvars().msBladeColor, {
{
.arc = "Alink",
.modelFileName = "al_swm.bmd",
.textureName = "al_SWM",
}
}},
{get_cvars().boomerangColor, {
{
.arc = "Alink", // Boomerang in Link's hand
.modelFileName = "al_boom.bmd",
.textureName = "L_al_boom00",
},
{
.arc = "E_mk", // Boomerang in Ook's hand
.modelFileName = "bm.bmd",
.textureName = "L_al_boom00",
},
{
.arc = "E_mk", // Boomerang in Ook's hand
.modelFileName = "bm.bmd",
.textureName = "bm_boom",
},
{
.arc = "O_gD_boom", // Get Item Model
.modelFileName = "o_gd_boom.bmd",
.textureName = "L_al_boom00",
}
}},
{get_cvars().ironBootsColor, {
{
.arc = "Bmdl", // Ordon Clothes Model
.modelFileName = "al_bootsh.bmd",
.textureName = "al_bootsH",
},
{
.arc = "Kmdl", // Hero's Tunic Model
.modelFileName = "al_bootsh.bmd",
.textureName = "al_bootsH",
},
{
.arc = "Zmdl", // Zora Armor Model
.modelFileName = "al_bootsh.bmd",
.textureName = "al_bootsH",
},
{
.arc = "Mmdl", // Magic Armor Model
.modelFileName = "al_bootsh.bmd",
.textureName = "al_bootsH",
},
{
.arc = "O_gD_boot", // Get Item Model
.modelFileName = "o_gd_al_bootsh.bmd",
.textureName = "al_bootsH",
}
}},
{get_cvars().spinnerColor, {
{
.arc = "Alink", // Spinner used by Link
.modelFileName = "al_sp.bmd",
.textureName = "al_SP",
},
{
.arc = "O_gD_SP", // Get Item Model
.modelFileName = "o_gd_al_sp.bmd",
.textureName = "al_SP",
}
}},
{get_cvars().linkHairColor, {
{
.arc = "Bmdl", // Ordon Clothes Model
.modelFileName = "bl_head.bmd",
.textureName = "bl_hair",
},
{
.arc = "Kmdl", // Hero's Tunic Model
.modelFileName = "al_head.bmd",
.textureName = "al_hair",
},
{
.arc = "Mmdl", // Magic Armor Model
.modelFileName = "ml_head.bmd",
.textureName = "al_hair",
}
}},
{get_cvars().wolfLinkColor, {
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_body",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.1",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.2",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.3",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.4",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.5",
}
}},
{get_cvars().eponaColor, {
{
.arc = "Horse",
.modelFileName = "hs.bmd",
.textureName = "hs_body",
},
{
.arc = "Horse",
.modelFileName = "hs.bmd",
.textureName = "hs_eye.1",
},
{
.arc = "Horse",
.modelFileName = "hs.bmd",
.textureName = "hs_eye.2",
},
{
.arc = "Horse",
.modelFileName = "hs.bmd",
.textureName = "hs_eye.3",
},
}},
};
}
return replacements;
}
@@ -1,21 +0,0 @@
#pragma once
/**
* File originally copied from console TPR with permission from isaac
* https://github.com/zsrtp/libtp_rel/blob/master/include/util/texture_utils.h
*/
#include "mod.hpp"
#include <gx.h>
#include <list>
#include <unordered_map>
uint32_t get_image_data_size(
uint32_t format, uint32_t width, uint32_t height, uint32_t mipmapCount);
void recolor_texture(
TextureReplacementData& replacementData, GXColor color, std::vector<u8>& newTextureDataOut);
std::unordered_map<ConfigVarHandle, std::list<TextureReplacementData>>& get_texture_replacements();
+1
View File
@@ -17,4 +17,5 @@ add_mod(shadow_mod
SOURCES src/mod.cpp SOURCES src/mod.cpp
MOD_JSON mod.json MOD_JSON mod.json
RES_DIR res RES_DIR res
BUNDLE
) )
+1
View File
@@ -16,4 +16,5 @@ add_mod(window_demo
FEATURES fmt webgpu FEATURES fmt webgpu
SOURCES src/logging.cpp src/mod.cpp SOURCES src/logging.cpp src/mod.cpp
MOD_JSON mod.json MOD_JSON mod.json
BUNDLE
) )
+2
View File
@@ -188,6 +188,8 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) {
g_stageHook = 0; g_stageHook = 0;
return mods::set_error(error, MOD_ERROR, "failed to register mod panel"); return mods::set_error(error, MOD_ERROR, "failed to register mod panel");
} }
mods::log::info("auxiliary WebGPU window ready");
return MOD_OK; return MOD_OK;
} }
+95
View File
@@ -0,0 +1,95 @@
*, *:before, *:after {
box-sizing: border-box;
}
body {
display: block;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: visible;
pointer-events: none;
}
console {
position: absolute;
bottom: 10dp;
left: 10dp;
width: 50%;
display: flex;
flex-direction: column;
background-color: rgba(0, 0, 0, 60%);
pointer-events: auto;
font-family: "Noto Mono";
font-size: 14dp;
color: #FFFFFF;
transition: background-color 0.8s linear-in-out;
}
output {
display: block;
overflow: hidden;
max-height: 480dp;
padding: 4dp 8dp;
line-height: 1.4em;
}
output[open] {
height: 480dp;
max-height: 480dp;
overflow-y: auto;
}
console:not([open]) {
pointer-events: none;
}
console:not([open])[fading] {
background-color: rgba(0, 0, 0, 0%);
}
console[open] {
background-color: rgba(0, 0, 0, 60%);
transition: none;
}
line {
display: block;
white-space: nowrap;
opacity: 1;
transition: opacity 0.8s linear-in-out;
}
output:not([open]) line[fading] {
opacity: 0;
}
output:not([open]) line[expired] {
display: none;
}
output[open] line {
opacity: 1;
transition: none;
}
line.cmd {
color: #FFD966;
}
console input {
display: none;
width: 100%;
background-color: rgba(0, 0, 0, 40%);
border: 0dp;
border-top: 1dp rgba(255, 255, 255, 20%);
color: #FFFFFF;
font-family: "Noto Mono";
font-size: 14dp;
padding: 4dp 8dp;
}
console[open] input {
display: block;
}
-184
View File
@@ -1,184 +0,0 @@
*, *:before, *:after {
box-sizing: border-box;
}
body {
width: 100%;
height: 100%;
z-index: 10;
}
popover {
position: absolute;
display: flex;
flex-flow: column;
font-family: "Fira Sans";
font-size: 14dp;
color: #E0DBC8;
border-radius: 14dp;
border: 2dp #92875B;
background-color: rgba(21, 22, 16, 96%);
backdrop-filter: blur(5dp);
box-shadow: 0 6dp 24dp 2dp rgba(0, 0, 0, 55%);
filter: opacity(0);
transform: scale(0.95);
transform-origin: center;
transition: filter transform 0.15s cubic-in-out;
focus: auto;
}
popover[open] {
filter: opacity(1);
transform: scale(1);
}
popover.color-picker {
width: 280dp;
padding: 18dp;
gap: 10dp;
}
color-sv {
display: block;
position: relative;
width: 240dp;
height: 150dp;
border-radius: 8dp;
box-shadow: rgba(146, 135, 91, 50%) 0 0 0 1dp;
drag: drag;
focus: auto;
}
color-hue,
color-alpha {
display: block;
position: relative;
width: 240dp;
height: 14dp;
border-radius: 7dp;
box-shadow: rgba(146, 135, 91, 50%) 0 0 0 1dp;
drag: drag;
focus: auto;
}
color-sv:focus-visible,
color-hue:focus-visible,
color-alpha:focus-visible {
box-shadow: #C2A42D 0 0 0 2dp;
}
color-sv.adjusting,
color-hue.adjusting,
color-alpha.adjusting {
box-shadow: #FFFFFF 0 0 0 3dp;
}
color-hue {
decorator: linear-gradient(90deg, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);
}
color-cursor {
display: block;
position: absolute;
width: 14dp;
height: 14dp;
border-radius: 7dp;
border: 2dp #ffffff;
box-shadow: 0 0 4dp 1dp rgba(0, 0, 0, 70%);
pointer-events: none;
}
color-heading {
display: block;
margin-top: 2dp;
font-family: "Fira Sans Condensed";
font-weight: bold;
font-size: 13dp;
text-transform: uppercase;
color: rgba(224, 219, 200, 55%);
}
color-presets {
display: flex;
flex-flow: row wrap;
gap: 5dp;
}
color-history {
display: flex;
flex-flow: row wrap;
gap: 5dp;
}
button.color-swatch-button {
width: 25dp;
height: 25dp;
padding: 0;
border-radius: 7dp;
}
color-chip {
display: block;
flex: 0 0 20dp;
width: 20dp;
height: 20dp;
border-radius: 5dp;
box-shadow: rgba(255, 255, 255, 45%) 0 0 0 1dp;
}
button.color-swatch-button color-chip {
width: 25dp;
height: 25dp;
border-radius: 7dp;
}
color-chip.empty,
color-swatch.empty {
background-color: rgba(224, 219, 200, 12%);
decorator: linear-gradient(135deg, rgba(224, 219, 200, 0) 45%,
rgba(194, 164, 45, 70%) 48%, rgba(194, 164, 45, 70%) 52%,
rgba(224, 219, 200, 0) 55%);
}
color-footer {
display: flex;
align-items: center;
gap: 6dp;
padding-top: 2dp;
}
button {
background-color: rgba(17, 16, 10, 35%);
padding: 4dp 8dp;
border-radius: 8dp;
box-shadow: rgba(146, 135, 91, 30%) 0 0 0 1dp;
color: #E0DBC8;
cursor: pointer;
focus: auto;
}
button:hover,
button:focus-visible {
background-color: rgba(204, 184, 119, 20%);
box-shadow: #C2A42D 0 0 0 2dp;
}
button:active {
background-color: rgba(204, 184, 119, 40%);
}
color-value {
display: block;
flex: 1 1 auto;
text-align: right;
font-family: "Noto Mono";
font-size: 12dp;
color: #FFFFFF;
cursor: pointer;
focus: auto;
}
color-value:hover,
color-value:focus-visible {
color: #C2A42D;
}
+3 -111
View File
@@ -48,12 +48,6 @@ window.modal {
max-width: 640dp; max-width: 640dp;
} }
@media (max-height: 896dp) {
window.modal {
max-height: 100%;
}
}
window[open] { window[open] {
filter: opacity(1); filter: opacity(1);
transform: scale(1); transform: scale(1);
@@ -68,16 +62,6 @@ window[open] {
} }
} }
@media (max-width: 768dp) {
body {
padding: 16dp;
}
window.modal {
width: 100%;
max-width: 100%;
}
}
window tab-bar { window tab-bar {
flex: 0 0 64dp; flex: 0 0 64dp;
height: 64dp; height: 64dp;
@@ -294,40 +278,6 @@ select-button input {
font-size: 20dp; font-size: 20dp;
} }
select-button.group-button icon {
flex: 0 0 24dp;
margin-left: auto;
width: 24dp;
height: 24dp;
font-size: 24dp;
color: inherit;
decorator: text("&#xe5cc;" center center);
}
select-button.group-button value {
display: none;
}
select-button.color-input value {
min-width: 0;
font-family: "Noto Mono";
font-size: 15dp;
}
select-button.color-input color-swatch {
display: block;
flex: 0 0 48dp;
width: 48dp;
height: 24dp;
border-radius: 7dp;
box-shadow: rgba(255, 255, 255, 45%) 0 0 0 1dp;
}
select-button.color-input color-swatch.empty {
background-color: rgba(224, 219, 200, 12%);
decorator: linear-gradient(135deg, rgba(224, 219, 200, 0) 45%, rgba(194, 164, 45, 70%) 48%, rgba(194, 164, 45, 70%) 52%, rgba(224, 219, 200, 0) 55%);
}
icon { icon {
width: 1em; width: 1em;
height: 1em; height: 1em;
@@ -435,17 +385,14 @@ progress {
margin: 6dp 0 2dp 0; margin: 6dp 0 2dp 0;
} }
progress fill {
background-color: rgba(194, 164, 45, 80%);
border-radius: 3dp;
}
progress.progress-done fill { progress.progress-done fill {
background-color: #44aa22; background-color: #44aa22;
border-radius: 3dp;
} }
progress.progress-ongoing fill { progress.progress-ongoing fill {
background-color: #2255bb; background-color: #2255bb;
border-radius: 3dp;
} }
button.achievement-clear { button.achievement-clear {
@@ -485,9 +432,7 @@ button.achievement-clear {
padding: 24dp; padding: 24dp;
gap: 20dp; gap: 20dp;
flex: 0 1 auto; flex: 0 1 auto;
min-height: 0;
width: 100%; width: 100%;
overflow: hidden;
text-align: left; text-align: left;
} }
@@ -528,7 +473,7 @@ window.modal.danger .modal-header icon {
.modal-body { .modal-body {
display: block; display: block;
width: 100%; width: 100%;
flex: 0 0 auto; flex: 0 1 auto;
min-width: 0; min-width: 0;
font-size: 20dp; font-size: 20dp;
color: #FFFFFF; color: #FFFFFF;
@@ -540,37 +485,6 @@ window.modal.danger .modal-header icon {
color: #92875B; color: #92875B;
} }
.modal-content {
display: none;
width: 100%;
flex: 1 1 auto;
min-height: 0;
overflow: hidden;
}
.modal-content.active {
display: flex;
flex-direction: column;
}
.modal-content pane {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-height: 0;
width: 100%;
gap: 8dp;
overflow: hidden auto;
}
.modal-content pane > * {
flex: 0 0 auto;
}
.modal-content pane > spacer {
display: none;
}
.verification-progress { .verification-progress {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -615,25 +529,3 @@ progress.verification-progress-bar {
flex: 0 0 auto; flex: 0 0 auto;
width: 100%; width: 100%;
} }
@media (max-height: 640dp) {
.modal-dialog {
padding: 16dp;
gap: 12dp;
}
.modal-body {
font-size: 17dp;
}
}
@media (max-width: 640dp) {
.modal-actions {
flex-direction: column;
}
.modal-actions button.modal-btn {
flex: 0 0 auto;
width: 100%;
}
}
-62
View File
@@ -1,62 +0,0 @@
#pragma once
/*
* ItemService check names.
*/
#define ITEM_CHECK_BOSS_PREFIX "boss:" /* <stage> */
#define ITEM_CHECK_BUG_PREFIX "bug:" /* <insect_item> */
#define ITEM_CHECK_CHEST_PREFIX "chest:" /* <stage>:<box> */
#define ITEM_CHECK_FREESTANDING_PREFIX "freestanding:" /* <stage>:<bit> */
#define ITEM_CHECK_POE_PREFIX "poe:" /* <stage>:<switch> */
#define ITEM_CHECK_SHOP_PREFIX "shop:" /* <stage>:<item> */
#define ITEM_CHECK_SKY_PREFIX "sky:" /* <stage>:<room> */
#define ITEM_CHECK_DUNGEON_REWARD_PREFIX "dungeon_reward:" /* <stage> */
#define ITEM_CHECK_ARCHERY_REWARD "archery_reward:F_SP109"
#define ITEM_CHECK_ASHEI_SKETCH "ashei_sketch"
#define ITEM_CHECK_AURU_MEMO "auru_memo"
#define ITEM_CHECK_BALL_AND_CHAIN "ball_and_chain:D_MN11"
#define ITEM_CHECK_BULBLIN_KEY "bulblin_key:D_MN09"
#define ITEM_CHECK_CORAL_EARRING "coral_earring"
#define ITEM_CHECK_CORO_BOTTLE "coro_bottle"
#define ITEM_CHECK_DUNGEON_MAP_SNOWPEAK "dungeon_map:D_MN11"
#define ITEM_CHECK_FAIRY_REWARD "fairy_reward:D_SB01"
#define ITEM_CHECK_FISHING_BOTTLE "fishing_bottle"
#define ITEM_CHECK_FISHING_HEART_PIECE "fishing_heart_piece"
#define ITEM_CHECK_GOATS_REWARD "goats_reward"
#define ITEM_CHECK_GORON_REWARD "goron_reward:F_SP113"
#define ITEM_CHECK_ILIA_CHARM "ilia_charm"
#define ITEM_CHECK_ILIA_MEMORY "ilia_memory"
#define ITEM_CHECK_IZA_REWARD_1 "iza_reward_1"
#define ITEM_CHECK_IZA_REWARD_2 "iza_reward_2"
#define ITEM_CHECK_JOVANI_REWARD_1 "jovani_reward_1"
#define ITEM_CHECK_JOVANI_REWARD_2 "jovani_reward_2"
#define ITEM_CHECK_KEY_SHARD_1 "key_shard_1:D_MN04"
#define ITEM_CHECK_KEY_SHARD_2 "key_shard_2:D_MN04"
#define ITEM_CHECK_KEY_SHARD_3 "key_shard_3:D_MN04"
#define ITEM_CHECK_ORDON_SHIELD "ordon_shield"
#define ITEM_CHECK_ORDON_SWORD "ordon_sword"
#define ITEM_CHECK_PLUMM_REWARD "plumm_minigame_reward"
#define ITEM_CHECK_PRAYER_REWARD "prayer_reward"
#define ITEM_CHECK_RENADO_LETTER "renado_letter"
#define ITEM_CHECK_SERA_REWARD "sera_reward"
#define ITEM_CHECK_SKYBOOK "skybook"
#define ITEM_CHECK_SNOWBOARD_REWARD "snowboard_race_reward"
#define ITEM_CHECK_STAR_REWARD_1 "star_reward_1"
#define ITEM_CHECK_STAR_REWARD_2 "star_reward_2"
#define ITEM_CHECK_TELMA_INVOICE "telma_invoice"
#define ITEM_CHECK_ULI_CRADLE_REWARD "uli_cradle_reward"
#define ITEM_CHECK_WOOD_STATUE "wood_statue"
#define ITEM_CHECK_ZORA_ARMOR "zora_armor"
#define ITEM_CHECK_DUNGEON_REWARD_FOREST "dungeon_reward:D_MN05"
#define ITEM_CHECK_DUNGEON_REWARD_GORON "dungeon_reward:D_MN04"
#define ITEM_CHECK_DUNGEON_REWARD_LAKEBED "dungeon_reward:D_MN01"
#define ITEM_CHECK_DUNGEON_REWARD_ARBITERS "dungeon_reward:D_MN10"
#define ITEM_CHECK_DUNGEON_REWARD_SNOWPEAK "dungeon_reward:D_MN11"
#define ITEM_CHECK_DUNGEON_REWARD_TIME "dungeon_reward:D_MN06"
#define ITEM_CHECK_DUNGEON_REWARD_CITY "dungeon_reward:D_MN07"
#define ITEM_CHECK_MASTER_SWORD "master_sword"
#define ITEM_CHECK_SHADOW_CRYSTAL "shadow_crystal"
+7 -9
View File
@@ -17,12 +17,10 @@ typedef uint64_t ItemGiveHandle;
/* /*
* Item check resolution and inventory grants. * Item check resolution and inventory grants.
* *
* Check names are case-sensitive. Resolvers must be free of side effects because previews may be * Check names are case-sensitive. Resolvers must be free of side effects because the game may
* resolved repeatedly. For checks representing a non-item side effect, vanilla_item is 0xFF: * resolve a check more than once, including once for display and again when granting the item.
* resolving to 0xFF preserves that side effect, while resolving to another item replaces it. * Registrations and pending grants are removed when the calling mod is detached. Callbacks run
* * on the game thread.
* Registrations and pending grants are removed when the calling mod is detached. Callbacks run on
* the game thread. Check names are listed in <mods/items.h>.
*/ */
/* Host-owned callback data, valid only for the duration of the callback. */ /* Host-owned callback data, valid only for the duration of the callback. */
@@ -74,14 +72,14 @@ typedef struct ItemService {
ModResult (*clear_check_resolver)(ModContext* ctx, ItemCheckHandle handle); ModResult (*clear_check_resolver)(ModContext* ctx, ItemCheckHandle handle);
/* Resolve a live preview without granting an item or notifying give observers. */ /* Resolve without granting an item or notifying give observers. */
ModResult (*resolve_check)( ModResult (*resolve_check)(
ModContext* ctx, const char* name, uint8_t vanilla_item, uint8_t* out_item); ModContext* ctx, const char* name, uint8_t vanilla_item, uint8_t* out_item);
/* /*
* Add a grant to the global FIFO. check_name may be NULL unless ITEM_GIVE_RESOLVE is set. * Add a grant to the global FIFO. check_name may be NULL unless ITEM_GIVE_RESOLVE is set.
* Entries wait until gameplay is in a safe state and are cleared when the active save slot * Returns MOD_UNAVAILABLE when the queue is full. Entries wait until gameplay is in a safe
* changes. * state and are cleared when the active save slot changes.
*/ */
ModResult (*give_item)( ModResult (*give_item)(
ModContext* ctx, const char* check_name, uint8_t item_no, uint32_t flags); ModContext* ctx, const char* check_name, uint8_t item_no, uint32_t flags);
+15 -50
View File
@@ -9,7 +9,7 @@
#define UI_SERVICE_ID "dev.twilitrealm.dusklight.ui" #define UI_SERVICE_ID "dev.twilitrealm.dusklight.ui"
#define UI_SERVICE_MAJOR 1u #define UI_SERVICE_MAJOR 1u
#define UI_SERVICE_MINOR 4u #define UI_SERVICE_MINOR 2u
/* /*
* UI primitives: a panel inside the host Mods window, mod-owned windows, dialogs, toasts, * UI primitives: a panel inside the host Mods window, mod-owned windows, dialogs, toasts,
@@ -51,8 +51,6 @@ typedef enum UiControlKind {
UI_CONTROL_NUMBER = 2, /* integer stepper with min/max/step */ UI_CONTROL_NUMBER = 2, /* integer stepper with min/max/step */
UI_CONTROL_STRING = 3, /* text input */ UI_CONTROL_STRING = 3, /* text input */
UI_CONTROL_SELECT = 4, /* one of `options`; the value is the option index */ UI_CONTROL_SELECT = 4, /* one of `options`; the value is the option index */
UI_CONTROL_COLOR = 5, /* RGB/RGBA color string with a picker */
UI_CONTROL_GROUP = 6, /* navigation row (on_pressed) */
} UiControlKind; } UiControlKind;
typedef enum UiControlBinding { typedef enum UiControlBinding {
@@ -62,14 +60,14 @@ typedef enum UiControlBinding {
/* The control reads and writes `config_var` (a ConfigService handle owned by the calling mod) /* The control reads and writes `config_var` (a ConfigService handle owned by the calling mod)
* directly: persistence, change notifications and the modified indicator (value != default) are * directly: persistence, change notifications and the modified indicator (value != default) are
* wired automatically. The var type must match the control kind: TOGGLE = bool, NUMBER and * wired automatically. The var type must match the control kind: TOGGLE = bool, NUMBER and
* SELECT = int, STRING and COLOR = string. Float vars are not bindable; use callbacks. */ * SELECT = int, STRING = string. Float vars are not bindable; use callbacks. */
UI_BINDING_CONFIG_VAR = 1, UI_BINDING_CONFIG_VAR = 1,
} UiControlBinding; } UiControlBinding;
/* Tagged by the control's kind: TOGGLE reads bool_value, NUMBER and SELECT read int_value, STRING /* Tagged by the control's kind: TOGGLE reads bool_value, NUMBER and SELECT read int_value, STRING
* and COLOR read string_value. string_value passed to a setter is only valid during the call; a * reads string_value. string_value passed to a setter is only valid during the call; a getter
* getter should point it at storage owned by the mod (e.g. a static buffer) that stays valid until * should point it at storage owned by the mod (e.g. a static buffer) that stays valid until the
* the next call into the mod the host copies it right after the getter returns. */ * next call into the mod the host copies it right after the getter returns. */
typedef struct UiControlValue { typedef struct UiControlValue {
uint32_t struct_size; uint32_t struct_size;
bool bool_value; bool bool_value;
@@ -93,11 +91,11 @@ typedef struct UiControlDesc {
/* Optional RML shown as contextual help when the control is focused or hovered. Only rendered /* Optional RML shown as contextual help when the control is focused or hovered. Only rendered
* where a help pane exists (mod window tabs). */ * where a help pane exists (mod window tabs). */
const char* help_rml; const char* help_rml;
UiControlBinding binding; /* ignored for BUTTON and GROUP */ UiControlBinding binding; /* ignored for BUTTON */
ConfigVarHandle config_var; /* UI_BINDING_CONFIG_VAR */ ConfigVarHandle config_var; /* UI_BINDING_CONFIG_VAR */
UiControlGetFn get; /* UI_BINDING_CALLBACKS (all kinds but BUTTON/GROUP) */ UiControlGetFn get; /* UI_BINDING_CALLBACKS (all kinds but BUTTON) */
UiControlSetFn set; /* UI_BINDING_CALLBACKS (all kinds but BUTTON/GROUP) */ UiControlSetFn set; /* UI_BINDING_CALLBACKS (all kinds but BUTTON) */
UiPressedFn on_pressed; /* BUTTON/GROUP only. Required for both. */ UiPressedFn on_pressed; /* BUTTON only. Required for BUTTON. */
UiPredicateFn is_disabled; /* optional */ UiPredicateFn is_disabled; /* optional */
/* Optional override for the modified indicator. CONFIG_VAR controls derive it from value != /* Optional override for the modified indicator. CONFIG_VAR controls derive it from value !=
* default when this is NULL. */ * default when this is NULL. */
@@ -117,24 +115,17 @@ typedef struct UiControlDesc {
const char* const* options; const char* const* options;
size_t option_count; size_t option_count;
int32_t max_length; /* STRING: maximum input length; < 1 means unlimited */ int32_t max_length; /* STRING: maximum input length; < 1 means unlimited */
/* COLOR: optional RRGGBB/RRGGBBAA values for presets. "rainbow" is a special value. */
const char* const* color_presets;
size_t color_preset_count;
bool color_alpha; /* COLOR: use RRGGBBAA values instead of RRGGBB */
} UiControlDesc; } UiControlDesc;
#define UI_CONTROL_DESC_INIT \ #define UI_CONTROL_DESC_INIT \
{sizeof(UiControlDesc), UI_CONTROL_BUTTON, NULL, NULL, UI_BINDING_CALLBACKS, 0u, NULL, NULL, \ {sizeof(UiControlDesc), UI_CONTROL_BUTTON, NULL, NULL, UI_BINDING_CALLBACKS, 0u, NULL, NULL, \
NULL, NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL, 0u, 0, NULL, 0u, false} NULL, NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL, 0u, 0}
/* Build pane contents. A non-MOD_OK result fails the mod. */
typedef ModResult (*UiPaneBuildFn)(
ModContext* ctx, UiElementHandle pane, void* user_data, ModError* out_error);
/* Build the panel contents. `panel` accepts the pane_add_* functions; it and /* Build the panel contents. `panel` accepts the pane_add_* functions; it and
* every element created in it are destroyed (handles invalidated) whenever the * every element created in it are destroyed (handles invalidated) whenever the
* panel is rebuilt, e.g. on tab switches. A non-MOD_OK result fails the mod. */ * panel is rebuilt, e.g. on tab switches. A non-MOD_OK result fails the mod. */
typedef UiPaneBuildFn UiPanelBuildFn; typedef ModResult (*UiPanelBuildFn)(
ModContext* ctx, UiElementHandle panel, void* user_data, ModError* out_error);
/* Called every frame while the panel is the visible tab. */ /* Called every frame while the panel is the visible tab. */
typedef ModResult (*UiPanelUpdateFn)(ModContext* ctx, void* user_data, ModError* out_error); typedef ModResult (*UiPanelUpdateFn)(ModContext* ctx, void* user_data, ModError* out_error);
@@ -148,19 +139,6 @@ typedef struct UiModsPanelDesc {
#define UI_MODS_PANEL_DESC_INIT {sizeof(UiModsPanelDesc), NULL, NULL, NULL} #define UI_MODS_PANEL_DESC_INIT {sizeof(UiModsPanelDesc), NULL, NULL, NULL}
/* Builds the contents associated with a group button. The target pane is cleared immediately
* before this callback and is valid only while its tab remains built. */
typedef UiPaneBuildFn UiGroupBuildFn;
typedef struct UiGroupDesc {
uint32_t struct_size;
const char* label; /* required */
UiGroupBuildFn build; /* required */
void* user_data;
} UiGroupDesc;
#define UI_GROUP_DESC_INIT {sizeof(UiGroupDesc), NULL, NULL, NULL}
/* Build one tab of a mod window. `left_pane` is the interactive column, /* Build one tab of a mod window. `left_pane` is the interactive column,
* `right_pane` shows contextual help (controls' help_rml and SELECT options * `right_pane` shows contextual help (controls' help_rml and SELECT options
* render there). Rebuilt on every tab activation. */ * render there). Rebuilt on every tab activation. */
@@ -218,15 +196,11 @@ typedef struct UiDialogDesc {
/* Fired on cancel (B/Escape) before the dialog closes; the dialog always /* Fired on cancel (B/Escape) before the dialog closes; the dialog always
* closes on dismiss. */ * closes on dismiss. */
UiDialogActionFn on_dismiss; UiDialogActionFn on_dismiss;
void* user_data; /* passed to build and on_dismiss */ void* user_data; /* passed to on_dismiss */
/* Optional content builder. The pane is rendered below body_rml and above the actions. Its
* handle and all child element handles remain valid until the dialog closes.
* Added in UiService minor version 4. */
UiPaneBuildFn build;
} UiDialogDesc; } UiDialogDesc;
#define UI_DIALOG_DESC_INIT \ #define UI_DIALOG_DESC_INIT \
{sizeof(UiDialogDesc), NULL, NULL, UI_DIALOG_NORMAL, NULL, NULL, 0u, NULL, NULL, NULL} {sizeof(UiDialogDesc), NULL, NULL, UI_DIALOG_NORMAL, NULL, NULL, 0u, NULL, NULL}
/* A tab added to the in-game menu bar. */ /* A tab added to the in-game menu bar. */
typedef struct UiMenuTabDesc { typedef struct UiMenuTabDesc {
@@ -319,17 +293,8 @@ typedef struct UiService {
ModResult (*push_toast)(ModContext* ctx, const UiToastDesc* desc); ModResult (*push_toast)(ModContext* ctx, const UiToastDesc* desc);
/* Minor version 2 */ /* Minor version 2 */
ModResult (*get_clipboard_text)(ModContext* ctx, char* buffer, size_t bufferSize, size_t* outLength);
ModResult (*get_clipboard_text)(
ModContext* ctx, char* buffer, size_t bufferSize, size_t* outLength);
ModResult (*set_clipboard_text)(ModContext* ctx, const char* text); ModResult (*set_clipboard_text)(ModContext* ctx, const char* text);
/* Minor version 3 */
/* Add a group button to one pane that builds controls in its paired pane.
* The panes must be the left and right handles from the same UiTabBuildFn call. */
ModResult (*pane_add_group)(ModContext* ctx, UiElementHandle group_pane,
UiElementHandle target_pane, const UiGroupDesc* desc, UiElementHandle* out_elem);
} UiService; } UiService;
MOD_DECLARE_SERVICE(UiService, svc_ui, UI_SERVICE_ID, UI_SERVICE_MAJOR, UI_SERVICE_MINOR); MOD_DECLARE_SERVICE(UiService, svc_ui, UI_SERVICE_ID, UI_SERVICE_MAJOR, UI_SERVICE_MINOR);
+1 -24
View File
@@ -1027,9 +1027,6 @@ void daAlink_c::setGetItemFace(u16 i_itemNo) {
|| i_itemNo == dItemNo_ANCIENT_DOCUMENT2_e || i_itemNo == dItemNo_ANCIENT_DOCUMENT2_e
|| i_itemNo == dItemNo_DROP_CONTAINER_e || i_itemNo == dItemNo_DROP_CONTAINER_e
|| i_itemNo == dItemNo_DROP_CONTAINER02_e || i_itemNo == dItemNo_DROP_CONTAINER02_e
#if TARGET_PC
|| i_itemNo == dItemNo_SHADOW_CRYSTAL_e
#endif
|| i_itemNo == dItemNo_DROP_CONTAINER03_e) || i_itemNo == dItemNo_DROP_CONTAINER03_e)
{ {
setFaceBasicBck(dRes_ID_ALANM_BCK_FI_e); setFaceBasicBck(dRes_ID_ALANM_BCK_FI_e);
@@ -2012,11 +2009,7 @@ void daAlink_c::setGetSubBgm(int i_itemNo) {
/* dItemNo_WEAR_KOKIRI_e */ SETYPE_NONE, /* dItemNo_WEAR_KOKIRI_e */ SETYPE_NONE,
/* dItemNo_ARMOR_e */ SETYPE_ITEM_GET, /* dItemNo_ARMOR_e */ SETYPE_ITEM_GET,
/* dItemNo_WEAR_ZORA_e */ SETYPE_ITEM_GET, /* dItemNo_WEAR_ZORA_e */ SETYPE_ITEM_GET,
#if TARGET_PC
/* dItemNo_SHADOW_CRYSTAL_e */ SETYPE_ITEM_GET,
#else
/* dItemNo_MAGIC_LV1_e */ SETYPE_NONE, /* dItemNo_MAGIC_LV1_e */ SETYPE_NONE,
#endif
/* dItemNo_DUNGEON_EXIT_2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_DUNGEON_EXIT_2_e */ SETYPE_ITEM_GET_MINI,
/* dItemNo_WALLET_LV1_e */ SETYPE_NONE, /* dItemNo_WALLET_LV1_e */ SETYPE_NONE,
/* dItemNo_WALLET_LV2_e */ SETYPE_ITEM_GET, /* dItemNo_WALLET_LV2_e */ SETYPE_ITEM_GET,
@@ -2314,13 +2307,8 @@ int daAlink_c::procCoGetItemInit() {
item_no = dComIfGp_event_getGtItm(); item_no = dComIfGp_event_getGtItm();
} }
#if TARGET_PC
const u32 giveTag = mDemo.getParam0() != 0x100 && mDemo.getParam1() != 0 ?
static_cast<u32>(mDemo.getParam1()) :
dusk::mods::item_give_queue_take_tag();
#endif
fpc_ProcID item_partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, fpc_ProcID item_partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1,
fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(giveTag)); fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(dusk::mods::item_give_queue_take_tag()));
if (item_partner_id != fpcM_ERROR_PROCESS_ID_e) { if (item_partner_id != fpcM_ERROR_PROCESS_ID_e) {
dComIfGp_event_setItemPartnerId(item_partner_id); dComIfGp_event_setItemPartnerId(item_partner_id);
} }
@@ -2545,16 +2533,6 @@ int daAlink_c::procCoGetItem() {
} else if (mProcVar2.field_0x300c == 0x23 && checkStageName("D_MN11")) { } else if (mProcVar2.field_0x300c == 0x23 && checkStageName("D_MN11")) {
field_0x32cc = 0x5C0; field_0x32cc = 0x5C0;
} else if (mProcVar2.field_0x300c == 0xE0) { } else if (mProcVar2.field_0x300c == 0xE0) {
#if TARGET_PC
// Dusklight increments the Poe count when the displayed item is granted.
if (dComIfGs_getPohSpiritNum() + 1 == 20) {
field_0x32cc = 0x4CF;
} else if (dComIfGs_getPohSpiritNum() + 1 == 60) {
field_0x32cc = 0x4D0;
} else {
field_0x32cc = mProcVar2.field_0x300c + 0x65;
}
#else
if (dComIfGs_getPohSpiritNum() == 20) { if (dComIfGs_getPohSpiritNum() == 20) {
field_0x32cc = 0x4CF; field_0x32cc = 0x4CF;
} else if (dComIfGs_getPohSpiritNum() == 60) { } else if (dComIfGs_getPohSpiritNum() == 60) {
@@ -2562,7 +2540,6 @@ int daAlink_c::procCoGetItem() {
} else { } else {
field_0x32cc = mProcVar2.field_0x300c + 0x65; field_0x32cc = mProcVar2.field_0x300c + 0x65;
} }
#endif
} else if (mProcVar2.field_0x300c == 0x21) { } else if (mProcVar2.field_0x300c == 0x21) {
static u32 const heartPieceMessage[5] = {0x86, 0x9C, 0x9D, 0x9E, 0x9F}; static u32 const heartPieceMessage[5] = {0x86, 0x9C, 0x9D, 0x9E, 0x9F};
field_0x32cc = heartPieceMessage[dComIfGs_getMaxLife() % 5]; field_0x32cc = heartPieceMessage[dComIfGs_getMaxLife() % 5];
+1 -6
View File
@@ -19,10 +19,6 @@
#include "f_op/f_op_actor_enemy.h" #include "f_op/f_op_actor_enemy.h"
#include "Z2AudioLib/Z2Instances.h" #include "Z2AudioLib/Z2Instances.h"
#if TARGET_PC
#include "mods/items.h"
#endif
enum daB_DS_Joint { enum daB_DS_Joint {
DS_JNT_BACKBONE1, DS_JNT_BACKBONE1,
DS_JNT_BACKBONE2, DS_JNT_BACKBONE2,
@@ -4090,8 +4086,7 @@ void daB_DS_c::executeBattle2Dead() {
dComIfGs_onStageBossEnemy(0x13); dComIfGs_onStageBossEnemy(0x13);
#if TARGET_PC #if TARGET_PC
// This reward has no original grant at this point in the cutscene. // This reward has no original grant at this point in the cutscene.
dusk::mods::item_check_enqueue_deferred( dusk::mods::item_check_enqueue("Arbiters Grounds Dungeon Reward", dItemNo_NONE_e);
ITEM_CHECK_DUNGEON_REWARD_ARBITERS, dItemNo_NONE_e);
#endif #endif
/* dSv_event_flag_c::F_0265 - Arbiter's Grounds - Arbiter's Grounds clear */ /* dSv_event_flag_c::F_0265 - Arbiter's Grounds - Arbiter's Grounds clear */
dComIfGs_onEventBit(0x2010); dComIfGs_onEventBit(0x2010);
-7
View File
@@ -1090,13 +1090,6 @@ inline int daDemo00_c::execute() {
break; break;
} }
case 1: case 1:
#if TARGET_PC
// Suppress demo22_01's M_077 write. On Dusklight, the transform flag
// is set by the Shadow Crystal item.
if (dSv_event_flag_c::saveBitLabels[sp0E] == dSv_event_flag_c::M_077) {
break;
}
#endif
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[sp0E]); dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[sp0E]);
break; break;
+8 -14
View File
@@ -727,25 +727,19 @@ void daE_HP_c::executeDead() {
} }
#if TARGET_PC #if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit( mItemCheckOverridden =
dusk::mods::item_give_tag_poe(bitSw), dItemNo_POU_SPIRIT_e, this); dusk::mods::item_check_poe(bitSw, dItemNo_POU_SPIRIT_e, this) != dItemNo_POU_SPIRIT_e;
mItemCheckHandled = true; if (mItemCheckOverridden) {
if (itemCheck.itemNo == dItemNo_NONE_e) { dusk::mods::item_check_enqueue_poe(bitSw, dItemNo_POU_SPIRIT_e);
dusk::mods::item_check_complete(itemCheck, this); } else
} else {
dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::ForcedDemo);
}
#else
dComIfGs_addPohSpiritNum();
#endif #endif
dComIfGs_addPohSpiritNum();
field_0x784 = -1; field_0x784 = -1;
#if !TARGET_PC if (dComIfGs_getPohSpiritNum() == 20 IF_DUSK(&&!mItemCheckOverridden)) {
if (dComIfGs_getPohSpiritNum() == 20) {
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[0x1c9]); dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[0x1c9]);
} }
#endif
movemode++; movemode++;
} }
@@ -765,7 +759,7 @@ void daE_HP_c::executeDead() {
field_0x788 = 1; field_0x788 = 1;
} }
} }
} else if (field_0x788 != 0 IF_DUSK(|| mItemCheckHandled)) { } else if (field_0x788 != 0 IF_DUSK(|| mItemCheckOverridden)) {
fopAcM_createDisappear(this, &current.pos, 8, 3, 0xff); fopAcM_createDisappear(this, &current.pos, 8, 3, 0xff);
fopAcM_delete(this); fopAcM_delete(this);
} else { } else {
+12 -21
View File
@@ -1133,24 +1133,21 @@ static void e_po_dead(e_po_class* i_this) {
camera_player->mCamera.SetTrimSize(0); camera_player->mCamera.SetTrimSize(0);
dComIfGp_event_reset(); dComIfGp_event_reset();
#if TARGET_PC #if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit( if (dusk::mods::item_check_poe(i_this->BitSW, dItemNo_POU_SPIRIT_e, a_this) ==
dusk::mods::item_give_tag_poe(i_this->BitSW), dItemNo_POU_SPIRIT_e, a_this); dItemNo_POU_SPIRIT_e)
daPy_getPlayerActorClass()->cancelOriginalDemo(); {
if (itemCheck.itemNo == dItemNo_NONE_e) { #endif
dusk::mods::item_check_complete(itemCheck, a_this); dComIfGs_addPohSpiritNum();
} else {
dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::ForcedDemo);
}
#else
dComIfGs_addPohSpiritNum();
#if !PLATFORM_SHIELD #if !PLATFORM_SHIELD
if (dComIfGs_getPohSpiritNum() == 0x14) { if (dComIfGs_getPohSpiritNum() == 0x14) {
/* dSv_event_flag_c::F_0457 - Castle Town - Revived cat */ /* dSv_event_flag_c::F_0457 - Castle Town - Revived cat */
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[457]); dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[457]);
} }
#endif #endif
daPy_getPlayerActorClass()->cancelOriginalDemo(); #if TARGET_PC
}
#endif #endif
daPy_getPlayerActorClass()->cancelOriginalDemo();
} else if (mArg0Check(i_this, 0) != 0) { } else if (mArg0Check(i_this, 0) != 0) {
if (!fopAcM_isSwitch(a_this, 0x22)) { if (!fopAcM_isSwitch(a_this, 0x22)) {
if (fopAcM_SearchByID(i_this->field_0x5B8, &local_1b0_actor) != 0 && if (fopAcM_SearchByID(i_this->field_0x5B8, &local_1b0_actor) != 0 &&
@@ -1277,16 +1274,10 @@ static void e_po_dead(e_po_class* i_this) {
} else { } else {
if (i_this->field_0x75C == -1) { if (i_this->field_0x75C == -1) {
#if TARGET_PC #if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit( const u8 itemNo =
dusk::mods::item_give_tag_poe(i_this->BitSW), dItemNo_POU_SPIRIT_e, a_this); dusk::mods::item_check_poe(i_this->BitSW, dItemNo_POU_SPIRIT_e, a_this);
if (itemCheck.itemNo == dItemNo_NONE_e) { i_this->field_0x75C = fopAcM_createItemForPresentDemo(&a_this->current.pos, itemNo,
dusk::mods::item_check_complete(itemCheck, a_this); 0, -1, -1, NULL, NULL, dusk::mods::item_give_tag_poe(i_this->BitSW));
i_this->field_0x75B = 1;
} else {
i_this->field_0x75C = fopAcM_createItemForPresentDemo(
&a_this->current.pos, itemCheck.itemNo, 0, -1, -1, NULL, NULL,
itemCheck.tag);
}
#else #else
i_this->field_0x75C = fopAcM_createItemForPresentDemo(&a_this->current.pos, 0xE0, 0, i_this->field_0x75C = fopAcM_createItemForPresentDemo(&a_this->current.pos, 0xE0, 0,
-1, -1, NULL, NULL); -1, -1, NULL, NULL);
+3 -16
View File
@@ -16,10 +16,6 @@
#include "f_op/f_op_camera_mng.h" #include "f_op/f_op_camera_mng.h"
#include <cstring> #include <cstring>
#if TARGET_PC
#include "mods/items.h"
#endif
class daE_RDB_HIO_c : public JORReflexible { class daE_RDB_HIO_c : public JORReflexible {
public: public:
daE_RDB_HIO_c(); daE_RDB_HIO_c();
@@ -1254,18 +1250,9 @@ static void demo_camera(e_rdb_class* i_this) {
} }
if (iVar1 != 0) { if (iVar1 != 0) {
#if TARGET_PC daPy_getPlayerActorClass()->changeDemoMode(11,
const auto itemCheck = dusk::mods::item_check_commit( DUSK_ITEM_CHECK_EXPR("bulblin_key:D_MN09", dItemNo_SMALL_KEY_e, &i_this->enemy), 0,
ITEM_CHECK_BULBLIN_KEY, dItemNo_SMALL_KEY_e, &i_this->enemy); 0);
if (itemCheck.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(itemCheck, &i_this->enemy);
}
daPy_getPlayerActorClass()->changeDemoMode(
11, itemCheck.itemNo == dItemNo_NONE_e ? 0 : itemCheck.itemNo,
itemCheck.itemNo == dItemNo_NONE_e ? 0 : static_cast<int>(itemCheck.tag), 0);
#else
daPy_getPlayerActorClass()->changeDemoMode(11, dItemNo_SMALL_KEY_e, 0, 0);
#endif
i_this->mDemoMode = 12; i_this->mDemoMode = 12;
i_this->field_0x10aa = 0; i_this->field_0x10aa = 0;
i_this->field_0xfe5 = 1; i_this->field_0xfe5 = 1;
-32
View File
@@ -27,10 +27,8 @@
#if TARGET_PC #if TARGET_PC
#include "dusk/frame_interpolation.h" #include "dusk/frame_interpolation.h"
#include "dusk/mods/item.hpp"
#include "dusk/settings.h" #include "dusk/settings.h"
#include "dusk/version.hpp" #include "dusk/version.hpp"
#include "mods/items.h"
#endif #endif
class dmg_rod_HIO_c : public JORReflexible { class dmg_rod_HIO_c : public JORReflexible {
@@ -2957,17 +2955,7 @@ static void lure_heart(dmg_rod_class* i_this) {
if (obj_life != NULL) { if (obj_life != NULL) {
fopAcM_delete(obj_life); fopAcM_delete(obj_life);
fopAcM_onItem(obj_life, 0x80); fopAcM_onItem(obj_life, 0x80);
#if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit(
ITEM_CHECK_FISHING_HEART_PIECE, dItemNo_KAKERA_HEART_e, actor);
if (itemCheck.itemNo == dItemNo_KAKERA_HEART_e) {
execItemGet(dItemNo_KAKERA_HEART_e, itemCheck.tag, actor);
} else if (itemCheck.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(itemCheck, actor);
}
#else
execItemGet(dItemNo_KAKERA_HEART_e); execItemGet(dItemNo_KAKERA_HEART_e);
#endif
u8 eventReg = dComIfGs_getEventReg(0xECFF); u8 eventReg = dComIfGs_getEventReg(0xECFF);
eventReg |= (u8)0x40; eventReg |= (u8)0x40;
dComIfGs_setEventReg(0xECFF, eventReg); dComIfGs_setEventReg(0xECFF, eventReg);
@@ -4102,15 +4090,7 @@ static void uki_catch(dmg_rod_class* i_this) {
} else if (mgfish->mCaughtType == MG_CATCH_BIN) { } else if (mgfish->mCaughtType == MG_CATCH_BIN) {
i_this->msgflow.init(actor, 0x139A, 0, NULL); i_this->msgflow.init(actor, 0x139A, 0, NULL);
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[468]); dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[468]);
#if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit(
ITEM_CHECK_FISHING_BOTTLE, dItemNo_EMPTY_BOTTLE_e, actor);
if (itemCheck.itemNo == dItemNo_EMPTY_BOTTLE_e) {
dComIfGs_setEmptyBottle();
}
#else
dComIfGs_setEmptyBottle(); dComIfGs_setEmptyBottle();
#endif
} else if (mgfish->mCaughtType == MG_CATCH_KN) { } else if (mgfish->mCaughtType == MG_CATCH_KN) {
i_this->msgflow.init(actor, 0x139C, 0, NULL); i_this->msgflow.init(actor, 0x139C, 0, NULL);
} else if (mgfish->mCaughtType == MG_CATCH_ED) { } else if (mgfish->mCaughtType == MG_CATCH_ED) {
@@ -4187,18 +4167,6 @@ static void uki_catch(dmg_rod_class* i_this) {
if (mgfish->mCaughtType == MG_CATCH_LH) { if (mgfish->mCaughtType == MG_CATCH_LH) {
dComIfGp_setItemRupeeCount(10.0f + cM_rndF(40.9f)); dComIfGp_setItemRupeeCount(10.0f + cM_rndF(40.9f));
} }
#if TARGET_PC
else if (mgfish->mCaughtType == MG_CATCH_BIN)
{
const auto itemCheck = dusk::mods::item_check_commit(
ITEM_CHECK_FISHING_BOTTLE, dItemNo_EMPTY_BOTTLE_e, actor);
if (itemCheck.itemNo == dItemNo_EMPTY_BOTTLE_e ||
itemCheck.itemNo == dItemNo_NONE_e)
{
dusk::mods::item_check_complete(itemCheck, actor);
}
}
#endif
} else { } else {
dComIfGs_addFishNum(fish_kind); dComIfGs_addFishNum(fish_kind);
if (i_this->field_0x14c0 != 0) { if (i_this->field_0x14c0 != 0) {
+2 -6
View File
@@ -1806,21 +1806,17 @@ int daNpcChin_c::_Evt_GameSucceed_CutMain(const int& param_0) {
#if TARGET_PC #if TARGET_PC
const char* itemCheckName = nullptr; const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (itemId1 == dItemNo_ARROW_LV2_e) { if (itemId1 == dItemNo_ARROW_LV2_e) {
itemCheckName = "star_reward_1"; itemCheckName = "star_reward_1";
} else if (itemId1 == dItemNo_ARROW_LV3_e) { } else if (itemId1 == dItemNo_ARROW_LV3_e) {
itemCheckName = "star_reward_2"; itemCheckName = "star_reward_2";
} }
if (itemCheckName != nullptr) { if (itemCheckName != nullptr) {
const auto itemCheck = itemId1 = dusk::mods::item_check(itemCheckName, itemId1, this);
dusk::mods::item_check_commit(itemCheckName, itemId1, this);
itemId1 = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
} }
#endif #endif
fpc_ProcID itemId2 = fopAcM_createItemForPresentDemo(&current.pos, itemId1, 0, -1, -1, fpc_ProcID itemId2 = fopAcM_createItemForPresentDemo(&current.pos, itemId1, 0, -1, -1,
0, 0 IF_DUSK_ARG(itemGiveTag)); 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
if (itemId2 != -1) { if (itemId2 != -1) {
dComIfGp_event_setItemPartnerId(itemId2); dComIfGp_event_setItemPartnerId(itemId2);
} }
+4 -6
View File
@@ -4019,16 +4019,14 @@ BOOL daNpc_grA_c::talk(void*) {
if (r26 && talkProc(NULL, TRUE, NULL)) { if (r26 && talkProc(NULL, TRUE, NULL)) {
if (mFlow.getEventId(&sp8) == 1) { if (mFlow.getEventId(&sp8) == 1) {
#if TARGET_PC #if TARGET_PC
u32 itemGiveTag = 0; const char* itemCheckName = nullptr;
if (sp8 == dItemNo_BOMB_IN_BAG_e) { if (sp8 == dItemNo_BOMB_IN_BAG_e) {
const auto itemCheck = itemCheckName = "goron_reward:F_SP113";
dusk::mods::item_check_commit("goron_reward:F_SP113", sp8, this); sp8 = dusk::mods::item_check(itemCheckName, sp8, this);
sp8 = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
} }
#endif #endif
field_0x1480 = fopAcM_createItemForPresentDemo(&current.pos, sp8, 0, -1, -1, NULL, field_0x1480 = fopAcM_createItemForPresentDemo(&current.pos, sp8, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(itemGiveTag)); NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
if (field_0x1480 != fpcM_ERROR_PROCESS_ID_e) { if (field_0x1480 != fpcM_ERROR_PROCESS_ID_e) {
s16 r25 = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff); s16 r25 = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff);
dComIfGp_getEvent()->reset(this); dComIfGp_getEvent()->reset(this);
+3 -5
View File
@@ -1495,11 +1495,9 @@ int daNpcIns_c::talk(void* param_1) {
#if TARGET_PC #if TARGET_PC
u32 itemGiveTag = 0; u32 itemGiveTag = 0;
if (s_givenInsectId != dItemNo_NONE_e) { if (s_givenInsectId != dItemNo_NONE_e) {
const auto itemCheck = dusk::mods::item_check_commit( itemNo = dusk::mods::item_check_bug(
dusk::mods::item_give_tag_bug(s_givenInsectId), itemNo & 0xFF, s_givenInsectId, itemNo & 0xFF, this);
this); itemGiveTag = dusk::mods::item_give_tag_bug(s_givenInsectId);
itemNo = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
s_givenInsectId = dItemNo_NONE_e; s_givenInsectId = dItemNo_NONE_e;
} }
#endif #endif
+4 -6
View File
@@ -1182,17 +1182,15 @@ int daNpc_Kkri_c::talk(void*) {
case 1: case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC #if TARGET_PC
u32 itemGiveTag = 0; const char* itemCheckName = nullptr;
if (item_no == dItemNo_OIL_BOTTLE3_e) { if (item_no == dItemNo_OIL_BOTTLE3_e) {
const auto itemCheck = itemCheckName = "coro_bottle";
dusk::mods::item_check_commit("coro_bottle", item_no, this); item_no = dusk::mods::item_check(itemCheckName, item_no, this);
item_no = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
} }
#endif #endif
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, item_no, mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, item_no,
0, -1, -1, NULL, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(itemGiveTag)); NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
} }
if (fopAcM_IsExecuting(mItemPartnerId)) { if (fopAcM_IsExecuting(mItemPartnerId)) {
+4 -6
View File
@@ -1192,16 +1192,14 @@ int daNpc_myna2_c::ECut_gameGoalSuccess(int i_staffId) {
int itemNo = 0; int itemNo = 0;
if (mFlow.getEventId(&itemNo) == 1) { if (mFlow.getEventId(&itemNo) == 1) {
#if TARGET_PC #if TARGET_PC
u32 itemGiveTag = 0; const char* itemCheckName = nullptr;
if (itemNo == dItemNo_KAKERA_HEART_e) { if (itemNo == dItemNo_KAKERA_HEART_e) {
const auto itemCheck = itemCheckName = "plumm_minigame_reward";
dusk::mods::item_check_commit("plumm_minigame_reward", itemNo, this); itemNo = dusk::mods::item_check(itemCheckName, itemNo, this);
itemNo = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
} }
#endif #endif
mItemPid = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, mItemPid = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(itemGiveTag)); NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
} }
break; break;
} }
+2 -6
View File
@@ -960,21 +960,17 @@ int daNpc_Pouya_c::cutHaveFavorToAsk(int param_0) {
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC #if TARGET_PC
const char* itemCheckName = nullptr; const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (local_64 == dItemNo_DROP_BOTTLE_e) { if (local_64 == dItemNo_DROP_BOTTLE_e) {
itemCheckName = "jovani_reward_1"; itemCheckName = "jovani_reward_1";
} else if (local_64 == dItemNo_SILVER_RUPEE_e) { } else if (local_64 == dItemNo_SILVER_RUPEE_e) {
itemCheckName = "jovani_reward_2"; itemCheckName = "jovani_reward_2";
} }
if (itemCheckName != nullptr) { if (itemCheckName != nullptr) {
const auto itemCheck = local_64 = dusk::mods::item_check(itemCheckName, local_64, this);
dusk::mods::item_check_commit(itemCheckName, local_64, this);
local_64 = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
} }
#endif #endif
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_64, 0, mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_64, 0,
-1, -1, 0, 0 IF_DUSK_ARG(itemGiveTag)); -1, -1, 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
} }
if (fopAcM_IsExecuting(mItemPartnerId)) { if (fopAcM_IsExecuting(mItemPartnerId)) {
field_0xfce = 1; field_0xfce = 1;
+2 -6
View File
@@ -2611,21 +2611,17 @@ BOOL daNpc_zrA_c::ECut_thanksBlast(int i_staffID) {
if (mFlow.getEventId(&item_id) == 1) { if (mFlow.getEventId(&item_id) == 1) {
#if TARGET_PC #if TARGET_PC
const char* itemCheckName = nullptr; const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (item_id == dItemNo_BOMB_IN_BAG_e) { if (item_id == dItemNo_BOMB_IN_BAG_e) {
itemCheckName = "iza_reward_1"; itemCheckName = "iza_reward_1";
} else if (item_id == dItemNo_BOMB_BAG_LV2_e) { } else if (item_id == dItemNo_BOMB_BAG_LV2_e) {
itemCheckName = "iza_reward_2"; itemCheckName = "iza_reward_2";
} }
if (itemCheckName != nullptr) { if (itemCheckName != nullptr) {
const auto itemCheck = item_id = dusk::mods::item_check(itemCheckName, item_id, this);
dusk::mods::item_check_commit(itemCheckName, item_id, this);
item_id = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
} }
#endif #endif
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_id, 0, -1, -1, NULL, mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_id, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(itemGiveTag)); NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
} }
field_0x9eb = true; field_0x9eb = true;
break; break;
+18 -71
View File
@@ -14,11 +14,6 @@
#include "d/actor/d_a_obj_ystone.h" #include "d/actor/d_a_obj_ystone.h"
#include <cstring> #include <cstring>
#if TARGET_PC
#include "dusk/mods/item.hpp"
#include "mods/items.h"
#endif
static DUSK_CONST char* l_arcName = "ef_Portal"; static DUSK_CONST char* l_arcName = "ef_Portal";
static char const* l_clearEvName[9] = { static char const* l_clearEvName[9] = {
@@ -82,27 +77,6 @@ static int getNowLevel() {
return -1; return -1;
} }
#if TARGET_PC
static const char* dungeon_reward_check_name(int level) {
switch (level) {
case 0:
return ITEM_CHECK_DUNGEON_REWARD_FOREST;
case 1:
return ITEM_CHECK_DUNGEON_REWARD_GORON;
case 2:
return ITEM_CHECK_DUNGEON_REWARD_LAKEBED;
case 4:
return ITEM_CHECK_DUNGEON_REWARD_SNOWPEAK;
case 5:
return ITEM_CHECK_DUNGEON_REWARD_TIME;
case 6:
return ITEM_CHECK_DUNGEON_REWARD_CITY;
default:
return nullptr;
}
}
#endif
void daObjBossWarp_c::initBaseMtx() { void daObjBossWarp_c::initBaseMtx() {
scale.y = 0.15f; scale.y = 0.15f;
mpModel->setBaseScale(scale); mpModel->setBaseScale(scale);
@@ -511,9 +485,6 @@ int daObjBossWarp_c::demoProc() {
player->onSceneChangeArea(scene, 0xff, 0); player->onSceneChangeArea(scene, 0xff, 0);
break; break;
case 4: // STONE_FALL case 4: // STONE_FALL
#if TARGET_PC
{
#endif
if (ystone != NULL) { if (ystone != NULL) {
mYstonePos = ystone->current.pos; mYstonePos = ystone->current.pos;
if (getNowLevel() < 3) { if (getNowLevel() < 3) {
@@ -535,52 +506,28 @@ int daObjBossWarp_c::demoProc() {
mDoMtx_stack_c::multVec(&mYstoneTargetPos, &mYstoneTargetPos); mDoMtx_stack_c::multVec(&mYstoneTargetPos, &mYstoneTargetPos);
mYstonePos.x = mYstoneTargetPos.x; mYstonePos.x = mYstoneTargetPos.x;
mYstonePos.z = mYstoneTargetPos.z; mYstonePos.z = mYstoneTargetPos.z;
#if TARGET_PC
int level = getNowLevel();
const char* checkName = dungeon_reward_check_name(level);
dusk::mods::ItemCheckResult itemCheck{};
bool applyVanillaReward = true;
if (checkName != nullptr) {
itemCheck = dusk::mods::item_check_commit(checkName, dItemNo_NONE_e, this);
applyVanillaReward = itemCheck.itemNo == dItemNo_NONE_e;
}
if (applyVanillaReward) {
switch (level) {
#else
switch (getNowLevel()) { switch (getNowLevel()) {
#endif case 0:
case 0: dComIfGs_onCollectCrystal(0);
dComIfGs_onCollectCrystal(0); break;
break; case 1:
case 1: dComIfGs_onCollectCrystal(1);
dComIfGs_onCollectCrystal(1); break;
break; case 2:
case 2: dComIfGs_onCollectCrystal(2);
dComIfGs_onCollectCrystal(2); break;
break; case 4:
case 4: dComIfGs_onCollectMirror(1);
dComIfGs_onCollectMirror(1); break;
break; case 5:
case 5: dComIfGs_onCollectMirror(2);
dComIfGs_onCollectMirror(2); break;
break; case 6:
case 6: dComIfGs_onCollectMirror(3);
dComIfGs_onCollectMirror(3); break;
break;
}
#if TARGET_PC
if (checkName != nullptr) {
dusk::mods::item_check_complete(itemCheck, this);
}
} else {
dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::Silent);
} }
#endif
mCounter = 0; mCounter = 0;
break; break;
#if TARGET_PC
}
#endif
case 5: // STONE_MIDNA case 5: // STONE_MIDNA
mCounter = 0; mCounter = 0;
break; break;
+6 -30
View File
@@ -287,11 +287,7 @@ int daItem_c::_daItem_create() {
BOOL flag = dItem_data::chkFlag(m_itemNo, 2); BOOL flag = dItem_data::chkFlag(m_itemNo, 2);
#if DEBUG #if DEBUG
#if TARGET_PC
if (dItem_fieldModelArc(m_itemNo) == NULL) {
#else
if (dItem_data::getFieldArc(m_itemNo) == NULL) { if (dItem_data::getFieldArc(m_itemNo) == NULL) {
#endif
// "Item Num<%d>: No Resource Archive specified!!\n" // "Item Num<%d>: No Resource Archive specified!!\n"
OS_REPORT_ERROR("アイテム番号<%d>:リソースのアーカイブ指定がありません!!\n", m_itemNo); OS_REPORT_ERROR("アイテム番号<%d>:リソースのアーカイブ指定がありません!!\n", m_itemNo);
return cPhs_ERROR_e; return cPhs_ERROR_e;
@@ -323,20 +319,6 @@ int daItem_c::_daItem_create() {
if (flag) { if (flag) {
CreateInit(); CreateInit();
} else { } else {
#if TARGET_PC
// Resolved items may have no field model; display their get-demo model instead.
if (dItem_data::getFieldArc(m_itemNo) == NULL) {
phase_state = dComIfG_resLoad(&mPhase, dItem_data::getArcName(m_itemNo));
if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, CheckItemCreateHeap, 0x4000)) {
return cPhs_ERROR_e;
}
CreateInit();
}
return phase_state;
}
#endif
phase_state = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo)); phase_state = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo));
if (phase_state == cPhs_COMPLEATE_e) { if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, CheckFieldItemCreateHeap, if (!fopAcM_entrySolidHeap(this, CheckFieldItemCreateHeap,
@@ -430,11 +412,7 @@ int daItem_c::_daItem_delete() {
mSparkleEmtr.remove(); mSparkleEmtr.remove();
} }
#if TARGET_PC
DeleteBase(dItem_fieldModelArc(m_itemNo));
#else
DeleteBase(dItem_data::getFieldArc(m_itemNo)); DeleteBase(dItem_data::getFieldArc(m_itemNo));
#endif
return 1; return 1;
} }
@@ -542,10 +520,9 @@ void daItem_c::procInitGetDemoEvent() {
#if TARGET_PC #if TARGET_PC
const u8 displayItemNo = m_itemNo; const u8 displayItemNo = m_itemNo;
const auto itemCheck = dusk::mods::item_check_commit(mItemGiveTag, mOriginalItemNo, this); if (mItemOverridden) {
m_itemNo = itemCheck.itemNo; m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
mItemGiveTag = itemCheck.tag; }
mItemOverridden = m_itemNo != mOriginalItemNo;
#endif #endif
m_item_id = fopAcM_createItemForTrBoxDemo( m_item_id = fopAcM_createItemForTrBoxDemo(
&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag)); &current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag));
@@ -930,10 +907,9 @@ void daItem_c::itemGetNextExecute() {
void daItem_c::itemGet() { void daItem_c::itemGet() {
#if TARGET_PC #if TARGET_PC
const u8 displayItemNo = m_itemNo; const u8 displayItemNo = m_itemNo;
const auto itemCheck = dusk::mods::item_check_commit(mItemGiveTag, mOriginalItemNo, this); if (mItemOverridden) {
m_itemNo = itemCheck.itemNo; m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
mItemGiveTag = itemCheck.tag; }
mItemOverridden = m_itemNo != mOriginalItemNo;
#endif #endif
switch (m_itemNo) { switch (m_itemNo) {
#if TARGET_PC #if TARGET_PC
+3 -21
View File
@@ -6,7 +6,6 @@
#include "d/dolzel_rel.h" // IWYU pragma: keep #include "d/dolzel_rel.h" // IWYU pragma: keep
#include "d/actor/d_a_obj_life_container.h" #include "d/actor/d_a_obj_life_container.h"
#include "d/d_a_itembase_static.h"
#include "d/d_com_inf_game.h" #include "d/d_com_inf_game.h"
#include "d/actor/d_a_player.h" #include "d/actor/d_a_player.h"
#include "d/d_item_data.h" #include "d/d_item_data.h"
@@ -185,23 +184,11 @@ int daObjLife_c::create() {
return cPhs_ERROR_e; return cPhs_ERROR_e;
} }
#if TARGET_PC
// Resolved items may have no field model; display their get-demo model instead.
const bool useGetModel = dItem_data::getFieldArc(m_itemNo) == NULL;
int phase_state = dComIfG_resLoad(&mPhase, dItem_fieldModelArc(m_itemNo));
if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(
this, useGetModel ? CheckItemCreateHeap : CheckFieldItemCreateHeap, 0x4000))
{
return cPhs_ERROR_e;
}
#else
int phase_state = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo)); int phase_state = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo));
if (phase_state == cPhs_COMPLEATE_e) { if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, CheckFieldItemCreateHeap, 0x4000)) { if (!fopAcM_entrySolidHeap(this, CheckFieldItemCreateHeap, 0x4000)) {
return cPhs_ERROR_e; return cPhs_ERROR_e;
} }
#endif
if (!Create()) { if (!Create()) {
return cPhs_ERROR_e; return cPhs_ERROR_e;
@@ -338,10 +325,9 @@ int daObjLife_c::initActionOrderGetDemo() {
#if TARGET_PC #if TARGET_PC
const u8 displayItemNo = m_itemNo; const u8 displayItemNo = m_itemNo;
const auto itemCheck = dusk::mods::item_check_commit(mItemGiveTag, mOriginalItemNo, this); if (mItemOverridden) {
m_itemNo = itemCheck.itemNo; m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
mItemGiveTag = itemCheck.tag; }
mItemOverridden = m_itemNo != mOriginalItemNo;
#endif #endif
mItemId = fopAcM_createItemForTrBoxDemo( mItemId = fopAcM_createItemForTrBoxDemo(
&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag)); &current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag));
@@ -533,11 +519,7 @@ int daObjLife_c::_delete() {
endEffect00(); endEffect00();
endEffect02(); endEffect02();
#if TARGET_PC
DeleteBase(dItem_fieldModelArc(m_itemNo));
#else
DeleteBase(dItem_data::getFieldArc(m_itemNo)); DeleteBase(dItem_data::getFieldArc(m_itemNo));
#endif
return 1; return 1;
} }
+1 -40
View File
@@ -1,6 +1,6 @@
/** /**
* @file d_a_obj_master_sword.cpp * @file d_a_obj_master_sword.cpp
* *
*/ */
#include "d/dolzel_rel.h" // IWYU pragma: keep #include "d/dolzel_rel.h" // IWYU pragma: keep
@@ -10,12 +10,6 @@
#include "d/d_com_inf_game.h" #include "d/d_com_inf_game.h"
#include "d/d_meter2_info.h" #include "d/d_meter2_info.h"
#if TARGET_PC
#include "d/d_item.h"
#include "dusk/mods/item.hpp"
#include "mods/items.h"
#endif
DUSK_GAME_DATA daObjMasterSword_Attr_c const daObjMasterSword_c::mAttr = {1.0f}; DUSK_GAME_DATA daObjMasterSword_Attr_c const daObjMasterSword_c::mAttr = {1.0f};
void daObjMasterSword_c::initBaseMtx() { void daObjMasterSword_c::initBaseMtx() {
@@ -151,11 +145,7 @@ void daObjMasterSword_c::create_init() {
int daObjMasterSword_c::create() { int daObjMasterSword_c::create() {
fopAcM_ct(this, daObjMasterSword_c); fopAcM_ct(this, daObjMasterSword_c);
#if TARGET_PC
if (dComIfGs_isEventBit(dSv_event_flag_c::F_0264)) {
#else
if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[getFlagNo()])) { if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[getFlagNo()])) {
#endif
return cPhs_ERROR_e; return cPhs_ERROR_e;
} }
@@ -196,41 +186,12 @@ int daObjMasterSword_c::execute() {
mBrk.play(); mBrk.play();
if (dComIfGs_isTmpBit(dSv_event_tmp_flag_c::tempBitLabels[73])) { if (dComIfGs_isTmpBit(dSv_event_tmp_flag_c::tempBitLabels[73])) {
#if TARGET_PC
const auto masterSword = dusk::mods::item_check_commit(
ITEM_CHECK_MASTER_SWORD, dItemNo_MASTER_SWORD_e, this);
if (masterSword.itemNo == dItemNo_MASTER_SWORD_e) {
dComIfGs_onItemFirstBit(dItemNo_MASTER_SWORD_e);
dMeter2Info_setSword(dItemNo_MASTER_SWORD_e, false);
dComIfGs_setSelectEquipSword(dItemNo_MASTER_SWORD_e);
dusk::mods::item_check_complete(masterSword, this);
} else if (masterSword.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(masterSword, this);
} else {
dusk::mods::item_check_enqueue(masterSword, dusk::mods::ItemGiveMode::Demo);
}
dComIfGp_setItemLifeCount(dComIfGs_getMaxLife(), 0);
const auto shadowCrystal = dusk::mods::item_check_commit(
ITEM_CHECK_SHADOW_CRYSTAL, dItemNo_SHADOW_CRYSTAL_e, this);
if (shadowCrystal.itemNo == dItemNo_SHADOW_CRYSTAL_e) {
execItemGet(shadowCrystal.itemNo, shadowCrystal.tag, this);
} else if (shadowCrystal.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(shadowCrystal, this);
} else {
dusk::mods::item_check_enqueue(shadowCrystal, dusk::mods::ItemGiveMode::Demo);
}
dComIfGs_onEventBit(dSv_event_flag_c::F_0264);
#else
dComIfGs_onItemFirstBit(dItemNo_MASTER_SWORD_e); dComIfGs_onItemFirstBit(dItemNo_MASTER_SWORD_e);
dMeter2Info_setSword(dItemNo_MASTER_SWORD_e, false); dMeter2Info_setSword(dItemNo_MASTER_SWORD_e, false);
dComIfGs_setSelectEquipSword(dItemNo_MASTER_SWORD_e); dComIfGs_setSelectEquipSword(dItemNo_MASTER_SWORD_e);
dComIfGp_setItemLifeCount(dComIfGs_getMaxLife(), 0); dComIfGp_setItemLifeCount(dComIfGs_getMaxLife(), 0);
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[getFlagNo()]); dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[getFlagNo()]);
#endif
fopAcM_delete(this); fopAcM_delete(this);
} }
+1 -1
View File
@@ -109,7 +109,7 @@ int daItemShield_c::__CreateHeap() {
int daItemShield_c::create() { int daItemShield_c::create() {
fopAcM_ct(this, daItemShield_c); fopAcM_ct(this, daItemShield_c);
m_itemNo = dItemNo_WOOD_SHIELD_e; m_itemNo = dItemNo_WOOD_SHIELD_e;
DUSK_ITEM_CHECK_PREVIEW("ordon_shield", m_itemNo, this); DUSK_ITEM_CHECK("ordon_shield", m_itemNo, this);
if (fopAcM_isSwitch(this, getSwBit2())) { if (fopAcM_isSwitch(this, getSwBit2())) {
OS_REPORT("木の盾:もう取ったので出ません\n"); OS_REPORT("木の盾:もう取ったので出ません\n");
return cPhs_ERROR_e; return cPhs_ERROR_e;
+1 -1
View File
@@ -37,7 +37,7 @@ int daObjSword_c::Create() {
cPhs_Step daObjSword_c::create() { cPhs_Step daObjSword_c::create() {
fopAcM_ct(this, daObjSword_c); fopAcM_ct(this, daObjSword_c);
m_itemNo = 0x28; m_itemNo = 0x28;
DUSK_ITEM_CHECK_PREVIEW("ordon_sword", m_itemNo, this); DUSK_ITEM_CHECK("ordon_sword", m_itemNo, this);
if (fopAcM_isItem(this, getItemBit())) { if (fopAcM_isItem(this, getItemBit())) {
return cPhs_ERROR_e; return cPhs_ERROR_e;
} }
+1 -1
View File
@@ -32,7 +32,7 @@ int daObjZCloth_c::Create() {
int daObjZCloth_c::create() { int daObjZCloth_c::create() {
fopAcM_ct(this, daObjZCloth_c); fopAcM_ct(this, daObjZCloth_c);
m_itemNo = 0x31; m_itemNo = 0x31;
DUSK_ITEM_CHECK_PREVIEW("zora_armor", m_itemNo, this); DUSK_ITEM_CHECK("zora_armor", m_itemNo, this);
int phase = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo)); int phase = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo));
if (phase == cPhs_COMPLEATE_e) { if (phase == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, (heapCallbackFunc)CheckFieldItemCreateHeap, 0x2fb0)) { if (!fopAcM_entrySolidHeap(this, (heapCallbackFunc)CheckFieldItemCreateHeap, 0x2fb0)) {
+3 -6
View File
@@ -363,13 +363,10 @@ int daTagStatue_c::demoProc() {
} }
#if TARGET_PC #if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit( item = dusk::mods::item_check_sky_character(item & 0xFF, this);
dusk::mods::item_give_tag_sky_character(), item & 0xFF, this);
item = itemCheck.itemNo;
#endif #endif
mItemId = mItemId = fopAcM_createItemForTrBoxDemo(&current.pos, item, -1,
fopAcM_createItemForTrBoxDemo(&current.pos, item, -1, fopAcM_GetRoomNo(this), 0, fopAcM_GetRoomNo(this), 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag_sky_character()));
0 IF_DUSK_ARG(itemCheck.tag));
JUT_ASSERT(580, mItemId != fpcM_ERROR_PROCESS_ID_e); JUT_ASSERT(580, mItemId != fpcM_ERROR_PROCESS_ID_e);
+4 -5
View File
@@ -1391,9 +1391,8 @@ u8 daTbox_c::getBombItemNoMain(u8 i_itemNo) {
int daTbox_c::setGetDemoItem() { int daTbox_c::setGetDemoItem() {
u8 item_no = getItemNo(); u8 item_no = getItemNo();
#if TARGET_PC #if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit( const u32 giveTag = dusk::mods::item_give_tag_chest(getTboxNo());
dusk::mods::item_give_tag_chest(getTboxNo()), mOriginalItemNo, this); item_no = dusk::mods::item_check_tagged(giveTag, mOriginalItemNo, this);
item_no = itemCheck.itemNo;
#endif #endif
if (item_no == dItemNo_BOMB_5_e || item_no == dItemNo_BOMB_10_e || item_no == dItemNo_BOMB_20_e || item_no == dItemNo_BOMB_30_e || if (item_no == dItemNo_BOMB_5_e || item_no == dItemNo_BOMB_10_e || item_no == dItemNo_BOMB_20_e || item_no == dItemNo_BOMB_30_e ||
item_no == dItemNo_WATER_BOMB_5_e || item_no == dItemNo_WATER_BOMB_10_e || item_no == dItemNo_WATER_BOMB_20_e || item_no == dItemNo_WATER_BOMB_30_e || item_no == dItemNo_WATER_BOMB_5_e || item_no == dItemNo_WATER_BOMB_10_e || item_no == dItemNo_WATER_BOMB_20_e || item_no == dItemNo_WATER_BOMB_30_e ||
@@ -1405,10 +1404,10 @@ int daTbox_c::setGetDemoItem() {
fpc_ProcID item_id; fpc_ProcID item_id;
if (field_0x718) { if (field_0x718) {
item_id = fopAcM_createItemForPresentDemo( item_id = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(itemCheck.tag)); &current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
} else { } else {
item_id = fopAcM_createItemForTrBoxDemo( item_id = fopAcM_createItemForTrBoxDemo(
&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(itemCheck.tag)); &current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
} }
if (item_id != fpcM_ERROR_PROCESS_ID_e) { if (item_id != fpcM_ERROR_PROCESS_ID_e) {
+4 -7
View File
@@ -383,18 +383,15 @@ int daTbox2_c::setGetDemoItem() {
u8 item_no = getItemNo(); u8 item_no = getItemNo();
#if TARGET_PC #if TARGET_PC
int tboxNo = fopAcM_GetParamBit(this, 16, 8); int tboxNo = fopAcM_GetParamBit(this, 16, 8);
const auto itemCheck = dusk::mods::item_check_commit( const u32 giveTag = dusk::mods::item_give_tag_chest(tboxNo);
dusk::mods::item_give_tag_chest(tboxNo), mOriginalItemNo, this); item_no = dusk::mods::item_check_tagged(giveTag, mOriginalItemNo, this);
item_no = itemCheck.itemNo;
#endif #endif
u32 partner_id; u32 partner_id;
if (mReturnRupee) { if (mReturnRupee) {
partner_id = fopAcM_createItemForPresentDemo( partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(itemCheck.tag));
} else { } else {
partner_id = fopAcM_createItemForTrBoxDemo( partner_id = fopAcM_createItemForTrBoxDemo(&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(itemCheck.tag));
} }
if (partner_id != -1) { if (partner_id != -1) {
-7
View File
@@ -57,13 +57,6 @@ int CheckItemCreateHeap(fopAc_ac_c* i_this) {
dItem_data::getBrkName(item_no), dItem_data::getBtpName(item_no)); dItem_data::getBrkName(item_no), dItem_data::getBtpName(item_no));
} }
#if TARGET_PC
const char* dItem_fieldModelArc(u8 itemNo) {
const char* arcName = dItem_data::getFieldArc(itemNo);
return arcName != NULL ? arcName : dItem_data::getArcName(itemNo);
}
#endif
int CheckFieldItemCreateHeap(fopAc_ac_c* i_this) { int CheckFieldItemCreateHeap(fopAc_ac_c* i_this) {
daItemBase_c* a_this = static_cast<daItemBase_c*>(i_this); daItemBase_c* a_this = static_cast<daItemBase_c*>(i_this);
+9
View File
@@ -30,6 +30,7 @@
#if TARGET_PC #if TARGET_PC
#include "dusk/camera_operators.hpp" #include "dusk/camera_operators.hpp"
#include "dusk/commands.hpp"
#include "dusk/frame_interpolation.h" #include "dusk/frame_interpolation.h"
#include "dusk/logging.h" #include "dusk/logging.h"
#include "dusk/action_bindings.h" #include "dusk/action_bindings.h"
@@ -1054,6 +1055,11 @@ void dCamera_c::debugDrawInit() {
bool dCamera_c::Run() { bool dCamera_c::Run() {
#if TARGET_PC #if TARGET_PC
ResetView(); ResetView();
if (dusk::isCameraDetached()) {
mFrameCounter++;
mTicks++;
return true;
}
if (executeDebugFlyCam() || dusk::mods::camera_run_operators(this)) { if (executeDebugFlyCam() || dusk::mods::camera_run_operators(this)) {
mFrameCounter++; mFrameCounter++;
mTicks++; mTicks++;
@@ -11123,6 +11129,9 @@ camera_class* dCam_getCamera() {
dCamera_c* dCam_getBody() { dCamera_c* dCam_getBody() {
camera_process_class* camera = (camera_process_class*)dCam_getCamera(); camera_process_class* camera = (camera_process_class*)dCam_getCamera();
#if TARGET_PC
if (camera == nullptr) { return nullptr; }
#endif
return &camera->mCamera; return &camera->mCamera;
} }
+1 -1
View File
@@ -18,7 +18,7 @@
#include <typeindex> #include <typeindex>
#include "JSystem/JKernel/JKRHeap.h" #include "JSystem/JKernel/JKRHeap.h"
#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_map.h"
#include "client/TracyScoped.hpp" #include "tracy/Tracy.hpp"
#include "dusk/frame_interpolation.h" #include "dusk/frame_interpolation.h"
#include "helpers/gx_helper.h" #include "helpers/gx_helper.h"
#include "dusk/logging.h" #include "dusk/logging.h"
+7 -40
View File
@@ -23,7 +23,6 @@
#include "m_Do/m_Do_graphic.h" #include "m_Do/m_Do_graphic.h"
#include <cstring> #include <cstring>
#include "dusk/utilities.hpp"
#include "dusk/version.hpp" #include "dusk/version.hpp"
#if TARGET_PC #if TARGET_PC
@@ -4154,43 +4153,11 @@ bool dFile_select_c::yesnoWakuAlpahAnm(u8 param_1) {
} }
#if TARGET_PC #if TARGET_PC
static dusk::utils::PaneCache mSelDtPanes[] = {
{MULTI_CHAR('tate_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n2'), 0.0f, 0.0f, false},
{MULTI_CHAR('gray_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('b_base'), 0.0f, 0.0f, false},
{MULTI_CHAR('b_base1'), 0.0f, 0.0f, false},
};
static dusk::utils::PaneCache fileSelPanes[] = {
{MULTI_CHAR('w_uzu00'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu01'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu02'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu03'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu04'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu05'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu06'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu07'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu08'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu09'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msg'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msE'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msR'), 0.0f, 0.0f, false},
{MULTI_CHAR('er_for0'), 0.0f, 0.0f, false},
{MULTI_CHAR('er_for1'), 0.0f, 0.0f, false},
};
void dFile_select_c::fileSelectWide() { void dFile_select_c::fileSelectWide() {
static bool cachedPanes = false; static bool cachedPanes = false;
// Get pre-scale values for each pane // Get pre-scale values for each pane
if (!cachedPanes) { if (!cachedPanes) {
for (dusk::utils::PaneCache& entry : mSelDtPanes) { for (PaneCache& entry : mSelDtPanes) {
J2DPane* pane = mSelDt.ScrDt->search(entry.tag); J2DPane* pane = mSelDt.ScrDt->search(entry.tag);
if (!entry.cached) { if (!entry.cached) {
entry.origTransX = pane->getTranslateX(); entry.origTransX = pane->getTranslateX();
@@ -4198,7 +4165,7 @@ void dFile_select_c::fileSelectWide() {
entry.cached = true; entry.cached = true;
} }
} }
for (dusk::utils::PaneCache& entry : fileSelPanes) { for (PaneCache& entry : fileSelPanes) {
J2DPane* pane = fileSel.Scr->search(entry.tag); J2DPane* pane = fileSel.Scr->search(entry.tag);
if (!entry.cached) { if (!entry.cached) {
entry.origTransX = pane->getTranslateX(); entry.origTransX = pane->getTranslateX();
@@ -4212,13 +4179,13 @@ void dFile_select_c::fileSelectWide() {
// Reset all panes // Reset all panes
mSelDt.ScrDt->scale(1.0f, 1.0f); mSelDt.ScrDt->scale(1.0f, 1.0f);
mSelDt.ScrDt->translate(0.0f, 0.0f); mSelDt.ScrDt->translate(0.0f, 0.0f);
for (dusk::utils::PaneCache& entry : mSelDtPanes) { for (PaneCache& entry : mSelDtPanes) {
J2DPane* pane = mSelDt.ScrDt->search(entry.tag); J2DPane* pane = mSelDt.ScrDt->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4); pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f); pane->scale(1.0f, 1.0f);
pane->translate(entry.origTransX, entry.origTransY); pane->translate(entry.origTransX, entry.origTransY);
} }
for (dusk::utils::PaneCache& entry : fileSelPanes) { for (PaneCache& entry : fileSelPanes) {
J2DPane* pane = fileSel.Scr->search(entry.tag); J2DPane* pane = fileSel.Scr->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4); pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f); pane->scale(1.0f, 1.0f);
@@ -4290,7 +4257,7 @@ void dFile_select_c::fileSelectWide() {
mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
// Spirals & Memory Card Text // Spirals & Memory Card Text
for (dusk::utils::PaneCache& entry : fileSelPanes) { for (PaneCache& entry : fileSelPanes) {
J2DPane* pane = fileSel.Scr->search(entry.tag); J2DPane* pane = fileSel.Scr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
} }
@@ -4316,7 +4283,7 @@ void dFile_select_c::fileSelectWide() {
const f32 wideShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (wideScaleFactor - mDoGph_gInf_c::hudAspectScaleDown); const f32 wideShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (wideScaleFactor - mDoGph_gInf_c::hudAspectScaleDown);
const f32 ultraShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (ultraScaleFactor - mDoGph_gInf_c::hudAspectScaleDown); const f32 ultraShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (ultraScaleFactor - mDoGph_gInf_c::hudAspectScaleDown);
for (dusk::utils::PaneCache& entry : mSelDtPanes) { for (PaneCache& entry : mSelDtPanes) {
const size_t index = &entry - mSelDtPanes; const size_t index = &entry - mSelDtPanes;
J2DPane* pane = mSelDt.ScrDt->search(entry.tag); J2DPane* pane = mSelDt.ScrDt->search(entry.tag);
pane->setBasePosition(J2DBasePosition_0); pane->setBasePosition(J2DBasePosition_0);
@@ -4384,7 +4351,7 @@ void dFile_select_c::fileSelectWide() {
} }
// Spirals & Memory Card Text // Spirals & Memory Card Text
for (dusk::utils::PaneCache& entry : fileSelPanes) { for (PaneCache& entry : fileSelPanes) {
J2DPane* pane = fileSel.Scr->search(entry.tag); J2DPane* pane = fileSel.Scr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
} }
+1 -17
View File
@@ -741,12 +741,8 @@ void item_func_ARMOR() {}
void item_func_WEAR_ZORA() {} void item_func_WEAR_ZORA() {}
void item_func_MAGIC_LV1() { void item_func_MAGIC_LV1() {
#if TARGET_PC
dComIfGs_onEventBit(0xD04);
#else
dComIfGp_setItemMagicCount(16); dComIfGp_setItemMagicCount(16);
dComIfGp_setItemMaxMagicCount(16); dComIfGp_setItemMaxMagicCount(16);
#endif
} }
void item_func_DUNGEON_EXIT_2() { void item_func_DUNGEON_EXIT_2() {
@@ -1156,15 +1152,7 @@ void item_func_M_MAYFLY() {}
void item_func_F_MAYFLY() {} void item_func_F_MAYFLY() {}
void item_func_POU_SPIRIT() { void item_func_POU_SPIRIT() {}
#if TARGET_PC
dComIfGs_addPohSpiritNum();
if (dComIfGs_getPohSpiritNum() == 20) {
/* dSv_event_flag_c::F_0457 - Castle Town - Revived cat */
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[457]);
}
#endif
}
void item_func_ANCIENT_DOCUMENT() { void item_func_ANCIENT_DOCUMENT() {
dComIfGs_setItem(SLOT_22, dItemNo_ANCIENT_DOCUMENT_e); dComIfGs_setItem(SLOT_22, dItemNo_ANCIENT_DOCUMENT_e);
@@ -1423,11 +1411,7 @@ int item_getcheck_func_WEAR_ZORA() {
} }
int item_getcheck_func_MAGIC_LV1() { int item_getcheck_func_MAGIC_LV1() {
#if TARGET_PC
return dComIfGs_isEventBit(0xD04);
#else
return -1; return -1;
#endif
} }
int item_getcheck_func_DUNGEON_EXIT_2() { int item_getcheck_func_DUNGEON_EXIT_2() {
-4
View File
@@ -53,11 +53,7 @@ DUSK_GAME_DATA dItem_itemResource dItem_data::item_resource[] = {
{"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x006B, 0x64, 0x0000}, {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x006B, 0x64, 0x0000},
{"O_gD_marm", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0041, 0x64, 0x01A9}, {"O_gD_marm", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0041, 0x64, 0x01A9},
{"O_gD_zora", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004D, 0x64, 0x01AA}, {"O_gD_zora", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004D, 0x64, 0x01AA},
#if TARGET_PC
{"O_gD_Injy", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002E, 0x64, 0x0000},
#else
{"O_gD_Injy", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002D, 0x64, 0x0000}, {"O_gD_Injy", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002D, 0x64, 0x0000},
#endif
{"O_gD_TKS", 0x0008,-0x0001, 0x0005,-0x0001,-0x0001, -0x1, -0x1, 0x0028, 0x64, 0x0000}, {"O_gD_TKS", 0x0008,-0x0001, 0x0005,-0x0001,-0x0001, -0x1, -0x1, 0x0028, 0x64, 0x0000},
{"O_gD_puL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0047, 0x64, 0x0000}, {"O_gD_puL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0047, 0x64, 0x0000},
{"O_gD_puL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0048, 0x64, 0x0000}, {"O_gD_puL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0048, 0x64, 0x0000},
+2 -36
View File
@@ -39,7 +39,6 @@
#if TARGET_PC #if TARGET_PC
#include "dusk/menu_pointer.h" #include "dusk/menu_pointer.h"
#include "dusk/utilities.hpp"
#endif #endif
typedef void (dMenu_Collect2D_c::*initFunc)(); typedef void (dMenu_Collect2D_c::*initFunc)();
@@ -102,44 +101,11 @@ dMenu_Collect2D_c::~dMenu_Collect2D_c() {
} }
#if TARGET_PC #if TARGET_PC
static dusk::utils::PaneCache mpScreenPanes[] = {
{MULTI_CHAR('sa_tex_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('op_tex_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('heart_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('wolf_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_0_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_1_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_2_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('fish_3_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('lett_4_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('maki_5_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n2'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('kabu_6n'), 0.0f, 0.0f, false},
{MULTI_CHAR('t_t00'), 0.0f, 0.0f, false},
{MULTI_CHAR('f_t00'), 0.0f, 0.0f, false},
{MULTI_CHAR('itemn_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('infotxtn'), 0.0f, 0.0f, false},
{MULTI_CHAR('sa_op_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('title_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('menu_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('center_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('info_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('lavel_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('modelbgn'), 0.0f, 0.0f, false},
};
void dMenu_Collect2D_c::menuCollectWide() { void dMenu_Collect2D_c::menuCollectWide() {
static bool cachedPanes = false; static bool cachedPanes = false;
// Get pre-scale values for each pane // Get pre-scale values for each pane
if (!cachedPanes) { if (!cachedPanes) {
for (dusk::utils::PaneCache& entry : mpScreenPanes) { for (PaneCache& entry : mpScreenPanes) {
J2DPane* pane = mpScreen->search(entry.tag); J2DPane* pane = mpScreen->search(entry.tag);
if (!entry.cached) { if (!entry.cached) {
entry.origTransX = pane->getTranslateX(); entry.origTransX = pane->getTranslateX();
@@ -153,7 +119,7 @@ void dMenu_Collect2D_c::menuCollectWide() {
// Reset all panes // Reset all panes
mpScreen->scale(1.0f, 1.0f); mpScreen->scale(1.0f, 1.0f);
mpScreen->translate(0.0f, 0.0f); mpScreen->translate(0.0f, 0.0f);
for (dusk::utils::PaneCache& entry : mpScreenPanes) { for (PaneCache& entry : mpScreenPanes) {
J2DPane* pane = mpScreen->search(entry.tag); J2DPane* pane = mpScreen->search(entry.tag);
pane->scale(1.0f, 1.0f); pane->scale(1.0f, 1.0f);
pane->translate(entry.origTransX, entry.origTransY); pane->translate(entry.origTransX, entry.origTransY);
-4
View File
@@ -36,7 +36,6 @@
#include "dusk/language.hpp" #include "dusk/language.hpp"
#include "dusk/logging.h" #include "dusk/logging.h"
#include "dusk/menu_pointer.h" #include "dusk/menu_pointer.h"
#include "dusk/mods/item.hpp"
#include "dusk/mods/svc/flow.hpp" #include "dusk/mods/svc/flow.hpp"
#include "dusk/settings.h" #include "dusk/settings.h"
#include "dusk/version.hpp" #include "dusk/version.hpp"
@@ -833,9 +832,6 @@ u32 dMsgObject_c::getMessageIndex(u32 param_0) {
u32 dMsgObject_c::getRevoMessageIndex(u32 param_1) { u32 dMsgObject_c::getRevoMessageIndex(u32 param_1) {
#if TARGET_PC #if TARGET_PC
const u16 sourceGroup = param_1 > 5000 ? static_cast<u16>(s_groupID) : 0;
param_1 = dusk::mods::item_check_message(sourceGroup, param_1);
u16 customGroup = 0; u16 customGroup = 0;
if (param_1 <= 0xffff && if (param_1 <= 0xffff &&
dusk::flow::custom_message_group(static_cast<u16>(param_1), customGroup)) dusk::flow::custom_message_group(static_cast<u16>(param_1), customGroup))
+9 -30
View File
@@ -10,7 +10,6 @@
#include <cstring> #include <cstring>
#include "JSystem/J2DGraph/J2DAnmLoader.h" #include "JSystem/J2DGraph/J2DAnmLoader.h"
#include "dusk/utilities.hpp"
#include "dusk/version.hpp" #include "dusk/version.hpp"
#include "f_op/f_op_msg_mng.h" #include "f_op/f_op_msg_mng.h"
@@ -1513,31 +1512,11 @@ void dName_c::selectCursorPosSet(int row) {
} }
#if TARGET_PC #if TARGET_PC
static dusk::utils::PaneCache l_tagName[] = {
{MULTI_CHAR('m_00_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_3'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_01_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_2'), 0.0f, 0.0f, false},
{MULTI_CHAR('m03_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_1'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_05_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_0'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_07_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_4'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_09_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_3'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_10_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_2'), 0.0f, 0.0f, false},
{MULTI_CHAR('m12_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_0'), 0.0f, 0.0f, false},
};
static dusk::utils::PaneCache l_nameTagName[] = {
{MULTI_CHAR('name_00'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_01'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_02'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_03'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_04'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_05'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_06'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_07'), 0.0f, 0.0f, false},
};
static dusk::utils::PaneCache l_nameCurTagName[] = {
{MULTI_CHAR('s__n_00'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_01'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_02'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_03'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_04'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_05'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_06'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_07'), 0.0f, 0.0f, false},
};
void dName_c::nameWide() { void dName_c::nameWide() {
static bool cachedPanes = false; static bool cachedPanes = false;
// Get pre-scale values for each pane // Get pre-scale values for each pane
if (!cachedPanes) { if (!cachedPanes) {
for (dusk::utils::PaneCache& entry : l_tagName) { for (PaneCache& entry : l_tagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
if (!entry.cached) { if (!entry.cached) {
entry.origTransX = pane->getTranslateX(); entry.origTransX = pane->getTranslateX();
@@ -1545,7 +1524,7 @@ void dName_c::nameWide() {
entry.cached = true; entry.cached = true;
} }
} }
for (dusk::utils::PaneCache& entry : l_nameTagName) { for (PaneCache& entry : l_nameTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
if (!entry.cached) { if (!entry.cached) {
entry.origTransX = pane->getTranslateX(); entry.origTransX = pane->getTranslateX();
@@ -1553,7 +1532,7 @@ void dName_c::nameWide() {
entry.cached = true; entry.cached = true;
} }
} }
for (dusk::utils::PaneCache& entry : l_nameCurTagName) { for (PaneCache& entry : l_nameCurTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
if (!entry.cached) { if (!entry.cached) {
entry.origTransX = pane->getTranslateX(); entry.origTransX = pane->getTranslateX();
@@ -1567,19 +1546,19 @@ void dName_c::nameWide() {
// Reset all panes // Reset all panes
nameIn.NameInScr->scale(1.0f, 1.0f); nameIn.NameInScr->scale(1.0f, 1.0f);
nameIn.NameInScr->translate(0.0f, 0.0f); nameIn.NameInScr->translate(0.0f, 0.0f);
for (dusk::utils::PaneCache& entry : l_tagName) { for (PaneCache& entry : l_tagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4); pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f); pane->scale(1.0f, 1.0f);
pane->translate(entry.origTransX, entry.origTransY); pane->translate(entry.origTransX, entry.origTransY);
} }
for (dusk::utils::PaneCache& entry : l_nameTagName) { for (PaneCache& entry : l_nameTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4); pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f); pane->scale(1.0f, 1.0f);
pane->translate(entry.origTransX, entry.origTransY); pane->translate(entry.origTransX, entry.origTransY);
} }
for (dusk::utils::PaneCache& entry : l_nameCurTagName) { for (PaneCache& entry : l_nameCurTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4); pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f); pane->scale(1.0f, 1.0f);
@@ -1595,17 +1574,17 @@ void dName_c::nameWide() {
break; break;
default: // Wii and Dusklight default: // Wii and Dusklight
// List of Characters Box // List of Characters Box
for (dusk::utils::PaneCache& entry : l_tagName) { for (PaneCache& entry : l_tagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
} }
// Letters being typed // Letters being typed
for (dusk::utils::PaneCache& entry : l_nameTagName) { for (PaneCache& entry : l_nameTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
} }
// Underscores when typing below letters // Underscores when typing below letters
for (dusk::utils::PaneCache& entry : l_nameCurTagName) { for (PaneCache& entry : l_nameCurTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag); J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
} }
+7 -28
View File
@@ -41,10 +41,9 @@
#if TARGET_PC #if TARGET_PC
#include "dusk/autosave.h" #include "dusk/autosave.h"
#include "dusk/d_trigger_view.h"
#include "dusk/memory.h" #include "dusk/memory.h"
#include "dusk/mods/item.hpp"
#include "dusk/ui/ui.hpp" #include "dusk/ui/ui.hpp"
#include "mods/items.h"
#endif #endif
#if DEBUG #if DEBUG
@@ -677,6 +676,10 @@ static int dScnPly_Draw(dScnPly_c* i_this) {
dComIfG_Ccsp()->Draw(); dComIfG_Ccsp()->Draw();
dComIfG_Bgsp().Draw(); dComIfG_Bgsp().Draw();
#if TARGET_PC
dusk::TriggerView::execute();
#endif
#if DEBUG #if DEBUG
dPath_Draw(); dPath_Draw();
#endif #endif
@@ -1194,35 +1197,11 @@ static int phase_1(dScnPly_c* i_this) {
} }
// Stage: Ordon Spring, Room: Ordon Spring // Stage: Ordon Spring, Room: Ordon Spring
#if TARGET_PC
const bool iliaMemoryArrival =
!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 &&
dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12;
const u32 iliaMemoryTag = dusk::mods::item_give_tag(ITEM_CHECK_ILIA_MEMORY);
if (!iliaMemoryArrival) {
dusk::mods::item_check_cancel(iliaMemoryTag);
}
if (iliaMemoryArrival)
#else
if (!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 && if (!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 &&
dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12) dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12)
#endif
{ {
#if TARGET_PC dComIfGs_onItemFirstBit(dItemNo_HORSE_FLUTE_e);
const auto itemCheck = dComIfGs_setItem(SLOT_21, dItemNo_HORSE_FLUTE_e);
dusk::mods::item_check_commit(iliaMemoryTag, dItemNo_HORSE_FLUTE_e, nullptr);
if (itemCheck.itemNo == dItemNo_HORSE_FLUTE_e) {
#endif
dComIfGs_onItemFirstBit(dItemNo_HORSE_FLUTE_e);
dComIfGs_setItem(SLOT_21, dItemNo_HORSE_FLUTE_e);
#if TARGET_PC
dusk::mods::item_check_complete(itemCheck, nullptr);
} else if (itemCheck.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(itemCheck, nullptr);
} else {
dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::Silent);
}
#endif
} }
if ((u8)dKy_darkworld_stage_check(dComIfGp_getStartStageName(), if ((u8)dKy_darkworld_stage_check(dComIfGp_getStartStageName(),
+5 -9
View File
@@ -906,17 +906,15 @@ int dShopSystem_c::seq_start(fopAc_ac_c* actor, dMsgFlow_c* i_flow) {
if (mFlow.getEventId(&itemNo) == 1) { if (mFlow.getEventId(&itemNo) == 1) {
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC #if TARGET_PC
u32 itemGiveTag = 0; const char* itemCheckName = nullptr;
if (itemNo == dItemNo_HALF_MILK_BOTTLE_e) { if (itemNo == dItemNo_HALF_MILK_BOTTLE_e) {
const auto itemCheck = itemCheckName = "sera_reward";
dusk::mods::item_check_commit("sera_reward", itemNo, actor); itemNo = dusk::mods::item_check(itemCheckName, itemNo, actor);
itemNo = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
} }
#endif #endif
mItemPartnerId = mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(itemGiveTag)); NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
} }
if (fpcEx_IsExist(mItemPartnerId)) { if (fpcEx_IsExist(mItemPartnerId)) {
@@ -1207,9 +1205,7 @@ int dShopSystem_c::seq_decide_yes(fopAc_ac_c* actor, dMsgFlow_c* i_flow) {
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC #if TARGET_PC
const u32 itemGiveTag = dusk::mods::item_give_tag_shop(itemNo & 0xFF); const u32 itemGiveTag = dusk::mods::item_give_tag_shop(itemNo & 0xFF);
const auto itemCheck = itemNo = dusk::mods::item_check_shop(itemNo & 0xFF, actor);
dusk::mods::item_check_commit(itemGiveTag, itemNo & 0xFF, actor);
itemNo = itemCheck.itemNo;
#endif #endif
mItemPartnerId = fopAcM_createItemForPresentDemo( mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, itemNo, 0, -1, -1, NULL, NULL IF_DUSK_ARG(itemGiveTag)); &current.pos, itemNo, 0, -1, -1, NULL, NULL IF_DUSK_ARG(itemGiveTag));
+21
View File
@@ -1537,6 +1537,27 @@ dStage_objectNameInf* dStage_searchName(char const* objName) {
return NULL; return NULL;
} }
#if TARGET_PC
dStage_objectNameInf* dStage_searchNameCI(char const* objName) {
dStage_objectNameInf* obj = l_objectName;
for (u32 i = 0; i < ARRAY_SIZEU(l_objectName); i++) {
const char* a = obj->name;
const char* b = objName;
while (*a && *b && tolower((unsigned char)*a) == tolower((unsigned char)*b)) {
++a;
++b;
}
if (*a == '\0' && *b == '\0') {
return obj;
}
obj++;
}
return NULL;
}
#endif
const char* dStage_getName(s16 procName, s8 argument) { const char* dStage_getName(s16 procName, s8 argument) {
static char tmp_name[dStage_NAME_LENGTH]; static char tmp_name[dStage_NAME_LENGTH];
+880
View File
@@ -0,0 +1,880 @@
#include "commands.hpp"
#include "JSystem/JUtility/JUTGamePad.h"
#include "SSystem/SComponent/c_math.h"
#include "SSystem/SComponent/c_sxyz.h"
#include "SSystem/SComponent/c_xyz.h"
#include "c/c_damagereaction.h"
#include "d/actor/d_a_alink.h"
#include "d/d_com_inf_actor.h"
#include "d/d_com_inf_game.h"
#include "d/d_camera.h"
#include "d/d_kankyo.h"
#include "d/d_stage.h"
#include "dusk/game_clock.h"
#include "f_op/f_op_actor_mng.h"
#include "f_pc/f_pc_layer.h"
#include "f_pc/f_pc_layer_iter.h"
#include "f_pc/f_pc_manager.h"
#include "f_pc/f_pc_node.h"
#include <algorithm>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "fmt/format.h"
namespace dusk {
namespace {
static constexpr int kMaxHistory = 64;
static std::vector<std::string> SplitArgs(std::string_view input) {
std::vector<std::string> args;
std::string cur;
for (char c : input) {
if (c == ' ' || c == '\t') {
if (!cur.empty()) {
args.push_back(std::move(cur));
cur.clear();
}
} else {
cur += c;
}
}
if (!cur.empty()) {
args.push_back(std::move(cur));
}
return args;
}
static std::optional<long> ParseLong(const std::string& s) {
if (s.empty()) {
return std::nullopt;
}
char* end = nullptr;
const long v = std::strtol(s.c_str(), &end, 0);
return (end != s.c_str() && *end == '\0') ? std::optional<long>{v} : std::nullopt;
}
static std::optional<float> ParseFloat(const std::string& s) {
if (s.empty()) {
return std::nullopt;
}
char* end = nullptr;
const float v = std::strtof(s.c_str(), &end);
return (end != s.c_str() && *end == '\0') ? std::optional<float>{v} : std::nullopt;
}
static const char* ActorShortName(s16 profname) {
const char* n = dStage_getName(profname, -1);
return n ? n : "?";
}
// Resolves @found / @link / @<id> to a process pointer, outputting an error on failure
static base_process_class* ParseProcArg(
const std::string& s, unsigned int foundProcId, const CommandOutput& output) {
if (s.empty() || s[0] != '@') {
output("Error: proc reference must start with @");
return nullptr;
}
const std::string inner = s.substr(1);
unsigned int id;
if (inner == "found") {
if (foundProcId == 0) {
output("Error: @found is not set");
return nullptr;
}
id = foundProcId;
} else if (inner == "link") {
auto* player = dComIfGp_getPlayer(0);
if (player == nullptr) {
output("Error: player not available");
return nullptr;
}
id = (unsigned int)fpcM_GetID(player);
} else {
const auto v = ParseLong(inner);
if (!v) {
output("Error: invalid proc ID");
return nullptr;
}
id = (unsigned int)*v;
}
auto* proc = fpcM_SearchByID(id);
if (proc == nullptr) {
output(fmt::format(FMT_STRING("Error: proc {} not found"), id));
return nullptr;
}
return proc;
}
// Like ParseProcArg but ensures the proc is an actor
static fopAc_ac_c* ParseActorArg(
const std::string& s, unsigned int foundProcId, const CommandOutput& output) {
auto* proc = ParseProcArg(s, foundProcId, output);
if (proc == nullptr) {
return nullptr;
}
if (!fopAcM_IsActor(proc)) {
output("Error: proc is not an actor");
return nullptr;
}
return static_cast<fopAc_ac_c*>(proc);
}
static std::optional<s16> ParseActorId(const std::string& s) {
if (const auto v = ParseLong(s)) {
return (s16)*v;
}
const auto* entry = dStage_searchNameCI(s.c_str());
return entry ? std::optional<s16>{entry->procname} : std::nullopt;
}
static std::optional<cXyz> ParseXYZ(const std::vector<std::string>& args, size_t i) {
const auto x = ParseFloat(args[i]), y = ParseFloat(args[i + 1]), z = ParseFloat(args[i + 2]);
return (x && y && z) ? std::optional<cXyz>{cXyz(*x, *y, *z)} : std::nullopt;
}
static bool TryAngle(
s16& out, const std::vector<std::string>& args, size_t i, const CommandOutput& output) {
if (args.size() <= i) {
return true;
}
const auto a = ParseLong(args[i]);
if (!a) {
output("Error: invalid angle");
return false;
}
out = (s16)*a;
return true;
}
static std::string actorLine(const base_process_class* proc) {
const auto* ac = static_cast<const fopAc_ac_c*>(proc);
return fmt::format(FMT_STRING("procId={} 0x{:04X} ({}) @ ({:.2f}, {:.2f}, {:.2f}) room={}"),
(unsigned int)proc->id, (unsigned int)(u16)proc->profname, ActorShortName(proc->profname),
ac->current.pos.x, ac->current.pos.y, ac->current.pos.z, (int)ac->current.roomNo);
}
static void recurseLayer(void* p, int (*callback)(void*, void*), void* ctx) {
auto* proc = static_cast<base_process_class*>(p);
if (fpcBs_Is_JustOfType(g_fpcNd_type, proc->subtype)) {
fpcLyIt_OnlyHere(&static_cast<process_node_class*>(p)->layer, callback, ctx);
}
}
struct ListContext {
s16 targetId;
std::vector<std::string>* output;
};
struct FindContext {
s16 targetId;
std::vector<base_process_class*> matches;
};
static int ListActorCallback(void* p, void* ctx) {
auto* proc = static_cast<base_process_class*>(p);
auto* context = static_cast<ListContext*>(ctx);
if (fopAcM_IsActor(proc) && (context->targetId < 0 || proc->profname == context->targetId)) {
context->output->push_back(" " + actorLine(proc));
}
recurseLayer(p, ListActorCallback, ctx);
return 1;
}
static int FindActorCallback(void* p, void* ctx) {
auto* proc = static_cast<base_process_class*>(p);
auto* context = static_cast<FindContext*>(ctx);
if (fopAcM_IsActor(proc) && proc->profname == context->targetId) {
context->matches.push_back(proc);
}
recurseLayer(p, FindActorCallback, ctx);
return 1;
}
struct CameraFlyState {
bool active = false;
cXyz startEye;
cXyz startCenter;
cXyz endEye;
cXyz endCenter;
float duration = 0.0f;
float elapsed = 0.0f;
};
CameraFlyState s_cameraFly;
static dCamera_c* getCamera() { return dCam_getBody(); }
static cXyz anglesToDir(s16 h, s16 v) {
return cXyz(cM_scos(v) * cM_ssin(h), cM_ssin(v), cM_scos(v) * cM_scos(h));
}
static void cameraToAngles(dCamera_c* cam, s16& h, s16& v) {
const cXyz eye = cam->iEye();
const cXyz ctr = cam->iCenter();
const float dx = ctr.x - eye.x;
const float dy = ctr.y - eye.y;
const float dz = ctr.z - eye.z;
const float hDist = sqrtf(dx * dx + dz * dz);
h = cM_atan2s(dx, dz);
v = cM_atan2s(dy, hDist);
}
static float smoothstep(float t) {
t = std::clamp(t, 0.0f, 1.0f);
return t * t * (3.0f - 2.0f * t);
}
static constexpr float kCamTargetDist = 100.0f;
} // namespace
bool isCameraDetached() { return s_cameraFly.active; }
void processCameraCommands() {
if (!s_cameraFly.active) { return; }
dCamera_c* cam = getCamera();
if (cam == nullptr) { s_cameraFly.active = false; return; }
cXyz eye, center;
if (s_cameraFly.duration <= 0.0f) {
eye = s_cameraFly.endEye;
center = s_cameraFly.endCenter;
} else {
s_cameraFly.elapsed += dusk::game_clock::kSimPeriod;
const float t = smoothstep(s_cameraFly.elapsed / s_cameraFly.duration);
eye = s_cameraFly.startEye + (s_cameraFly.endEye - s_cameraFly.startEye) * t;
center = s_cameraFly.startCenter + (s_cameraFly.endCenter - s_cameraFly.startCenter) * t;
if (s_cameraFly.elapsed >= s_cameraFly.duration) {
eye = s_cameraFly.endEye;
center = s_cameraFly.endCenter;
s_cameraFly.startEye = s_cameraFly.endEye;
s_cameraFly.startCenter = s_cameraFly.endCenter;
s_cameraFly.duration = 0.0f;
}
}
cam->Reset(center, eye);
cam->mDebugFlyCam.initialized = false;
}
void runCommand(std::string_view cmdLine, CommandState& state, const CommandOutput& output) {
if (state.echoEnabled) {
output(fmt::format(FMT_STRING("> {}"), cmdLine));
}
if (!cmdLine.empty()) {
if (state.history.empty() || state.history.back() != cmdLine) {
state.history.push_back(std::string(cmdLine));
if ((int)state.history.size() > kMaxHistory) {
state.history.erase(state.history.begin());
}
}
}
auto args = SplitArgs(cmdLine);
if (args.empty()) {
return;
}
const auto& cmd = args[0];
auto requirePlayer = [&]() -> daAlink_c* {
auto* p = (daAlink_c*)dComIfGp_getPlayer(0);
if (p == nullptr) {
output("Error: player not available");
}
return p;
};
if (cmd == "tp") {
auto* player = requirePlayer();
if (player == nullptr) {
return;
}
if (args.size() >= 2 && args[1].starts_with('@')) {
auto* ac = ParseActorArg(args[1], state.foundProcId, output);
if (ac == nullptr) {
return;
}
if (args.size() >= 3 && args[2].starts_with('@')) {
auto* destAc = ParseActorArg(args[2], state.foundProcId, output);
if (destAc == nullptr) {
return;
}
const cXyz destPos = destAc->current.pos;
ac->current.pos = destPos;
output(fmt::format(FMT_STRING("Moved actor {} to ({:.2f}, {:.2f}, {:.2f})"), ac->id,
destPos.x, destPos.y, destPos.z));
return;
}
if (args.size() >= 5) {
const auto pos = ParseXYZ(args, 2);
if (!pos) {
output("Error: invalid coordinates");
return;
}
ac->current.pos = *pos;
if (!TryAngle(ac->shape_angle.y, args, 5, output)) {
return;
}
output(fmt::format(FMT_STRING("Moved actor {} to ({:.2f}, {:.2f}, {:.2f})"), ac->id,
pos->x, pos->y, pos->z));
return;
}
player->current.pos = ac->current.pos;
output(fmt::format(FMT_STRING("Teleported to actor {} ({:.2f}, {:.2f}, {:.2f})"),
ac->id, ac->current.pos.x, ac->current.pos.y, ac->current.pos.z));
return;
}
if (args.size() < 4) {
output("Usage: tp <x> <y> <z> [angle] | tp @<procId> [<x> <y> <z> [angle] | @link]");
return;
}
const auto pos = ParseXYZ(args, 1);
if (!pos) {
output("Error: invalid coordinates");
return;
}
player->current.pos = *pos;
if (!TryAngle(player->shape_angle.y, args, 4, output)) {
return;
}
output(fmt::format(
FMT_STRING("Teleported to ({:.2f}, {:.2f}, {:.2f})"), pos->x, pos->y, pos->z));
return;
}
if (cmd == "spawn") {
if (args.size() < 2) {
output("Usage: spawn <actorId> [params] [x y z] [angle]");
return;
}
auto* player = requirePlayer();
if (player == nullptr) {
return;
}
const auto actorId = ParseActorId(args[1]);
if (!actorId) {
output("Error: unknown actor ID or name");
return;
}
long paramsL = -1;
if (args.size() >= 3) {
const auto p = ParseLong(args[2]);
if (!p) {
output("Error: invalid params");
return;
}
paramsL = *p;
}
cXyz pos = player->current.pos;
if (args.size() >= 6) {
const auto spawnPos = ParseXYZ(args, 3);
if (!spawnPos) {
output("Error: invalid spawn coordinates");
return;
}
pos = *spawnPos;
}
s16 angleY = 0;
if (!TryAngle(angleY, args, 6, output)) {
return;
}
csXyz angle;
angle.set(0, angleY, 0);
cXyz scale(1.0f, 1.0f, 1.0f);
layer_class* savedLayer = fpcLy_CurrentLayer();
base_process_class* playScene = fpcM_SearchByName(fpcNm_PLAY_SCENE_e);
if (playScene != nullptr) {
fpcLy_SetCurrentLayer(&((process_node_class*)playScene)->layer);
}
unsigned int result = fopAcM_create(
*actorId, (u32)paramsL, &pos, player->current.roomNo, &angle, &scale, (s8)-1);
fpcLy_SetCurrentLayer(savedLayer);
output(result != 0 ? fmt::format(FMT_STRING("Spawned actorId=0x{:04X} procId={}"),
(unsigned int)(u16)*actorId, result) :
fmt::format(FMT_STRING("Failed to spawn actorId=0x{:04X}"),
(unsigned int)(u16)*actorId));
return;
}
if (cmd == "reset") {
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
output("Soft reset triggered");
return;
}
if (cmd == "warp") {
if (args.size() < 4) {
output("Usage: warp <stageName> <point> <roomNo> [layer=-1]");
output(" e.g. warp F_SP121 0 0");
return;
}
const auto pointL = ParseLong(args[2]), roomL = ParseLong(args[3]);
if (!pointL || !roomL) {
output("Error: invalid point or room number");
return;
}
long layerL = -1;
if (args.size() >= 5) {
const auto l = ParseLong(args[4]);
if (!l) {
output("Error: invalid layer");
return;
}
layerL = *l;
}
state.lastWarpStage = args[1];
dComIfGp_setNextStage(state.lastWarpStage.c_str(), (s16)*pointL, (s8)*roomL, (s8)layerL);
output(fmt::format(FMT_STRING("Warping to {} point={} room={} layer={}"), args[1],
(int)(s16)*pointL, (int)(s8)*roomL, (int)(s8)layerL));
return;
}
if (cmd == "list") {
s16 targetId = -1;
if (args.size() >= 2) {
const auto id = ParseActorId(args[1]);
if (!id) {
output("Error: unknown actor ID or name");
return;
}
targetId = *id;
}
std::vector<std::string> results;
ListContext ctx{targetId, &results};
fpcLyIt_OnlyHere(fpcLy_RootLayer(), ListActorCallback, &ctx);
output(results.empty() ? "No matching actors found" :
fmt::format(FMT_STRING("Found {} actor(s):"), results.size()));
for (const auto& r : results) {
output(r);
}
return;
}
if (cmd == "killall") {
if (args.size() < 2) {
output("Usage: killall <actorId|name>");
return;
}
const auto targetId = ParseActorId(args[1]);
if (!targetId) {
output("Error: unknown actor ID or name");
return;
}
FindContext ctx{*targetId, {}};
fpcLyIt_OnlyHere(fpcLy_RootLayer(), FindActorCallback, &ctx);
for (auto* proc : ctx.matches) {
fpcM_Delete(proc);
}
output(fmt::format(FMT_STRING("Deleted {} actor(s) of type {} ({})"),
(int)ctx.matches.size(), (unsigned int)(u16)*targetId, ActorShortName(*targetId)));
return;
}
if (cmd == "heal") {
auto* player = requirePlayer();
if (player == nullptr) {
return;
}
const u16 maxLife = dComIfGs_getMaxLife() / 5 * 4;
u16 newLife = maxLife;
if (args.size() >= 2) {
const auto amount = ParseLong(args[1]);
if (!amount) {
output("Error: invalid amount");
return;
}
newLife =
(u16)std::max(0L, std::min((long)maxLife, (long)dComIfGs_getLife() + *amount));
}
dComIfGs_setLife(newLife);
output(fmt::format(FMT_STRING("Health: {}/{}"), (int)newLife, (int)maxLife));
return;
}
if (cmd == "kill") {
if (args.size() >= 2) {
auto* proc = ParseProcArg(args[1], state.foundProcId, output);
if (proc == nullptr) {
return;
}
fpcM_Delete(proc);
output(fmt::format(FMT_STRING("Deleted proc {}"), proc->id));
} else {
auto* player = requirePlayer();
if (player == nullptr) {
return;
}
dComIfGs_setLife(0);
output("Set Link's health to 0");
}
return;
}
if (cmd == "freeze" || cmd == "unfreeze") {
const bool doFreeze = (cmd == "freeze");
if (args.size() < 2) {
g_dComIfAc_gameInfo.mPause = doFreeze;
output(doFreeze ? "Global freeze on" : "Global freeze off");
return;
}
auto* ac = ParseActorArg(args[1], state.foundProcId, output);
if (ac == nullptr) {
return;
}
if (doFreeze) {
fpcM_PauseEnable(ac, 1);
output(fmt::format(FMT_STRING("Froze actor {}"), (unsigned int)ac->id));
} else {
fpcM_PauseDisable(ac, 1);
output(fmt::format(FMT_STRING("Unfroze actor {}"), (unsigned int)ac->id));
}
return;
}
if (cmd == "rate") {
if (args.size() >= 2) {
const auto hz = ParseLong(args[1]);
if (!hz || *hz <= 0) {
output("Error: rate must be a positive integer");
return;
}
dusk::game_clock::set_sim_rate((float)*hz);
}
output(fmt::format(FMT_STRING("Sim rate: {:.0f} hz"), dusk::game_clock::get_sim_rate()));
return;
}
if (cmd == "ebf") {
if (args.size() >= 2) {
const auto val = ParseLong(args[1]);
if (!val || *val < 0 || *val > 255) {
output("Error: value must be 0-255");
return;
}
cDmr_SkipInfo = (u8)*val;
}
output(fmt::format(FMT_STRING("EBF = {}"), (int)cDmr_SkipInfo));
return;
}
if (cmd == "time") {
if (args.size() >= 2) {
const auto t = ParseFloat(args[1]);
if (!t || *t < 0.0f || *t > 360.0f) {
output("Error: time must be a float between 0 and 360");
return;
}
dKy_instant_timechg(*t);
}
output(fmt::format(FMT_STRING("Time: {:.2f} ({}:{:02d})"), dComIfGs_getTime(),
dKy_getdaytime_hour(), dKy_getdaytime_minute()));
return;
}
if (cmd == "rupees") {
const u16 maxRupees = dComIfGs_getRupeeMax();
if (args.size() >= 2) {
const auto amount = ParseLong(args[1]);
if (!amount) {
output("Error: invalid amount");
return;
}
dComIfGs_setRupee((u16)std::max(0L, std::min((long)maxRupees, *amount)));
}
output(fmt::format(FMT_STRING("Rupees: {}/{}"), (int)dComIfGs_getRupee(), (int)maxRupees));
return;
}
if (cmd == "find") {
if (args.size() < 2) {
if (state.foundProcId == 0) {
output("@found is not set");
return;
}
auto* proc = fpcM_SearchByID(state.foundProcId);
output(proc != nullptr && fopAcM_IsActor(proc) ?
"@found = " + actorLine(proc) :
fmt::format(FMT_STRING("@found = {} (no longer exists)"),
(unsigned int)state.foundProcId));
return;
}
const auto targetId = ParseActorId(args[1]);
if (!targetId) {
output("Error: unknown actor ID or name");
return;
}
int targetN = 1;
if (args.size() >= 3) {
const auto n = ParseLong(args[2]);
if (!n || *n < 1) {
output("Error: index must be >= 1");
return;
}
targetN = (int)*n;
}
FindContext ctx{*targetId, {}};
fpcLyIt_OnlyHere(fpcLy_RootLayer(), FindActorCallback, &ctx);
if (ctx.matches.empty()) {
output(fmt::format(FMT_STRING("No actors found for '{}'"), args[1]));
return;
}
std::sort(ctx.matches.begin(), ctx.matches.end(),
[](const base_process_class* a, const base_process_class* b) { return a->id < b->id; });
if (targetN > (int)ctx.matches.size()) {
output(fmt::format(
FMT_STRING("Error: only {} actor(s) of that type exist"), (int)ctx.matches.size()));
return;
}
auto* picked = ctx.matches[(size_t)(targetN - 1)];
state.foundProcId = picked->id;
output(fmt::format(
FMT_STRING("@found [{}/{}] {}"), targetN, (int)ctx.matches.size(), actorLine(picked)));
return;
}
if (cmd == "camera") {
const std::string sub = args.size() >= 2 ? args[1] : "";
if (sub == "attach") {
s_cameraFly.active = false;
output("Camera attached");
return;
}
auto* cam = getCamera();
if (cam == nullptr) { output("Error: camera not available"); return; }
if (sub == "detach") {
s_cameraFly.active = true;
s_cameraFly.duration = 0.0f;
s_cameraFly.startEye = s_cameraFly.endEye = cam->iEye();
s_cameraFly.startCenter = s_cameraFly.endCenter = cam->iCenter();
output("Camera detached");
return;
}
if (sub == "tp") {
// camera tp @<ref>
if (args.size() >= 3 && !args[2].empty() && args[2][0] == '@') {
auto* ac = ParseActorArg(args[2], state.foundProcId, output);
if (ac == nullptr) { return; }
const cXyz actorPos = ac->current.pos;
const cXyz dir = anglesToDir(ac->shape_angle.y, 0);
const cXyz newEye = cXyz(actorPos.x - dir.x * kCamTargetDist,
actorPos.y - dir.y * kCamTargetDist + 50.0f,
actorPos.z - dir.z * kCamTargetDist);
s_cameraFly.active = true; s_cameraFly.duration = 0.0f;
s_cameraFly.startEye = s_cameraFly.endEye = newEye;
s_cameraFly.startCenter = s_cameraFly.endCenter = actorPos;
output(fmt::format(FMT_STRING("Camera moved to actor {}"), ac->id));
return;
}
// camera tp x y z [h] [v]
if (args.size() < 5) {
output("Usage: camera tp <x> <y> <z> [h_angle] [v_angle] | camera tp @<ref>");
return;
}
const auto pos = ParseXYZ(args, 2);
if (!pos) { output("Error: invalid coordinates"); return; }
s16 h = 0, v = 0;
cameraToAngles(cam, h, v);
if (args.size() >= 6) {
const auto hv = ParseLong(args[5]);
if (!hv) { output("Error: invalid h_angle"); return; }
h = (s16)*hv;
}
if (args.size() >= 7) {
const auto vv = ParseLong(args[6]);
if (!vv) { output("Error: invalid v_angle"); return; }
v = (s16)*vv;
}
const cXyz dir = anglesToDir(h, v);
const cXyz center = cXyz(pos->x + dir.x * kCamTargetDist,
pos->y + dir.y * kCamTargetDist,
pos->z + dir.z * kCamTargetDist);
s_cameraFly.active = true; s_cameraFly.duration = 0.0f;
s_cameraFly.startEye = s_cameraFly.endEye = *pos;
s_cameraFly.startCenter = s_cameraFly.endCenter = center;
output(fmt::format(FMT_STRING("Camera teleported to ({:.2f}, {:.2f}, {:.2f})"),
pos->x, pos->y, pos->z));
return;
}
if (sub == "pos") {
s16 h = 0, v = 0;
cameraToAngles(cam, h, v);
const cXyz eye = cam->iEye();
output(fmt::format(FMT_STRING("Camera: ({:.2f}, {:.2f}, {:.2f}) h={} v={}"),
eye.x, eye.y, eye.z, (int)h, (int)v));
return;
}
if (sub == "fly") {
// camera fly <time> <x> <y> <z> [h] [v] | camera fly <time> @<ref>
if (args.size() < 4) {
output("Usage: camera fly <time> <x> <y> <z> [h_angle] [v_angle] | camera fly <time> @<ref>");
return;
}
const auto t = ParseFloat(args[2]);
if (!t || *t <= 0) { output("Error: invalid time"); return; }
const float flyTime = *t;
cXyz targetPos;
s16 h = 0, v = 0;
if (!args[3].empty() && args[3][0] == '@') {
auto* ac = ParseActorArg(args[3], state.foundProcId, output);
if (ac == nullptr) { return; }
targetPos = ac->current.pos;
h = ac->shape_angle.y;
v = 0;
} else {
if (args.size() < 6) {
output("Usage: camera fly <time> <x> <y> <z> [h_angle] [v_angle] | camera fly <time> @<ref>");
return;
}
const auto pos = ParseXYZ(args, 3);
if (!pos) { output("Error: invalid coordinates"); return; }
targetPos = *pos;
cameraToAngles(cam, h, v);
if (args.size() >= 7) {
const auto hv = ParseLong(args[6]); if (!hv) { output("Error: invalid h_angle"); return; } h = (s16)*hv;
}
if (args.size() >= 8) {
const auto vv = ParseLong(args[7]); if (!vv) { output("Error: invalid v_angle"); return; } v = (s16)*vv;
}
}
const cXyz dir = anglesToDir(h, v);
const cXyz endCenter = cXyz(targetPos.x + dir.x * kCamTargetDist,
targetPos.y + dir.y * kCamTargetDist,
targetPos.z + dir.z * kCamTargetDist);
s_cameraFly.active = true;
s_cameraFly.startEye = cam->iEye();
s_cameraFly.startCenter = cam->iCenter();
s_cameraFly.endEye = targetPos;
s_cameraFly.endCenter = endCenter;
s_cameraFly.duration = flyTime;
s_cameraFly.elapsed = 0.0f;
output(fmt::format(FMT_STRING("Camera flying to ({:.2f}, {:.2f}, {:.2f}) over {:.1f}s"),
targetPos.x, targetPos.y, targetPos.z, flyTime));
return;
}
output("Usage: camera detach | attach | tp ... | fly ... | pos");
return;
}
if (cmd == "echo") {
if (args.size() >= 2 && args[1] == "off") {
state.echoEnabled = false;
output("Echo disabled");
} else if (args.size() >= 2 && args[1] == "on") {
state.echoEnabled = true;
output("Echo enabled");
} else {
output(state.echoEnabled ? "Echo: on" : "Echo: off");
}
return;
}
if (cmd == "transform") {
auto* player = requirePlayer();
if (player == nullptr) { return; }
player->procCoMetamorphoseInit();
output("Transforming");
return;
}
if (cmd == "angle") {
auto* player = requirePlayer();
if (player == nullptr) { return; }
if (args.size() >= 2) {
const auto a = ParseLong(args[1]);
if (!a) { output("Error: invalid angle"); return; }
player->shape_angle.y = (s16)*a;
output(fmt::format(FMT_STRING("Angle set to {}"), (int)(s16)*a));
} else {
output(fmt::format(FMT_STRING("Angle: {}"), (int)player->shape_angle.y));
}
return;
}
if (cmd == "pos") {
auto* player = requirePlayer();
if (player == nullptr) {
return;
}
output(fmt::format(FMT_STRING("pos: {:.4f} {:.4f} {:.4f}"), player->current.pos.x,
player->current.pos.y, player->current.pos.z));
output(
fmt::format(FMT_STRING("stage: {} room: {} entry: {}"), dComIfGp_getStartStageName(),
(int)player->current.roomNo, (int)dComIfGp_getStartStagePoint()));
return;
}
if (cmd == "help") {
output("@<ref> = @<procId> | @found | @link");
output("");
output("angle [value] Get or set Link's facing angle");
output("camera detach | attach Detach or reattach camera");
output("camera pos Print camera position and angles");
output("camera tp <x> <y> <z> [h] [v] Teleport camera (h/v are s16 angles)");
output("camera tp @<ref> Teleport camera to actor");
output("camera fly <t> <x> <y> <z> [h] [v] Fly camera over t seconds (h/v are s16 angles)");
output("camera fly <t> @<ref> Fly camera to actor");
output("ebf [0-255] Get or set cDmr_SkipInfo");
output("echo on | off Enable or disable command echo");
output("find <id|name> [n=1] Store nth actor as @found");
output("freeze [@<ref>] Freeze globally, or freeze actor");
output("heal [amount] Heal to max, or by relative amount");
output("kill Set Link health to 0");
output("kill @<ref> Delete proc");
output("killall <id|name> Delete all actors of a type");
output("list [id|name] List actors in scene");
output("pos Print player position and stage");
output("rate [hz] Get or set sim rate (1-480, default 30)");
output("reset Soft reset");
output("rupees [amount] Get or set rupee count");
output("spawn <id|name> [params] [x y z] [angle] Spawn actor");
output("time [0-360] Get or set time of day");
output("tp <x> <y> <z> [angle] Teleport Link to coords");
output("tp @<ref> Teleport Link to actor");
output("tp @<ref> <x> <y> <z> [angle] Move actor to coords");
output("tp @<ref> @<ref> Move actor to actor");
output("transform Force transform");
output("unfreeze [@<ref>] Unfreeze globally, or unfreeze actor");
output("warp <stage> <point> <room> [layer] Warp to stage");
return;
}
output(fmt::format(FMT_STRING("Unknown command '{}' (try 'help')"), cmd));
}
} // namespace dusk
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <functional>
#include <string>
#include <string_view>
#include <vector>
namespace dusk {
using CommandOutput = std::function<void(std::string)>;
struct CommandState {
std::vector<std::string> history;
unsigned int foundProcId = 0;
std::string lastWarpStage;
bool echoEnabled = true;
};
// Execute a single command line. Calls output() for every line of response.
// Manages history internally; callers need only hold a CommandState.
void runCommand(std::string_view cmdLine, CommandState& state, const CommandOutput& output);
// Per-sim-tick update for camera fly animation.
void processCameraCommands();
// Returns true when the dev camera is detached (suppresses the game's camera update).
bool isCameraDetached();
} // namespace dusk
+417
View File
@@ -0,0 +1,417 @@
#include "dusk/d_trigger_view.h"
#include "dusk/settings.h"
#include "d/d_debug_viewer.h"
#include "d/d_com_inf_game.h"
#include "d/d_stage.h"
#include "d/d_path.h"
#include "d/d_attention.h"
#include "d/actor/d_a_alink.h"
#include "d/actor/d_a_kytag08.h"
#include "d/actor/d_a_e_rb.h"
#include "d/actor/d_a_e_rd.h"
#include "f_op/f_op_actor_mng.h"
#include "f_op/f_op_actor_tag.h"
#include "f_pc/f_pc_name.h"
#include "m_Do/m_Do_mtx.h"
#include "SSystem/SComponent/c_tag.h"
#include "SSystem/SComponent/c_list.h"
#include "JSystem/JMath/JMath.h"
namespace dusk {
namespace TriggerView {
static u8 s_opacity;
typedef void (*DrawCallback)(fopAc_ac_c*);
static void searchActorForCallback(s16 actorName, DrawCallback callback) {
node_class* node = g_fopAcTg_Queue.mpHead;
for (int i = 0; i < g_fopAcTg_Queue.mSize; i++) {
if (node != NULL) {
create_tag_class* tag = (create_tag_class*)node;
fopAc_ac_c* actorData = (fopAc_ac_c*)tag->mpTagData;
bool checkAll = actorName == -1;
if (actorData != NULL && (fopAcM_GetName(actorData) == actorName || checkAll)) {
callback(actorData);
}
node = node->mpNextNode;
}
}
}
static void drawSceneExit(fopAc_ac_c* actor) {
struct daScex_c : public fopAc_ac_c {
/* 0x568 */ Mtx mMatrix;
};
daScex_c* scex = (daScex_c*)actor;
cXyz points[8];
points[0].set(-actor->scale.x, actor->scale.y, -actor->scale.z);
points[1].set(actor->scale.x, actor->scale.y, -actor->scale.z);
points[2].set(-actor->scale.x, actor->scale.y, actor->scale.z);
points[3].set(actor->scale.x, actor->scale.y, actor->scale.z);
points[4].set(-actor->scale.x, 0.0f, -actor->scale.z);
points[5].set(actor->scale.x, 0.0f, -actor->scale.z);
points[6].set(-actor->scale.x, 0.0f, actor->scale.z);
points[7].set(actor->scale.x, 0.0f, actor->scale.z);
mDoMtx_inverse(scex->mMatrix, mDoMtx_stack_c::get());
mDoMtx_multVecArray(mDoMtx_stack_c::get(), points, points, 8);
GXColor color = {0xFF, 0x00, 0xFF, s_opacity};
dDbVw_drawCube8pXlu(points, color);
}
static void drawMidnaStop(fopAc_ac_c* actor) {
struct daMidnaStop_c : public fopAc_ac_c {
/* 0x568 */ u8 data[0x5C0 - 0x568];
/* 0x5C0 */ f32 mRangeXZ;
/* 0x5C4 */ f32 mRangeY;
};
daMidnaStop_c* mstop = (daMidnaStop_c*)actor;
GXColor color = {0x4A, 0x36, 0xBA, s_opacity};
dDbVw_drawCylinderXlu(mstop->current.pos, mstop->scale.x * 100.0f, mstop->scale.y, color, 1);
}
static void drawPlumTag(fopAc_ac_c* actor) {
GXColor color = {0x00, 0xFF, 0x00, s_opacity};
dDbVw_drawCylinderXlu(actor->current.pos, actor->scale.x * 100.0f, 1000000.0f, color, 1);
}
static void drawPlumSearch(fopAc_ac_c* actor) {
GXColor color = {0xFF, 0x00, 0x00, s_opacity};
const f32 search_dist = 500.0f;
dDbVw_drawCircleXlu(actor->attention_info.position, search_dist + 160.0f, color, 1, 12);
}
static void drawSwitchArea(fopAc_ac_c* actor) {
struct daSwc00_c : public fopAc_ac_c {
/* 0x568 */ cXyz mStart;
/* 0x574 */ cXyz mEnd;
/* 0x580 */ u16 mEventID;
/* 0x582 */ u8 mAction;
};
daSwc00_c* swc = (daSwc00_c*)actor;
int shape_type = (fopAcM_GetParam(actor) >> 0x12) & 3;
GXColor color = {0x00, 0x00, 0xFF, s_opacity};
if (shape_type == 3) {
dDbVw_drawCylinderXlu(swc->current.pos, JMAFastSqrt(swc->scale.x) - 30.0f, swc->scale.y, color, 1);
} else if (shape_type == 0) {
cXyz size = swc->mEnd - swc->mStart;
size *= 0.5f;
cXyz pos = swc->mStart + size;
csXyz angle(swc->current.angle.x, swc->current.angle.y, swc->current.angle.z);
dDbVw_drawCubeXlu(pos, size, angle, color);
}
}
static void drawEventArea(fopAc_ac_c* actor) {
u8 type = (actor->shape_angle.z & 0xFF);
if (type == 0xFF) {
type = 0;
}
if (type == 15 || type == 16) {
GXColor color = {0xFF, 0xFF, 0x00, s_opacity};
cXyz points[8];
points[0].set(-actor->scale.x, actor->scale.y, -actor->scale.z);
points[1].set(actor->scale.x, actor->scale.y, -actor->scale.z);
points[2].set(-actor->scale.x, actor->scale.y, actor->scale.z);
points[3].set(actor->scale.x, actor->scale.y, actor->scale.z);
points[4].set(-actor->scale.x, 0.0f, -actor->scale.z);
points[5].set(actor->scale.x, 0.0f, -actor->scale.z);
points[6].set(-actor->scale.x, 0.0f, actor->scale.z);
points[7].set(actor->scale.x, 0.0f, actor->scale.z);
mDoMtx_stack_c::transS(actor->home.pos.x, actor->home.pos.y, actor->home.pos.z);
mDoMtx_stack_c::YrotS(actor->current.angle.y);
mDoMtx_multVecArray(mDoMtx_stack_c::get(), points, points, 8);
dDbVw_drawCube8pXlu(points, color);
} else {
GXColor outer_color = {0xFF, 0x00, 0x00, s_opacity};
GXColor inner_color = {0x00, 0xFF, 0x00, s_opacity};
cXyz pos = actor->current.pos;
daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0);
if (player != NULL && pos.y < player->mLinkAcch.GetGroundH()) {
pos.y = player->mLinkAcch.GetGroundH() + 100.0f;
}
const f32 inner_scale = 0.83f;
dDbVw_drawCircleXlu(pos, actor->scale.x * inner_scale, inner_color, 1, 20);
dDbVw_drawCircleXlu(pos, actor->scale.x, outer_color, 1, 20);
}
}
static void drawEventTag(fopAc_ac_c* actor) {
GXColor color = {0x00, 0xC8, 0xFF, s_opacity};
u16 area_type = actor->home.angle.x & 0x8000;
if (area_type == 0x8000) {
cXyz start(actor->current.pos.x - (actor->scale.x * 0.5f), actor->current.pos.y,
actor->current.pos.z - (actor->scale.z * 0.5f));
cXyz end(actor->current.pos.x + (actor->scale.x * 0.5f),
actor->current.pos.y + actor->scale.y,
actor->current.pos.z + (actor->scale.z * 0.5f));
cXyz points[8];
points[0].set(start.x, start.y, start.z);
points[1].set(start.x, start.y, end.z);
points[2].set(end.x, start.y, end.z);
points[3].set(end.x, start.y, start.z);
points[4].set(start.x, end.y, start.z);
points[5].set(start.x, end.y, end.z);
points[6].set(end.x, end.y, end.z);
points[7].set(end.x, end.y, start.z);
dDbVw_drawCube8pXlu(points, color);
} else {
cXyz pos = actor->current.pos;
pos.y -= actor->scale.y;
dDbVw_drawCylinderXlu(pos, actor->scale.x, actor->scale.y * 2, color, 1);
}
}
static void drawTWGate(fopAc_ac_c* actor) {
GXColor color = {0xFF, 0xFF, 0xFF, s_opacity};
dDbVw_drawCylinderXlu(actor->current.pos, actor->scale.x * 100.0f, actor->scale.y * 100.0f, color, 1);
}
static u8 s_pathColorIndex = 0;
static void drawPaths(dStage_dPath_c* paths) {
static const GXColor colors[8] = {
{0xFF, 0xFF, 0xFF}, {0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00}, {0x00, 0xFF, 0x00},
{0x00, 0x00, 0xFF}, {0xFF, 0xFF, 0x00}, {0xFF, 0x00, 0xFF}, {0x00, 0xFF, 0xFF},
};
cXyz cubeSize = {30.0f, 30.0f, 30.0f};
csXyz cubeAngle = {0, 0, 0};
for (int i = 0; i < (int)paths->num; i++) {
dPath* path = &paths->m_path[i];
GXColor color = colors[(s_pathColorIndex++) & 7];
color.a = s_opacity;
cXyz a, b;
if (dPath_ChkClose(path) && path->m_num > 2) {
a = (Vec)path->m_points[0].m_position;
b = (Vec)path->m_points[(int)path->m_num - 1].m_position;
dDbVw_drawLineXlu(a, b, color, 1, 10);
}
for (int j = 0; j < (int)path->m_num - 1; j++) {
a = (Vec)path->m_points[j].m_position;
b = (Vec)path->m_points[j + 1].m_position;
dDbVw_drawLineXlu(a, b, color, 1, 10);
dDbVw_drawCubeXlu(a, cubeSize, cubeAngle, color);
}
dDbVw_drawCubeXlu(b, cubeSize, cubeAngle, color);
}
}
static void drawStagePaths() {
dStage_dPath_c* stagePaths = g_dComIfG_gameInfo.play.getStage().getPath2Inf();
if (stagePaths != nullptr) {
drawPaths(stagePaths);
}
}
static void drawCurrentRoomPaths() {
fopAc_ac_c* player = dComIfGp_getPlayer(0);
if (player == nullptr) {
return;
}
s32 roomNo = fopAcM_GetRoomNo(player);
if (roomNo < 0 || roomNo >= 64) {
return;
}
dStage_dPath_c* roomPaths = dStage_roomControl_c::mStatus[roomNo].mRoomDt.getPath2Inf();
if (roomPaths != nullptr) {
drawPaths(roomPaths);
}
}
static void drawCheckpointTag(fopAc_ac_c* actor) {
struct daTagChgRestart_c : public fopAc_ac_c {
/* 0x568 */ cXyz mVertices[4];
};
daTagChgRestart_c* chk = (daTagChgRestart_c*)actor;
GXColor color = {0x29, 0xF0, 0xFF, s_opacity};
cXyz points[8];
mDoMtx_stack_c::transS(actor->current.pos.x, actor->current.pos.y, actor->current.pos.z);
mDoMtx_stack_c::YrotM(actor->current.angle.y);
points[0] = chk->mVertices[0];
points[1] = chk->mVertices[1];
points[2] = chk->mVertices[3];
points[3] = chk->mVertices[2];
points[4] = chk->mVertices[0];
points[5] = chk->mVertices[1];
points[6] = chk->mVertices[3];
points[7] = chk->mVertices[2];
mDoMtx_multVecArray(mDoMtx_stack_c::get(), points, points, 8);
fopAc_ac_c* player = dComIfGp_getPlayer(0);
if (player != nullptr) {
for (int i = 0; i < 8; i++) {
points[i].y = player->current.pos.y;
}
}
for (int i = 0; i < 4; i++) {
points[i].y += 1000.0f;
}
dDbVw_drawCube8pXlu(points, color);
}
static void drawTransformDists(fopAc_ac_c* actor) {
if (fopAcM_GetGroup(actor) == 4 && !(actor->actor_status & fopAcStts_UNK_0x8000000_e)) {
GXColor near_color = {0x00, 0xFF, 0x00, s_opacity};
GXColor far_color = {0xFF, 0x00, 0x00, s_opacity};
const f32 near_dist = 400.0f;
const f32 far_dist = 5000.0f;
dDbVw_drawCircleXlu(actor->eyePos, near_dist, near_color, 1, 20);
dDbVw_drawCircleXlu(actor->eyePos, far_dist, far_color, 1, 20);
const s16 view_range = 0x4000;
cXyz offset(0.0f, 0.0f, far_dist);
cXyz endpos;
mDoMtx_stack_c::transS(actor->eyePos.x, actor->eyePos.y, actor->eyePos.z);
mDoMtx_stack_c::YrotM(actor->shape_angle.y);
mDoMtx_stack_c::YrotM(-view_range);
mDoMtx_stack_c::multVec(&offset, &endpos);
dDbVw_drawLineXlu(actor->eyePos, endpos, far_color, 1, 10);
mDoMtx_stack_c::transS(actor->eyePos.x, actor->eyePos.y, actor->eyePos.z);
mDoMtx_stack_c::YrotM(actor->shape_angle.y);
mDoMtx_stack_c::YrotM(view_range);
mDoMtx_stack_c::multVec(&offset, &endpos);
dDbVw_drawLineXlu(actor->eyePos, endpos, far_color, 1, 10);
mDoMtx_stack_c::transS(actor->eyePos.x, actor->eyePos.y, actor->eyePos.z);
mDoMtx_stack_c::YrotM(actor->shape_angle.y);
mDoMtx_stack_c::multVec(&offset, &endpos);
dDbVw_drawLineXlu(actor->eyePos, endpos, far_color, 1, 10);
}
}
static void drawAttentionDists(fopAc_ac_c* actor) {
if (fopAcM_GetGroup(actor) != 4) {
return;
}
GXColor lock_color = {0x00, 0x00, 0xFF, s_opacity};
GXColor talk_color = {0x00, 0xFF, 0x00, s_opacity};
dist_entry& lock_inf = dAttention_c::getDistTable(actor->attention_info.distances[fopAc_attn_LOCK_e]);
dist_entry& talk_inf = dAttention_c::getDistTable(actor->attention_info.distances[fopAc_attn_TALK_e]);
dDbVw_drawCircleXlu(actor->attention_info.position, lock_inf.mDistMax, lock_color, 1, 20);
dDbVw_drawCircleXlu(actor->attention_info.position, talk_inf.mDistMax, talk_color, 1, 20);
}
static void drawPurpleMistAvoid(fopAc_ac_c* actor) {
kytag08_class* tag = (kytag08_class*)actor;
GXColor avoidColor = {0x00, 0xFF, 0x00, s_opacity};
GXColor targetColor = {0xFF, 0x00, 0xFF, s_opacity};
dDbVw_drawCircleXlu(tag->mAvoidPos, tag->mSize.x * 45.0f * tag->mSizeScale, avoidColor, 1, 20);
cXyz cubeSize(10.0f, 10.0f, 10.0f);
csXyz cubeAngle(0, 0, 0);
dDbVw_drawCubeXlu(tag->mAvoidPos, cubeSize, cubeAngle, avoidColor);
dDbVw_drawCubeXlu(tag->mTargetAvoidPos, cubeSize, cubeAngle, targetColor);
}
static void drawLeeverData(fopAc_ac_c* actor) {
e_rb_class* leever = (e_rb_class*)actor;
if (leever->isChild) {
return;
}
GXColor color = {0xFF, 0x00, 0x00, s_opacity};
GXColor color2 = {0x00, 0x00, 0xFF, s_opacity};
cXyz pos = actor->current.pos;
daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0);
if (player != nullptr && pos.y < player->mLinkAcch.GetGroundH()) {
pos.y = player->mLinkAcch.GetGroundH() + 100.0f;
}
dDbVw_drawCircleXlu(pos, leever->appearRange * 100.0f, color, 1, 20);
dDbVw_drawCircleXlu(pos, leever->field_0xa69 * 100.0f, color2, 1, 20);
}
void execute() {
const auto& settings = getTransientSettings().triggerView;
s_opacity = (u8)(255.0f * (settings.opacity / 100.0f));
if (settings.loadZones) {
searchActorForCallback(fpcNm_SCENE_EXIT_e, drawSceneExit);
}
if (settings.midnaStops) {
searchActorForCallback(fpcNm_Tag_Mstop_e, drawMidnaStop);
}
if (settings.switchAreas) {
searchActorForCallback(fpcNm_SWC00_e, drawSwitchArea);
}
if (settings.eventAreas) {
searchActorForCallback(fpcNm_TAG_EVENT_e, drawEventTag);
searchActorForCallback(fpcNm_TAG_EVTAREA_e, drawEventArea);
searchActorForCallback(fpcNm_TAG_MYNA2_e, drawPlumTag);
searchActorForCallback(fpcNm_MYNA2_e, drawPlumSearch);
}
if (settings.twilightGates) {
searchActorForCallback(fpcNm_Tag_TWGate_e, drawTWGate);
}
if (settings.paths) {
s_pathColorIndex = 0;
drawStagePaths();
drawCurrentRoomPaths();
}
if (settings.checkpoints) {
searchActorForCallback(fpcNm_Tag_ChgRestart_e, drawCheckpointTag);
}
if (settings.transformDists) {
searchActorForCallback(-1, drawTransformDists);
}
if (settings.attentionDists) {
searchActorForCallback(-1, drawAttentionDists);
}
if (settings.purpleMistAvoid) {
searchActorForCallback(fpcNm_KYTAG08_e, drawPurpleMistAvoid);
}
if (settings.leevers) {
searchActorForCallback(fpcNm_E_RB_e, drawLeeverData);
}
}
} // namespace TriggerView
} // namespace dusk
+7
View File
@@ -0,0 +1,7 @@
#pragma once
namespace dusk {
namespace TriggerView {
void execute();
}
}
+10
View File
@@ -50,6 +50,16 @@ void reset() {
s_simTickActive = false; s_simTickActive = false;
} }
void set_sim_rate(float hz) {
const float maximumHz = aurora::time::kMaximumTimeScale / kSimPeriod;
aurora::time::set_scale(std::clamp(hz, 1.0f, maximumHz) * kSimPeriod);
reset();
}
float get_sim_rate() {
return aurora::time::scale() / kSimPeriod;
}
const FrameTiming& advance() { const FrameTiming& advance() {
const auto nativeNow = native_clock::now(); const auto nativeNow = native_clock::now();
const auto gameNow = game_clock::now(); const auto gameNow = game_clock::now();
+4
View File
@@ -28,4 +28,8 @@ float sample_interpolation_step();
float consume_interval(const void* consumer); float consume_interval(const void* consumer);
// Sets the effective simulation rate through the game clock time scale.
void set_sim_rate(float hz);
float get_sim_rate();
} // namespace dusk::game_clock } // namespace dusk::game_clock
+136
View File
@@ -0,0 +1,136 @@
#include "dusk/game_combos.h"
#include "SSystem/SComponent/c_API_controller_pad.h"
#include "SSystem/SComponent/c_xyz.h"
#include "d/actor/d_a_alink.h"
#include "d/d_com_inf_game.h"
#include "dusk/settings.h"
#include "m_Do/m_Do_controller_pad.h"
namespace dusk {
namespace {
cXyz s_savedTeleportPos{};
s16 s_savedTeleportAngle = 0;
bool s_hasTeleportPos = false;
static daAlink_c* getPlayer() {
return (daAlink_c*)dComIfGp_getPlayer(0);
}
static void consumeButtons(u32 mask) {
mDoCPd_c::getCpadInfo(PAD_1).mPressedButtonFlags &= ~mask;
mDoCPd_c::getCpadInfo(PAD_1).mButtonFlags &= ~mask;
}
// Table: holdMask, trigMask, strict, condition, action, consumeMask, exclusive
static const GameCombo kCombos[] = {
// Move Link (L+R+Y), pass-through, non-exclusive
{
PAD_TRIGGER_R | PAD_TRIGGER_L,
PAD_BUTTON_Y,
false,
[] { return (bool)getSettings().game.enableMoveLinkCombo; },
[] { getTransientSettings().moveLinkActive = !getTransientSettings().moveLinkActive; },
0,
false,
},
// Quick Transform (R+Y, strictly only R held)
{
PAD_TRIGGER_R,
PAD_BUTTON_Y,
true,
[] { return getPlayer() != nullptr; },
[] { getPlayer()->handleQuickTransform(); },
PAD_BUTTON_Y,
false,
},
// Wolf Howl (R+X)
{
PAD_TRIGGER_R,
PAD_BUTTON_X,
false,
[] { return getPlayer() != nullptr; },
[] { getPlayer()->handleWolfHowl(); },
PAD_BUTTON_X,
false,
},
// Teleport save (R+D-pad Up), consumes D-pad Up, exclusive
{
PAD_TRIGGER_R,
PAD_BUTTON_UP,
false,
[] { return getSettings().game.enableTeleportCombo && getPlayer() != nullptr; },
[] {
auto* p = getPlayer();
s_savedTeleportPos = p->current.pos;
s_savedTeleportAngle = p->shape_angle.y;
s_hasTeleportPos = true;
},
PAD_BUTTON_UP,
true,
},
// Teleport load (R+D-pad Down), consumes D-pad Down, exclusive
{
PAD_TRIGGER_R,
PAD_BUTTON_DOWN,
false,
[] {
return getSettings().game.enableTeleportCombo && s_hasTeleportPos &&
getPlayer() != nullptr;
},
[] {
auto* p = getPlayer();
p->current.pos = s_savedTeleportPos;
p->shape_angle.y = s_savedTeleportAngle;
p->mNormalSpeed = 0.0f;
},
PAD_BUTTON_DOWN,
true,
},
// Moon Jump (R+A, hold), continuous, pass-through, non-exclusive
{
PAD_TRIGGER_R | PAD_BUTTON_A,
0,
false,
[] { return getSettings().game.moonJump && getPlayer() != nullptr; },
[] { getPlayer()->speed.y = 56.0f; },
0,
false,
},
};
} // namespace
void processGameCombos() {
if (!getSettings().game.enableMoveLinkCombo) {
getTransientSettings().moveLinkActive = false;
}
const u32 held = mDoCPd_c::getHold(PAD_1);
const u32 trig = mDoCPd_c::getTrig(PAD_1);
for (const auto& combo : kCombos) {
if ((held & combo.holdMask) != combo.holdMask) {
continue;
}
if (combo.strict && (held & ~combo.trigMask) != combo.holdMask) {
continue;
}
if (combo.trigMask != 0 && !(trig & combo.trigMask)) {
continue;
}
if (combo.condition != nullptr && !combo.condition()) {
continue;
}
combo.action();
if (combo.consumeMask != 0) {
consumeButtons(combo.consumeMask);
}
if (combo.exclusive) {
return;
}
}
}
} // namespace dusk
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <dolphin/types.h>
namespace dusk {
struct GameCombo {
using ConditionFn = bool(*)();
using ActionFn = void(*)();
u32 holdMask; // all of these must be held (getHold)
u32 trigMask; // at least one must be newly triggered (getTrig); 0 = fires every frame while holdMask is held
bool strict; // if true: (held & ~trigMask) must equal holdMask exactly, no extra buttons held
ConditionFn condition; // extra game-state guard; nullptr = no extra check
ActionFn action; // runs when the combo is fired
u32 consumeMask; // buttons to clear after firing; 0 = pass-through
bool exclusive; // stop evaluating future combos if this fires
};
void processGameCombos();
} // namespace dusk
-7
View File
@@ -264,13 +264,6 @@ namespace dusk {
} }
previousTurboActive = turboActive; previousTurboActive = turboActive;
previousSlowActive = slowDown; previousSlowActive = slowDown;
if (frame_interp::get_ui_tick_pending() && mDoMain::developmentMode == 1 && (mDoCPd_c::getHold(PAD_1) & (PAD_TRIGGER_R | PAD_TRIGGER_L)) == (PAD_TRIGGER_R | PAD_TRIGGER_L) && mDoCPd_c::getTrigY(PAD_1)) {
getTransientSettings().moveLinkActive = !getTransientSettings().moveLinkActive;
}
if (mDoMain::developmentMode != 1) {
getTransientSettings().moveLinkActive = false;
}
} }
void ImGuiConsole::PreDraw() { void ImGuiConsole::PreDraw() {
+20
View File
@@ -89,6 +89,26 @@ namespace dusk {
ImGui::EndMenu(); ImGui::EndMenu();
} }
auto& triggerView = getTransientSettings().triggerView;
if (ImGui::BeginMenu("Trigger View")) {
ImGui::Checkbox("Load Zones", &triggerView.loadZones);
ImGui::Checkbox("Event Areas", &triggerView.eventAreas);
ImGui::Checkbox("Event Tags", &triggerView.eventTags);
ImGui::Checkbox("Switch Areas", &triggerView.switchAreas);
ImGui::Checkbox("Midna Stops", &triggerView.midnaStops);
ImGui::Checkbox("Twilight Gates", &triggerView.twilightGates);
ImGui::Checkbox("Checkpoints", &triggerView.checkpoints);
ImGui::Checkbox("Paths", &triggerView.paths);
ImGui::Separator();
ImGui::Checkbox("Transform Distances", &triggerView.transformDists);
ImGui::Checkbox("Attention Distances", &triggerView.attentionDists);
ImGui::Checkbox("Purple Mist Avoid", &triggerView.purpleMistAvoid);
ImGui::Checkbox("Leever Ranges", &triggerView.leevers);
ImGui::Separator();
ImGui::SliderFloat("Opacity##triggers", &triggerView.opacity, 0.0f, 100.0f);
ImGui::EndMenu();
}
if (!dusk::IsGameLaunched) { if (!dusk::IsGameLaunched) {
ImGui::BeginDisabled(); ImGui::BeginDisabled();
} }
+1 -1
View File
@@ -76,7 +76,7 @@ namespace dusk {
{ dItemNo_WEAR_KOKIRI_e, {"Hero's Clothes"} }, { dItemNo_WEAR_KOKIRI_e, {"Hero's Clothes"} },
{ dItemNo_ARMOR_e, {"Magic Armor"} }, { dItemNo_ARMOR_e, {"Magic Armor"} },
{ dItemNo_WEAR_ZORA_e, {"Zora Armor"} }, { dItemNo_WEAR_ZORA_e, {"Zora Armor"} },
{ dItemNo_SHADOW_CRYSTAL_e, {"Shadow Crystal"} }, { dItemNo_MAGIC_LV1_e, {"Magic Level 1"} },
{ dItemNo_DUNGEON_EXIT_2_e, {"Ooccoo Sr.", ITEMTYPE_EQUIP_e} }, { dItemNo_DUNGEON_EXIT_2_e, {"Ooccoo Sr.", ITEMTYPE_EQUIP_e} },
{ dItemNo_WALLET_LV1_e, {"Wallet"} }, { dItemNo_WALLET_LV1_e, {"Wallet"} },
{ dItemNo_WALLET_LV2_e, {"Big Wallet"} }, { dItemNo_WALLET_LV2_e, {"Big Wallet"} },
+6 -67
View File
@@ -1,31 +1,21 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <unordered_map>
class fopAc_ac_c; class fopAc_ac_c;
namespace dusk::mods { namespace dusk::mods {
enum class ItemGiveMode : uint8_t {
Demo,
Silent,
ForcedDemo,
};
struct ItemCheckResult {
uint32_t tag = 0;
uint8_t itemNo = 0;
};
uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver); uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver);
ItemCheckResult item_check_commit(const char* name, uint8_t itemNo, fopAc_ac_c* giver); uint8_t item_check_tagged(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver);
ItemCheckResult item_check_commit(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver);
uint8_t item_check_chest(uint8_t boxNo, uint8_t itemNo, fopAc_ac_c* chest); uint8_t item_check_chest(uint8_t boxNo, uint8_t itemNo, fopAc_ac_c* chest);
uint8_t item_check_boss(uint8_t itemNo, fopAc_ac_c* boss); uint8_t item_check_boss(uint8_t itemNo, fopAc_ac_c* boss);
uint8_t item_check_freestanding(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* item); uint8_t item_check_freestanding(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* item);
uint8_t item_check_poe(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* poe);
uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver); uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver);
uint8_t item_check_bug(uint8_t insectId, uint8_t itemNo, fopAc_ac_c* agitha);
uint8_t item_check_sky_character(uint8_t itemNo, fopAc_ac_c* statue);
uint32_t item_give_tag(const char* name); uint32_t item_give_tag(const char* name);
uint32_t item_give_tag_chest(uint8_t boxNo); uint32_t item_give_tag_chest(uint8_t boxNo);
@@ -36,63 +26,12 @@ uint32_t item_give_tag_shop(uint8_t itemNo);
uint32_t item_give_tag_bug(uint8_t insectId); uint32_t item_give_tag_bug(uint8_t insectId);
uint32_t item_give_tag_sky_character(); uint32_t item_give_tag_sky_character();
void item_check_enqueue_deferred(const char* name, uint8_t itemNo); void item_check_enqueue(const char* name, uint8_t itemNo);
bool item_check_enqueue(ItemCheckResult check, ItemGiveMode mode); void item_check_enqueue_poe(uint8_t bitNo, uint8_t itemNo);
void item_check_complete(ItemCheckResult check, fopAc_ac_c* giver);
void item_check_cancel(uint32_t giveTag);
void item_check_clear_committed();
void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver); void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver);
uint32_t item_check_message(uint16_t group, uint32_t messageId);
bool item_give_queue_dispatching(); bool item_give_queue_dispatching();
uint32_t item_give_queue_take_tag(); uint32_t item_give_queue_take_tag();
namespace detail {
struct CommittedCheck {
uint8_t vanillaItem = 0;
uint8_t resolvedItem = 0;
bool queued = false;
};
class ItemCommitStore {
public:
CommittedCheck* find(uint32_t giveTag) {
const auto it = mChecks.find(giveTag);
return it != mChecks.end() ? &it->second : nullptr;
}
const CommittedCheck* find(uint32_t giveTag) const {
const auto it = mChecks.find(giveTag);
return it != mChecks.end() ? &it->second : nullptr;
}
template <typename ResolveFn>
CommittedCheck& commit(uint32_t giveTag, uint8_t vanillaItem, ResolveFn&& resolve) {
const auto existing = mChecks.find(giveTag);
if (existing != mChecks.end()) {
return existing->second;
}
const uint8_t resolvedItem = std::forward<ResolveFn>(resolve)();
return mChecks
.emplace(
giveTag, CommittedCheck{.vanillaItem = vanillaItem, .resolvedItem = resolvedItem})
.first->second;
}
bool contains(uint32_t giveTag) const { return mChecks.contains(giveTag); }
void erase(uint32_t giveTag) { mChecks.erase(giveTag); }
void clear() { mChecks.clear(); }
bool empty() const { return mChecks.empty(); }
private:
std::unordered_map<uint32_t, CommittedCheck> mChecks;
};
} // namespace detail
} // namespace dusk::mods } // namespace dusk::mods
+29 -144
View File
@@ -6,14 +6,9 @@
#include "aurora/lib/logging.hpp" #include "aurora/lib/logging.hpp"
#include "d/d_com_inf_game.h" #include "d/d_com_inf_game.h"
#include "d/d_item_data.h"
#include "mods/items.h"
#include <fmt/format.h> #include <fmt/format.h>
#include <algorithm>
#include <array>
#include <cstring>
#include <exception> #include <exception>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@@ -50,78 +45,6 @@ struct PendingResolve {
void* userData = nullptr; void* userData = nullptr;
}; };
struct MessageCheck {
uint16_t group = 0;
uint16_t messageId = 0;
const char* name = nullptr;
uint8_t vanillaItem = 0;
bool enqueueAtDisplay = true;
};
constexpr std::array kMessageChecks{
MessageCheck{
.group = 0,
.messageId = 1822,
.name = ITEM_CHECK_FISHING_BOTTLE,
.vanillaItem = dItemNo_EMPTY_BOTTLE_e,
},
MessageCheck{
.group = 7,
.messageId = 7564,
.name = ITEM_CHECK_FISHING_HEART_PIECE,
.vanillaItem = dItemNo_KAKERA_HEART_e,
},
MessageCheck{
.group = 7,
.messageId = 7578,
.name = ITEM_CHECK_FISHING_HEART_PIECE,
.vanillaItem = dItemNo_KAKERA_HEART_e,
},
MessageCheck{
.group = 5,
.messageId = 5001,
.name = ITEM_CHECK_DUNGEON_REWARD_FOREST,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 6011,
.name = ITEM_CHECK_DUNGEON_REWARD_GORON,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 7001,
.name = ITEM_CHECK_DUNGEON_REWARD_LAKEBED,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 9301,
.name = ITEM_CHECK_DUNGEON_REWARD_SNOWPEAK,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 9401,
.name = ITEM_CHECK_DUNGEON_REWARD_TIME,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 11001,
.name = ITEM_CHECK_DUNGEON_REWARD_CITY,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 0,
.messageId = 233,
.name = ITEM_CHECK_ILIA_MEMORY,
.vanillaItem = dItemNo_HORSE_FLUTE_e,
.enqueueAtDisplay = false,
},
};
std::unordered_map<LoadedMod*, ModItemChecks> s_modChecks; std::unordered_map<LoadedMod*, ModItemChecks> s_modChecks;
std::unordered_set<std::string> s_warnedCollisions; std::unordered_set<std::string> s_warnedCollisions;
ItemCheckHandle s_nextCheckHandle = 1; ItemCheckHandle s_nextCheckHandle = 1;
@@ -156,28 +79,7 @@ std::string bug_check_name(uint8_t insectId) {
} }
std::string sky_character_check_name() { std::string sky_character_check_name() {
return fmt::format("sky:{}:{}", current_stage_name(), dStage_roomControl_c::getStayNo()); return fmt::format("skychar:{}:{}", current_stage_name(), dStage_roomControl_c::getStayNo());
}
bool ilia_memory_context() {
return std::strcmp(current_stage_name(), "R_SP109") == 0 &&
dComIfGp_roomControl_getStayNo() == 0 && dComIfG_play_c::getLayerNo(0) == 9;
}
uint16_t item_get_message(uint8_t itemNo) {
if (itemNo == dItemNo_KAKERA_HEART_e) {
constexpr std::array<uint16_t, 5> heartPieceMessages{0x86, 0x9C, 0x9D, 0x9E, 0x9F};
return heartPieceMessages[dComIfGs_getMaxLife() % heartPieceMessages.size()];
}
return static_cast<uint16_t>(itemNo + 0x65);
}
void invalidate_committed_check(const std::string& name) {
if (name.empty()) {
item_check_clear_committed();
} else {
item_check_cancel(item_give_tag(name.c_str()));
}
} }
} // namespace } // namespace
@@ -274,6 +176,14 @@ uint8_t item_check_freestanding(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* item)
return item_check(name.c_str(), itemNo, item); return item_check(name.c_str(), itemNo, item);
} }
uint8_t item_check_poe(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* poe) {
if (s_modChecks.empty()) {
return itemNo;
}
const auto name = poe_check_name(bitNo);
return item_check(name.c_str(), itemNo, poe);
}
uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver) { uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver) {
if (s_modChecks.empty()) { if (s_modChecks.empty()) {
return itemNo; return itemNo;
@@ -282,24 +192,20 @@ uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver) {
return item_check(name.c_str(), itemNo, giver); return item_check(name.c_str(), itemNo, giver);
} }
uint32_t item_check_message(uint16_t group, uint32_t messageId) { uint8_t item_check_bug(uint8_t insectId, uint8_t itemNo, fopAc_ac_c* agitha) {
for (const auto& check : kMessageChecks) { if (s_modChecks.empty()) {
if (check.group != group || check.messageId != messageId || return itemNo;
(check.group == 0 && check.messageId == 233 && !ilia_memory_context()))
{
continue;
}
const ItemCheckResult result = item_check_commit(check.name, check.vanillaItem, nullptr);
if (result.itemNo == check.vanillaItem || result.itemNo == dItemNo_NONE_e) {
return messageId;
}
if (check.enqueueAtDisplay) {
item_check_enqueue(result, ItemGiveMode::Silent);
}
return item_get_message(result.itemNo);
} }
return messageId; const auto name = bug_check_name(insectId);
return item_check(name.c_str(), itemNo, agitha);
}
uint8_t item_check_sky_character(uint8_t itemNo, fopAc_ac_c* statue) {
if (s_modChecks.empty()) {
return itemNo;
}
const auto name = sky_character_check_name();
return item_check(name.c_str(), itemNo, statue);
} }
uint32_t item_give_tag_chest(uint8_t boxNo) { uint32_t item_give_tag_chest(uint8_t boxNo) {
@@ -330,6 +236,10 @@ uint32_t item_give_tag_sky_character() {
return item_give_tag(sky_character_check_name().c_str()); return item_give_tag(sky_character_check_name().c_str());
} }
void item_check_enqueue_poe(uint8_t bitNo, uint8_t itemNo) {
item_check_enqueue(poe_check_name(bitNo).c_str(), itemNo);
}
namespace svc { namespace svc {
ModResult item_check_set_override(LoadedMod& mod, const char* name, uint8_t itemNo) { ModResult item_check_set_override(LoadedMod& mod, const char* name, uint8_t itemNo) {
@@ -337,12 +247,10 @@ ModResult item_check_set_override(LoadedMod& mod, const char* name, uint8_t item
for (auto& checkOverride : checks.overrides) { for (auto& checkOverride : checks.overrides) {
if (checkOverride.name == name) { if (checkOverride.name == name) {
checkOverride.itemNo = itemNo; checkOverride.itemNo = itemNo;
invalidate_committed_check(checkOverride.name);
return MOD_OK; return MOD_OK;
} }
} }
checks.overrides.push_back({.name = name, .itemNo = itemNo}); checks.overrides.push_back({.name = name, .itemNo = itemNo});
invalidate_committed_check(checks.overrides.back().name);
return MOD_OK; return MOD_OK;
} }
@@ -353,9 +261,6 @@ ModResult item_check_clear_override(LoadedMod& mod, const char* name) {
} }
const auto removed = std::erase_if(modIt->second.overrides, const auto removed = std::erase_if(modIt->second.overrides,
[&](const auto& checkOverride) { return checkOverride.name == name; }); [&](const auto& checkOverride) { return checkOverride.name == name; });
if (removed != 0) {
invalidate_committed_check(name);
}
return removed != 0 ? MOD_OK : MOD_INVALID_ARGUMENT; return removed != 0 ? MOD_OK : MOD_INVALID_ARGUMENT;
} }
@@ -367,7 +272,6 @@ ModResult item_check_add_resolver(LoadedMod& mod, const char* name, ItemCheckRes
resolver.fn = fn; resolver.fn = fn;
resolver.userData = userData; resolver.userData = userData;
outHandle = resolver.handle; outHandle = resolver.handle;
invalidate_committed_check(resolver.name);
return MOD_OK; return MOD_OK;
} }
@@ -376,31 +280,12 @@ ModResult item_check_remove_resolver(LoadedMod& mod, ItemCheckHandle handle) {
if (modIt == s_modChecks.end()) { if (modIt == s_modChecks.end()) {
return MOD_INVALID_ARGUMENT; return MOD_INVALID_ARGUMENT;
} }
const auto resolverIt = const auto removed = std::erase_if(
std::find_if(modIt->second.resolvers.begin(), modIt->second.resolvers.end(), modIt->second.resolvers, [&](const auto& resolver) { return resolver.handle == handle; });
[&](const auto& resolver) { return resolver.handle == handle; }); return removed != 0 ? MOD_OK : MOD_INVALID_ARGUMENT;
if (resolverIt == modIt->second.resolvers.end()) {
return MOD_INVALID_ARGUMENT;
}
const std::string name = resolverIt->name;
modIt->second.resolvers.erase(resolverIt);
invalidate_committed_check(name);
return MOD_OK;
} }
void item_checks_remove_mod(LoadedMod& mod) { void item_checks_remove_mod(LoadedMod& mod) {
if (const auto modIt = s_modChecks.find(&mod); modIt != s_modChecks.end()) {
for (const auto& resolver : modIt->second.resolvers) {
if (resolver.name.empty()) {
item_check_clear_committed();
break;
}
invalidate_committed_check(resolver.name);
}
for (const auto& checkOverride : modIt->second.overrides) {
invalidate_committed_check(checkOverride.name);
}
}
s_modChecks.erase(&mod); s_modChecks.erase(&mod);
s_warnedCollisions.clear(); s_warnedCollisions.clear();
} }
+37 -130
View File
@@ -17,7 +17,6 @@
#include <exception> #include <exception>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <utility>
#include <vector> #include <vector>
namespace dusk::mods { namespace dusk::mods {
@@ -29,8 +28,6 @@ aurora::Module Log{"dusk::mods::item_gives"};
std::deque<std::string> s_giveNames; std::deque<std::string> s_giveNames;
std::unordered_map<std::string, uint32_t> s_giveNameIds; std::unordered_map<std::string, uint32_t> s_giveNameIds;
detail::ItemCommitStore s_committedChecks;
const char* item_give_name(uint32_t tag) { const char* item_give_name(uint32_t tag) {
if (tag == 0 || tag > s_giveNames.size()) { if (tag == 0 || tag > s_giveNames.size()) {
return nullptr; return nullptr;
@@ -95,18 +92,7 @@ void notify_gives(const char* checkName, uint8_t itemNo, fopAc_ac_c* giver, Item
} }
} }
void complete_check(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver, ItemGiveOrigin origin) { constexpr size_t kGiveQueueLimit = 64;
if (const auto* committed = s_committedChecks.find(giveTag);
committed != nullptr && committed->resolvedItem != itemNo)
{
Log.error("committed check '{}' completed with item {:#x} instead of {:#x}",
item_give_name(giveTag) != nullptr ? item_give_name(giveTag) : "", itemNo,
committed->resolvedItem);
}
s_committedChecks.erase(giveTag);
notify_gives(item_give_name(giveTag), itemNo, giver, origin);
}
constexpr int kGiveMaxRetries = 5; constexpr int kGiveMaxRetries = 5;
struct QueuedGive { struct QueuedGive {
@@ -115,7 +101,6 @@ struct QueuedGive {
uint8_t itemNo = 0; uint8_t itemNo = 0;
bool silent = false; bool silent = false;
bool resolveAtDispatch = false; bool resolveAtDispatch = false;
bool forced = false;
}; };
std::deque<QueuedGive> s_giveQueue; std::deque<QueuedGive> s_giveQueue;
@@ -126,11 +111,6 @@ bool s_inFlight = false;
bool s_inFlightSpawned = false; bool s_inFlightSpawned = false;
bool s_dispatchingSilent = false; bool s_dispatchingSilent = false;
bool has_forced_give() {
return std::any_of(
s_giveQueue.begin(), s_giveQueue.end(), [](const QueuedGive& give) { return give.forced; });
}
bool safe_to_dispatch() { bool safe_to_dispatch() {
daAlink_c* link = daAlink_getAlinkActorClass(); daAlink_c* link = daAlink_getAlinkActorClass();
if (link == nullptr) { if (link == nullptr) {
@@ -162,13 +142,13 @@ bool safe_to_dispatch() {
bool resolve_queued_give(const QueuedGive& give, ItemGiveOrigin origin, uint8_t& outItem) { bool resolve_queued_give(const QueuedGive& give, ItemGiveOrigin origin, uint8_t& outItem) {
outItem = give.itemNo; outItem = give.itemNo;
if (give.resolveAtDispatch) { if (give.resolveAtDispatch) {
outItem = item_check_commit(give.tag, give.itemNo, nullptr).itemNo; outItem = item_check(item_give_name(give.tag), give.itemNo, nullptr);
} }
if (outItem != dItemNo_NONE_e) { if (outItem != dItemNo_NONE_e) {
return true; return true;
} }
complete_check(dItemNo_NONE_e, give.tag, nullptr, origin); notify_gives(item_give_name(give.tag), dItemNo_NONE_e, nullptr, origin);
return false; return false;
} }
@@ -191,40 +171,11 @@ void dispatch_demo_give() {
daAlink_c* link = daAlink_getAlinkActorClass(); daAlink_c* link = daAlink_getAlinkActorClass();
dComIfGp_getEvent()->setGtItm(s_inFlightItem); dComIfGp_getEvent()->setGtItm(s_inFlightItem);
link->mProcID = daAlink_c::PROC_GET_ITEM; link->procCoGetItemInit();
const s16 eventIndex = dComIfGp_getEventManager().getEventIdx(link, "DEFAULT_GETITEM", 0xFF); const s16 eventIndex = dComIfGp_getEventManager().getEventIdx(link, "DEFAULT_GETITEM", 0xFF);
fopAcM_orderChangeEventId(link, eventIndex, 1, 0xFFFF); fopAcM_orderChangeEventId(link, eventIndex, 1, 0xFFFF);
} }
void dispatch_next_give() {
while (!s_giveQueue.empty()) {
while (!s_giveQueue.empty() && s_giveQueue.front().silent) {
const QueuedGive give = s_giveQueue.front();
s_giveQueue.pop_front();
dispatch_silent_give(give);
}
if (s_giveQueue.empty()) {
return;
}
const QueuedGive give = s_giveQueue.front();
s_giveQueue.pop_front();
uint8_t itemNo = 0;
if (!resolve_queued_give(give, ITEM_GIVE_ORIGIN_QUEUE, itemNo)) {
continue;
}
s_inFlightGive = give;
s_inFlightItem = itemNo;
s_inFlightRetries = 0;
s_inFlight = true;
s_inFlightSpawned = false;
dispatch_demo_give();
return;
}
}
} // namespace } // namespace
uint32_t item_give_tag(const char* name) { uint32_t item_give_tag(const char* name) {
@@ -241,30 +192,16 @@ uint32_t item_give_tag(const char* name) {
return tag; return tag;
} }
ItemCheckResult item_check_commit(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver) { uint8_t item_check_tagged(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver) {
const char* name = item_give_name(giveTag); const char* name = item_give_name(giveTag);
if (name == nullptr) { return name != nullptr ? item_check(name, itemNo, giver) : itemNo;
return {.tag = giveTag, .itemNo = itemNo};
}
if (const auto* committed = s_committedChecks.find(giveTag); committed != nullptr) {
if (committed->vanillaItem != itemNo) {
Log.error("committed check '{}' changed vanilla item from {:#x} to {:#x}", name,
committed->vanillaItem, itemNo);
}
return {.tag = giveTag, .itemNo = committed->resolvedItem};
}
const auto& committed =
s_committedChecks.commit(giveTag, itemNo, [&] { return item_check(name, itemNo, giver); });
return {.tag = giveTag, .itemNo = committed.resolvedItem};
} }
ItemCheckResult item_check_commit(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { void item_check_enqueue(const char* name, uint8_t itemNo) {
return item_check_commit(item_give_tag(name), itemNo, giver); if (s_giveQueue.size() >= kGiveQueueLimit) {
} Log.warn("item give queue is full; dropping check '{}'", name != nullptr ? name : "");
return;
void item_check_enqueue_deferred(const char* name, uint8_t itemNo) { }
s_giveQueue.push_back({ s_giveQueue.push_back({
.tag = item_give_tag(name), .tag = item_give_tag(name),
.itemNo = itemNo, .itemNo = itemNo,
@@ -272,53 +209,6 @@ void item_check_enqueue_deferred(const char* name, uint8_t itemNo) {
}); });
} }
bool item_check_enqueue(ItemCheckResult check, ItemGiveMode mode) {
auto* committed = s_committedChecks.find(check.tag);
if (committed == nullptr) {
Log.error("cannot enqueue uncommitted check '{}'",
item_give_name(check.tag) != nullptr ? item_give_name(check.tag) : "");
return false;
}
if (committed->resolvedItem != check.itemNo) {
Log.error("committed check '{}' changed item from {:#x} to {:#x}",
item_give_name(check.tag) != nullptr ? item_give_name(check.tag) : "", check.itemNo,
committed->resolvedItem);
return false;
}
if (committed->queued) {
return false;
}
committed->queued = true;
s_giveQueue.push_back({
.tag = check.tag,
.itemNo = committed->resolvedItem,
.silent = mode == ItemGiveMode::Silent,
.forced = mode == ItemGiveMode::ForcedDemo,
});
if (mode == ItemGiveMode::ForcedDemo && !s_inFlight) {
dispatch_next_give();
}
return true;
}
void item_check_complete(ItemCheckResult check, fopAc_ac_c* giver) {
complete_check(check.itemNo, check.tag, giver, ITEM_GIVE_ORIGIN_GAME);
}
void item_check_cancel(uint32_t giveTag) {
s_committedChecks.erase(giveTag);
std::erase_if(s_giveQueue,
[&](const QueuedGive& give) { return give.owner == nullptr && give.tag == giveTag; });
}
void item_check_clear_committed() {
std::erase_if(s_giveQueue, [&](const QueuedGive& give) {
return give.owner == nullptr && s_committedChecks.contains(give.tag);
});
s_committedChecks.clear();
}
void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver) { void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver) {
ItemGiveOrigin origin = ITEM_GIVE_ORIGIN_GAME; ItemGiveOrigin origin = ITEM_GIVE_ORIGIN_GAME;
if (s_dispatchingSilent) { if (s_dispatchingSilent) {
@@ -328,7 +218,7 @@ void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver) {
s_inFlight = false; s_inFlight = false;
s_inFlightSpawned = false; s_inFlightSpawned = false;
} }
complete_check(itemNo, giveTag, giver, origin); notify_gives(item_give_name(giveTag), itemNo, giver, origin);
} }
bool item_give_queue_dispatching() { bool item_give_queue_dispatching() {
@@ -346,21 +236,17 @@ uint32_t item_give_queue_take_tag() {
namespace svc { namespace svc {
void item_gives_tick() { void item_gives_tick() {
if (!s_inFlight && s_giveQueue.empty()) { if ((!s_inFlight && s_giveQueue.empty()) || !safe_to_dispatch()) {
return; return;
} }
if (s_inFlight) { if (s_inFlight) {
if (!safe_to_dispatch()) {
return;
}
if (++s_inFlightRetries > kGiveMaxRetries) { if (++s_inFlightRetries > kGiveMaxRetries) {
Log.error("item {:#x} for '{}' did not complete after {} attempts; dropping it", Log.error("item {:#x} for '{}' did not complete after {} attempts; dropping it",
s_inFlightItem, s_inFlightItem,
item_give_name(s_inFlightGive.tag) != nullptr ? item_give_name(s_inFlightGive.tag) : item_give_name(s_inFlightGive.tag) != nullptr ? item_give_name(s_inFlightGive.tag) :
"", "",
kGiveMaxRetries); kGiveMaxRetries);
item_check_cancel(s_inFlightGive.tag);
s_inFlight = false; s_inFlight = false;
s_inFlightSpawned = false; s_inFlightSpawned = false;
return; return;
@@ -370,10 +256,29 @@ void item_gives_tick() {
return; return;
} }
if (!has_forced_give() && !safe_to_dispatch()) { while (!s_giveQueue.empty() && s_giveQueue.front().silent) {
const QueuedGive give = s_giveQueue.front();
s_giveQueue.pop_front();
dispatch_silent_give(give);
}
if (s_giveQueue.empty()) {
return; return;
} }
dispatch_next_give();
const QueuedGive give = s_giveQueue.front();
uint8_t itemNo = 0;
if (!resolve_queued_give(give, ITEM_GIVE_ORIGIN_QUEUE, itemNo)) {
s_giveQueue.pop_front();
return;
}
s_giveQueue.pop_front();
s_inFlightGive = give;
s_inFlightItem = itemNo;
s_inFlightRetries = 0;
s_inFlight = true;
s_inFlightSpawned = false;
dispatch_demo_give();
} }
void item_gives_clear() { void item_gives_clear() {
@@ -384,10 +289,12 @@ void item_gives_clear() {
s_giveQueue.clear(); s_giveQueue.clear();
s_inFlight = false; s_inFlight = false;
s_inFlightSpawned = false; s_inFlightSpawned = false;
s_committedChecks.clear();
} }
ModResult item_give_enqueue(LoadedMod& mod, const char* checkName, uint8_t itemNo, uint32_t flags) { ModResult item_give_enqueue(LoadedMod& mod, const char* checkName, uint8_t itemNo, uint32_t flags) {
if (s_giveQueue.size() >= kGiveQueueLimit) {
return MOD_UNAVAILABLE;
}
s_giveQueue.push_back({ s_giveQueue.push_back({
.owner = &mod, .owner = &mod,
.tag = item_give_tag(checkName), .tag = item_give_tag(checkName),
+35 -135
View File
@@ -20,13 +20,11 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <climits> #include <climits>
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <string_view>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -250,16 +248,18 @@ void wire_callback_binding(
if (!slot_live(guardHandle)) { if (!slot_live(guardHandle)) {
return value; return value;
} }
guarded_call( guarded_call(*modPtr, "control getter", [&] {
*modPtr, "control getter", [&] { get(modPtr->context.get(), userData, &value); }); get(modPtr->context.get(), userData, &value);
});
return value; return value;
}; };
const auto setValue = [modPtr, set, userData, guardHandle](const UiControlValue& value) { const auto setValue = [modPtr, set, userData, guardHandle](const UiControlValue& value) {
if (!slot_live(guardHandle)) { if (!slot_live(guardHandle)) {
return; return;
} }
guarded_call( guarded_call(*modPtr, "control setter", [&] {
*modPtr, "control setter", [&] { set(modPtr->context.get(), userData, &value); }); set(modPtr->context.get(), userData, &value);
});
}; };
switch (desc.kind) { switch (desc.kind) {
case UI_CONTROL_TOGGLE: case UI_CONTROL_TOGGLE:
@@ -280,7 +280,6 @@ void wire_callback_binding(
}; };
break; break;
case UI_CONTROL_STRING: case UI_CONTROL_STRING:
case UI_CONTROL_COLOR:
spec.getString = [getValue]() -> Rml::String { spec.getString = [getValue]() -> Rml::String {
const UiControlValue value = getValue(); const UiControlValue value = getValue();
return value.string_value != nullptr ? value.string_value : ""; return value.string_value != nullptr ? value.string_value : "";
@@ -359,8 +358,7 @@ bool wire_config_var_binding(LoadedMod& mod, const UiControlDesc& desc, ui::ModC
} }
return true; return true;
} }
case UI_CONTROL_STRING: case UI_CONTROL_STRING: {
case UI_CONTROL_COLOR: {
const auto find = [modPtr, varHandle] { const auto find = [modPtr, varHandle] {
return static_cast<ConfigVar<std::string>*>( return static_cast<ConfigVar<std::string>*>(
config_find_var(*modPtr, varHandle, CONFIG_VAR_STRING)); config_find_var(*modPtr, varHandle, CONFIG_VAR_STRING));
@@ -403,8 +401,9 @@ void on_mod_window_destroyed(uint64_t handle) {
const UiWindowClosedFn onClosed = released->value.onClosed; const UiWindowClosedFn onClosed = released->value.onClosed;
void* userData = released->value.onClosedUserData; void* userData = released->value.onClosedUserData;
if (mod != nullptr && onClosed != nullptr) { if (mod != nullptr && onClosed != nullptr) {
guarded_call(*mod, "window on_closed callback", guarded_call(*mod, "window on_closed callback", [&] {
[&] { onClosed(mod->context.get(), handle, userData); }); onClosed(mod->context.get(), handle, userData);
});
} }
} }
@@ -452,8 +451,9 @@ ui::ModalAction make_dialog_action(LoadedMod& mod, uint64_t handle, const UiDial
return; // already being torn down return; // already being torn down
} }
if (fn != nullptr) { if (fn != nullptr) {
guarded_call(*modPtr, "dialog action callback", guarded_call(*modPtr, "dialog action callback", [&] {
[&] { fn(modPtr->context.get(), handle, userData); }); fn(modPtr->context.get(), handle, userData);
});
} }
// The callback may have closed the dialog already // The callback may have closed the dialog already
if (!keepOpen && dialog_open(handle)) { if (!keepOpen && dialog_open(handle)) {
@@ -494,8 +494,9 @@ void ui_update_mods_panels(LoadedMod& mod) {
if (!mod.active || panel.update == nullptr) { if (!mod.active || panel.update == nullptr) {
return; return;
} }
invoke_mod_ui_callback(mod, "mod UI panel update", invoke_mod_ui_callback(mod, "mod UI panel update", [&](ModError* error) {
[&](ModError* error) { return panel.update(mod.context.get(), panel.userData, error); }); return panel.update(mod.context.get(), panel.userData, error);
});
} }
ModResult ui_pane_add_section(LoadedMod& mod, uint64_t pane, const char* title) { ModResult ui_pane_add_section(LoadedMod& mod, uint64_t pane, const char* title) {
@@ -567,16 +568,15 @@ ModResult ui_pane_add_control(
spec.isModified = wrap_predicate(mod, desc.is_modified, desc.user_data, pane); spec.isModified = wrap_predicate(mod, desc.is_modified, desc.user_data, pane);
switch (desc.kind) { switch (desc.kind) {
case UI_CONTROL_BUTTON: case UI_CONTROL_BUTTON:
case UI_CONTROL_GROUP: spec.kind = ui::ModControlSpec::Kind::Button;
spec.kind = desc.kind == UI_CONTROL_BUTTON ? ui::ModControlSpec::Kind::Button :
ui::ModControlSpec::Kind::Group;
spec.onPressed = [modPtr = &mod, fn = desc.on_pressed, userData = desc.user_data, spec.onPressed = [modPtr = &mod, fn = desc.on_pressed, userData = desc.user_data,
guardHandle = pane] { guardHandle = pane] {
if (!slot_live(guardHandle)) { if (!slot_live(guardHandle)) {
return; return;
} }
guarded_call(*modPtr, "control on_pressed callback", guarded_call(*modPtr, "control on_pressed callback", [&] {
[&] { fn(modPtr->context.get(), userData); }); fn(modPtr->context.get(), userData);
});
}; };
break; break;
case UI_CONTROL_TOGGLE: case UI_CONTROL_TOGGLE:
@@ -599,13 +599,6 @@ ModResult ui_pane_add_control(
spec.kind = ui::ModControlSpec::Kind::String; spec.kind = ui::ModControlSpec::Kind::String;
spec.maxLength = desc.max_length < 1 ? -1 : desc.max_length; spec.maxLength = desc.max_length < 1 ? -1 : desc.max_length;
break; break;
case UI_CONTROL_COLOR:
spec.kind = ui::ModControlSpec::Kind::Color;
spec.colorAlpha = desc.color_alpha;
for (size_t i = 0; i < desc.color_preset_count; ++i) {
spec.colorPresets.emplace_back(desc.color_presets[i]);
}
break;
case UI_CONTROL_SELECT: case UI_CONTROL_SELECT:
spec.kind = ui::ModControlSpec::Kind::Select; spec.kind = ui::ModControlSpec::Kind::Select;
if (slot->helpPane == nullptr) { if (slot->helpPane == nullptr) {
@@ -621,7 +614,7 @@ ModResult ui_pane_add_control(
return MOD_INVALID_ARGUMENT; return MOD_INVALID_ARGUMENT;
} }
if (desc.kind != UI_CONTROL_BUTTON && desc.kind != UI_CONTROL_GROUP) { if (desc.kind != UI_CONTROL_BUTTON) {
if (desc.binding == UI_BINDING_CONFIG_VAR) { if (desc.binding == UI_BINDING_CONFIG_VAR) {
if (!wire_config_var_binding(mod, desc, spec)) { if (!wire_config_var_binding(mod, desc, spec)) {
Log.error("[{}] pane_add_control: config var handle {:#x} is unknown or its type " Log.error("[{}] pane_add_control: config var handle {:#x} is unknown or its type "
@@ -648,39 +641,6 @@ ModResult ui_pane_add_control(
return MOD_OK; return MOD_OK;
} }
ModResult ui_pane_add_group(LoadedMod& mod, uint64_t groupPaneHandle, uint64_t targetPaneHandle,
const UiGroupDesc& desc, uint64_t* outElem) {
auto* groupSlot = resolve(mod, groupPaneHandle, UiSlotKind::Pane, "pane_add_group");
auto* targetSlot = resolve(mod, targetPaneHandle, UiSlotKind::Pane, "pane_add_group");
if (groupSlot == nullptr || targetSlot == nullptr) {
return MOD_INVALID_ARGUMENT;
}
if (groupSlot->helpPane != targetSlot->pane) {
Log.error("[{}] pane_add_group: panes are not a paired tab layout", mod.metadata.id);
return MOD_UNSUPPORTED;
}
auto* groupPane = groupSlot->pane;
auto* targetPane = targetSlot->pane;
auto& button = groupPane->add_group_button(ui::GroupButton::Props{.text = desc.label});
groupPane->register_control(button, *targetPane,
[modPtr = &mod, groupPaneHandle, targetPaneHandle, build = desc.build,
userData = desc.user_data](ui::Pane&) {
if (!slot_live(groupPaneHandle) || !slot_live(targetPaneHandle) || !modPtr->active) {
return;
}
invoke_mod_ui_callback(*modPtr, "mod UI group build", [&](ModError* error) {
return build(modPtr->context.get(), targetPaneHandle, userData, error);
});
});
if (outElem != nullptr) {
auto& elemSlot = alloc_slot(mod, UiSlotKind::Control, *outElem);
track_element(*outElem, elemSlot, *button.root());
}
return MOD_OK;
}
ModResult ui_elem_set_text(LoadedMod& mod, uint64_t elem, const char* text) { ModResult ui_elem_set_text(LoadedMod& mod, uint64_t elem, const char* text) {
auto* slot = resolve(mod, elem, UiSlotKind::Text, "elem_set_text"); auto* slot = resolve(mod, elem, UiSlotKind::Text, "elem_set_text");
if (slot == nullptr) { if (slot == nullptr) {
@@ -808,8 +768,7 @@ ModResult ui_window_close(LoadedMod& mod, uint64_t handle) {
return MOD_OK; return MOD_OK;
} }
ModResult ui_dialog_push( ModResult ui_dialog_push(LoadedMod& mod, const UiDialogDesc& desc, uint64_t& outHandle) {
LoadedMod& mod, const UiDialogDesc& desc, UiPaneBuildFn build, uint64_t& outHandle) {
outHandle = 0; outHandle = 0;
if (!aurora::rmlui::is_initialized()) { if (!aurora::rmlui::is_initialized()) {
return MOD_UNAVAILABLE; return MOD_UNAVAILABLE;
@@ -839,8 +798,9 @@ ModResult ui_dialog_push(
return; // already being torn down return; // already being torn down
} }
if (fn != nullptr) { if (fn != nullptr) {
guarded_call(*modPtr, "dialog on_dismiss callback", guarded_call(*modPtr, "dialog on_dismiss callback", [&] {
[&] { fn(modPtr->context.get(), handle, userData); }); fn(modPtr->context.get(), handle, userData);
});
} }
if (dialog_open(handle)) { if (dialog_open(handle)) {
static_cast<ModDialog&>(modal).close(); static_cast<ModDialog&>(modal).close();
@@ -850,18 +810,8 @@ ModResult ui_dialog_push(
props.actions.push_back(make_dialog_action(mod, handle, desc.actions[i])); props.actions.push_back(make_dialog_action(mod, handle, desc.actions[i]));
} }
auto dialog = std::make_unique<ModDialog>( auto dialog =
std::move(props), [handle] { on_mod_dialog_destroyed(handle); }); std::make_unique<ModDialog>(std::move(props), [handle] { on_mod_dialog_destroyed(handle); });
if (build != nullptr) {
auto& pane = dialog->content_pane();
const uint64_t paneHandle = wrap_pane(mod, pane, nullptr);
invoke_mod_ui_callback(mod, "mod UI dialog build", [&](ModError* error) {
return build(mod.context.get(), paneHandle, desc.user_data, error);
});
if (!mod.active) {
return MOD_ERROR;
}
}
if (auto* slot = slot_from_handle(handle)) { if (auto* slot = slot_from_handle(handle)) {
slot->document = dialog.get(); slot->document = dialog.get();
} }
@@ -965,8 +915,9 @@ std::vector<ModMenuTabEntry> ui_mod_menu_tabs() {
if (!slot_live(handle) || !modPtr->active) { if (!slot_live(handle) || !modPtr->active) {
return; // registered by a since-unloaded mod image return; // registered by a since-unloaded mod image
} }
guarded_call(*modPtr, "menu tab on_selected callback", guarded_call(*modPtr, "menu tab on_selected callback", [&] {
[&] { fn(modPtr->context.get(), userData); }); fn(modPtr->context.get(), userData);
});
}}); }});
} }
} }
@@ -1087,8 +1038,7 @@ void ui_remove_mod(LoadedMod& mod) {
} }
} }
ModResult ui_get_clipboard_text( ModResult ui_get_clipboard_text(LoadedMod& mod, char* buffer, size_t bufferSize, size_t* outLength) {
LoadedMod& mod, char* buffer, size_t bufferSize, size_t* outLength) {
if (outLength != nullptr) { if (outLength != nullptr) {
*outLength = 0; *outLength = 0;
} }
@@ -1132,38 +1082,18 @@ namespace {
// Validation of the tagged control descriptor: required fields per kind/binding. Value // Validation of the tagged control descriptor: required fields per kind/binding. Value
// translation and cvar wiring live in loader/ui.cpp. // translation and cvar wiring live in loader/ui.cpp.
bool valid_color_preset(const char* value, bool alpha) {
const std::string_view text{value};
if (text == "rainbow") {
return true;
}
if (text.size() != 6 && (!alpha || text.size() != 8)) {
return false;
}
return std::ranges::all_of(text, [](char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
});
}
bool valid_control_desc(const UiControlDesc& desc) { bool valid_control_desc(const UiControlDesc& desc) {
constexpr size_t kLegacyDescSize = offsetof(UiControlDesc, color_presets); if (desc.struct_size < sizeof(UiControlDesc) || desc.label == nullptr) {
if (desc.struct_size < kLegacyDescSize || desc.label == nullptr) {
return false; return false;
} }
switch (desc.kind) { switch (desc.kind) {
case UI_CONTROL_BUTTON: case UI_CONTROL_BUTTON:
case UI_CONTROL_GROUP:
return desc.on_pressed != nullptr; return desc.on_pressed != nullptr;
case UI_CONTROL_TOGGLE: case UI_CONTROL_TOGGLE:
case UI_CONTROL_NUMBER: case UI_CONTROL_NUMBER:
case UI_CONTROL_STRING: case UI_CONTROL_STRING:
case UI_CONTROL_SELECT: case UI_CONTROL_SELECT:
break; break;
case UI_CONTROL_COLOR:
if (desc.struct_size < sizeof(UiControlDesc)) {
return false;
}
break;
default: default:
return false; return false;
} }
@@ -1177,18 +1107,6 @@ bool valid_control_desc(const UiControlDesc& desc) {
} }
} }
} }
if (desc.kind == UI_CONTROL_COLOR && desc.color_preset_count != 0) {
if (desc.color_presets == nullptr) {
return false;
}
for (size_t i = 0; i < desc.color_preset_count; ++i) {
if (desc.color_presets[i] == nullptr ||
!valid_color_preset(desc.color_presets[i], desc.color_alpha))
{
return false;
}
}
}
switch (desc.binding) { switch (desc.binding) {
case UI_BINDING_CALLBACKS: case UI_BINDING_CALLBACKS:
return desc.get != nullptr && desc.set != nullptr; return desc.get != nullptr && desc.set != nullptr;
@@ -1265,20 +1183,6 @@ ModResult ui_pane_add_control(ModContext* context, UiElementHandle pane, const U
return ui_impl::ui_pane_add_control(*mod, pane, *desc, outElem); return ui_impl::ui_pane_add_control(*mod, pane, *desc, outElem);
} }
ModResult ui_pane_add_group(ModContext* context, UiElementHandle groupPane,
UiElementHandle targetPane, const UiGroupDesc* desc, UiElementHandle* outElem) {
if (outElem != nullptr) {
*outElem = 0;
}
auto* mod = mod_from_context(context);
if (mod == nullptr || groupPane == 0 || targetPane == 0 || desc == nullptr ||
desc->struct_size < sizeof(UiGroupDesc) || desc->label == nullptr || desc->build == nullptr)
{
return MOD_INVALID_ARGUMENT;
}
return ui_impl::ui_pane_add_group(*mod, groupPane, targetPane, *desc, outElem);
}
ModResult ui_elem_set_text(ModContext* context, UiElementHandle elem, const char* text) { ModResult ui_elem_set_text(ModContext* context, UiElementHandle elem, const char* text) {
auto* mod = mod_from_context(context); auto* mod = mod_from_context(context);
if (mod == nullptr || elem == 0 || text == nullptr) { if (mod == nullptr || elem == 0 || text == nullptr) {
@@ -1349,8 +1253,7 @@ ModResult ui_dialog_push(ModContext* context, const UiDialogDesc* desc, UiDialog
*outDialog = 0; *outDialog = 0;
} }
auto* mod = mod_from_context(context); auto* mod = mod_from_context(context);
constexpr size_t kLegacyDescSize = offsetof(UiDialogDesc, build); if (mod == nullptr || desc == nullptr || desc->struct_size < sizeof(UiDialogDesc) ||
if (mod == nullptr || desc == nullptr || desc->struct_size < kLegacyDescSize ||
desc->title == nullptr || desc->body_rml == nullptr || desc->actions == nullptr || desc->title == nullptr || desc->body_rml == nullptr || desc->actions == nullptr ||
desc->action_count == 0 || desc->variant > UI_DIALOG_DANGER) desc->action_count == 0 || desc->variant > UI_DIALOG_DANGER)
{ {
@@ -1361,9 +1264,8 @@ ModResult ui_dialog_push(ModContext* context, const UiDialogDesc* desc, UiDialog
return MOD_INVALID_ARGUMENT; return MOD_INVALID_ARGUMENT;
} }
} }
const UiPaneBuildFn build = desc->struct_size >= sizeof(UiDialogDesc) ? desc->build : nullptr;
uint64_t handle = 0; uint64_t handle = 0;
const auto result = ui_impl::ui_dialog_push(*mod, *desc, build, handle); const auto result = ui_impl::ui_dialog_push(*mod, *desc, handle);
if (result == MOD_OK && outDialog != nullptr) { if (result == MOD_OK && outDialog != nullptr) {
*outDialog = handle; *outDialog = handle;
} }
@@ -1504,8 +1406,7 @@ ModResult ui_dialog_add_action(
return ui_impl::ui_dialog_add_action(*mod, dialog, *action); return ui_impl::ui_dialog_add_action(*mod, dialog, *action);
} }
ModResult ui_get_clipboard_text( ModResult ui_get_clipboard_text(ModContext* ctx, char* buffer, size_t bufferSize, size_t* outLength) {
ModContext* ctx, char* buffer, size_t bufferSize, size_t* outLength) {
auto* mod = mod_from_context(ctx); auto* mod = mod_from_context(ctx);
if (mod == nullptr || (buffer == nullptr && bufferSize != 0)) { if (mod == nullptr || (buffer == nullptr && bufferSize != 0)) {
return MOD_INVALID_ARGUMENT; return MOD_INVALID_ARGUMENT;
@@ -1550,7 +1451,6 @@ constexpr UiService s_uiService{
.push_toast = ui_push_toast, .push_toast = ui_push_toast,
.get_clipboard_text = ui_get_clipboard_text, .get_clipboard_text = ui_get_clipboard_text,
.set_clipboard_text = ui_set_clipboard_text, .set_clipboard_text = ui_set_clipboard_text,
.pane_add_group = ui_pane_add_group,
}; };
} // namespace } // namespace
+19 -4
View File
@@ -1,7 +1,6 @@
#include "dusk/settings.h" #include "dusk/settings.h"
#include <aurora/aurora.h> #include <aurora/aurora.h>
#include "dusk/config.hpp" #include "dusk/config.hpp"
#include "dusk/ui/ui.hpp"
#include "dusk/game_mode.hpp" #include "dusk/game_mode.hpp"
namespace dusk { namespace dusk {
@@ -17,7 +16,6 @@ UserSettings g_userSettings = {
.rememberWindowSize {"video.rememberWindowSize", false}, .rememberWindowSize {"video.rememberWindowSize", false},
.lastWindowWidth {"video.lastWindowWidth", 0}, .lastWindowWidth {"video.lastWindowWidth", 0},
.lastWindowHeight {"video.lastWindowHeight", 0}, .lastWindowHeight {"video.lastWindowHeight", 0},
.uiScale {"video.uiScale", 100},
}, },
.audio = { .audio = {
@@ -160,6 +158,8 @@ UserSettings g_userSettings = {
.removeQuestMapMarkers {"game.removeQuestMapMarkers", false}, .removeQuestMapMarkers {"game.removeQuestMapMarkers", false},
.showInputViewer {"game.showInputViewer", false}, .showInputViewer {"game.showInputViewer", false},
.showInputViewerGyro {"game.showInputViewerGyro", false}, .showInputViewerGyro {"game.showInputViewerGyro", false},
.enableMoveLinkCombo {"game.enableMoveLinkCombo", false},
.enableTeleportCombo {"game.enableTeleportCombo", false},
.lastSelectedGameModeId {"game.lastSelectedGameModeId", gamemode::kVanillaGameModeId} .lastSelectedGameModeId {"game.lastSelectedGameModeId", gamemode::kVanillaGameModeId}
}, },
@@ -230,8 +230,6 @@ void registerSettings() {
Register(g_userSettings.video.rememberWindowSize); Register(g_userSettings.video.rememberWindowSize);
Register(g_userSettings.video.lastWindowWidth); Register(g_userSettings.video.lastWindowWidth);
Register(g_userSettings.video.lastWindowHeight); Register(g_userSettings.video.lastWindowHeight);
Register(g_userSettings.video.uiScale,
[](const int&, const int&) { dusk::ui::apply_scale(); });
// Audio // Audio
Register(g_userSettings.audio.masterVolume); Register(g_userSettings.audio.masterVolume);
@@ -312,6 +310,8 @@ void registerSettings() {
Register(g_userSettings.game.removeQuestMapMarkers); Register(g_userSettings.game.removeQuestMapMarkers);
Register(g_userSettings.game.showInputViewer); Register(g_userSettings.game.showInputViewer);
Register(g_userSettings.game.showInputViewerGyro); Register(g_userSettings.game.showInputViewerGyro);
Register(g_userSettings.game.enableMoveLinkCombo);
Register(g_userSettings.game.enableTeleportCombo);
Register(g_userSettings.game.lastSelectedGameModeId); Register(g_userSettings.game.lastSelectedGameModeId);
Register(g_userSettings.game.fastSpinner); Register(g_userSettings.game.fastSpinner);
Register(g_userSettings.game.infiniteHearts); Register(g_userSettings.game.infiniteHearts);
@@ -403,6 +403,21 @@ static TransientSettings g_transientSettings = {
.colliderViewOpacity = 50.0f, .colliderViewOpacity = 50.0f,
.drawRange = 100.0f, .drawRange = 100.0f,
}, },
.triggerView = {
.loadZones = false,
.eventAreas = false,
.switchAreas = false,
.eventTags = false,
.midnaStops = false,
.twilightGates = false,
.checkpoints = false,
.paths = false,
.transformDists = false,
.attentionDists = false,
.purpleMistAvoid = false,
.leevers = false,
.opacity = 75.0f,
},
.turboMode = false, .turboMode = false,
}; };

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