Compare commits

...

34 Commits

Author SHA1 Message Date
gymnast86 af17623e69 remove unneeded include and const qualifier 2026-08-14 23:49:56 -07:00
gymnast86 c01610dc37 fix includes 2026-08-14 23:20:46 -07:00
gymnast86 f4700c9636 manual cleanup 2026-08-14 23:05:00 -07:00
gymnast86 5d01c93839 Merge branch 'main' of https://github.com/TwilitRealm/dusk into basic-cosmetics-mod 2026-08-14 21:59:46 -07:00
gymnast86 0c546819f4 update descriptions 2026-08-14 21:59:34 -07:00
gymnast86 094f0cd78d use TextureService instead of overwriting model file data on load 2026-08-14 21:59:22 -07:00
gymnast86 c251aba39d fix default midna hair color 2026-08-14 21:58:02 -07:00
TakaRikka c722cb1be0 update aurora 2026-08-14 19:12:14 -07:00
Luke Street 0f5a248401 Time revamp (#2304)
* Don't Use OSCalendarTime for Speedrun Timing

- Shouldn't rely on OSTicksToCalendarTime since it will be changed to handle time zone conversion, and that doesn't make sense on elapsed time

* Use OSGetSystemTime Extension

- Fixes desyncing issues with save file time and a couple other odd instances, particularly on mobile platforms that suspend apps

* Time revamp

* Update aurora

* Split IGT/RTA calculations

* Shift-Turbo to slow down

---------

Co-authored-by: SuperDude88 <82904174+SuperDude88@users.noreply.github.com>
2026-08-14 00:07:54 -06:00
matthewdavidrichardanderson 045fbd9b46 request presentation sync for all captures (#2293)
Co-authored-by: matthewdavidrichardanderson <matthewdavidrichardanderson@mfb.com>
2026-08-13 23:54:29 -06:00
doop 46da33be37 Use texture replacement API for HQ minimap textures (#2303) 2026-08-13 23:44:36 -06:00
TakaRikka 008a18c142 Merge pull request #2292 from TwilitRealm/item-svc
initial item service implementation
2026-08-12 15:34:45 -07:00
TakaRikka 5b4def820a couple fixes 2026-08-12 15:02:33 -07:00
TakaRikka 635901d3bd update game service major version 2026-08-12 13:59:01 -07:00
TakaRikka 5abc956448 daTbox2 handling 2026-08-11 14:45:53 -07:00
TakaRikka 0c3bbbaba0 standardize some check names 2026-08-10 12:35:08 -07:00
TakaRikka 71b2eedc82 initial item service implementation 2026-08-09 16:40:26 -07:00
gymnast86 0da58ac91d gx.h instead of dolphin/gx/GXstruct.h 2026-08-09 10:13:51 -07:00
gymnast86 c29cafa8a2 constexpr descriptions 2026-08-09 10:13:32 -07:00
gymnast86 5e669c8f0f fix mod.json 2026-08-09 09:24:49 -07:00
gymnast86 3e625cf006 Merge branch 'main' of https://github.com/TwilitRealm/dusk into basic-cosmetics-mod 2026-08-09 09:13:18 -07:00
gymnast86 c6f497b525 move option descriptions 2026-08-09 08:52:29 -07:00
gymnast86 79d3c7c76a recolor epona's eyelids 2026-08-09 08:52:16 -07:00
gymnast86 16dd1118d1 add support for ordon sword recoloring 2026-08-09 08:18:28 -07:00
gymnast86 e9918cc8ed cleanup 2026-08-09 08:17:42 -07:00
gymnast86 058304dc26 recolor heart piece models 2026-08-08 20:10:50 -07:00
gymnast86 d55b44b5a5 override colors for items on other models 2026-08-08 20:10:21 -07:00
gymnast86 469575fd65 fix pane cache again 2026-08-08 07:09:28 -07:00
gymnast86 4883c11182 Merge branch 'main' of https://github.com/TwilitRealm/dusk into basic-cosmetics-mod 2026-08-07 18:34:19 -07:00
gymnast86 7ac2340312 add UI colors 2026-08-07 18:33:40 -07:00
gymnast86 3352ea3dde fix PaneCache naming conflict 2026-08-07 18:32:41 -07:00
gymnast86 6481d12a3d add lantern, midna, and heart colors 2026-08-06 09:38:00 -07:00
gymnast86 960d016113 get most colors working 2026-08-05 08:01:57 -07:00
gymnast86 e02251a669 template setup 2026-08-04 15:28:11 -07:00
131 changed files with 4712 additions and 553 deletions
+1
View File
@@ -519,6 +519,7 @@ if (DUSK_ENABLE_CODE_MODS AND CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR
add_subdirectory(mods/ao_mod)
add_subdirectory(mods/shadow_mod)
add_subdirectory(mods/window_demo)
add_subdirectory(mods/basic_cosmetics_mod)
endif ()
if (APPLE)
+1 -1
+6
View File
@@ -1436,6 +1436,7 @@ set(DUSK_FILES
src/dusk/gamepad_color.cpp
src/dusk/globals.cpp
src/dusk/gyro.cpp
src/dusk/hq_minimap.cpp
src/dusk/imgui/ImGuiActorSpawner.cpp
src/dusk/imgui/ImGuiBloomWindow.cpp
src/dusk/imgui/ImGuiBloomWindow.hpp
@@ -1472,6 +1473,9 @@ set(DUSK_FILES
src/dusk/mods/loader/native_module.hpp
src/dusk/mods/loader/prepatch.cpp
src/dusk/mods/loader/prepatch.hpp
src/dusk/mods/item.hpp
src/dusk/mods/item_checks.cpp
src/dusk/mods/item_gives.cpp
src/dusk/mods/log_buffer.cpp
src/dusk/mods/log_buffer.hpp
src/dusk/mods/manifest.cpp
@@ -1483,6 +1487,8 @@ set(DUSK_FILES
src/dusk/mods/svc/gfx.cpp
src/dusk/mods/svc/hook.cpp
src/dusk/mods/svc/host.cpp
src/dusk/mods/svc/item.cpp
src/dusk/mods/svc/item.hpp
src/dusk/mods/svc/log.cpp
src/dusk/mods/svc/overlay.cpp
src/dusk/mods/svc/registry.cpp
+3
View File
@@ -96,6 +96,9 @@ public:
/* 0xDE0 */ cXyz field_0xde0;
/* 0xDEC */ u8 field_0xdec[0xdf9 - 0xdec];
/* 0xDF9 */ u8 field_0xdf9;
#if TARGET_PC
bool mItemCheckOverridden;
#endif
};
STATIC_ASSERT(sizeof(daE_HP_c) == 0xdfc);
+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_anmData_s const m_anmDataTable[53];
private:
// private:
/* 0x568 */ request_of_phase_process_class mPhase;
/* 0x570 */ J3DModel* mpModel;
/* 0x574 */ J3DModel* mpShadowModel;
+5
View File
@@ -88,6 +88,11 @@ private:
/* 0x99C */ dPa_followEcallBack mEffect2;
/* 0x9B0 */ Z2SoundObjSimple mSound;
/* 0x9D0 */ u8 mIsHookCarry;
#if TARGET_PC
u8 mOriginalItemNo;
bool mItemOverridden;
bool mOverrideHover;
#endif
};
STATIC_ASSERT(sizeof(daObjLife_c) == 0x9d4);
+3
View File
@@ -194,6 +194,9 @@ private:
/* 0x9FC */ u8 field_0x9fc;
/* 0x9FD */ u8 field_0x9fd;
/* 0xA00 */ Mtx mDrawMtx;
#if TARGET_PC
u8 mOriginalItemNo;
#endif
};
STATIC_ASSERT(sizeof(daTbox_c) == 0xA30);
+4
View File
@@ -83,6 +83,10 @@ private:
/* 0xAC0 */ u8 field_0xAC0[0xAC4 - 0xAC0];
/* 0xAC4 */ int mStaffIdx;
/* 0xAC8 */ dBgW* mBoxBgW;
#if TARGET_PC
bool mParamsInit;
u8 mOriginalItemNo;
#endif
};
STATIC_ASSERT(sizeof(daTbox2_c) == 0xACC);
+4
View File
@@ -145,6 +145,10 @@ public:
/* 0x9BC */ u8 field_0x9bc[4];
/* 0x9C0 */ u8 field_0x9c0;
/* 0x9C1 */ u8 field_0x9c1;
#if TARGET_PC
u8 mOriginalItemNo;
bool mItemOverridden;
#endif
}; // Size: 0x9C4
#endif /* D_A_D_A_ITEM_STATIC_H */
+7
View File
@@ -50,6 +50,9 @@ public:
csXyz* getRotateP();
cXyz* getPosP();
const char* getShopArcname();
#if TARGET_PC
const ResourceData& getResourceData() const;
#endif
u16 getHeapSize();
void CreateInit();
void set_mtx();
@@ -110,6 +113,10 @@ private:
/* 0x960 */ s16 mAngleX;
/* 0x962 */ s16 mAngleY;
/* 0x964 */ u8 mShopItemID;
#if TARGET_PC
ResourceData mOverrideData;
bool mItemOverridden;
#endif
};
int CheckShopItemCreateHeap(fopAc_ac_c* i_this);
+1 -1
View File
@@ -37,7 +37,7 @@ public:
CPaneMgrAlpha* getNoDatBase() { return mNoDatBase; }
void draw() { _draw(); }
private:
// private:
/* 0x04 */ JKRArchive* mArchive;
/* 0x08 */ dDlst_FileInfo_c mFileInfo;
/* 0x1C */ u8 field_0x1c[4];
+1 -29
View File
@@ -13,36 +13,8 @@ class dFile_info_c;
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 {
+3 -1
View File
@@ -11,7 +11,9 @@ public:
static DUSK_GAME_DATA u8* mData;
};
void execItemGet(u8 item_id);
class fopAc_ac_c;
void execItemGet(
u8 item_id IF_DUSK_ARG(u32 item_give_tag = 0) IF_DUSK_ARG(fopAc_ac_c* giver = NULL));
void item_func_HEART();
void item_func_GREEN_RUPEE();
+1 -1
View File
@@ -30,7 +30,7 @@ public:
void drawSelf() { mpKanteraIcon->draw(); }
private:
// private:
/* 0x04 */ dDlst_KanteraIcon_c* mpKanteraIcon;
/* 0x08 */ CPaneMgr* mpParent;
/* 0x0C */ CPaneMgr* mpGauge;
+1 -44
View File
@@ -15,49 +15,6 @@ class dMenu_Fishing_c;
class dMenu_Skill_c;
class dMenu_Insect_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_Collect2DTop_c : public dDlst_base_c {
public:
@@ -172,7 +129,7 @@ public:
J2DPicture* getBlackTex() { return mpBlackTex; }
u8 getSubWindowOpenCheck() { return mSubWindowOpenCheck; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRExpHeap* mpSubHeap;
/* 0x00C */ void* field_0xc;
+1 -1
View File
@@ -43,7 +43,7 @@ public:
u8 getStatus() { return mStatus; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -270,7 +270,7 @@ public:
static DUSK_GAME_DATA dMenu_Fmap_c* MyClass;
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRExpHeap* mpTalkHeap;
/* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -60,7 +60,7 @@ public:
u8 getStatus() { return mStatus; }
private:
// private:
/* 0x04 */ JKRExpHeap* mpHeap;
/* 0x08 */ JKRArchive* mpArchive;
/* 0x0C */ STControl* mpStick;
+1 -1
View File
@@ -64,7 +64,7 @@ public:
u8 getStatus() { return mStatus; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -117,7 +117,7 @@ public:
return ((u8)mUseFlag & (u8)i_flag) != 0 ? true : false;
}
private:
// private:
/* 0x004 */ J2DScreen* mpBackScreen;
/* 0x008 */ J2DScreen* mpScreen;
/* 0x00C */ J2DScreen* mpClipScreen;
+1 -1
View File
@@ -84,7 +84,7 @@ public:
void drawFlag0() { mDrawFlag = 0; }
void setStatus(u8 i_status) { mStatus = i_status; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ STControl* mpStick;
/* 0x00C */ CSTControl* mpCStick;
+1 -1
View File
@@ -278,7 +278,7 @@ public:
u8 getEndStatus() { return mEndStatus; }
void setUseType(u8 type) { mUseType = type; }
private:
// private:
/* 0x0004 */ JKRArchive* mpArchive;
/* 0x0008 */ mDoDvdThd_mountArchive_c* mpMount;
/* 0x000C */ STControl* stick;
+1 -1
View File
@@ -58,7 +58,7 @@ public:
u8 getStatus() { return mStatus; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -154,7 +154,7 @@ public:
}
#endif
private:
// private:
/* 0x004 */ item_params mItemParams[4];
/* 0x074 */ JKRExpHeap* heap;
/* 0x078 */ J2DScreen* mpScreen;
+2 -2
View File
@@ -25,7 +25,7 @@ public:
void setTextBoxPtr(J2DTextBox* p_textBox) { mpTextBoxPtr = p_textBox; }
void resetType() { mType = 0x47; }
private:
// private:
/* 0x04 */ J2DTextBox* mpTextBoxPtr;
/* 0x08 */ f32 mPosX;
/* 0x0C */ f32 mPosY;
@@ -53,7 +53,7 @@ public:
void setRupeeColor(u8 color) { mRupeeColor = color; }
private:
// private:
/* 0x004 */ COutFontSet_c* mpOfs[35];
/* 0x090 */ J2DPicture* mpPane[70];
/* 0x1A8 */ f32 mAlphaRatio;
-28
View File
@@ -12,34 +12,6 @@ class J2DTextBox;
class JUTFont;
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 {
public:
dNm_HIO_c();
+5
View File
@@ -318,6 +318,11 @@ public:
/* 0x566 */ s8 field_0x566;
/* 0x567 */ s8 field_0x567;
#if TARGET_PC
u32 mItemGiveTag;
u8 mItemGiveOriginalNo;
#endif
#if !__MWERKS__
s8 actor_last_base_field;
#endif
+20 -13
View File
@@ -79,6 +79,10 @@ struct fopAcM_prm_class {
/* 0x1C */ fpc_ProcID parent_id;
/* 0x20 */ s8 argument;
/* 0x21 */ s8 room_no;
#if TARGET_PC
u32 mItemGiveTag;
u8 mItemGiveOriginalNo;
#endif
};
struct fopAcM_search4ev_prm {
@@ -516,8 +520,9 @@ s32 fopAcM_SearchByName(s16 i_procName, fopAc_ac_c** i_outActor);
fopAcM_prm_class* fopAcM_CreateAppend();
fopAcM_prm_class* createAppend(u16 i_setId, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
fpc_ProcID i_parentId);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
fpc_ProcID i_parentId IF_DUSK_ARG(u32 i_itemGiveTag = 0)
IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF));
void fopAcM_Log(fopAc_ac_c const* i_actor, char const* i_message);
@@ -526,19 +531,20 @@ s32 fopAcM_delete(fopAc_ac_c* i_actor);
s32 fopAcM_delete(fpc_ProcID i_actorID);
fpc_ProcID fopAcM_create(s16 i_procName, u16 i_setId, u32 i_parameters, const cXyz* i_pos,
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc);
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc IF_DUSK_ARG(u32 i_itemGiveTag = 0));
fpc_ProcID fopAcM_create(s16 i_procName, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument IF_DUSK_ARG(u32 i_itemGiveTag = 0));
inline fpc_ProcID fopAcM_Create(s16 i_procName, createFunc i_createFunc, void* params) {
return fpcM_Create(i_procName, i_createFunc,params);
}
fopAc_ac_c* fopAcM_fastCreate(s16 i_procName, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc, void* i_createFuncData);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument, createFunc i_createFunc,
void* i_createFuncData IF_DUSK_ARG(u32 i_itemGiveTag = 0)
IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF));
fopAc_ac_c* fopAcM_fastCreate(const char* i_actorname, u32 i_parameters, const cXyz* i_pos,
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale,
@@ -623,11 +629,11 @@ fopAc_ac_c* fopAcM_getItemEventPartner(const fopAc_ac_c*);
fopAc_ac_c* fopAcM_getEventPartner(const fopAc_ac_c*);
fpc_ProcID fopAcM_createItemForPresentDemo(cXyz const* i_pos, int i_itemNo, u8 param_2,
int i_itemBitNo, int i_roomNo, csXyz const* i_angle,
cXyz const* i_scale);
int i_itemBitNo, int i_roomNo, csXyz const* i_angle,
cXyz const* i_scale IF_DUSK_ARG(u32 i_itemGiveTag = 0));
fpc_ProcID fopAcM_createItemForTrBoxDemo(cXyz const* i_pos, int i_itemNo, int i_itemBitNo,
int i_roomNo, csXyz const* i_angle, cXyz const* i_scale);
int i_roomNo, csXyz const* i_angle, cXyz const* i_scale IF_DUSK_ARG(u32 i_itemGiveTag = 0));
u8 fopAcM_getItemNoFromTableNo(u8 i_tableNo);
@@ -641,11 +647,12 @@ fpc_ProcID fopAcM_createItemFromTable(cXyz const* i_pos, int i_tableNo, int i_it
bool i_createDirect);
fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitNo,
const csXyz* i_angle, int i_roomNo, const cXyz* scale, u8 param_7);
const csXyz* i_angle, int i_roomNo, const cXyz* scale,
u8 param_7 IF_DUSK_ARG(u32 i_itemGiveTag = 0));
fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF,
f32 i_speedY, int param_8);
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY,
int param_8 IF_DUSK_ARG(const char* i_itemCheckName = NULL));
fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, int param_6,
+13
View File
@@ -256,4 +256,17 @@ using std::isnan;
#define DUSK_CONST IF_DUSK(const)
#define DUSK_CONSTEXPR IF_DUSK(constexpr)
#if TARGET_PC && defined(DUSK_BUILDING_GAME)
#include "dusk/mods/item.hpp"
#define DUSK_ITEM_CHECK(name, item_no, giver) \
(item_no) = ::dusk::mods::item_check(name, (item_no), giver)
#define DUSK_ITEM_CHECK_EXPR(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))
#else
#define DUSK_ITEM_CHECK(name, item_no, giver)
#define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) (item_no)
#define DUSK_GIVE_TAG(name)
#endif
#endif
+1 -1
View File
@@ -68,7 +68,7 @@ public:
JKRMemArchive* getArchive() const { return mArchive; }
JKRHeap* getHeap() const { return mHeap; }
private:
// private:
/* 0x14 */ u8 mMountDirection;
/* 0x18 */ s32 mEntryNumber;
/* 0x1C */ JKRMemArchive* mArchive;
+5 -10
View File
@@ -38,7 +38,7 @@ void JFWDisplay::ctor_subroutine(bool enableAlpha) {
mTickRate = 0;
mCombinationRatio = 0.0f;
field_0x30 = 0;
field_0x2c = OSGetTick();
field_0x2c = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
field_0x34 = 0;
field_0x48 = 0;
field_0x4a = 0;
@@ -258,7 +258,7 @@ void JFWDisplay::beginRender() {
waitForTick(mTickRate, mFrameRate);
JUTVideo::getManager()->waitRetraceIfNeed();
OSTick tick = OSGetTick();
OSTick tick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
field_0x30 = tick - field_0x2c;
field_0x2c = tick;
field_0x34 = field_0x2c - JUTVideo::getVideoLastTick();
@@ -371,7 +371,7 @@ constexpr auto FRAME_PERIOD = std::chrono::duration_cast<std::chrono::nanosecond
constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2;
static void waitPrecise(Limiter& limiter, Limiter::duration_t targetNs) {
const auto sleepTime = limiter.Sleep(targetNs);
const auto sleepTime = limiter.Sleep(targetNs);
dusk::frameUsagePct =
100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(targetNs));
}
@@ -381,15 +381,12 @@ static void waitForTick(u32 p1, u16 p2) {
#if TARGET_PC
static Limiter limiter;
if (dusk::frame_interp::is_enabled() && !dusk::getTransientSettings().skipFrameRateLimit) {
if (dusk::frame_interp::is_enabled() || dusk::getTransientSettings().turboMode) {
limiter.Reset();
dusk::frameUsagePct = 0.f;
return;
}
if (dusk::getTransientSettings().skipFrameRateLimit) {
p1 = OS_TIMER_CLOCK / 120;
}
if (fopOvlpM_IsPeek() && dusk::getTransientSettings().stateShareLoadActive) {
return;
}
@@ -399,7 +396,6 @@ static void waitForTick(u32 p1, u16 p2) {
if (p1 != 0) {
#if TARGET_PC
static Limiter limiter;
waitPrecise(limiter, static_cast<Uint64>(OSTicksToMicroseconds(p1)) * 1000ULL);
#else
static OSTime nextTick = OSGetTime();
@@ -413,7 +409,6 @@ static void waitForTick(u32 p1, u16 p2) {
} else {
u32 uVar1 = (p2 == 0) ? 1 : p2;
#if TARGET_PC
static Limiter limiter;
waitPrecise(limiter, static_cast<Uint64>((RETRACE_PERIOD * uVar1).count()));
#else
static u32 nextCount = VIGetRetraceCount();
+2 -2
View File
@@ -42,7 +42,7 @@ JUTVideo::JUTVideo(GXRenderModeObj const* param_0) {
mRetraceCount = VIGetRetraceCount();
field_0x10 = 1;
field_0x18 = 0;
sVideoLastTick = OSGetTick();
sVideoLastTick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
sVideoInterval = 670000;
mPreRetraceCallback = VISetPreRetraceCallback(preRetraceProc);
mPostRetraceCallback = VISetPostRetraceCallback(postRetraceProc);
@@ -68,7 +68,7 @@ void JUTVideo::preRetraceProc(u32 retrace_count) {
(*sManager->mPreCallback)(retrace_count);
}
OSTick tick = OSGetTick();
OSTick tick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
sVideoInterval = tick - sVideoLastTick;
sVideoLastTick = tick;
+34
View File
@@ -0,0 +1,34 @@
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
)
target_link_libraries(basic_cosmetics_mod PRIVATE xxHash::xxhash)
+7
View File
@@ -0,0 +1,7 @@
{
"id": "dev.twilitrealm.basic_cosmetics",
"name": "Basic Cosmetics",
"version": "1.0.0",
"author": "Twilit Realm",
"description": "Change Basic Cosmetic Colors"
}
@@ -0,0 +1,94 @@
#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;
}
}
@@ -0,0 +1,30 @@
#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);
+686
View File
@@ -0,0 +1,686 @@
#include "hooks.hpp"
#include "mod.hpp"
#include "color_utils.hpp"
#include "midna_hair_color.hpp"
#include "types.h"
#include "mods/svc/hook.hpp"
#include "mods/svc/log.hpp"
#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_meter_button.h"
#include "d/d_meter_hakusha.h"
#include "d/d_meter2.h"
#include "d/d_meter2_draw.h"
#include "d/d_meter2_info.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"
#include "SSystem/SComponent/c_phase.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 = *get_midna_hair_normalColor();
if (dKy_darkworld_check()) {
midna->field_0x6e8 = *get_midna_hair_normalKColor();
midna->field_0x6ec = *get_midna_hair_normalKColor2();
} else {
midna->field_0x6e8 = *get_midna_hair_lNormalKColor();
midna->field_0x6ec = *get_midna_hair_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 = *get_midna_hair_bigKColor();
if (dKy_darkworld_check()) {
color = *get_midna_hair_bigColor();
kcolor2 = *get_midna_hair_normalKColor2();
} else {
color = *get_midna_hair_lBigColor();
kcolor2 = *get_midna_hair_lBigKColor2();
}
} else {
color = *get_midna_hair_normalColor();
if (dKy_darkworld_check()) {
kcolor1 = *get_midna_hair_normalKColor();
kcolor2 = *get_midna_hair_normalKColor2();
} else {
kcolor1 = *get_midna_hair_lNormalKColor();
kcolor2 = *get_midna_hair_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
@@ -0,0 +1,7 @@
#pragma once
#include "mods/service.hpp"
ModResult add_all_hooks();
ModResult remove_all_hooks();
@@ -0,0 +1,141 @@
#include "midna_hair_color.hpp"
#include "mod.hpp"
#include "mods/svc/log.hpp"
#include <array>
static constexpr std::array<std::array<GXColor, 9>, 10> midnaHairColors = {{
// 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}
}}
}};
static struct {
GXColorS10 l_normalColor = { 0x50, 0x00, 0x00, 0x00 };
GXColor l_normalKColor = { 0xB4, 0x87, 0x00, 0x00 };
GXColor l_normalKColor2 = { 0x00, 0xC3, 0xC3, 0x00 };
GXColorS10 l_bigColor = { 0xFF, 0x64, 0x78, 0x00 };
GXColor l_bigKColor = { 0x1E, 0x00, 0x00, 0x00 };
GXColor l_lNormalKColor = { 0xFF, 0xDC, 0x00, 0x00 };
GXColor l_lNormalKColor2 = { 0x00, 0xC3, 0xEB, 0x00 };
GXColorS10 l_lBigColor = { 0xFF, 0x78, 0x00, 0x00 };
GXColor l_lBigKColor2 = { 0xAA, 0xFF, 0xC3, 0x00 };
} currentMidnaHairColors;
void set_all_midna_hair_colors() {
auto& g_cvars = get_cvars();
auto hairBaseColor = get_int_option(g_cvars.midnaHairBaseColor, 0);
auto hairTipsColor = get_int_option(g_cvars.midnaHairTipsColor, 0);
// Colors we have to convert to GXColorS10
auto& normalColor = midnaHairColors.at(hairBaseColor)[2];
auto& bigColor = midnaHairColors.at(hairBaseColor)[5];
auto& lBigColor = midnaHairColors.at(hairBaseColor)[4];
currentMidnaHairColors.l_normalColor = GXColorS10{normalColor.r, normalColor.g, normalColor.b};
currentMidnaHairColors.l_normalKColor = midnaHairColors.at(hairBaseColor)[1];
currentMidnaHairColors.l_normalKColor2 = midnaHairColors.at(hairTipsColor)[7];
currentMidnaHairColors.l_bigColor = GXColorS10{bigColor.r, bigColor.g, bigColor.b};
currentMidnaHairColors.l_bigKColor = midnaHairColors.at(hairBaseColor)[3];
currentMidnaHairColors.l_lNormalKColor = midnaHairColors.at(hairBaseColor)[0];
currentMidnaHairColors.l_lNormalKColor2 = midnaHairColors.at(hairTipsColor)[6];
currentMidnaHairColors.l_lBigColor = GXColorS10{lBigColor.r, lBigColor.g, lBigColor.b};
currentMidnaHairColors.l_lBigKColor2 = midnaHairColors.at(hairTipsColor)[8];
}
const GXColorS10* get_midna_hair_normalColor() {
return &currentMidnaHairColors.l_normalColor;
}
const GXColor* get_midna_hair_normalKColor() {
return &currentMidnaHairColors.l_normalKColor;
}
const GXColor* get_midna_hair_normalKColor2() {
return &currentMidnaHairColors.l_normalKColor2;
}
const GXColorS10* get_midna_hair_bigColor() {
return &currentMidnaHairColors.l_bigColor;
}
const GXColor* get_midna_hair_bigKColor() {
return &currentMidnaHairColors.l_bigKColor;
}
const GXColor* get_midna_hair_lNormalKColor() {
return &currentMidnaHairColors.l_lNormalKColor;
}
const GXColor* get_midna_hair_lNormalKColor2() {
return &currentMidnaHairColors.l_lNormalKColor2;
}
const GXColorS10* get_midna_hair_lBigColor() {
return &currentMidnaHairColors.l_lBigColor;
}
const GXColor* get_midna_hair_lBigKColor2() {
return &currentMidnaHairColors.l_lBigKColor2;
}
@@ -0,0 +1,15 @@
#pragma once
#include <gx.h>
void set_all_midna_hair_colors();
const GXColorS10* get_midna_hair_normalColor();
const GXColor* get_midna_hair_normalKColor();
const GXColor* get_midna_hair_normalKColor2();
const GXColorS10* get_midna_hair_bigColor();
const GXColor* get_midna_hair_bigKColor();
const GXColor* get_midna_hair_lNormalKColor();
const GXColor* get_midna_hair_lNormalKColor2();
const GXColorS10* get_midna_hair_lBigColor();
const GXColor* get_midna_hair_lBigKColor2();
+460
View File
@@ -0,0 +1,460 @@
#include "mod.hpp"
#include "color_utils.hpp"
#include "hooks.hpp"
#include "midna_hair_color.hpp"
#include "option_descriptions.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 /*= false*/) {
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 (is_valid_hex_color_str(colorStr) || (colorStr == "rainbow" && allowRainbow)) {
if (is_valid_hex_color_str(colorStr)) {
return hex_color_str_to_gx_color(colorStr);
}
if (allowRainbow) {
// Assume rainbow if not a valid hex str
auto color = get_rainbow_rgb(127.5f);
color.r /= 2;
color.g /= 2;
color.b /= 2;
return color;
}
}
return std::nullopt;
}
namespace {
UiWindowHandle g_cosmeticsWindow = 0;
bool g_loadedAllBaseTextures = false;
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));
}
}
void add_cosmetic_option(UiElementHandle left, ConfigVarHandle cvar, const char* name, const char* helpRml) {
UiControlDesc control = UI_CONTROL_DESC_INIT;
control.kind = UI_CONTROL_STRING;
control.label = name;
control.help_rml = helpRml;
control.binding = UI_BINDING_CONFIG_VAR;
control.config_var = cvar;
control.max_length = 6;
add_control(left, control);
}
void add_cosmetic_option_with_rainbow(UiElementHandle left, ConfigVarHandle cvar, const char* name, const char* helpRml) {
UiControlDesc control = UI_CONTROL_DESC_INIT;
control.kind = UI_CONTROL_STRING;
control.label = name;
control.help_rml = helpRml;
control.binding = UI_BINDING_CONFIG_VAR;
control.config_var = cvar;
control.max_length = 7;
add_control(left, 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);
}
ModResult build_equipment_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
(void)right;
add_cosmetic_option(left, g_cvars.herosTunicCapColor, "Hero's Tunic Cap Color", kLinksCapColorsExplanation);
add_cosmetic_option(left, g_cvars.herosTunicTorsoColor, "Hero's Tunic Body Color", kLinksShirtColorsExplanation);
add_cosmetic_option(left, g_cvars.herosTunicSkirtColor, "Hero's Tunic Skirt Color", kLinksSkirtColorsExplanation);
add_cosmetic_option(left, g_cvars.zoraArmorCapColor, "Zora Armor Cap Color", kZoraCapColorsExplanation);
add_cosmetic_option(left, g_cvars.zoraArmorHelmetColor, "Zora Armor Helmet Color", kZoraHelmetColorsExplanation);
add_cosmetic_option(left, g_cvars.zoraArmorTorsoColor, "Zora Armor Torso Color", kZoraTorsoColorsExplanation);
add_cosmetic_option(left, g_cvars.zoraArmorScalesColor, "Zora Armor Scales Color", kZoraScalesColorsExplanation);
add_cosmetic_option(left, g_cvars.zoraArmorFlippersColor, "Zora Armor Flippers Color", kZoraFlippersColorsExplanation);
add_cosmetic_option_with_rainbow(left, g_cvars.lanternGlowColor, "Lantern Glow Color", kLanternGlowColorsExplanation);
add_cosmetic_option(left, g_cvars.woodenSwordColor, "Wooden Sword Color", kWoodenSwordColorsExplanation);
add_cosmetic_option(left, g_cvars.ordonSwordBladeColor, "Ordon Sword Blade Color", kOrdonBladeColorsExplanation);
add_cosmetic_option(left, g_cvars.ordonSwordHandleColor, "Ordon Sword Handle Color", kOrdonHandleColorsExplanation);
add_cosmetic_option(left, g_cvars.msBladeColor, "Master Sword Blade Color", kMSBladeColorsExplanation);
add_cosmetic_option(left, g_cvars.msHandleColor, "Master Sword Handle Color", kMSHandleColorsExplanation);
add_cosmetic_option_with_rainbow(left, g_cvars.lightSwordGlowColor, "Light Sword Glow Color", kSwordGlowColorsExplanation);
add_cosmetic_option(left, g_cvars.boomerangColor, "Boomerang Color", kBoomerangColorsExplanation);
add_cosmetic_option(left, g_cvars.ironBootsColor, "Iron Boots Color", kIronBootsColorsExplanation);
add_cosmetic_option(left, g_cvars.spinnerColor, "Spinner Color", kSpinnerColorsExplanation);
return MOD_OK;
}
ModResult build_ui_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
(void)right;
add_cosmetic_option(left, g_cvars.aButtonColor, "A Button Color", kAButtonColorsExplanation);
add_cosmetic_option(left, g_cvars.bButtonColor, "B Button Color", kBButtonColorsExplanation);
add_cosmetic_option(left, g_cvars.xButtonColor, "X Button Color", kXButtonColorsExplanation);
add_cosmetic_option(left, g_cvars.yButtonColor, "Y Button Color", kYButtonColorsExplanation);
add_cosmetic_option(left, g_cvars.zButtonColor, "Z Button Color", kZButtonColorsExplanation);
add_cosmetic_option(left, g_cvars.heartColor, "Heart Color", kHeartColorsExplanation);
return MOD_OK;
}
ModResult build_misc_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
(void)right;
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");
add_cosmetic_option(left, g_cvars.midnaChargeRingColor, "Midna Charge Ring Color", kChargeRingColorsExplanation);
add_cosmetic_option(left, g_cvars.linkHairColor, "Link's Hair Color", kLinksHairColorsExplanation);
add_cosmetic_option(left, g_cvars.wolfLinkColor, "Wolf Link Color", kWolfLinkColorsExplanation);
add_cosmetic_option(left, g_cvars.eponaColor, "Epona Color", kEponaColorsExplanation);
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 Colors";
tabs[0].build = build_equipment_colors_tab;
tabs[1].title = "UI Colors";
tabs[1].build = build_ui_colors_tab;
tabs[2].title = "Misc Colors";
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_BUTTON;
control.label = "Open Cosmetics Menu";
control.on_pressed = on_open_cosmetics_menu;
add_control(panel, control);
return MOD_OK;
}
}
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;
}
// 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
auto size = get_image_data_size(imageHeader->format, imageHeader->width,
imageHeader->height, imageHeader->mipmapEnabled ? imageHeader->mipmapCount : 1);
replacement.baseTextureData.resize(size);
std::memcpy(replacement.baseTextureData.data(), tex->getImgDataPtr(i), size);
// Calculate the texture hash
auto textureHash = XXH64(replacement.baseTextureData.data(), size, 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 = imageHeader->mipmapCount;
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;
}
// If we don't have a valid color, don't continue either
auto maybeColor = get_config_var_color(configVar);
if (!maybeColor.has_value()) {
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);
replacement.data.data = newTexture.data();
replacement.data.size = newTexture.size();
// Register the new data
auto result = svc_texture->register_data(mod_ctx, &replacement.key, &replacement.data, &replacement.handle);
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));
}
curColor = color;
}
}
}
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);
}
}
}
}
#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;
return MOD_OK;
}
MOD_EXPORT ModResult mod_update(ModError*) {
update_rainbow_rgb(1.0f);
set_all_midna_hair_colors();
if (!g_loadedAllBaseTextures) {
load_base_texture_data();
}
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();
return MOD_OK;
}
}
+65
View File
@@ -0,0 +1,65 @@
#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);
@@ -0,0 +1,353 @@
#pragma once
constexpr const char* kLinksCapColorsExplanation =
"Set the color of the Hero's Tunic cap with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kLinksShirtColorsExplanation =
"Set the color of the Hero's Tunic shirt with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kLinksSkirtColorsExplanation =
"Set the color of the Hero's Tunic skirt with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kZoraCapColorsExplanation =
"Set the color of the Zora Armor cap with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kZoraHelmetColorsExplanation =
"Set the color of the Zora Armor helmet with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kZoraTorsoColorsExplanation =
"Set the color of the Zora Armor torso with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kZoraScalesColorsExplanation =
"Set the color of the Zora Armor scales with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kZoraFlippersColorsExplanation =
"Set the color of the Zora Armor flippers with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kWoodenSwordColorsExplanation =
"Set the color of the wooden sword with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kLanternGlowColorsExplanation =
"Set the color of the lantern light with any 6 digit hex code, or with \"rainbow\" for a rainbow effect.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: f68821<br/>"
"- Yellow: f6f321<br/>"
"- Green: 00ff00<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 8000ff<br/>"
"- White: a0a0a0<br/>";
constexpr const char* kOrdonBladeColorsExplanation =
"Set the color of the Ordon Sword's blade with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: f68821<br/>"
"- Yellow: f6f321<br/>"
"- Green: 00ff00<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 8000ff<br/>"
"- White: a0a0a0<br/>"
"- Cyan: 30d0d0<br/>";
constexpr const char* kOrdonHandleColorsExplanation =
"Set the color of the Ordon Sword's handle with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: f68821<br/>"
"- Yellow: f6f321<br/>"
"- Green: 00ff00<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 8000ff<br/>"
"- White: a0a0a0<br/>"
"- Cyan: 30d0d0<br/>";
constexpr const char* kMSBladeColorsExplanation =
"Set the color of the Master Sword's blade with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: f68821<br/>"
"- Yellow: f6f321<br/>"
"- Green: 00ff00<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 8000ff<br/>"
"- White: a0a0a0<br/>"
"- Cyan: 30d0d0<br/>";
constexpr const char* kMSHandleColorsExplanation =
"Set the color of the Master Sword's handle with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: f68821<br/>"
"- Yellow: f6f321<br/>"
"- Green: 00ff00<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 8000ff<br/>"
"- White: a0a0a0<br/>"
"- Cyan: 30d0d0<br/>";
constexpr const char* kSwordGlowColorsExplanation =
"Set the color of the light sword glow with any 6 digit hex code, or with \"rainbow\" for a rainbow effect.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: f68821<br/>"
"- Yellow: f6f321<br/>"
"- Green: 00ff00<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 8000ff<br/>"
"- White: a0a0a0<br/>";
constexpr const char* kBoomerangColorsExplanation =
"Set the color of the Gale Boomerang with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kIronBootsColorsExplanation =
"Set the color of the Iron Boots with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kSpinnerColorsExplanation =
"Set the color of the Spinner with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kAButtonColorsExplanation =
"Set the color of the A button with any 6 digit hex code. A reload may be necessary to see changes.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: ff5000<br/>"
"- Yellow: ffaf00<br/>"
"- Dark Green: 0080ff<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 8000ff<br/>"
"- Grey: 5555ff<br/>"
"- Pink: ff20ff<br/>";
constexpr const char* kBButtonColorsExplanation =
"Set the color of the B button with any 6 digit hex code. A reload may be necessary to see changes.<br/>"
"Common colors:<br/>"
"- Orange: ffff40<br/>"
"- Pink: ffa0ff<br/>"
"- Green: 00e87b<br/>"
"- Blue: 00aaff<br/>"
"- Purple: 6078ff<br/>"
"- Black: 000000<br/>"
"- Teal: 00f3ff<br/>";
constexpr const char* kXButtonColorsExplanation =
"Set the color of the X button with any 6 digit hex code. A reload may be necessary to see changes.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: ff8200<br/>"
"- Yellow: f7df00<br/>"
"- Lime Green: 70ff00<br/>"
"- Dark Green: 00bd11<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 800088<br/>"
"- Black: 000000<br/>"
"- Pink: ff00aa<br/>"
"- Cyan: 00ffff<br/>";
constexpr const char* kYButtonColorsExplanation =
"Set the color of the Y button with any 6 digit hex code. A reload may be necessary to see changes.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: ff8200<br/>"
"- Yellow: f7df00<br/>"
"- Lime Green: 70ff00<br/>"
"- Dark Green: 00bd11<br/>"
"- Blue: 0000ff<br/>"
"- Purple: 800088<br/>"
"- Black: 000000<br/>"
"- Pink: ff00aa<br/>"
"- Cyan: 00ffff<br/>";
constexpr const char* kZButtonColorsExplanation =
"Set the Z button color with any 6 digit hex code. A reload may be necessary to see changes.<br/>"
"Common colors:<br/>"
"- Red: ff0000<br/>"
"- Orange: ff8200<br/>"
"- Yellow: f7df00<br/>"
"- Lime Green: 70ff00<br/>"
"- Dark Green: 00bd11<br/>"
"- Purple: 800088<br/>"
"- Black: 000000<br/>"
"- Light Blue: 00ffff<br/>";
constexpr const char* kHeartColorsExplanation =
"Set the heart color with any 6 digit hex code. This will also change the color of heart drops. A reload may be necessary to see changes.<br/>"
"Common colors:<br/>"
"- Orange: ffff40<br/>"
"- Pink: ffa0ff<br/>"
"- Green: 00e87b<br/>"
"- Blue: 00aaff<br/>"
"- Purple: 6078ff<br/>"
"- Black: 000000<br/>"
"- Teal: 00f3ff<br/>";
constexpr const char* kChargeRingColorsExplanation =
"Set the color of Midna's charge ring with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Pink: ff9f9f<br/>"
"- Red: ff0000<br/>"
"- Yellow: ffff00<br/>"
"- Green: 00ff00<br/>"
"- Blue: 0000ff<br/>"
"- Purple: ff00ff<br/>"
"- Brown: 331900<br/>"
"- White: feffff<br/>"
"- Black: 000000<br/>";
constexpr const char* kLinksHairColorsExplanation =
"Set the color of Link's hair with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kWolfLinkColorsExplanation =
"Set the color of Wolf Link with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
constexpr const char* kEponaColorsExplanation =
"Set the color of Epona with any 6 digit hex code.<br/>"
"Common colors:<br/>"
"- Red: ab706e<br/>"
"- Blue: 6382a0<br/>"
"- Purple: 94749a<br/>"
"- Orange: ec8644<br/>"
"- Yellow: b9ab00<br/>"
"- Pink: ec9fc8<br/>"
"- Black: 505154<br/>"
"- White: f8f7f4<br/>"
"- Brown: 91723e<br/>";
@@ -0,0 +1,672 @@
#include "texture_utils.hpp"
#include "color_utils.hpp"
#include "mod.hpp"
#include "mods/svc/config.h"
#include "mods/svc/log.hpp"
// Forward declaration needed for J3DModelLoader to be happy
class J3DVertexData;
#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);
}
constexpr int32_t blockWidth = 4;
constexpr int32_t blockHeight = 4;
const int32_t roundedWidth = replacementData.key.width + ((blockWidth - (replacementData.key.width % blockWidth)) % blockWidth);
const int32_t roundedHeight = replacementData.key.height + ((blockHeight - (replacementData.key.height % blockHeight)) % blockHeight);
const int32_t totalPixels = roundedWidth * roundedHeight;
auto* pixelPtr = newTextureDataOut.data();
for (int32_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];
}
}
}
// 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() || replacementData.key.width % 8 != 0 || replacementData.key.height % 8 != 0) {
return false;
}
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;
const uint8_t* readPtr = cmprOut.data();
uint8_t* writePtr = cmprOut.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) {
const uint32_t topTileIdx = (2 * by) * tilesX_I8 + bx;
const uint32_t bottomTileIdx = (2 * by + 1) * tilesX_I8 + bx;
const uint8_t* topTileData = readPtr + (topTileIdx * 32);
const uint8_t* bottomTileData = readPtr + (bottomTileIdx * 32);
uint8_t subBlockPixels[4][16];
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
subBlockPixels[0][row * 4 + col] = topTileData[row * 8 + col];
subBlockPixels[1][row * 4 + col] = topTileData[row * 8 + col + 4];
}
}
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
subBlockPixels[2][row * 4 + col] = bottomTileData[row * 8 + col];
subBlockPixels[3][row * 4 + col] = bottomTileData[row * 8 + col + 4];
}
}
// Write 32 bytes of CMPR output into the same buffer
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);
}
// Resize for new CMPR image
const size_t finalSize = writePtr - cmprOut.data();
cmprOut.resize(finalSize);
// 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;
}
@@ -0,0 +1,19 @@
#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 -1
View File
@@ -16,7 +16,7 @@
* of letting them corrupt memory.
*/
#define GAME_SERVICE_ID "dev.twilitrealm.dusklight.game"
#define GAME_SERVICE_MAJOR 1u
#define GAME_SERVICE_MAJOR 2u
#define GAME_SERVICE_MINOR 0u
typedef struct GameService {
+94
View File
@@ -0,0 +1,94 @@
#pragma once
#include <mods/api.h>
#ifdef __cplusplus
#include <mods/service.hpp>
#endif
#define ITEM_SERVICE_ID "dev.twilitrealm.dusklight.item"
#define ITEM_SERVICE_MAJOR 2u
#define ITEM_SERVICE_MINOR 0u
/* 0 is never a valid handle. */
typedef uint64_t ItemCheckHandle;
typedef uint64_t ItemGiveHandle;
/*
* Item check resolution and inventory grants.
*
* Check names are case-sensitive. Resolvers must be free of side effects because the game may
* resolve a check more than once, including once for display and again when granting the item.
* Registrations and pending grants are removed when the calling mod is detached. Callbacks run
* on the game thread.
*/
/* Host-owned callback data, valid only for the duration of the callback. */
typedef struct ItemCheckInfo {
const char* name;
const void* giver_actor; /* fopAc_ac_c*, or NULL when unavailable */
uint8_t vanilla_item;
uint8_t current_item;
} ItemCheckInfo;
/* Return true and write out_item to replace current_item, or false to leave it unchanged. */
typedef bool (*ItemCheckResolveFn)(
ModContext* ctx, const ItemCheckInfo* info, uint8_t* out_item, void* user_data);
typedef enum ItemGiveOrigin {
ITEM_GIVE_ORIGIN_GAME = 0,
ITEM_GIVE_ORIGIN_QUEUE = 1,
ITEM_GIVE_ORIGIN_QUEUE_SILENT = 2,
} ItemGiveOrigin;
/* Host-owned callback data, valid only for the duration of the callback. */
typedef struct ItemGiveInfo {
const char* check_name; /* NULL when the grant is not attributed to a check */
const void* giver_actor; /* fopAc_ac_c*, or NULL when unavailable */
uint8_t item;
uint8_t origin; /* ItemGiveOrigin */
} ItemGiveInfo;
typedef void (*ItemGiveObserveFn)(ModContext* ctx, const ItemGiveInfo* info, void* user_data);
enum {
/* Apply the inventory change without a get-item demo. */
ITEM_GIVE_SILENT = 1u << 0,
/* Resolve item_no as the named check's vanilla item when the queue entry is dispatched. */
ITEM_GIVE_RESOLVE = 1u << 1,
};
typedef struct ItemService {
ServiceHeader header;
/* Set or replace the calling mod's fixed override for name. */
ModResult (*set_check_override)(ModContext* ctx, const char* name, uint8_t item_no);
ModResult (*clear_check_override)(ModContext* ctx, const char* name);
/* Register for name, or for every check when name is NULL. out_handle may be NULL. */
ModResult (*set_check_resolver)(ModContext* ctx, const char* name, ItemCheckResolveFn fn,
void* user_data, ItemCheckHandle* out_handle);
ModResult (*clear_check_resolver)(ModContext* ctx, ItemCheckHandle handle);
/* Resolve without granting an item or notifying give observers. */
ModResult (*resolve_check)(
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.
* Returns MOD_UNAVAILABLE when the queue is full. Entries wait until gameplay is in a safe
* state and are cleared when the active save slot changes.
*/
ModResult (*give_item)(
ModContext* ctx, const char* check_name, uint8_t item_no, uint32_t flags);
/* Observe inventory grants. out_handle may be NULL. */
ModResult (*observe_gives)(
ModContext* ctx, ItemGiveObserveFn fn, void* user_data, ItemGiveHandle* out_handle);
ModResult (*unobserve_gives)(ModContext* ctx, ItemGiveHandle handle);
} ItemService;
MOD_DECLARE_SERVICE(ItemService, svc_item, ITEM_SERVICE_ID, ITEM_SERVICE_MAJOR, ITEM_SERVICE_MINOR);
+7 -1
View File
@@ -2260,6 +2260,12 @@ int daAlink_c::procCoGetItemInit() {
s16 var_r22 = 0;
BOOL var_r31 = FALSE;
BOOL var_r30 = FALSE;
#if TARGET_PC
// Queued grants have no event partner, so create the demo item from the event's GtItm.
if (dusk::mods::item_give_queue_dispatching()) {
mDemo.setParam0(0x100);
}
#endif
if (mProcID == PROC_GET_ITEM || mProcID == PROC_INSECT_CATCH ||
(mProcID == PROC_PREACTION_UNEQUIP && !checkNoUpperAnime()))
{
@@ -2299,7 +2305,7 @@ int daAlink_c::procCoGetItemInit() {
}
fpc_ProcID item_partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1,
fopAcM_GetRoomNo(this), NULL, NULL);
fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(dusk::mods::item_give_queue_take_tag()));
if (item_partner_id != fpcM_ERROR_PROCESS_ID_e) {
dComIfGp_event_setItemPartnerId(item_partner_id);
}
+4
View File
@@ -4084,6 +4084,10 @@ void daB_DS_c::executeBattle2Dead() {
camera->mCamera.SetTrimSize(0);
dComIfGp_event_reset();
dComIfGs_onStageBossEnemy(0x13);
#if TARGET_PC
// This reward has no original grant at this point in the cutscene.
dusk::mods::item_check_enqueue("Arbiters Grounds Dungeon Reward", dItemNo_NONE_e);
#endif
/* dSv_event_flag_c::F_0265 - Arbiter's Grounds - Arbiter's Grounds clear */
dComIfGs_onEventBit(0x2010);
fopAcM_delete(this);
+1 -1
View File
@@ -170,7 +170,7 @@ void daDitem_c::actionEvent() {
if (chkDead()) {
if (!chkArgFlag(0x1)) {
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
}
if (m_itemNo == dItemNo_KAKERA_HEART_e) {
+12 -5
View File
@@ -726,11 +726,18 @@ void daE_HP_c::executeDead() {
fopAcM_onSwitch(this, bitSw);
}
dComIfGs_addPohSpiritNum();
#if TARGET_PC
mItemCheckOverridden =
dusk::mods::item_check_poe(bitSw, dItemNo_POU_SPIRIT_e, this) != dItemNo_POU_SPIRIT_e;
if (mItemCheckOverridden) {
dusk::mods::item_check_enqueue_poe(bitSw, dItemNo_POU_SPIRIT_e);
} else
#endif
dComIfGs_addPohSpiritNum();
field_0x784 = -1;
if (dComIfGs_getPohSpiritNum() == 20) {
if (dComIfGs_getPohSpiritNum() == 20 IF_DUSK(&&!mItemCheckOverridden)) {
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[0x1c9]);
}
@@ -752,13 +759,13 @@ void daE_HP_c::executeDead() {
field_0x788 = 1;
}
}
} else if (field_0x788 != 0) {
} else if (field_0x788 != 0 IF_DUSK(|| mItemCheckOverridden)) {
fopAcM_createDisappear(this, &current.pos, 8, 3, 0xff);
fopAcM_delete(this);
} else {
if (field_0x784 == -1) {
field_0x784 = fopAcM_createItemForPresentDemo(&current.pos, dItemNo_POU_SPIRIT_e, 0, -1,
-1, 0, 0);
field_0x784 = fopAcM_createItemForPresentDemo(&current.pos, dItemNo_POU_SPIRIT_e, 0,
-1, -1, 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag_poe(bitSw)));
}
if (fopAcM_IsExecuting(field_0x784) != FALSE) {
+16 -1
View File
@@ -1132,12 +1132,20 @@ static void e_po_dead(e_po_class* i_this) {
camera_player->mCamera.Start();
camera_player->mCamera.SetTrimSize(0);
dComIfGp_event_reset();
dComIfGs_addPohSpiritNum();
#if TARGET_PC
if (dusk::mods::item_check_poe(i_this->BitSW, dItemNo_POU_SPIRIT_e, a_this) ==
dItemNo_POU_SPIRIT_e)
{
#endif
dComIfGs_addPohSpiritNum();
#if !PLATFORM_SHIELD
if (dComIfGs_getPohSpiritNum() == 0x14) {
/* dSv_event_flag_c::F_0457 - Castle Town - Revived cat */
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[457]);
}
#endif
#if TARGET_PC
}
#endif
daPy_getPlayerActorClass()->cancelOriginalDemo();
} else if (mArg0Check(i_this, 0) != 0) {
@@ -1265,8 +1273,15 @@ static void e_po_dead(e_po_class* i_this) {
}
} else {
if (i_this->field_0x75C == -1) {
#if TARGET_PC
const u8 itemNo =
dusk::mods::item_check_poe(i_this->BitSW, dItemNo_POU_SPIRIT_e, a_this);
i_this->field_0x75C = fopAcM_createItemForPresentDemo(&a_this->current.pos, itemNo,
0, -1, -1, NULL, NULL, dusk::mods::item_give_tag_poe(i_this->BitSW));
#else
i_this->field_0x75C = fopAcM_createItemForPresentDemo(&a_this->current.pos, 0xE0, 0,
-1, -1, NULL, NULL);
#endif
}
if (fopAcM_IsExecuting(i_this->field_0x75C)) {
i_this->field_0x762 =
+3 -1
View File
@@ -1250,7 +1250,9 @@ static void demo_camera(e_rdb_class* i_this) {
}
if (iVar1 != 0) {
daPy_getPlayerActorClass()->changeDemoMode(11, 32, 0, 0);
daPy_getPlayerActorClass()->changeDemoMode(11,
DUSK_ITEM_CHECK_EXPR("bulblin_key:D_MN09", dItemNo_SMALL_KEY_e, &i_this->enemy), 0,
0);
i_this->mDemoMode = 12;
i_this->field_0x10aa = 0;
i_this->field_0xfe5 = 1;
+3 -1
View File
@@ -931,7 +931,9 @@ static void get_demo(e_th_ball_class* i_this) {
case 0:
break;
case 1:
demo_id = fopAcM_createItemForTrBoxDemo(&i_this->current.pos, dItemNo_IRONBALL_e, -1, fopAcM_GetRoomNo(i_this), NULL, NULL);
demo_id = fopAcM_createItemForTrBoxDemo(&i_this->current.pos,
DUSK_ITEM_CHECK_EXPR("ball_and_chain:D_MN11", dItemNo_IRONBALL_e, i_this), -1,
fopAcM_GetRoomNo(i_this), NULL, NULL DUSK_GIVE_TAG("ball_and_chain:D_MN11"));
JUT_ASSERT(1670, demo_id != fpcM_ERROR_PROCESS_ID_e);
i_this->mDemoMode = 2;
break;
+3 -1
View File
@@ -1744,7 +1744,9 @@ int daNpc_Aru_c::cutSpeakTo(int i_staffID) {
switch (eventId) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("goats_reward", itemNo, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemNo,
0, -1, -1, NULL, NULL DUSK_GIVE_TAG("goats_reward"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+3 -2
View File
@@ -1001,8 +1001,9 @@ BOOL daNpcAshB_c::EvCut_Appear(int i_staffID) {
case '0008':
local_30[0] = 0;
if (mFlow.getEventId(local_30) == 1) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, local_30[0], 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("ashei_sketch", local_30[0], this);
mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, local_30[0], 0, -1, -1, 0, 0 DUSK_GIVE_TAG("ashei_sketch"));
dComIfGp_event_setItemPartnerId(mItemPartnerId);
mItemPartnerId = -1;
}
+12 -1
View File
@@ -1804,8 +1804,19 @@ int daNpcChin_c::_Evt_GameSucceed_CutMain(const int& param_0) {
itemId1 = 0;
}
#if TARGET_PC
const char* itemCheckName = nullptr;
if (itemId1 == dItemNo_ARROW_LV2_e) {
itemCheckName = "star_reward_1";
} else if (itemId1 == dItemNo_ARROW_LV3_e) {
itemCheckName = "star_reward_2";
}
if (itemCheckName != nullptr) {
itemId1 = dusk::mods::item_check(itemCheckName, itemId1, this);
}
#endif
fpc_ProcID itemId2 = fopAcM_createItemForPresentDemo(&current.pos, itemId1, 0, -1, -1,
0, 0);
0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
if (itemId2 != -1) {
dComIfGp_event_setItemPartnerId(itemId2);
}
+3 -1
View File
@@ -1333,7 +1333,9 @@ void daNpc_Fairy_c::PresentDemoCall() {
item_no = 0;
}
fpc_ProcID id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("fairy_reward:D_SB01", item_no, this);
fpc_ProcID id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, -1, NULL,
NULL DUSK_GIVE_TAG("fairy_reward:D_SB01"));
if (id != fpcM_ERROR_PROCESS_ID_e) {
dComIfGp_event_setItemPartnerId(id);
}
+9 -2
View File
@@ -4018,8 +4018,15 @@ BOOL daNpc_grA_c::talk(void*) {
}
if (r26 && talkProc(NULL, TRUE, NULL)) {
if (mFlow.getEventId(&sp8) == 1) {
field_0x1480 =
fopAcM_createItemForPresentDemo(&current.pos, sp8, 0, -1, -1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (sp8 == dItemNo_BOMB_IN_BAG_e) {
itemCheckName = "goron_reward:F_SP113";
sp8 = dusk::mods::item_check(itemCheckName, sp8, this);
}
#endif
field_0x1480 = fopAcM_createItemForPresentDemo(&current.pos, sp8, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
if (field_0x1480 != fpcM_ERROR_PROCESS_ID_e) {
s16 r25 = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff);
dComIfGp_getEvent()->reset(this);
+3 -1
View File
@@ -1681,7 +1681,9 @@ int daNpc_grO_c::talk(void* param_1) {
if (facePlayerFlag && talkProc(NULL, TRUE, NULL)) {
if (mType == TYPE_MINES) {
if (mFlow.getEventId(&itemId) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, itemId, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("key_shard_2:D_MN04", itemId, this);
mItemID = fopAcM_createItemForPresentDemo(&current.pos, itemId, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("key_shard_2:D_MN04"));
if (mItemID != fpcM_ERROR_PROCESS_ID_e) {
s16 eventIdx = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xFF);
dComIfGp_getEvent()->reset(this);
+3 -1
View File
@@ -1338,7 +1338,9 @@ int daNpc_grR_c::talk(void* param_1) {
if (bVar1 && talkProc(NULL, TRUE, NULL)) {
if (mType == TYPE_0) {
if (mFlow.getEventId(&i_itemNo) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, i_itemNo, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("key_shard_3:D_MN04", i_itemNo, this);
mItemID = fopAcM_createItemForPresentDemo(&current.pos, i_itemNo, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("key_shard_3:D_MN04"));
if (mItemID != fpcM_ERROR_PROCESS_ID_e) {
s16 i_eventID = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xFF);
+3 -2
View File
@@ -1190,8 +1190,9 @@ int daNpc_grS_c::talk(void* param_0) {
if (unkFlag1 && talkProc(NULL, 1, NULL)) {
if (mType == 0) {
if (mFlow.getEventId(&unkInt2) == 1) {
mPresentItemId =
fopAcM_createItemForPresentDemo(&current.pos, unkInt2, 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("key_shard_1:D_MN04", unkInt2, this);
mPresentItemId = fopAcM_createItemForPresentDemo(&current.pos, unkInt2, 0, -1,
-1, 0, 0 DUSK_GIVE_TAG("key_shard_1:D_MN04"));
if (mPresentItemId != fpcM_ERROR_PROCESS_ID_e) {
s16 eventIdx =
+6 -4
View File
@@ -971,8 +971,9 @@ BOOL daNpcImpal_c::EvCut_ImpalAppear1(int i_cut_index) {
if (talkProc(NULL, 1, NULL)) {
int evt_id = 0;
if (mFlow.getEventId(&evt_id) == 1) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, evt_id, 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("ilia_charm", evt_id, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, evt_id, 0, -1, -1, 0, 0 DUSK_GIVE_TAG("ilia_charm"));
if (mItemPartnerId != 0xffffffff) {
s16 evt_idx =
dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff);
@@ -1060,8 +1061,9 @@ BOOL daNpcImpal_c::EvCut_CopyRod(int i_cut_index) {
case '0003':
int evt_id = 0;
if (mFlow.getEventId(&evt_id) == 1) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, evt_id, 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("skybook", evt_id, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, evt_id, 0, -1, -1, 0, 0 DUSK_GIVE_TAG("skybook"));
dComIfGp_event_setItemPartnerId(mItemPartnerId);
mItemPartnerId = -1;
}
+16 -1
View File
@@ -11,6 +11,10 @@
#include "d/d_msg_object.h"
#include <cstring>
#if TARGET_PC
static u8 s_givenInsectId = dItemNo_NONE_e;
#endif
enum Ins_RES_File_ID {
/* BCK */
/* 0x06 */ BCK_INS_F_HAPPY = 0x6,
@@ -1259,6 +1263,7 @@ int daNpcIns_c::waitPresent(void* param_1) {
daPy_py_c* player = daPy_getPlayerActorClass();
player->changeOriginalDemo();
player->changeDemoMode(0x25, 2, type, 0);
IF_DUSK(s_givenInsectId = type;)
} else {
mInsectMsgNo = 0x719;
}
@@ -1487,7 +1492,17 @@ int daNpcIns_c::talk(void* param_1) {
OS_REPORT("会話終了時 イベントID=%d アイテムNo=%d\n", eventID, itemNo);
if (eventID == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
#if TARGET_PC
u32 itemGiveTag = 0;
if (s_givenInsectId != dItemNo_NONE_e) {
itemNo = dusk::mods::item_check_bug(
s_givenInsectId, itemNo & 0xFF, this);
itemGiveTag = dusk::mods::item_give_tag_bug(s_givenInsectId);
s_givenInsectId = dItemNo_NONE_e;
}
#endif
mItemID = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1,
-1, NULL, NULL IF_DUSK_ARG(itemGiveTag));
if (mItemID != fpcM_ERROR_PROCESS_ID_e) {
daPy_getPlayerActorClass()->cancelOriginalDemo();
+10 -1
View File
@@ -1181,7 +1181,16 @@ int daNpc_Kkri_c::talk(void*) {
switch (eventId) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, -1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (item_no == dItemNo_OIL_BOTTLE3_e) {
itemCheckName = "coro_bottle";
item_no = dusk::mods::item_check(itemCheckName, item_no, this);
}
#endif
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, item_no,
0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+2 -1
View File
@@ -1236,8 +1236,9 @@ int daNpc_Len_c::talk(void* param_0) {
switch (evt_id) {
case 1:
if (mItemPartnerId == -1) {
DUSK_ITEM_CHECK("renado_letter", local_18, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_18,
0, -1, -1, 0, 0);
0, -1, -1, 0, 0 DUSK_GIVE_TAG("renado_letter"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
mEvtNo = 1;
+4 -1
View File
@@ -2393,7 +2393,10 @@ int daNpc_Maro_c::cutArrowTutorial(int arg0) {
switch (evt_ret) {
case 1: {
if (mItemPartnerId == -1) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, evt_id, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("archery_reward:F_SP109", evt_id, this);
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, evt_id, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("archery_reward:F_SP109"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+9 -1
View File
@@ -1191,7 +1191,15 @@ int daNpc_myna2_c::ECut_gameGoalSuccess(int i_staffId) {
case 20: {
int itemNo = 0;
if (mFlow.getEventId(&itemNo) == 1) {
mItemPid = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (itemNo == dItemNo_KAKERA_HEART_e) {
itemCheckName = "plumm_minigame_reward";
itemNo = dusk::mods::item_check(itemCheckName, itemNo, this);
}
#endif
mItemPid = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
break;
}
+12 -1
View File
@@ -958,8 +958,19 @@ int daNpc_Pouya_c::cutHaveFavorToAsk(int param_0) {
switch (evt_id) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC
const char* itemCheckName = nullptr;
if (local_64 == dItemNo_DROP_BOTTLE_e) {
itemCheckName = "jovani_reward_1";
} else if (local_64 == dItemNo_SILVER_RUPEE_e) {
itemCheckName = "jovani_reward_2";
}
if (itemCheckName != nullptr) {
local_64 = dusk::mods::item_check(itemCheckName, local_64, this);
}
#endif
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_64, 0,
-1, -1, 0, 0);
-1, -1, 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
field_0xfce = 1;
+2 -1
View File
@@ -720,7 +720,8 @@ fpc_ProcID daNpcPray_c::createHeart() {
mDoMtx_stack_c::ZXYrotS(rot);
mDoMtx_stack_c::multVec(&offset, &offset);
pos += offset;
return fopAcM_createItemForBoss(&pos, dItemNo_KAKERA_HEART_e, fopAcM_GetRoomNo(this), &rot, &size, 0.0f, 0.0f, 0);
return fopAcM_createItemForBoss(&pos, dItemNo_KAKERA_HEART_e, fopAcM_GetRoomNo(this), &rot,
&size, 0.0f, 0.0f, 0 IF_DUSK_ARG("prayer_reward"));
}
BOOL daNpcPray_c::_Evt_GetHeart(int i_staffID) {
+6 -2
View File
@@ -1310,7 +1310,9 @@ bool daNpcRafrel_c::talk(void* param_0) {
OS_REPORT("会話終了時 イベントID=%d アイテムNo=%d\n", eventId, itemNo);
if (eventId == 1) {
field_0xe00 = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("auru_memo", itemNo, this);
field_0xe00 = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("auru_memo"));
if (field_0xe00 != fpcM_ERROR_PROCESS_ID_e) {
s16 eventIdx = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xFF);
dComIfGp_getEvent()->reset(this);
@@ -1562,7 +1564,9 @@ int daNpcRafrel_c::EvCut_Appear(int i_staffId) {
int itemNo = 0;
u16 eventId = mFlow.getEventId(&itemNo);
if (eventId == 1) {
field_0xe00 = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("auru_memo", itemNo, this);
field_0xe00 = fopAcM_createItemForPresentDemo(
&current.pos, itemNo, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("auru_memo"));
dComIfGp_event_setItemPartnerId(field_0xe00);
field_0xe00 = fpcM_ERROR_PROCESS_ID_e;
}
+2 -1
View File
@@ -823,8 +823,9 @@ BOOL daNpcThe_c::talk(void* param_0) {
}
int item_no = 0;
if (mFlow.getEventId(&item_no) == 1) {
DUSK_ITEM_CHECK("telma_invoice", item_no, this);
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, -1,
NULL, NULL);
NULL, NULL DUSK_GIVE_TAG("telma_invoice"));
if (mItemID != -1) {
s16 event_id = dComIfGp_getEventManager().getEventIdx(
this, "DEFAULT_GETITEM", 0xff);
+3 -2
View File
@@ -1165,8 +1165,9 @@ int daNpc_Uri_c::cutEndCarryTutorial(int param_1) {
(s32)mFlow.getEventId(&local_48) == 1)
{
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, local_48, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("uli_cradle_reward", local_48, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_48, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("uli_cradle_reward"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
/* T_0007 - Ordon Village - During Uli's pick-up tutorial */
+6 -4
View File
@@ -2213,8 +2213,9 @@ int daNpc_ykW_c::cutEndSnowboardRace(int param_0) {
switch (eventId) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemId, 0,
-1, -1, 0, 0);
DUSK_ITEM_CHECK("snowboard_race_reward", itemId, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemId, 0, -1,
-1, 0, 0 DUSK_GIVE_TAG("snowboard_race_reward"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
@@ -2911,8 +2912,9 @@ int daNpc_ykW_c::talk(void* param_0) {
switch (eventId) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("dungeon_map:D_MN11", itemNo, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemNo,
0, -1, -1, 0, 0 DUSK_GIVE_TAG("dungeon_map:D_MN11"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+13 -2
View File
@@ -2609,8 +2609,19 @@ BOOL daNpc_zrA_c::ECut_thanksBlast(int i_staffID) {
case 31: {
int item_id = 0;
if (mFlow.getEventId(&item_id) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_id,
0, -1, -1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (item_id == dItemNo_BOMB_IN_BAG_e) {
itemCheckName = "iza_reward_1";
} else if (item_id == dItemNo_BOMB_BAG_LV2_e) {
itemCheckName = "iza_reward_2";
}
if (itemCheckName != nullptr) {
item_id = dusk::mods::item_check(itemCheckName, item_id, this);
}
#endif
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_id, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
field_0x9eb = true;
break;
+3 -2
View File
@@ -1681,8 +1681,9 @@ BOOL daNpc_zrC_c::ECut_earringGet(int i_staffID) {
case 40: {
int item_no = 0;
if (mFlow.getEventId(&item_no) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_no,
0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("coral_earring", item_no, this);
mItemID = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("coral_earring"));
}
break;
}
+3 -2
View File
@@ -1729,8 +1729,9 @@ BOOL daNpc_zrZ_c::ECut_clothesGet(int i_staffID) {
}
item_no = 0;
if (mFlow.getEventId(&item_no) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_no,
0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("zora_armor", item_no, this);
mItemID = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("zora_armor"));
}
break;
+65 -14
View File
@@ -206,7 +206,7 @@ void daItem_c::CreateInit() {
initBaseMtx();
animPlay(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
if (m_itemNo == dItemNo_BOOMERANG_e) {
if (m_itemNo == dItemNo_BOOMERANG_e IF_DUSK(&&!mItemOverridden)) {
itemGetNextExecute();
} else if ((m_itemNo == dItemNo_ORANGE_RUPEE_e || m_itemNo == dItemNo_SILVER_RUPEE_e) &&
mSparkleEmtr.getEmitter() == NULL)
@@ -264,6 +264,17 @@ int daItem_c::_daItem_create() {
shape_angle.z = 0;
shape_angle.x = 0;
#if TARGET_PC
const u32 params = fopAcM_GetParam(this);
mOriginalItemNo = params & 0xFF;
const u8 resolvedItem = dusk::mods::item_check_freestanding(
daItem_prm::getItemBitNo(this), mOriginalItemNo, this);
mItemOverridden = resolvedItem != mOriginalItemNo;
mItemGiveTag = dusk::mods::item_give_tag_freestanding(daItem_prm::getItemBitNo(this));
if (mItemOverridden) {
fopAcM_SetParam(this, (params & 0xFFFFFF00) | resolvedItem);
}
#endif
field_0x95d = true;
}
@@ -507,9 +518,18 @@ void daItem_c::procInitGetDemoEvent() {
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(dEvtCnd_CANGETITEM_e);
m_item_id = fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this),
NULL, NULL);
#if TARGET_PC
const u8 displayItemNo = m_itemNo;
if (mItemOverridden) {
m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
}
#endif
m_item_id = fopAcM_createItemForTrBoxDemo(
&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag));
JUT_ASSERT(0, m_item_id != fpcM_ERROR_PROCESS_ID_e);
#if TARGET_PC
m_itemNo = displayItemNo;
#endif
setStatus(STATUS_WAIT_GET_DEMO_EVENT_e);
}
@@ -521,7 +541,7 @@ void daItem_c::procWaitGetDemoEvent() {
dComIfGp_event_setItemPartnerId(m_item_id);
}
} else {
if (m_itemNo == dItemNo_BOOMERANG_e) {
if (m_itemNo == dItemNo_BOOMERANG_e IF_DUSK(&&!mItemOverridden)) {
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(dEvtCnd_CANGETITEM_e);
return;
@@ -537,7 +557,9 @@ void daItem_c::procWaitGetDemoEvent() {
procInitSimpleGetDemo();
itemGet();
if (!haveItem) {
if (!haveItem IF_DUSK(&&(!mItemOverridden || (m_itemNo >= dItemNo_GREEN_RUPEE_e &&
m_itemNo <= dItemNo_SILVER_RUPEE_e))))
{
dComIfGs_offItemFirstBit(m_itemNo);
}
} else {
@@ -864,6 +886,12 @@ void daItem_c::itemGetNextExecute() {
procInitGetDemoEvent();
break;
default:
#if TARGET_PC
if (mItemOverridden) {
procInitGetDemoEvent();
break;
}
#endif
// "[daItem_c] Get process not defined[%d]\n"
OS_REPORT_ERROR("[daItem_c]ゲット処理が定義されていません[%d]\n", m_itemNo);
}
@@ -877,38 +905,48 @@ void daItem_c::itemGetNextExecute() {
}
void daItem_c::itemGet() {
#if TARGET_PC
const u8 displayItemNo = m_itemNo;
if (mItemOverridden) {
m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
}
#endif
switch (m_itemNo) {
#if TARGET_PC
case dItemNo_UTAWA_HEART_e:
case dItemNo_KAKERA_HEART_e:
#endif
case dItemNo_HEART_e:
mDoAud_seStart(Z2SE_HEART_PIECE_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_GREEN_RUPEE_e:
mDoAud_seStart(Z2SE_GREEN_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_BLUE_RUPEE_e:
mDoAud_seStart(Z2SE_BLUE_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_YELLOW_RUPEE_e:
mDoAud_seStart(Z2SE_BLUE_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_RED_RUPEE_e:
mDoAud_seStart(Z2SE_RED_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_PURPLE_RUPEE_e:
mDoAud_seStart(Z2SE_RED_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_ORANGE_RUPEE_e:
mDoAud_seStart(Z2SE_RED_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_SILVER_RUPEE_e:
mDoAud_seStart(Z2SE_RED_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_BOOMERANG_e:
break;
@@ -918,12 +956,25 @@ void daItem_c::itemGet() {
case dItemNo_ARROW_1_e:
case dItemNo_PACHINKO_SHOT_e:
mDoAud_seStart(Z2SE_CONSUMP_ITEM_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
#if TARGET_PC
break;
#endif
default:
#if TARGET_PC
if (mItemOverridden) {
mDoAud_seStart(Z2SE_CONSUMP_ITEM_GET, NULL, 0, 0);
execItemGet(m_itemNo, mItemGiveTag, this);
break;
}
#endif
// "[daItem_c] Get process not defined[%d]\n"
OS_REPORT_ERROR("[daItem_c]ゲット処理が定義されていません[%d]\n", m_itemNo);
break;
}
#if TARGET_PC
m_itemNo = displayItemNo;
#endif
}
BOOL daItem_c::checkCountTimer() {
+39 -2
View File
@@ -97,6 +97,12 @@ int daObjLife_c::Create() {
field_0x94c = 0.7f;
mRotateSpeed = 7000;
#if TARGET_PC
if (mOverrideHover) {
fopAcM_SetGravity(this, 0.0f);
mRotateSpeed = 550;
}
#endif
setEffect();
mSound.init(&current.pos, 1);
return 1;
@@ -140,6 +146,27 @@ int daObjLife_c::create() {
home.angle.x = home.angle.z = 0;
current.angle.x = current.angle.z = 0;
shape_angle.x = shape_angle.z = 0;
#if TARGET_PC
const u32 params = fopAcM_GetParam(this);
const u8 parameterItemNo = params & 0xFF;
if (mItemGiveOriginalNo == dItemNo_NONE_e) {
mOriginalItemNo = parameterItemNo;
const u8 resolvedItem =
dusk::mods::item_check_freestanding(getSaveBitNo(), mOriginalItemNo, this);
mItemGiveTag = dusk::mods::item_give_tag_freestanding(getSaveBitNo());
mItemOverridden = resolvedItem != mOriginalItemNo;
if (mItemOverridden) {
fopAcM_SetParam(this, (params & 0xFFFFFF00) | resolvedItem);
}
} else {
mOriginalItemNo = mItemGiveOriginalNo;
mItemOverridden = parameterItemNo != mItemGiveOriginalNo;
}
mOverrideHover =
mItemOverridden &&
(mOriginalItemNo == dItemNo_UTAWA_HEART_e ||
(mOriginalItemNo >= dItemNo_M_BEETLE_e && mOriginalItemNo <= dItemNo_F_MAYFLY_e));
#endif
mIsPrmsInit = true;
}
@@ -153,7 +180,7 @@ int daObjLife_c::create() {
return cPhs_ERROR_e;
}
if (m_itemNo == dItemNo_UTAWA_HEART_e && dComIfGs_isStageLife()) {
if (m_itemNo == dItemNo_UTAWA_HEART_e && dComIfGs_isStageLife() IF_DUSK(&&!mItemOverridden)) {
return cPhs_ERROR_e;
}
@@ -296,8 +323,18 @@ int daObjLife_c::initActionOrderGetDemo() {
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(dEvtCnd_CANGETITEM_e);
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL);
#if TARGET_PC
const u8 displayItemNo = m_itemNo;
if (mItemOverridden) {
m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
}
#endif
mItemId = fopAcM_createItemForTrBoxDemo(
&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag));
JUT_ASSERT(699, mItemId != fpcM_ERROR_PROCESS_ID_e);
#if TARGET_PC
m_itemNo = displayItemNo;
#endif
setStatus(STATUS_ORDER_GET_DEMO_e);
return 1;
+4 -2
View File
@@ -109,6 +109,7 @@ int daItemShield_c::__CreateHeap() {
int daItemShield_c::create() {
fopAcM_ct(this, daItemShield_c);
m_itemNo = dItemNo_WOOD_SHIELD_e;
DUSK_ITEM_CHECK("ordon_shield", m_itemNo, this);
if (fopAcM_isSwitch(this, getSwBit2())) {
OS_REPORT("木の盾:もう取ったので出ません\n");
return cPhs_ERROR_e;
@@ -240,8 +241,9 @@ int daItemShield_c::initActionOrderGetDemo() {
daItemBase_c::hide();
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(dEvtCnd_CANGETITEM_e);
mItemId =
fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), 0, 0);
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos,
DUSK_ITEM_CHECK_EXPR("ordon_shield", dItemNo_WOOD_SHIELD_e, this), -1,
fopAcM_GetRoomNo(this), 0, 0 DUSK_GIVE_TAG("ordon_shield"));
JUT_ASSERT(682, mItemId != fpcM_ERROR_PROCESS_ID_e)
setStatus(STATUS_ORDERGETDEMO);
return 1;
+4 -2
View File
@@ -37,6 +37,7 @@ int daObjSword_c::Create() {
cPhs_Step daObjSword_c::create() {
fopAcM_ct(this, daObjSword_c);
m_itemNo = 0x28;
DUSK_ITEM_CHECK("ordon_sword", m_itemNo, this);
if (fopAcM_isItem(this, getItemBit())) {
return cPhs_ERROR_e;
}
@@ -71,8 +72,9 @@ int daObjSword_c::initActionOrderGetDemo() {
hide();
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(8);
mProcID = fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this),
NULL, NULL);
mProcID =
fopAcM_createItemForTrBoxDemo(&current.pos, DUSK_ITEM_CHECK_EXPR("ordon_sword", 0x28, this),
-1, fopAcM_GetRoomNo(this), NULL, NULL DUSK_GIVE_TAG("ordon_sword"));
setStatus(1);
return 1;
}
+3 -2
View File
@@ -255,8 +255,9 @@ int daObjWStatue_c::initActionOrderGetDemo() {
s16 eventIdx = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff);
dComIfGp_getEvent()->reset(this);
fopAcM_orderChangeEventId(this, eventIdx, 1, 0xffff);
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, 0xffffffff,
fopAcM_GetRoomNo(this), 0, 0);
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos,
DUSK_ITEM_CHECK_EXPR("wood_statue", m_itemNo, this), 0xffffffff, fopAcM_GetRoomNo(this),
0, 0 DUSK_GIVE_TAG("wood_statue"));
JUT_ASSERT(544, mItemId != fpcM_ERROR_PROCESS_ID_e);
setStatus(STATUS_ORDER_GET_DEMO);
return 1;
+1
View File
@@ -32,6 +32,7 @@ int daObjZCloth_c::Create() {
int daObjZCloth_c::create() {
fopAcM_ct(this, daObjZCloth_c);
m_itemNo = 0x31;
DUSK_ITEM_CHECK("zora_armor", m_itemNo, this);
int phase = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo));
if (phase == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, (heapCallbackFunc)CheckFieldItemCreateHeap, 0x2fb0)) {
+59 -16
View File
@@ -11,6 +11,20 @@
#include "m_Do/m_Do_lib.h"
#include <cstring>
#if TARGET_PC
#include "d/d_item_data.h"
const ResourceData& daShopItem_c::getResourceData() const {
if (mItemOverridden) {
return mOverrideData;
}
return mData[mShopItemID];
}
#define SHOP_RESOURCE_DATA getResourceData()
#else
#define SHOP_RESOURCE_DATA mData[mShopItemID]
#endif
const char* daShopItem_c::getShopArcname() {
switch (m_itemNo) {
case dItemNo_NONE_e:
@@ -88,7 +102,29 @@ const char* daShopItem_c::getShopArcname() {
return NULL;
}
return mData[mShopItemID].get_arcName();
#if TARGET_PC
if (m_itemNo != dItemNo_NONE_e && mItemGiveOriginalNo == dItemNo_NONE_e) {
mItemGiveOriginalNo = m_itemNo;
const u8 resolvedItem = dusk::mods::item_check_shop(mItemGiveOriginalNo, this);
mItemOverridden = resolvedItem != mItemGiveOriginalNo;
if (mItemOverridden) {
mOverrideData = mData[mShopItemID];
mOverrideData.mArcName = dItem_data::getArcName(resolvedItem);
mOverrideData.mBmdName = dItem_data::getBmdName(resolvedItem);
mOverrideData.mBtkName = dItem_data::getBtkName(resolvedItem);
mOverrideData.mBckName = dItem_data::getBckName(resolvedItem);
mOverrideData.mBrkName = dItem_data::getBrkName(resolvedItem);
mOverrideData.mBtpName = dItem_data::getBtpName(resolvedItem);
mOverrideData.mTevFrm = dItem_data::getTevFrm(resolvedItem);
mOverrideData.mBtpFrm = -1;
mOverrideData.mFlag = static_cast<u32>(-1);
mOverrideData.mOffsetY = mShopItemID == SHOP_ITEMNO_ARMOR ? 60.0f : 15.0f;
mOverrideData.mScale = 1.0f;
}
}
#endif
return SHOP_RESOURCE_DATA.get_arcName();
}
DUSK_GAME_DATA const f32 daShopItem_c::m_cullfar_max = 5000.0f;
@@ -104,6 +140,11 @@ u16 daShopItem_c::getHeapSize() {
OS_REPORT("ShopItemID [%u]\n", a_ShopItemID);
ASSERT(a_ShopItemID < SHOP_ITEMNO_MAX);
#if TARGET_PC
if (mItemOverridden) {
return 0x8000;
}
#endif
return HeapSizeTbl[a_ShopItemID];
}
@@ -119,11 +160,11 @@ void daShopItem_c::CreateInit() {
if (strcmp("R_SP109", dComIfGp_getStartStageName()) == 0 && dComIfGp_getStartStageRoomNo() == 1)
{
scale.set(mData[mShopItemID].get_scale() * 0.8f, mData[mShopItemID].get_scale() * 0.8f,
mData[mShopItemID].get_scale() * 0.8f);
scale.set(SHOP_RESOURCE_DATA.get_scale() * 0.8f, SHOP_RESOURCE_DATA.get_scale() * 0.8f,
SHOP_RESOURCE_DATA.get_scale() * 0.8f);
} else {
scale.set(mData[mShopItemID].get_scale(), mData[mShopItemID].get_scale(),
mData[mShopItemID].get_scale());
scale.set(SHOP_RESOURCE_DATA.get_scale(), SHOP_RESOURCE_DATA.get_scale(),
SHOP_RESOURCE_DATA.get_scale());
}
home.pos = current.pos;
@@ -137,8 +178,8 @@ void daShopItem_c::set_mtx() {
if (daShopItem_prm::getGroup(this) == 15) {
mDoMtx_stack_c::transS(current.pos.x, current.pos.y, current.pos.z);
} else {
mDoMtx_stack_c::transS(current.pos.x, current.pos.y + mData[mShopItemID].get_offsetY(),
current.pos.z);
mDoMtx_stack_c::transS(
current.pos.x, current.pos.y + SHOP_RESOURCE_DATA.get_offsetY(), current.pos.z);
}
MTXCopy(mDoMtx_stack_c::get(), mMtx);
@@ -147,8 +188,8 @@ void daShopItem_c::set_mtx() {
if (daShopItem_prm::getGroup(this) == 15) {
mDoMtx_stack_c::ZXYrotM(-11300, 32700, 7300);
} else {
mDoMtx_stack_c::ZXYrotM(mAngleX + mData[mShopItemID].get_angleX(),
mData[mShopItemID].get_angleY(), mData[mShopItemID].get_angleZ());
mDoMtx_stack_c::ZXYrotM(mAngleX + SHOP_RESOURCE_DATA.get_angleX(),
SHOP_RESOURCE_DATA.get_angleY(), SHOP_RESOURCE_DATA.get_angleZ());
}
mDoMtx_stack_c::ZXYrotM(current.angle.x, current.angle.y, current.angle.z);
@@ -156,7 +197,7 @@ void daShopItem_c::set_mtx() {
if (daShopItem_prm::getGroup(this) == 15) {
mDoMtx_stack_c::XrotM(0);
} else {
mDoMtx_stack_c::XrotM(mData[mShopItemID].get_angleOffsetX());
mDoMtx_stack_c::XrotM(SHOP_RESOURCE_DATA.get_angleOffsetX());
}
mpModel->setBaseTRMtx(mDoMtx_stack_c::get());
@@ -190,29 +231,31 @@ void daShopItem_c::setShadow() {
}
BOOL daShopItem_c::chkFlag(int i_flag) {
return mData[mShopItemID].get_flag() & i_flag;
return SHOP_RESOURCE_DATA.get_flag() & i_flag;
}
s8 daShopItem_c::getTevFrm() {
return mData[mShopItemID].get_tevfrm();
return SHOP_RESOURCE_DATA.get_tevfrm();
}
s8 daShopItem_c::getBtpFrm() {
return mData[mShopItemID].get_btpfrm();
return SHOP_RESOURCE_DATA.get_btpfrm();
}
u8 daShopItem_c::getShadowSize() {
return mData[mShopItemID].get_shadowSize();
return SHOP_RESOURCE_DATA.get_shadowSize();
}
u8 daShopItem_c::getCollisionH() {
return mData[mShopItemID].get_collisionH();
return SHOP_RESOURCE_DATA.get_collisionH();
}
u8 daShopItem_c::getCollisionR() {
return mData[mShopItemID].get_collisionR();
return SHOP_RESOURCE_DATA.get_collisionR();
}
#undef SHOP_RESOURCE_DATA
int daShopItem_c::_create() {
fopAcM_ct(this, daShopItem_c);
+5 -3
View File
@@ -362,9 +362,11 @@ int daTagStatue_c::demoProc() {
item = dItemNo_AIR_LETTER_e;
}
mItemId =
fopAcM_createItemForTrBoxDemo(&current.pos, item, -1,
fopAcM_GetRoomNo(this), 0, 0);
#if TARGET_PC
item = dusk::mods::item_check_sky_character(item & 0xFF, this);
#endif
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos, item, -1,
fopAcM_GetRoomNo(this), 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag_sky_character()));
JUT_ASSERT(580, mItemId != fpcM_ERROR_PROCESS_ID_e);
+13 -2
View File
@@ -1390,6 +1390,10 @@ u8 daTbox_c::getBombItemNoMain(u8 i_itemNo) {
int daTbox_c::setGetDemoItem() {
u8 item_no = getItemNo();
#if TARGET_PC
const u32 giveTag = dusk::mods::item_give_tag_chest(getTboxNo());
item_no = dusk::mods::item_check_tagged(giveTag, mOriginalItemNo, this);
#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 ||
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_BOMB_INSECT_5_e || item_no == dItemNo_BOMB_INSECT_10_e || item_no == dItemNo_BOMB_INSECT_20_e || item_no == dItemNo_BOMB_INSECT_30_e)
@@ -1399,9 +1403,11 @@ int daTbox_c::setGetDemoItem() {
fpc_ProcID item_id;
if (field_0x718) {
item_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 1, -1, -1, NULL, NULL);
item_id = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
} else {
item_id = fopAcM_createItemForTrBoxDemo(&current.pos, item_no, -1, -1, NULL, NULL);
item_id = fopAcM_createItemForTrBoxDemo(
&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
}
if (item_id != fpcM_ERROR_PROCESS_ID_e) {
@@ -1785,6 +1791,11 @@ void daTbox_c::mode_exec() {
cPhs_Step daTbox_c::create1st() {
if (!mParamsInit) {
field_0x980 = home.angle.x;
#if TARGET_PC
mOriginalItemNo = (home.angle.z >> 8) & 0xFF;
const u8 resolvedItem = dusk::mods::item_check_chest(getTboxNo(), mOriginalItemNo, this);
home.angle.z = static_cast<s16>((home.angle.z & ~0xFF00) | (resolvedItem << 8));
#endif
field_0x982 = home.angle.z;
home.angle.z = 0;
home.angle.x = 0;
+18 -2
View File
@@ -138,6 +138,17 @@ int daTbox2_c::create1st() {
fopAcM_ct(this, daTbox2_c);
mModelType = getModelType();
#if TARGET_PC
if (!mParamsInit) {
mOriginalItemNo = getItemNo();
int tboxNo = fopAcM_GetParamBit(this, 16, 8);
const u8 resolvedItem = dusk::mods::item_check_chest(tboxNo, mOriginalItemNo, this);
u32 params = (fopAcM_GetParam(this) & 0xFFFFFF00) | resolvedItem;
fopAcM_SetParam(this, params);
mParamsInit = true;
}
#endif
int phase_state = dComIfG_resLoad(&mPhase, l_arcName);
if (phase_state == cPhs_COMPLEATE_e) {
u32 heap_size;
@@ -370,12 +381,17 @@ void daTbox2_c::actionOpenWait() {
int daTbox2_c::setGetDemoItem() {
u8 item_no = getItemNo();
#if TARGET_PC
int tboxNo = fopAcM_GetParamBit(this, 16, 8);
const u32 giveTag = dusk::mods::item_give_tag_chest(tboxNo);
item_no = dusk::mods::item_check_tagged(giveTag, mOriginalItemNo, this);
#endif
u32 partner_id;
if (mReturnRupee) {
partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 1, -1, -1, NULL, NULL);
partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
} else {
partner_id = fopAcM_createItemForTrBoxDemo(&current.pos, item_no, -1, -1, NULL, NULL);
partner_id = fopAcM_createItemForTrBoxDemo(&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
}
if (partner_id != -1) {
+7
View File
@@ -45,6 +45,12 @@ int CheckShopItemCreateHeap(fopAc_ac_c* i_this) {
daShopItem_c* a_this1 = static_cast<daShopItem_c*>(i_this);
daShopItem_c* a_this2 = static_cast<daShopItem_c*>(i_this);
#if TARGET_PC
const ResourceData& data = a_this2->getResourceData();
return a_this1->CreateItemHeap(data.get_arcName(), data.get_bmdName(), data.get_btk1Name(),
data.get_bpk1Name(), data.get_bck1Name(), data.get_bxa1Name(), data.get_brk1Name(),
data.get_btp1Name());
#else
u8 a_ShopItemID = a_this2->getShopItemID();
return a_this1->CreateItemHeap(daShopItem_c::mData[a_ShopItemID].get_arcName(),
daShopItem_c::mData[a_ShopItemID].get_bmdName(),
@@ -54,4 +60,5 @@ int CheckShopItemCreateHeap(fopAc_ac_c* i_this) {
daShopItem_c::mData[a_ShopItemID].get_bxa1Name(),
daShopItem_c::mData[a_ShopItemID].get_brk1Name(),
daShopItem_c::mData[a_ShopItemID].get_btp1Name());
#endif
}
+8
View File
@@ -335,6 +335,14 @@ bool dEvent_manager_c::setObjectArchive(DUSK_CONST char* arcname) {
if (arcname != NULL) {
rt = dComIfG_getObjectRes(arcname, DataFileName);
#if TARGET_PC
if (rt != nullptr && strcmp(arcname, "Prayer") == 0) {
// pointer to Prayer event `011get_item` prm0 in it's event_list.dat
u8* itemNo = static_cast<u8*>(rt) + 0x927;
*itemNo =
dusk::mods::item_check("prayer_reward", dItemNo_KAKERA_HEART_e, NULL);
}
#endif
int base_status = mEventList[BASE_ACTOR].init((char*)rt, -1);
#if DEBUG
+40 -7
View File
@@ -23,6 +23,7 @@
#include "m_Do/m_Do_graphic.h"
#include <cstring>
#include "dusk/utilities.hpp"
#include "dusk/version.hpp"
#if TARGET_PC
@@ -4110,11 +4111,43 @@ bool dFile_select_c::yesnoWakuAlpahAnm(u8 param_1) {
}
#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() {
static bool cachedPanes = false;
// Get pre-scale values for each pane
if (!cachedPanes) {
for (PaneCache& entry : mSelDtPanes) {
for (dusk::utils::PaneCache& entry : mSelDtPanes) {
J2DPane* pane = mSelDt.ScrDt->search(entry.tag);
if (!entry.cached) {
entry.origTransX = pane->getTranslateX();
@@ -4122,7 +4155,7 @@ void dFile_select_c::fileSelectWide() {
entry.cached = true;
}
}
for (PaneCache& entry : fileSelPanes) {
for (dusk::utils::PaneCache& entry : fileSelPanes) {
J2DPane* pane = fileSel.Scr->search(entry.tag);
if (!entry.cached) {
entry.origTransX = pane->getTranslateX();
@@ -4136,13 +4169,13 @@ void dFile_select_c::fileSelectWide() {
// Reset all panes
mSelDt.ScrDt->scale(1.0f, 1.0f);
mSelDt.ScrDt->translate(0.0f, 0.0f);
for (PaneCache& entry : mSelDtPanes) {
for (dusk::utils::PaneCache& entry : mSelDtPanes) {
J2DPane* pane = mSelDt.ScrDt->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f);
pane->translate(entry.origTransX, entry.origTransY);
}
for (PaneCache& entry : fileSelPanes) {
for (dusk::utils::PaneCache& entry : fileSelPanes) {
J2DPane* pane = fileSel.Scr->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f);
@@ -4214,7 +4247,7 @@ void dFile_select_c::fileSelectWide() {
mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
// Spirals & Memory Card Text
for (PaneCache& entry : fileSelPanes) {
for (dusk::utils::PaneCache& entry : fileSelPanes) {
J2DPane* pane = fileSel.Scr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
}
@@ -4240,7 +4273,7 @@ void dFile_select_c::fileSelectWide() {
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);
for (PaneCache& entry : mSelDtPanes) {
for (dusk::utils::PaneCache& entry : mSelDtPanes) {
const size_t index = &entry - mSelDtPanes;
J2DPane* pane = mSelDt.ScrDt->search(entry.tag);
pane->setBasePosition(J2DBasePosition_0);
@@ -4308,7 +4341,7 @@ void dFile_select_c::fileSelectWide() {
}
// Spirals & Memory Card Text
for (PaneCache& entry : fileSelPanes) {
for (dusk::utils::PaneCache& entry : fileSelPanes) {
J2DPane* pane = fileSel.Scr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
}
+2 -1
View File
@@ -274,8 +274,9 @@ inline void getItemFunc(u8 i_itemNo) {
item_func_ptr[i_itemNo]();
}
void execItemGet(u8 i_itemNo) {
void execItemGet(u8 i_itemNo IF_DUSK_ARG(u32 i_itemGiveTag) IF_DUSK_ARG(fopAc_ac_c* i_giver)) {
getItemFunc(i_itemNo);
IF_DUSK(dusk::mods::item_granted(i_itemNo, i_itemGiveTag, i_giver);)
}
static int (*item_getcheck_func_ptr[256])() = {
+1 -1
View File
@@ -2346,7 +2346,7 @@ void dScnKy_env_light_c::setLight() {
u8 next_pal_end_id;
#if TARGET_PC
const f32 deltaTime = dusk::game_clock::consume_interval(this);
timeScale = deltaTime / dusk::game_clock::period_for_original_frames(1.0f);
timeScale = deltaTime / dusk::game_clock::kSimPeriod;
#endif
setLight_palno_get(&g_env_light.PrevCol, &g_env_light.UseCol, &g_env_light.wether_pat0,
&g_env_light.wether_pat1, &prev_pal_start_id, &prev_pal_end_id,
+1 -1
View File
@@ -118,7 +118,7 @@ static int dKyeff_Create(kankyo_class* i_this) {
if (strcmp(dComIfGp_getStartStageName(), "Name") == 0) {
camera_process_class* camera = dComIfGp_getCamera(0);
OSTime time = OSGetTime();
OSTime time = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime());
OSTicksToCalendarTime(time, &calendar);
g_env_light.global_wind_influence.vec.x = 1.0f;
+3 -128
View File
@@ -16,23 +16,14 @@
#ifdef TARGET_PC
#include "dusk/settings.h"
#include "dusk/hq_minimap.hpp"
#include "m_Do/m_Do_graphic.h"
#include <dolphin/gx/GXAurora.h>
#include <aurora/math.hpp>
#include <algorithm>
#include <array>
#include <cmath>
#include <functional>
#include <limits>
#include <numbers>
#include <span>
constexpr u16 kMapIconResolutionMultiplier = 4;
constexpr u16 kMapImageSide = 16 * kMapIconResolutionMultiplier;
constexpr u32 kMapImageTotalPixels = kMapImageSide * kMapImageSide;
typedef std::function<u8(size_t, size_t)> PaintI8Fn;
u16 scaled_map_axis(u16 value, f32 scale) {
const auto scaledValue =
@@ -59,27 +50,6 @@ aurora::Vec2<u16> map_render_size_for(u16 width, u16 height) {
scaled_map_axis(height, irScaleY * hudScale),
};
}
void paint_i8(std::span<u8> dst, size_t width, PaintI8Fn paint) {
const auto blocksAcross = width >> 3;
for (size_t i = 0; i < dst.size(); i++) {
// 8x4 block swizzling for I8
const auto blockIdx = i >> 5;
const auto localIdx = i & 31;
const auto blockY = blockIdx / blocksAcross;
const auto blockX = blockIdx % blocksAcross;
const auto localY = localIdx >> 3;
const auto localX = localIdx & 7;
const auto x = (blockX << 3) + localX;
const auto y = (blockY << 2) + localY;
dst[i] = paint(x, y);
}
}
#endif
void dMpath_n::dTexObjAggregate_c::create() {
@@ -100,106 +70,11 @@ void dMpath_n::dTexObjAggregate_c::create() {
JUT_ASSERT(72, image != NULL);
JUT_ASSERT(73, image->minFilter == GX_NEAR);
JUT_ASSERT(74, image->magFilter == GX_NEAR);
IF_DUSK(dusk::hq_minimap::register_pointer(data[lp1], reinterpret_cast<u8*>(image) + image->imageOffset));
mDoLib_setResTimgObj(image, mp_texObj[lp1], 0, NULL);
}
#if TARGET_PC
static bool hqTexsDrawn = false;
static u8 hqCircleData[kMapImageTotalPixels];
static u8 hqCircleAltData[kMapImageTotalPixels];
static u8 hqNijumaruData[kMapImageTotalPixels];
static u8 hqEnterData[kMapImageTotalPixels];
static u8 hqTryForceData[kMapImageTotalPixels];
if (!hqTexsDrawn) {
constexpr auto center = kMapImageSide / 2.0f;
constexpr auto radiusSq = center * center;
// 6: map_icon_circle16x16_4i.bti - simple circle
paint_i8(std::span{hqCircleData}, kMapImageSide, [=](auto x, auto y) {
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
return (dx * dx + dy * dy < radiusSq) ? 0x11 : 0;
});
// 4: im_map_icon_circle_4i.bti - outlined circle
paint_i8(std::span{hqCircleAltData}, kMapImageSide, [=](auto x, auto y) {
constexpr auto innerRadius = kMapImageSide * 3.0f / 8.0f;
constexpr auto innerRadiusSq = innerRadius * innerRadius;
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
return dSq < radiusSq ? (dSq < innerRadiusSq ? 0x22 : 0x11) : 0;
});
// 3: im_map_icon_nijumaru_4i.bti - concentric rings
paint_i8(std::span{hqNijumaruData}, kMapImageSide, [=](auto x, auto y) {
constexpr u8 nijumaruRings[] = {0x11, 0x22, 0x11, 0x11, 0x22, 0x22};
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
if (dSq < radiusSq) {
const auto ringIndex =
static_cast<size_t>(std::trunc(std::sqrt(dSq) / kMapImageSide * 12));
return nijumaruRings[ringIndex];
}
return u8{0};
});
// 2: im_map_icon_enter_4i.bti - outlined octagram
paint_i8(std::span{hqEnterData}, kMapImageSide, [=](auto x, auto y) {
constexpr auto outlineWidth = kMapImageSide / 6.0f;
const auto adx = std::abs((x + 0.5f) - center);
const auto ady = std::abs((y + 0.5f) - center);
const auto dist =
std::min(adx + ady, std::max(adx, ady) * std::numbers::sqrt2_v<float>) -
kMapImageSide / 2.0f;
return dist > 0.0f ? 0 : (dist > -outlineWidth ? 0x22 : 0x33);
});
// 5: im_map_icon_try_force_4i.bti - outlined circle with triangle
paint_i8(std::span{hqTryForceData}, kMapImageSide, [=](auto x, auto y) {
constexpr auto innerRadiusNorm = 5.0f / 12.0f;
constexpr auto innerRadius = kMapImageSide * innerRadiusNorm;
constexpr auto innerRadiusSq = innerRadius * innerRadius;
constexpr auto triRadius = kMapImageSide * innerRadiusNorm / 2.0f;
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
const auto triSideDist = (std::numbers::sqrt3_v<float> * std::abs(dx) - dy) * 0.5f;
const auto insideTri = std::max(dy, triSideDist) < triRadius;
return insideTri ? 0x22 : (dSq < radiusSq ? (dSq < innerRadiusSq ? 0x33 : 0x22) : 0);
});
hqTexsDrawn = true;
}
constexpr auto replacements = std::to_array<std::pair<size_t, const u8*> >({
{2, hqEnterData},
{3, hqNijumaruData},
{4, hqCircleAltData},
{5, hqTryForceData},
{6, hqCircleData},
});
for (const auto& [idx, data] : replacements) {
JKR_DELETE(mp_texObj[idx]);
const auto texobj = JKR_NEW TGXTexObj();
GXInitTexObj(
texobj, data, kMapImageSide, kMapImageSide, GX_TF_I8, GX_CLAMP, GX_CLAMP, GX_FALSE);
GXInitTexObjLOD(texobj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
mp_texObj[idx] = texobj;
}
#endif
IF_DUSK(dusk::hq_minimap::initialize_if_needed());
}
void dMpath_n::dTexObjAggregate_c::remove() {
+36 -2
View File
@@ -39,6 +39,7 @@
#if TARGET_PC
#include "dusk/menu_pointer.h"
#include "dusk/utilities.hpp"
#endif
typedef void (dMenu_Collect2D_c::*initFunc)();
@@ -101,11 +102,44 @@ dMenu_Collect2D_c::~dMenu_Collect2D_c() {
}
#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() {
static bool cachedPanes = false;
// Get pre-scale values for each pane
if (!cachedPanes) {
for (PaneCache& entry : mpScreenPanes) {
for (dusk::utils::PaneCache& entry : mpScreenPanes) {
J2DPane* pane = mpScreen->search(entry.tag);
if (!entry.cached) {
entry.origTransX = pane->getTranslateX();
@@ -119,7 +153,7 @@ void dMenu_Collect2D_c::menuCollectWide() {
// Reset all panes
mpScreen->scale(1.0f, 1.0f);
mpScreen->translate(0.0f, 0.0f);
for (PaneCache& entry : mpScreenPanes) {
for (dusk::utils::PaneCache& entry : mpScreenPanes) {
J2DPane* pane = mpScreen->search(entry.tag);
pane->scale(1.0f, 1.0f);
pane->translate(entry.origTransX, entry.origTransY);
+1 -1
View File
@@ -1798,7 +1798,7 @@ void dMenu_Ring_c::drawSelectItem() {
if (field_0x674[i] != 0) {
#if TARGET_PC
mSelectItemSlideElapsed[i] += dusk::game_clock::consume_interval(this);
const f32 u = std::min(mSelectItemSlideElapsed[i] / dusk::game_clock::period_for_original_frames(10.0f), 1.0f);
const f32 u = std::min(mSelectItemSlideElapsed[i] / (dusk::game_clock::kSimPeriod * 10.0f), 1.0f);
if (u >= 1.0f) {
setSelectItemForce(i);
} else {
+7 -5
View File
@@ -123,7 +123,13 @@ public:
#endif
}
void setCaptureFlag() { mFlag = 1; }
void setCaptureFlag() {
mFlag = 1;
#ifdef TARGET_PC
dusk::frame_interp::request_presentation_sync();
#endif
}
bool checkDraw() { return mFlag; }
u8 getAlpha() { return mAlpha; }
u8 getTopFlag() { return mTopFlag; }
@@ -1092,10 +1098,6 @@ void dMw_c::dMw_ring_create(u8 i_origin) {
}
mpCapture->setCaptureFlag();
#ifdef TARGET_PC
dusk::frame_interp::request_presentation_sync();
#endif
}
bool dMw_c::dMw_ring_delete() {
+30 -9
View File
@@ -10,6 +10,7 @@
#include <cstring>
#include "JSystem/J2DGraph/J2DAnmLoader.h"
#include "dusk/utilities.hpp"
#include "dusk/version.hpp"
#include "f_op/f_op_msg_mng.h"
@@ -1512,11 +1513,31 @@ void dName_c::selectCursorPosSet(int row) {
}
#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() {
static bool cachedPanes = false;
// Get pre-scale values for each pane
if (!cachedPanes) {
for (PaneCache& entry : l_tagName) {
for (dusk::utils::PaneCache& entry : l_tagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
if (!entry.cached) {
entry.origTransX = pane->getTranslateX();
@@ -1524,7 +1545,7 @@ void dName_c::nameWide() {
entry.cached = true;
}
}
for (PaneCache& entry : l_nameTagName) {
for (dusk::utils::PaneCache& entry : l_nameTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
if (!entry.cached) {
entry.origTransX = pane->getTranslateX();
@@ -1532,7 +1553,7 @@ void dName_c::nameWide() {
entry.cached = true;
}
}
for (PaneCache& entry : l_nameCurTagName) {
for (dusk::utils::PaneCache& entry : l_nameCurTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
if (!entry.cached) {
entry.origTransX = pane->getTranslateX();
@@ -1546,19 +1567,19 @@ void dName_c::nameWide() {
// Reset all panes
nameIn.NameInScr->scale(1.0f, 1.0f);
nameIn.NameInScr->translate(0.0f, 0.0f);
for (PaneCache& entry : l_tagName) {
for (dusk::utils::PaneCache& entry : l_tagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f);
pane->translate(entry.origTransX, entry.origTransY);
}
for (PaneCache& entry : l_nameTagName) {
for (dusk::utils::PaneCache& entry : l_nameTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f);
pane->translate(entry.origTransX, entry.origTransY);
}
for (PaneCache& entry : l_nameCurTagName) {
for (dusk::utils::PaneCache& entry : l_nameCurTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->setBasePosition(J2DBasePosition_4);
pane->scale(1.0f, 1.0f);
@@ -1574,17 +1595,17 @@ void dName_c::nameWide() {
break;
default: // Wii and Dusklight
// List of Characters Box
for (PaneCache& entry : l_tagName) {
for (dusk::utils::PaneCache& entry : l_tagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
}
// Letters being typed
for (PaneCache& entry : l_nameTagName) {
for (dusk::utils::PaneCache& entry : l_nameTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
}
// Underscores when typing below letters
for (PaneCache& entry : l_nameCurTagName) {
for (dusk::utils::PaneCache& entry : l_nameCurTagName) {
J2DPane* pane = nameIn.NameInScr->search(entry.tag);
pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
}
+2 -2
View File
@@ -1828,7 +1828,7 @@ int dSv_info_c::memory_to_card(char* card_ptr, int dataNum) {
savedata->getPlayer().getPlayerInfo().setTotalTime(play_time);
}
savedata->getPlayer().getPlayerStatusB().setDateIpl(OSGetTime());
savedata->getPlayer().getPlayerStatusB().setDateIpl(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()));
memcpy(card_ptr, savedata, sizeof(dSv_save_c));
card_ptr += 0x958;
@@ -2035,7 +2035,7 @@ void flagFile_c::listenPropertyEvent(const JORPropertyEvent* i_event) {
}
case 102: {
OSCalendarTime time;
OSTicksToCalendarTime(OSGetTime(), &time);
OSTicksToCalendarTime(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()), &time);
const char* start_stage_name = dComIfGp_getStartStageName();
char filename[64];

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