From 7434a0f834866294e7691bd86ab9527011265585 Mon Sep 17 00:00:00 2001 From: Giorgio Mendieta <31053658+GiorgioMendieta@users.noreply.github.com> Date: Sun, 9 Aug 2026 00:14:52 +0200 Subject: [PATCH 01/15] Enable color diagnostics for CMake (#2273) --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 476190563a..f89d82f587 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +set(CMAKE_COLOR_DIAGNOSTICS ON) # Folder-based instead of target-based organization # in Visual Studio and Xcode generators From 71b2eedc826464de4714b0c0fc257d057a57648e Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Sun, 9 Aug 2026 16:40:26 -0700 Subject: [PATCH 02/15] initial item service implementation --- files.cmake | 5 + include/d/actor/d_a_e_hp.h | 3 + include/d/actor/d_a_obj_life_container.h | 5 + include/d/actor/d_a_tbox.h | 3 + include/d/d_a_item_static.h | 4 + include/d/d_a_shop_item_static.h | 7 + include/d/d_item.h | 4 +- include/f_op/f_op_actor.h | 5 + include/f_op/f_op_actor_mng.h | 33 ++- include/global.h | 13 + sdk/include/mods/svc/item.h | 94 +++++++ src/d/actor/d_a_alink_demo.inc | 8 +- src/d/actor/d_a_b_ds.cpp | 4 + src/d/actor/d_a_demo_item.cpp | 2 +- src/d/actor/d_a_e_hp.cpp | 17 +- src/d/actor/d_a_e_po.cpp | 17 +- src/d/actor/d_a_e_rdb.cpp | 4 +- src/d/actor/d_a_e_th_ball.cpp | 4 +- src/d/actor/d_a_npc_aru.cpp | 4 +- src/d/actor/d_a_npc_ashB.cpp | 5 +- src/d/actor/d_a_npc_chin.cpp | 13 +- src/d/actor/d_a_npc_fairy.cpp | 4 +- src/d/actor/d_a_npc_gra.cpp | 11 +- src/d/actor/d_a_npc_gro.cpp | 4 +- src/d/actor/d_a_npc_grr.cpp | 4 +- src/d/actor/d_a_npc_grs.cpp | 5 +- src/d/actor/d_a_npc_impal.cpp | 10 +- src/d/actor/d_a_npc_ins.cpp | 17 +- src/d/actor/d_a_npc_kkri.cpp | 11 +- src/d/actor/d_a_npc_len.cpp | 3 +- src/d/actor/d_a_npc_maro.cpp | 5 +- src/d/actor/d_a_npc_myna2.cpp | 10 +- src/d/actor/d_a_npc_pouya.cpp | 13 +- src/d/actor/d_a_npc_prayer.cpp | 3 +- src/d/actor/d_a_npc_rafrel.cpp | 8 +- src/d/actor/d_a_npc_the.cpp | 3 +- src/d/actor/d_a_npc_uri.cpp | 5 +- src/d/actor/d_a_npc_ykw.cpp | 10 +- src/d/actor/d_a_npc_zra.inc | 15 +- src/d/actor/d_a_npc_zrc.cpp | 5 +- src/d/actor/d_a_npc_zrz.cpp | 5 +- src/d/actor/d_a_obj_item.cpp | 79 +++++- src/d/actor/d_a_obj_life_container.cpp | 41 ++- src/d/actor/d_a_obj_shield.cpp | 6 +- src/d/actor/d_a_obj_sword.cpp | 6 +- src/d/actor/d_a_obj_wood_statue.cpp | 5 +- src/d/actor/d_a_obj_zcloth.cpp | 1 + src/d/actor/d_a_shop_item.cpp | 75 +++-- src/d/actor/d_a_tag_statue_evt.cpp | 8 +- src/d/actor/d_a_tbox.cpp | 15 +- src/d/d_a_shop_item_static.cpp | 7 + src/d/d_event_manager.cpp | 8 + src/d/d_item.cpp | 3 +- src/d/d_shop_system.cpp | 20 +- src/dusk/mods/item.hpp | 37 +++ src/dusk/mods/item_checks.cpp | 294 +++++++++++++++++++ src/dusk/mods/item_gives.cpp | 343 +++++++++++++++++++++++ src/dusk/mods/svc/item.cpp | 151 ++++++++++ src/dusk/mods/svc/item.hpp | 29 ++ src/dusk/mods/svc/registry.cpp | 1 + src/dusk/mods/svc/registry.hpp | 1 + src/dusk/mods/svc/save.cpp | 4 + src/f_op/f_op_actor.cpp | 4 + src/f_op/f_op_actor_mng.cpp | 69 +++-- 64 files changed, 1477 insertions(+), 135 deletions(-) create mode 100644 sdk/include/mods/svc/item.h create mode 100644 src/dusk/mods/item.hpp create mode 100644 src/dusk/mods/item_checks.cpp create mode 100644 src/dusk/mods/item_gives.cpp create mode 100644 src/dusk/mods/svc/item.cpp create mode 100644 src/dusk/mods/svc/item.hpp diff --git a/files.cmake b/files.cmake index 4fddf9b9dd..6fc4762be3 100644 --- a/files.cmake +++ b/files.cmake @@ -1472,6 +1472,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 +1486,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 diff --git a/include/d/actor/d_a_e_hp.h b/include/d/actor/d_a_e_hp.h index 8941f1e2a6..594c0de01f 100644 --- a/include/d/actor/d_a_e_hp.h +++ b/include/d/actor/d_a_e_hp.h @@ -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); diff --git a/include/d/actor/d_a_obj_life_container.h b/include/d/actor/d_a_obj_life_container.h index 14187d8289..1b98c1afe8 100644 --- a/include/d/actor/d_a_obj_life_container.h +++ b/include/d/actor/d_a_obj_life_container.h @@ -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); diff --git a/include/d/actor/d_a_tbox.h b/include/d/actor/d_a_tbox.h index 2b48a156eb..6d0528d29d 100644 --- a/include/d/actor/d_a_tbox.h +++ b/include/d/actor/d_a_tbox.h @@ -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); diff --git a/include/d/d_a_item_static.h b/include/d/d_a_item_static.h index 36c445d511..6b8b321b2b 100644 --- a/include/d/d_a_item_static.h +++ b/include/d/d_a_item_static.h @@ -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 */ diff --git a/include/d/d_a_shop_item_static.h b/include/d/d_a_shop_item_static.h index eeda9185da..7126578092 100644 --- a/include/d/d_a_shop_item_static.h +++ b/include/d/d_a_shop_item_static.h @@ -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); diff --git a/include/d/d_item.h b/include/d/d_item.h index d9bdfd18c9..bf1252f9d6 100644 --- a/include/d/d_item.h +++ b/include/d/d_item.h @@ -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(); diff --git a/include/f_op/f_op_actor.h b/include/f_op/f_op_actor.h index 1dce990866..2ac262ced1 100644 --- a/include/f_op/f_op_actor.h +++ b/include/f_op/f_op_actor.h @@ -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 diff --git a/include/f_op/f_op_actor_mng.h b/include/f_op/f_op_actor_mng.h index 6461c823d1..b23fc40383 100644 --- a/include/f_op/f_op_actor_mng.h +++ b/include/f_op/f_op_actor_mng.h @@ -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, diff --git a/include/global.h b/include/global.h index f77694c133..4a86f0678f 100644 --- a/include/global.h +++ b/include/global.h @@ -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 diff --git a/sdk/include/mods/svc/item.h b/sdk/include/mods/svc/item.h new file mode 100644 index 0000000000..81a2589f6d --- /dev/null +++ b/sdk/include/mods/svc/item.h @@ -0,0 +1,94 @@ +#pragma once + +#include + +#ifdef __cplusplus +#include +#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); diff --git a/src/d/actor/d_a_alink_demo.inc b/src/d/actor/d_a_alink_demo.inc index cf1a222c9c..c38b430908 100644 --- a/src/d/actor/d_a_alink_demo.inc +++ b/src/d/actor/d_a_alink_demo.inc @@ -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(¤t.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); } diff --git a/src/d/actor/d_a_b_ds.cpp b/src/d/actor/d_a_b_ds.cpp index 17bdd9564b..e0623acaba 100644 --- a/src/d/actor/d_a_b_ds.cpp +++ b/src/d/actor/d_a_b_ds.cpp @@ -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); diff --git a/src/d/actor/d_a_demo_item.cpp b/src/d/actor/d_a_demo_item.cpp index 209bd483c4..5876c26718 100644 --- a/src/d/actor/d_a_demo_item.cpp +++ b/src/d/actor/d_a_demo_item.cpp @@ -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) { diff --git a/src/d/actor/d_a_e_hp.cpp b/src/d/actor/d_a_e_hp.cpp index 51ac16fcf3..8c9d9398ea 100644 --- a/src/d/actor/d_a_e_hp.cpp +++ b/src/d/actor/d_a_e_hp.cpp @@ -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, ¤t.pos, 8, 3, 0xff); fopAcM_delete(this); } else { if (field_0x784 == -1) { - field_0x784 = fopAcM_createItemForPresentDemo(¤t.pos, dItemNo_POU_SPIRIT_e, 0, -1, - -1, 0, 0); + field_0x784 = fopAcM_createItemForPresentDemo(¤t.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) { diff --git a/src/d/actor/d_a_e_po.cpp b/src/d/actor/d_a_e_po.cpp index 1c4e95fa61..f2971f23a3 100644 --- a/src/d/actor/d_a_e_po.cpp +++ b/src/d/actor/d_a_e_po.cpp @@ -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 = diff --git a/src/d/actor/d_a_e_rdb.cpp b/src/d/actor/d_a_e_rdb.cpp index 7eccf34ef5..ed0459432f 100644 --- a/src/d/actor/d_a_e_rdb.cpp +++ b/src/d/actor/d_a_e_rdb.cpp @@ -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("Hyrule Castle King Bulblin Key", dItemNo_SMALL_KEY_e, &i_this->enemy), 0, + 0); i_this->mDemoMode = 12; i_this->field_0x10aa = 0; i_this->field_0xfe5 = 1; diff --git a/src/d/actor/d_a_e_th_ball.cpp b/src/d/actor/d_a_e_th_ball.cpp index e9be033af2..4a4c322cd1 100644 --- a/src/d/actor/d_a_e_th_ball.cpp +++ b/src/d/actor/d_a_e_th_ball.cpp @@ -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("Snowpeak Ruins Ball and Chain", dItemNo_IRONBALL_e, i_this), -1, + fopAcM_GetRoomNo(i_this), NULL, NULL DUSK_GIVE_TAG("Snowpeak Ruins Ball and Chain")); JUT_ASSERT(1670, demo_id != fpcM_ERROR_PROCESS_ID_e); i_this->mDemoMode = 2; break; diff --git a/src/d/actor/d_a_npc_aru.cpp b/src/d/actor/d_a_npc_aru.cpp index 7e6558b877..9d9356a698 100644 --- a/src/d/actor/d_a_npc_aru.cpp +++ b/src/d/actor/d_a_npc_aru.cpp @@ -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(¤t.pos, itemNo, 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Herding Goats Reward", itemNo, this); + mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, itemNo, + 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("Herding Goats Reward")); } if (fopAcM_IsExecuting(mItemPartnerId)) { diff --git a/src/d/actor/d_a_npc_ashB.cpp b/src/d/actor/d_a_npc_ashB.cpp index a4dd015906..29e349ba55 100644 --- a/src/d/actor/d_a_npc_ashB.cpp +++ b/src/d/actor/d_a_npc_ashB.cpp @@ -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(¤t.pos, local_30[0], 0, -1, -1, 0, 0); + DUSK_ITEM_CHECK("Ashei Sketch", local_30[0], this); + mItemPartnerId = fopAcM_createItemForPresentDemo( + ¤t.pos, local_30[0], 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Ashei Sketch")); dComIfGp_event_setItemPartnerId(mItemPartnerId); mItemPartnerId = -1; } diff --git a/src/d/actor/d_a_npc_chin.cpp b/src/d/actor/d_a_npc_chin.cpp index f31cac9822..826cb27b53 100644 --- a/src/d/actor/d_a_npc_chin.cpp +++ b/src/d/actor/d_a_npc_chin.cpp @@ -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 Prize 1"; + } else if (itemId1 == dItemNo_ARROW_LV3_e) { + itemCheckName = "STAR Prize 2"; + } + if (itemCheckName != nullptr) { + itemId1 = dusk::mods::item_check(itemCheckName, itemId1, this); + } +#endif fpc_ProcID itemId2 = fopAcM_createItemForPresentDemo(¤t.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); } diff --git a/src/d/actor/d_a_npc_fairy.cpp b/src/d/actor/d_a_npc_fairy.cpp index 5de667f237..62d5367708 100644 --- a/src/d/actor/d_a_npc_fairy.cpp +++ b/src/d/actor/d_a_npc_fairy.cpp @@ -1333,7 +1333,9 @@ void daNpc_Fairy_c::PresentDemoCall() { item_no = 0; } - fpc_ProcID id = fopAcM_createItemForPresentDemo(¤t.pos, item_no, 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Cave of Ordeals Great Fairy Reward", item_no, this); + fpc_ProcID id = fopAcM_createItemForPresentDemo(¤t.pos, item_no, 0, -1, -1, NULL, + NULL DUSK_GIVE_TAG("Cave of Ordeals Great Fairy Reward")); if (id != fpcM_ERROR_PROCESS_ID_e) { dComIfGp_event_setItemPartnerId(id); } diff --git a/src/d/actor/d_a_npc_gra.cpp b/src/d/actor/d_a_npc_gra.cpp index a43491a57a..ab0ce4aae8 100644 --- a/src/d/actor/d_a_npc_gra.cpp +++ b/src/d/actor/d_a_npc_gra.cpp @@ -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(¤t.pos, sp8, 0, -1, -1, NULL, NULL); +#if TARGET_PC + const char* itemCheckName = nullptr; + if (sp8 == dItemNo_BOMB_IN_BAG_e) { + itemCheckName = "Zoras Domain Underwater Goron"; + sp8 = dusk::mods::item_check(itemCheckName, sp8, this); + } +#endif + field_0x1480 = fopAcM_createItemForPresentDemo(¤t.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); diff --git a/src/d/actor/d_a_npc_gro.cpp b/src/d/actor/d_a_npc_gro.cpp index bdf984f35f..bb9ec6068c 100644 --- a/src/d/actor/d_a_npc_gro.cpp +++ b/src/d/actor/d_a_npc_gro.cpp @@ -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(¤t.pos, itemId, 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Goron Mines Gor Ebizo Key Shard", itemId, this); + mItemID = fopAcM_createItemForPresentDemo(¤t.pos, itemId, 0, -1, -1, + NULL, NULL DUSK_GIVE_TAG("Goron Mines Gor Ebizo Key Shard")); if (mItemID != fpcM_ERROR_PROCESS_ID_e) { s16 eventIdx = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xFF); dComIfGp_getEvent()->reset(this); diff --git a/src/d/actor/d_a_npc_grr.cpp b/src/d/actor/d_a_npc_grr.cpp index ee0d50d858..529686bf05 100644 --- a/src/d/actor/d_a_npc_grr.cpp +++ b/src/d/actor/d_a_npc_grr.cpp @@ -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(¤t.pos, i_itemNo, 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Goron Mines Gor Liggs Key Shard", i_itemNo, this); + mItemID = fopAcM_createItemForPresentDemo(¤t.pos, i_itemNo, 0, -1, -1, + NULL, NULL DUSK_GIVE_TAG("Goron Mines Gor Liggs Key Shard")); if (mItemID != fpcM_ERROR_PROCESS_ID_e) { s16 i_eventID = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xFF); diff --git a/src/d/actor/d_a_npc_grs.cpp b/src/d/actor/d_a_npc_grs.cpp index abab07d22b..9b3a08f432 100644 --- a/src/d/actor/d_a_npc_grs.cpp +++ b/src/d/actor/d_a_npc_grs.cpp @@ -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(¤t.pos, unkInt2, 0, -1, -1, 0, 0); + DUSK_ITEM_CHECK("Goron Mines Gor Amato Key Shard", unkInt2, this); + mPresentItemId = fopAcM_createItemForPresentDemo(¤t.pos, unkInt2, 0, -1, + -1, 0, 0 DUSK_GIVE_TAG("Goron Mines Gor Amato Key Shard")); if (mPresentItemId != fpcM_ERROR_PROCESS_ID_e) { s16 eventIdx = diff --git a/src/d/actor/d_a_npc_impal.cpp b/src/d/actor/d_a_npc_impal.cpp index 5ac057ce06..3d483a1bd9 100644 --- a/src/d/actor/d_a_npc_impal.cpp +++ b/src/d/actor/d_a_npc_impal.cpp @@ -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(¤t.pos, evt_id, 0, -1, -1, 0, 0); + DUSK_ITEM_CHECK("Ilia Charm", evt_id, this); + mItemPartnerId = fopAcM_createItemForPresentDemo( + ¤t.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(¤t.pos, evt_id, 0, -1, -1, 0, 0); + DUSK_ITEM_CHECK("Skybook From Impaz", evt_id, this); + mItemPartnerId = fopAcM_createItemForPresentDemo( + ¤t.pos, evt_id, 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Skybook From Impaz")); dComIfGp_event_setItemPartnerId(mItemPartnerId); mItemPartnerId = -1; } diff --git a/src/d/actor/d_a_npc_ins.cpp b/src/d/actor/d_a_npc_ins.cpp index 0ce91ca914..ede3f53664 100644 --- a/src/d/actor/d_a_npc_ins.cpp +++ b/src/d/actor/d_a_npc_ins.cpp @@ -11,6 +11,10 @@ #include "d/d_msg_object.h" #include +#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(¤t.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(¤t.pos, itemNo, 0, -1, + -1, NULL, NULL IF_DUSK_ARG(itemGiveTag)); if (mItemID != fpcM_ERROR_PROCESS_ID_e) { daPy_getPlayerActorClass()->cancelOriginalDemo(); diff --git a/src/d/actor/d_a_npc_kkri.cpp b/src/d/actor/d_a_npc_kkri.cpp index 9b6b17d2e9..55e0e551c4 100644 --- a/src/d/actor/d_a_npc_kkri.cpp +++ b/src/d/actor/d_a_npc_kkri.cpp @@ -1181,7 +1181,16 @@ int daNpc_Kkri_c::talk(void*) { switch (eventId) { case 1: if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { - mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.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(¤t.pos, item_no, + 0, -1, -1, NULL, + NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName))); } if (fopAcM_IsExecuting(mItemPartnerId)) { diff --git a/src/d/actor/d_a_npc_len.cpp b/src/d/actor/d_a_npc_len.cpp index 7b7acab6fd..1e3ce2c1c1 100644 --- a/src/d/actor/d_a_npc_len.cpp +++ b/src/d/actor/d_a_npc_len.cpp @@ -1236,8 +1236,9 @@ int daNpc_Len_c::talk(void* param_0) { switch (evt_id) { case 1: if (mItemPartnerId == -1) { + DUSK_ITEM_CHECK("Renados Letter", local_18, this); mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, local_18, - 0, -1, -1, 0, 0); + 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Renados Letter")); } if (fopAcM_IsExecuting(mItemPartnerId)) { mEvtNo = 1; diff --git a/src/d/actor/d_a_npc_maro.cpp b/src/d/actor/d_a_npc_maro.cpp index 4e2c3ad870..7b538e8514 100644 --- a/src/d/actor/d_a_npc_maro.cpp +++ b/src/d/actor/d_a_npc_maro.cpp @@ -2393,7 +2393,10 @@ int daNpc_Maro_c::cutArrowTutorial(int arg0) { switch (evt_ret) { case 1: { if (mItemPartnerId == -1) { - mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, evt_id, 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Talo Sharpshooting", evt_id, this); + mItemPartnerId = + fopAcM_createItemForPresentDemo(¤t.pos, evt_id, 0, -1, -1, + NULL, NULL DUSK_GIVE_TAG("Talo Sharpshooting")); } if (fopAcM_IsExecuting(mItemPartnerId)) { diff --git a/src/d/actor/d_a_npc_myna2.cpp b/src/d/actor/d_a_npc_myna2.cpp index ede18be556..b7c3ea1eaa 100644 --- a/src/d/actor/d_a_npc_myna2.cpp +++ b/src/d/actor/d_a_npc_myna2.cpp @@ -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(¤t.pos, itemNo, 0, -1, -1, NULL, NULL); +#if TARGET_PC + const char* itemCheckName = nullptr; + if (itemNo == dItemNo_KAKERA_HEART_e) { + itemCheckName = "Plumm Fruit Balloon Minigame"; + itemNo = dusk::mods::item_check(itemCheckName, itemNo, this); + } +#endif + mItemPid = fopAcM_createItemForPresentDemo(¤t.pos, itemNo, 0, -1, -1, NULL, + NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName))); } break; } diff --git a/src/d/actor/d_a_npc_pouya.cpp b/src/d/actor/d_a_npc_pouya.cpp index 72bad48e4b..d7ad2036f9 100644 --- a/src/d/actor/d_a_npc_pouya.cpp +++ b/src/d/actor/d_a_npc_pouya.cpp @@ -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 20 Poe Soul Reward"; + } else if (local_64 == dItemNo_SILVER_RUPEE_e) { + itemCheckName = "Jovani 60 Poe Soul Reward"; + } + if (itemCheckName != nullptr) { + local_64 = dusk::mods::item_check(itemCheckName, local_64, this); + } +#endif mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.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; diff --git a/src/d/actor/d_a_npc_prayer.cpp b/src/d/actor/d_a_npc_prayer.cpp index 9191bc0315..e27eba137e 100644 --- a/src/d/actor/d_a_npc_prayer.cpp +++ b/src/d/actor/d_a_npc_prayer.cpp @@ -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("Charlo Donation Blessing")); } BOOL daNpcPray_c::_Evt_GetHeart(int i_staffID) { diff --git a/src/d/actor/d_a_npc_rafrel.cpp b/src/d/actor/d_a_npc_rafrel.cpp index 8a4e5d03ef..ca2cc7ede3 100644 --- a/src/d/actor/d_a_npc_rafrel.cpp +++ b/src/d/actor/d_a_npc_rafrel.cpp @@ -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(¤t.pos, itemNo, 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Auru Gift To Fyer", itemNo, this); + field_0xe00 = fopAcM_createItemForPresentDemo(¤t.pos, itemNo, 0, -1, -1, + NULL, NULL DUSK_GIVE_TAG("Auru Gift To Fyer")); 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(¤t.pos, itemNo, 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Auru Gift To Fyer", itemNo, this); + field_0xe00 = fopAcM_createItemForPresentDemo( + ¤t.pos, itemNo, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("Auru Gift To Fyer")); dComIfGp_event_setItemPartnerId(field_0xe00); field_0xe00 = fpcM_ERROR_PROCESS_ID_e; } diff --git a/src/d/actor/d_a_npc_the.cpp b/src/d/actor/d_a_npc_the.cpp index 01b7fbb66c..8e113a33aa 100644 --- a/src/d/actor/d_a_npc_the.cpp +++ b/src/d/actor/d_a_npc_the.cpp @@ -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(¤t.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); diff --git a/src/d/actor/d_a_npc_uri.cpp b/src/d/actor/d_a_npc_uri.cpp index 44df1060d2..aed64d4c3a 100644 --- a/src/d/actor/d_a_npc_uri.cpp +++ b/src/d/actor/d_a_npc_uri.cpp @@ -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(¤t.pos, local_48, 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Uli Cradle Delivery", local_48, this); + mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, local_48, 0, -1, -1, + NULL, NULL DUSK_GIVE_TAG("Uli Cradle Delivery")); } if (fopAcM_IsExecuting(mItemPartnerId)) { /* T_0007 - Ordon Village - During Uli's pick-up tutorial */ diff --git a/src/d/actor/d_a_npc_ykw.cpp b/src/d/actor/d_a_npc_ykw.cpp index 7ca9c4f36f..38d7bdb412 100644 --- a/src/d/actor/d_a_npc_ykw.cpp +++ b/src/d/actor/d_a_npc_ykw.cpp @@ -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(¤t.pos, itemId, 0, - -1, -1, 0, 0); + DUSK_ITEM_CHECK("Snowboard Racing Prize", itemId, this); + mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, itemId, 0, -1, + -1, 0, 0 DUSK_GIVE_TAG("Snowboard Racing Prize")); } 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(¤t.pos, itemNo, 0, -1, -1, 0, 0); + DUSK_ITEM_CHECK("Snowpeak Ruins Mansion Map", itemNo, this); + mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, itemNo, + 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Snowpeak Ruins Mansion Map")); } if (fopAcM_IsExecuting(mItemPartnerId)) { diff --git a/src/d/actor/d_a_npc_zra.inc b/src/d/actor/d_a_npc_zra.inc index 2ada4601c8..3846051bbe 100644 --- a/src/d/actor/d_a_npc_zra.inc +++ b/src/d/actor/d_a_npc_zra.inc @@ -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(¤t.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 Helping Hand"; + } else if (item_id == dItemNo_BOMB_BAG_LV2_e) { + itemCheckName = "Iza Raging Rapids Minigame"; + } + if (itemCheckName != nullptr) { + item_id = dusk::mods::item_check(itemCheckName, item_id, this); + } +#endif + mItemID = fopAcM_createItemForPresentDemo(¤t.pos, item_id, 0, -1, -1, NULL, + NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName))); } field_0x9eb = true; break; diff --git a/src/d/actor/d_a_npc_zrc.cpp b/src/d/actor/d_a_npc_zrc.cpp index 60ae6713e5..b800d13f80 100644 --- a/src/d/actor/d_a_npc_zrc.cpp +++ b/src/d/actor/d_a_npc_zrc.cpp @@ -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(¤t.pos, item_no, - 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Gift From Ralis", item_no, this); + mItemID = fopAcM_createItemForPresentDemo( + ¤t.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("Gift From Ralis")); } break; } diff --git a/src/d/actor/d_a_npc_zrz.cpp b/src/d/actor/d_a_npc_zrz.cpp index 9e45b34f86..62312a5b81 100644 --- a/src/d/actor/d_a_npc_zrz.cpp +++ b/src/d/actor/d_a_npc_zrz.cpp @@ -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(¤t.pos, item_no, - 0, -1, -1, NULL, NULL); + DUSK_ITEM_CHECK("Rutelas Blessing", item_no, this); + mItemID = fopAcM_createItemForPresentDemo( + ¤t.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("Rutelas Blessing")); } break; diff --git a/src/d/actor/d_a_obj_item.cpp b/src/d/actor/d_a_obj_item.cpp index 0dc9ff833b..1176f9aab3 100644 --- a/src/d/actor/d_a_obj_item.cpp +++ b/src/d/actor/d_a_obj_item.cpp @@ -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(¤t.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( + ¤t.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() { diff --git a/src/d/actor/d_a_obj_life_container.cpp b/src/d/actor/d_a_obj_life_container.cpp index 22706ba447..3c800e946f 100644 --- a/src/d/actor/d_a_obj_life_container.cpp +++ b/src/d/actor/d_a_obj_life_container.cpp @@ -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(¤t.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(¤t.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( + ¤t.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; diff --git a/src/d/actor/d_a_obj_shield.cpp b/src/d/actor/d_a_obj_shield.cpp index 3941011043..aad1594107 100644 --- a/src/d/actor/d_a_obj_shield.cpp +++ b/src/d/actor/d_a_obj_shield.cpp @@ -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(¤t.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), 0, 0); + mItemId = fopAcM_createItemForTrBoxDemo(¤t.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; diff --git a/src/d/actor/d_a_obj_sword.cpp b/src/d/actor/d_a_obj_sword.cpp index b346c4232e..c393edb33b 100644 --- a/src/d/actor/d_a_obj_sword.cpp +++ b/src/d/actor/d_a_obj_sword.cpp @@ -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(¤t.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), - NULL, NULL); + mProcID = + fopAcM_createItemForTrBoxDemo(¤t.pos, DUSK_ITEM_CHECK_EXPR("Ordon Sword", 0x28, this), + -1, fopAcM_GetRoomNo(this), NULL, NULL DUSK_GIVE_TAG("Ordon Sword")); setStatus(1); return 1; } diff --git a/src/d/actor/d_a_obj_wood_statue.cpp b/src/d/actor/d_a_obj_wood_statue.cpp index 7c6d69d576..626f36adae 100644 --- a/src/d/actor/d_a_obj_wood_statue.cpp +++ b/src/d/actor/d_a_obj_wood_statue.cpp @@ -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(¤t.pos, m_itemNo, 0xffffffff, - fopAcM_GetRoomNo(this), 0, 0); + mItemId = fopAcM_createItemForTrBoxDemo(¤t.pos, + DUSK_ITEM_CHECK_EXPR("Wooden Statue", m_itemNo, this), 0xffffffff, fopAcM_GetRoomNo(this), + 0, 0 DUSK_GIVE_TAG("Wooden Statue")); JUT_ASSERT(544, mItemId != fpcM_ERROR_PROCESS_ID_e); setStatus(STATUS_ORDER_GET_DEMO); return 1; diff --git a/src/d/actor/d_a_obj_zcloth.cpp b/src/d/actor/d_a_obj_zcloth.cpp index 49580d2589..46561b2471 100644 --- a/src/d/actor/d_a_obj_zcloth.cpp +++ b/src/d/actor/d_a_obj_zcloth.cpp @@ -32,6 +32,7 @@ int daObjZCloth_c::Create() { int daObjZCloth_c::create() { fopAcM_ct(this, daObjZCloth_c); m_itemNo = 0x31; + DUSK_ITEM_CHECK("Rutelas Blessing", 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)) { diff --git a/src/d/actor/d_a_shop_item.cpp b/src/d/actor/d_a_shop_item.cpp index e8fafd1012..3e768a1684 100644 --- a/src/d/actor/d_a_shop_item.cpp +++ b/src/d/actor/d_a_shop_item.cpp @@ -11,6 +11,20 @@ #include "m_Do/m_Do_lib.h" #include +#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(-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); diff --git a/src/d/actor/d_a_tag_statue_evt.cpp b/src/d/actor/d_a_tag_statue_evt.cpp index 8a4c0c4397..de5666aef9 100644 --- a/src/d/actor/d_a_tag_statue_evt.cpp +++ b/src/d/actor/d_a_tag_statue_evt.cpp @@ -362,9 +362,11 @@ int daTagStatue_c::demoProc() { item = dItemNo_AIR_LETTER_e; } - mItemId = - fopAcM_createItemForTrBoxDemo(¤t.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(¤t.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); diff --git a/src/d/actor/d_a_tbox.cpp b/src/d/actor/d_a_tbox.cpp index 1d1826b7e7..c77478da91 100644 --- a/src/d/actor/d_a_tbox.cpp +++ b/src/d/actor/d_a_tbox.cpp @@ -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(¤t.pos, item_no, 1, -1, -1, NULL, NULL); + item_id = fopAcM_createItemForPresentDemo( + ¤t.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag)); } else { - item_id = fopAcM_createItemForTrBoxDemo(¤t.pos, item_no, -1, -1, NULL, NULL); + item_id = fopAcM_createItemForTrBoxDemo( + ¤t.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((home.angle.z & ~0xFF00) | (resolvedItem << 8)); +#endif field_0x982 = home.angle.z; home.angle.z = 0; home.angle.x = 0; diff --git a/src/d/d_a_shop_item_static.cpp b/src/d/d_a_shop_item_static.cpp index ece0a288c5..635f07f5ec 100644 --- a/src/d/d_a_shop_item_static.cpp +++ b/src/d/d_a_shop_item_static.cpp @@ -45,6 +45,12 @@ int CheckShopItemCreateHeap(fopAc_ac_c* i_this) { daShopItem_c* a_this1 = static_cast(i_this); daShopItem_c* a_this2 = static_cast(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 } diff --git a/src/d/d_event_manager.cpp b/src/d/d_event_manager.cpp index 154a1c9288..28464aceaf 100644 --- a/src/d/d_event_manager.cpp +++ b/src/d/d_event_manager.cpp @@ -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(rt) + 0x927; + *itemNo = + dusk::mods::item_check("Charlo Donation Blessing", dItemNo_KAKERA_HEART_e, NULL); + } +#endif int base_status = mEventList[BASE_ACTOR].init((char*)rt, -1); #if DEBUG diff --git a/src/d/d_item.cpp b/src/d/d_item.cpp index 8ca950a52a..5484bd584a 100644 --- a/src/d/d_item.cpp +++ b/src/d/d_item.cpp @@ -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])() = { diff --git a/src/d/d_shop_system.cpp b/src/d/d_shop_system.cpp index 5871d65d2a..d15004b656 100644 --- a/src/d/d_shop_system.cpp +++ b/src/d/d_shop_system.cpp @@ -905,8 +905,16 @@ int dShopSystem_c::seq_start(fopAc_ac_c* actor, dMsgFlow_c* i_flow) { int itemNo; if (mFlow.getEventId(&itemNo) == 1) { if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { - mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, itemNo, 0, -1, - -1, NULL, NULL); +#if TARGET_PC + const char* itemCheckName = nullptr; + if (itemNo == dItemNo_HALF_MILK_BOTTLE_e) { + itemCheckName = "Ordon Cat Rescue"; + itemNo = dusk::mods::item_check(itemCheckName, itemNo, actor); + } +#endif + mItemPartnerId = + fopAcM_createItemForPresentDemo(¤t.pos, itemNo, 0, -1, -1, NULL, + NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName))); } if (fpcEx_IsExist(mItemPartnerId)) { @@ -1195,8 +1203,12 @@ int dShopSystem_c::seq_decide_yes(fopAc_ac_c* actor, dMsgFlow_c* i_flow) { if (mFlow.getEventId(&itemNo) == 1) { if (i_flow->doFlow(actor, NULL, 0)) { if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { - mItemPartnerId = - fopAcM_createItemForPresentDemo(¤t.pos, itemNo, 0, -1, -1, NULL, NULL); +#if TARGET_PC + const u32 itemGiveTag = dusk::mods::item_give_tag_shop(itemNo & 0xFF); + itemNo = dusk::mods::item_check_shop(itemNo & 0xFF, actor); +#endif + mItemPartnerId = fopAcM_createItemForPresentDemo( + ¤t.pos, itemNo, 0, -1, -1, NULL, NULL IF_DUSK_ARG(itemGiveTag)); } if (fpcEx_IsExist(mItemPartnerId)) { diff --git a/src/dusk/mods/item.hpp b/src/dusk/mods/item.hpp new file mode 100644 index 0000000000..ab98b899e3 --- /dev/null +++ b/src/dusk/mods/item.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +class fopAc_ac_c; + +namespace dusk::mods { + +uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver); +uint8_t item_check_tagged(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver); + +uint8_t item_check_chest(uint8_t boxNo, uint8_t itemNo, fopAc_ac_c* chest); +uint8_t item_check_boss(uint8_t itemNo, fopAc_ac_c* boss); +uint8_t item_check_freestanding(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* item); +uint8_t item_check_poe(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* poe); +uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver); +uint8_t item_check_bug(uint8_t insectId, uint8_t itemNo, fopAc_ac_c* agitha); +uint8_t item_check_sky_character(uint8_t itemNo, fopAc_ac_c* statue); + +uint32_t item_give_tag(const char* name); +uint32_t item_give_tag_chest(uint8_t boxNo); +uint32_t item_give_tag_boss(); +uint32_t item_give_tag_freestanding(uint8_t bitNo); +uint32_t item_give_tag_poe(uint8_t bitNo); +uint32_t item_give_tag_shop(uint8_t itemNo); +uint32_t item_give_tag_bug(uint8_t insectId); +uint32_t item_give_tag_sky_character(); + +void item_check_enqueue(const char* name, uint8_t itemNo); +void item_check_enqueue_poe(uint8_t bitNo, uint8_t itemNo); + +void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver); + +bool item_give_queue_dispatching(); +uint32_t item_give_queue_take_tag(); + +} // namespace dusk::mods diff --git a/src/dusk/mods/item_checks.cpp b/src/dusk/mods/item_checks.cpp new file mode 100644 index 0000000000..79a6c993b9 --- /dev/null +++ b/src/dusk/mods/item_checks.cpp @@ -0,0 +1,294 @@ +#include "item.hpp" + +#include "dusk/mod_loader.hpp" +#include "dusk/mods/loader/loader.hpp" +#include "dusk/mods/svc/item.hpp" + +#include "aurora/lib/logging.hpp" +#include "d/d_com_inf_game.h" + +#include + +#include +#include +#include +#include +#include + +namespace dusk::mods { +namespace { + +aurora::Module Log{"dusk::mods::item_checks"}; + +struct CheckOverride { + std::string name; + uint8_t itemNo = 0; +}; + +struct CheckResolver { + ItemCheckHandle handle = 0; + std::string name; + ItemCheckResolveFn fn = nullptr; + void* userData = nullptr; +}; + +struct ModItemChecks { + std::vector overrides; + std::vector resolvers; +}; + +struct PendingResolve { + LoadedMod* mod = nullptr; + bool fixedValue = false; + uint8_t itemNo = 0; + ItemCheckResolveFn fn = nullptr; + void* userData = nullptr; +}; + +std::unordered_map s_modChecks; +std::unordered_set s_warnedCollisions; +ItemCheckHandle s_nextCheckHandle = 1; + +const char* current_stage_name() { + const char* stageName = dComIfGp_getStartStageName(); + return stageName != nullptr ? stageName : ""; +} + +std::string chest_check_name(uint8_t boxNo) { + return fmt::format("chest:{}:{}", current_stage_name(), boxNo); +} + +std::string boss_check_name() { + return fmt::format("boss:{}", current_stage_name()); +} + +std::string freestanding_check_name(uint8_t bitNo) { + return fmt::format("freestanding:{}:{}", current_stage_name(), bitNo); +} + +std::string poe_check_name(uint8_t bitNo) { + return fmt::format("poe:{}:{}", current_stage_name(), bitNo); +} + +std::string shop_check_name(uint8_t itemNo) { + return fmt::format("shop:{}:{}", current_stage_name(), itemNo); +} + +std::string bug_check_name(uint8_t insectId) { + return fmt::format("bug:{}", insectId); +} + +std::string sky_character_check_name() { + return fmt::format("skychar:{}:{}", current_stage_name(), dStage_roomControl_c::getStayNo()); +} + +} // namespace + +uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { + if (name == nullptr || *name == '\0' || s_modChecks.empty()) { + return itemNo; + } + + // Callbacks may change registrations, so copy the applicable chain before invoking one. + std::vector resolves; + LoadedMod* previousOverrideOwner = nullptr; + for (auto& mod : ModLoader::instance().mods()) { + if (!mod.active) { + continue; + } + + const auto modIt = s_modChecks.find(&mod); + if (modIt == s_modChecks.end()) { + continue; + } + + for (const auto& checkOverride : modIt->second.overrides) { + if (checkOverride.name != name) { + continue; + } + if (previousOverrideOwner != nullptr && s_warnedCollisions.emplace(name).second) { + Log.warn("check '{}' is overridden by [{}] and [{}]; [{}] wins by load order", name, + previousOverrideOwner->metadata.id, mod.metadata.id, mod.metadata.id); + } + previousOverrideOwner = &mod; + resolves.push_back({.mod = &mod, .fixedValue = true, .itemNo = checkOverride.itemNo}); + } + + for (const auto& resolver : modIt->second.resolvers) { + if (resolver.name.empty() || resolver.name == name) { + resolves.push_back({.mod = &mod, .fn = resolver.fn, .userData = resolver.userData}); + } + } + } + + ItemCheckInfo info{ + .name = name, + .giver_actor = giver, + .vanilla_item = itemNo, + .current_item = itemNo, + }; + for (const auto& resolve : resolves) { + if (!resolve.mod->active) { + continue; + } + if (resolve.fixedValue) { + info.current_item = resolve.itemNo; + continue; + } + + uint8_t resolvedItem = info.current_item; + try { + if (resolve.fn(resolve.mod->context.get(), &info, &resolvedItem, resolve.userData)) { + info.current_item = resolvedItem; + } + } catch (const std::exception& e) { + fail_mod(*resolve.mod, MOD_ERROR, + fmt::format("Exception in item check resolver for '{}': {}", name, e.what())); + } catch (...) { + fail_mod(*resolve.mod, MOD_ERROR, + fmt::format("Unknown exception in item check resolver for '{}'", name)); + } + } + return info.current_item; +} + +uint8_t item_check_chest(uint8_t boxNo, uint8_t itemNo, fopAc_ac_c* chest) { + if (s_modChecks.empty()) { + return itemNo; + } + const auto name = chest_check_name(boxNo); + return item_check(name.c_str(), itemNo, chest); +} + +uint8_t item_check_boss(uint8_t itemNo, fopAc_ac_c* boss) { + if (s_modChecks.empty()) { + return itemNo; + } + const auto name = boss_check_name(); + return item_check(name.c_str(), itemNo, boss); +} + +uint8_t item_check_freestanding(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* item) { + if (s_modChecks.empty()) { + return itemNo; + } + const auto name = freestanding_check_name(bitNo); + return item_check(name.c_str(), itemNo, item); +} + +uint8_t item_check_poe(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* poe) { + if (s_modChecks.empty()) { + return itemNo; + } + const auto name = poe_check_name(bitNo); + return item_check(name.c_str(), itemNo, poe); +} + +uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver) { + if (s_modChecks.empty()) { + return itemNo; + } + const auto name = shop_check_name(itemNo); + return item_check(name.c_str(), itemNo, giver); +} + +uint8_t item_check_bug(uint8_t insectId, uint8_t itemNo, fopAc_ac_c* agitha) { + if (s_modChecks.empty()) { + return itemNo; + } + const auto name = bug_check_name(insectId); + return item_check(name.c_str(), itemNo, agitha); +} + +uint8_t item_check_sky_character(uint8_t itemNo, fopAc_ac_c* statue) { + if (s_modChecks.empty()) { + return itemNo; + } + const auto name = sky_character_check_name(); + return item_check(name.c_str(), itemNo, statue); +} + +uint32_t item_give_tag_chest(uint8_t boxNo) { + return item_give_tag(chest_check_name(boxNo).c_str()); +} + +uint32_t item_give_tag_boss() { + return item_give_tag(boss_check_name().c_str()); +} + +uint32_t item_give_tag_freestanding(uint8_t bitNo) { + return item_give_tag(freestanding_check_name(bitNo).c_str()); +} + +uint32_t item_give_tag_poe(uint8_t bitNo) { + return item_give_tag(poe_check_name(bitNo).c_str()); +} + +uint32_t item_give_tag_shop(uint8_t itemNo) { + return item_give_tag(shop_check_name(itemNo).c_str()); +} + +uint32_t item_give_tag_bug(uint8_t insectId) { + return item_give_tag(bug_check_name(insectId).c_str()); +} + +uint32_t item_give_tag_sky_character() { + return item_give_tag(sky_character_check_name().c_str()); +} + +void item_check_enqueue_poe(uint8_t bitNo, uint8_t itemNo) { + item_check_enqueue(poe_check_name(bitNo).c_str(), itemNo); +} + +namespace svc { + +ModResult item_check_set_override(LoadedMod& mod, const char* name, uint8_t itemNo) { + auto& checks = s_modChecks[&mod]; + for (auto& checkOverride : checks.overrides) { + if (checkOverride.name == name) { + checkOverride.itemNo = itemNo; + return MOD_OK; + } + } + checks.overrides.push_back({.name = name, .itemNo = itemNo}); + return MOD_OK; +} + +ModResult item_check_clear_override(LoadedMod& mod, const char* name) { + const auto modIt = s_modChecks.find(&mod); + if (modIt == s_modChecks.end()) { + return MOD_INVALID_ARGUMENT; + } + const auto removed = std::erase_if(modIt->second.overrides, + [&](const auto& checkOverride) { return checkOverride.name == name; }); + return removed != 0 ? MOD_OK : MOD_INVALID_ARGUMENT; +} + +ModResult item_check_add_resolver(LoadedMod& mod, const char* name, ItemCheckResolveFn fn, + void* userData, ItemCheckHandle& outHandle) { + auto& resolver = s_modChecks[&mod].resolvers.emplace_back(); + resolver.handle = s_nextCheckHandle++; + resolver.name = name != nullptr ? name : ""; + resolver.fn = fn; + resolver.userData = userData; + outHandle = resolver.handle; + return MOD_OK; +} + +ModResult item_check_remove_resolver(LoadedMod& mod, ItemCheckHandle handle) { + const auto modIt = s_modChecks.find(&mod); + if (modIt == s_modChecks.end()) { + return MOD_INVALID_ARGUMENT; + } + const auto removed = std::erase_if( + modIt->second.resolvers, [&](const auto& resolver) { return resolver.handle == handle; }); + return removed != 0 ? MOD_OK : MOD_INVALID_ARGUMENT; +} + +void item_checks_remove_mod(LoadedMod& mod) { + s_modChecks.erase(&mod); + s_warnedCollisions.clear(); +} + +} // namespace svc +} // namespace dusk::mods diff --git a/src/dusk/mods/item_gives.cpp b/src/dusk/mods/item_gives.cpp new file mode 100644 index 0000000000..7610adc276 --- /dev/null +++ b/src/dusk/mods/item_gives.cpp @@ -0,0 +1,343 @@ +#include "item.hpp" + +#include "dusk/mod_loader.hpp" +#include "dusk/mods/loader/loader.hpp" +#include "dusk/mods/svc/item.hpp" + +#include "aurora/lib/logging.hpp" +#include "d/actor/d_a_alink.h" +#include "d/d_com_inf_game.h" +#include "d/d_item.h" +#include "d/d_item_data.h" +#include "f_op/f_op_actor_mng.h" + +#include + +#include +#include +#include +#include +#include + +namespace dusk::mods { +namespace { + +aurora::Module Log{"dusk::mods::item_gives"}; + +// deque keeps previously returned c_str pointers valid if a callback interns another name. +std::deque s_giveNames; +std::unordered_map s_giveNameIds; + +const char* item_give_name(uint32_t tag) { + if (tag == 0 || tag > s_giveNames.size()) { + return nullptr; + } + return s_giveNames[tag - 1].c_str(); +} + +struct GiveObserver { + ItemGiveHandle handle = 0; + ItemGiveObserveFn fn = nullptr; + void* userData = nullptr; +}; + +struct PendingObserver { + LoadedMod* mod = nullptr; + ItemGiveObserveFn fn = nullptr; + void* userData = nullptr; +}; + +std::unordered_map> s_modObservers; +ItemGiveHandle s_nextGiveHandle = 1; +size_t s_observerCount = 0; + +void notify_gives(const char* checkName, uint8_t itemNo, fopAc_ac_c* giver, ItemGiveOrigin origin) { + if (s_observerCount == 0) { + return; + } + + // Callbacks may change registrations, so copy them before invoking one. + std::vector observers; + for (auto& mod : ModLoader::instance().mods()) { + if (!mod.active) { + continue; + } + const auto modIt = s_modObservers.find(&mod); + if (modIt == s_modObservers.end()) { + continue; + } + for (const auto& observer : modIt->second) { + observers.push_back({.mod = &mod, .fn = observer.fn, .userData = observer.userData}); + } + } + + const ItemGiveInfo info{ + .check_name = checkName, + .giver_actor = giver, + .item = itemNo, + .origin = static_cast(origin), + }; + for (const auto& observer : observers) { + if (!observer.mod->active) { + continue; + } + try { + observer.fn(observer.mod->context.get(), &info, observer.userData); + } catch (const std::exception& e) { + fail_mod(*observer.mod, MOD_ERROR, + fmt::format("Exception in item give observer: {}", e.what())); + } catch (...) { + fail_mod(*observer.mod, MOD_ERROR, "Unknown exception in item give observer"); + } + } +} + +constexpr size_t kGiveQueueLimit = 64; +constexpr int kGiveMaxRetries = 5; + +struct QueuedGive { + LoadedMod* owner = nullptr; + uint32_t tag = 0; + uint8_t itemNo = 0; + bool silent = false; + bool resolveAtDispatch = false; +}; + +std::deque s_giveQueue; +QueuedGive s_inFlightGive{}; +uint8_t s_inFlightItem = 0; +int s_inFlightRetries = 0; +bool s_inFlight = false; +bool s_inFlightSpawned = false; +bool s_dispatchingSilent = false; + +bool safe_to_dispatch() { + daAlink_c* link = daAlink_getAlinkActorClass(); + if (link == nullptr) { + return false; + } + + // make sure player is in a safe action and not already in an event before dispatching + switch (link->mProcID) { + case daAlink_c::PROC_WAIT: + case daAlink_c::PROC_TIRED_WAIT: + case daAlink_c::PROC_MOVE: + case daAlink_c::PROC_WOLF_WAIT: + case daAlink_c::PROC_WOLF_TIRED_WAIT: + case daAlink_c::PROC_WOLF_MOVE: + case daAlink_c::PROC_ATN_MOVE: + case daAlink_c::PROC_WOLF_ATN_AC_MOVE: + break; + default: + return false; + } + if (link->checkEventRun()) { + return false; + } + + int itemId = 0; + return link->mMsgFlow.getEventId(&itemId) == 0; +} + +bool resolve_queued_give(const QueuedGive& give, ItemGiveOrigin origin, uint8_t& outItem) { + outItem = give.itemNo; + if (give.resolveAtDispatch) { + outItem = item_check(item_give_name(give.tag), give.itemNo, nullptr); + } + if (outItem != dItemNo_NONE_e) { + return true; + } + + notify_gives(item_give_name(give.tag), dItemNo_NONE_e, nullptr, origin); + return false; +} + +void dispatch_silent_give(const QueuedGive& give) { + uint8_t itemNo = 0; + if (!resolve_queued_give(give, ITEM_GIVE_ORIGIN_QUEUE_SILENT, itemNo)) { + return; + } + + Log.debug("dispatching silent item {:#x} for '{}'", itemNo, + item_give_name(give.tag) != nullptr ? item_give_name(give.tag) : ""); + s_dispatchingSilent = true; + execItemGet(itemNo, give.tag, nullptr); + s_dispatchingSilent = false; +} + +void dispatch_demo_give() { + Log.debug("dispatching item {:#x} for '{}'", s_inFlightItem, + item_give_name(s_inFlightGive.tag) != nullptr ? item_give_name(s_inFlightGive.tag) : ""); + + daAlink_c* link = daAlink_getAlinkActorClass(); + dComIfGp_getEvent()->setGtItm(s_inFlightItem); + link->mProcID = daAlink_c::PROC_GET_ITEM; + const s16 eventIndex = dComIfGp_getEventManager().getEventIdx(link, "DEFAULT_GETITEM", 0xFF); + fopAcM_orderChangeEventId(link, eventIndex, 1, 0xFFFF); +} + +} // namespace + +uint32_t item_give_tag(const char* name) { + if (name == nullptr || *name == '\0') { + return 0; + } + if (const auto it = s_giveNameIds.find(name); it != s_giveNameIds.end()) { + return it->second; + } + + s_giveNames.emplace_back(name); + const auto tag = static_cast(s_giveNames.size()); + s_giveNameIds.emplace(s_giveNames.back(), tag); + return tag; +} + +uint8_t item_check_tagged(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver) { + const char* name = item_give_name(giveTag); + return name != nullptr ? item_check(name, itemNo, giver) : itemNo; +} + +void item_check_enqueue(const char* name, uint8_t itemNo) { + if (s_giveQueue.size() >= kGiveQueueLimit) { + Log.warn("item give queue is full; dropping check '{}'", name != nullptr ? name : ""); + return; + } + s_giveQueue.push_back({ + .tag = item_give_tag(name), + .itemNo = itemNo, + .resolveAtDispatch = true, + }); +} + +void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver) { + ItemGiveOrigin origin = ITEM_GIVE_ORIGIN_GAME; + if (s_dispatchingSilent) { + origin = ITEM_GIVE_ORIGIN_QUEUE_SILENT; + } else if (s_inFlight && itemNo == s_inFlightItem && giveTag == s_inFlightGive.tag) { + origin = ITEM_GIVE_ORIGIN_QUEUE; + s_inFlight = false; + s_inFlightSpawned = false; + } + notify_gives(item_give_name(giveTag), itemNo, giver, origin); +} + +bool item_give_queue_dispatching() { + return s_inFlight && !s_inFlightSpawned; +} + +uint32_t item_give_queue_take_tag() { + if (!item_give_queue_dispatching()) { + return 0; + } + s_inFlightSpawned = true; + return s_inFlightGive.tag; +} + +namespace svc { + +void item_gives_tick() { + if ((!s_inFlight && s_giveQueue.empty()) || !safe_to_dispatch()) { + return; + } + + if (s_inFlight) { + if (++s_inFlightRetries > kGiveMaxRetries) { + Log.error("item {:#x} for '{}' did not complete after {} attempts; dropping it", + s_inFlightItem, + item_give_name(s_inFlightGive.tag) != nullptr ? item_give_name(s_inFlightGive.tag) : + "", + kGiveMaxRetries); + s_inFlight = false; + s_inFlightSpawned = false; + return; + } + s_inFlightSpawned = false; + dispatch_demo_give(); + return; + } + + while (!s_giveQueue.empty() && s_giveQueue.front().silent) { + const QueuedGive give = s_giveQueue.front(); + s_giveQueue.pop_front(); + dispatch_silent_give(give); + } + if (s_giveQueue.empty()) { + return; + } + + const QueuedGive give = s_giveQueue.front(); + uint8_t itemNo = 0; + if (!resolve_queued_give(give, ITEM_GIVE_ORIGIN_QUEUE, itemNo)) { + s_giveQueue.pop_front(); + return; + } + + s_giveQueue.pop_front(); + s_inFlightGive = give; + s_inFlightItem = itemNo; + s_inFlightRetries = 0; + s_inFlight = true; + s_inFlightSpawned = false; + dispatch_demo_give(); +} + +void item_gives_clear() { + if (!s_giveQueue.empty() || s_inFlight) { + Log.info("dropping {} pending item give(s)", + s_giveQueue.size() + static_cast(s_inFlight)); + } + s_giveQueue.clear(); + s_inFlight = false; + s_inFlightSpawned = false; +} + +ModResult item_give_enqueue(LoadedMod& mod, const char* checkName, uint8_t itemNo, uint32_t flags) { + if (s_giveQueue.size() >= kGiveQueueLimit) { + return MOD_UNAVAILABLE; + } + s_giveQueue.push_back({ + .owner = &mod, + .tag = item_give_tag(checkName), + .itemNo = itemNo, + .silent = (flags & ITEM_GIVE_SILENT) != 0, + .resolveAtDispatch = (flags & ITEM_GIVE_RESOLVE) != 0, + }); + return MOD_OK; +} + +ModResult item_give_add_observer( + LoadedMod& mod, ItemGiveObserveFn fn, void* userData, ItemGiveHandle& outHandle) { + auto& observer = s_modObservers[&mod].emplace_back(); + observer.handle = s_nextGiveHandle++; + observer.fn = fn; + observer.userData = userData; + outHandle = observer.handle; + ++s_observerCount; + return MOD_OK; +} + +ModResult item_give_remove_observer(LoadedMod& mod, ItemGiveHandle handle) { + const auto modIt = s_modObservers.find(&mod); + if (modIt == s_modObservers.end()) { + return MOD_INVALID_ARGUMENT; + } + const auto removed = std::erase_if( + modIt->second, [&](const auto& observer) { return observer.handle == handle; }); + s_observerCount -= removed; + return removed != 0 ? MOD_OK : MOD_INVALID_ARGUMENT; +} + +void item_gives_remove_mod(LoadedMod& mod) { + if (const auto modIt = s_modObservers.find(&mod); modIt != s_modObservers.end()) { + s_observerCount -= modIt->second.size(); + s_modObservers.erase(modIt); + } + std::erase_if(s_giveQueue, [&](const QueuedGive& give) { return give.owner == &mod; }); + if (s_inFlight && s_inFlightGive.owner == &mod) { + // The event system already owns this grant, so it can no longer be canceled safely. + s_inFlightGive.owner = nullptr; + } +} + +} // namespace svc +} // namespace dusk::mods diff --git a/src/dusk/mods/svc/item.cpp b/src/dusk/mods/svc/item.cpp new file mode 100644 index 0000000000..f80fc16b9b --- /dev/null +++ b/src/dusk/mods/svc/item.cpp @@ -0,0 +1,151 @@ +#include "item.hpp" + +#include "registry.hpp" + +#include "dusk/mods/item.hpp" +#include "dusk/mods/loader/loader.hpp" + +#include "d/d_item_data.h" + +#include + +namespace dusk::mods::svc { +namespace { + +constexpr size_t kMaxCheckNameLength = 256; +constexpr uint32_t kGiveFlagMask = ITEM_GIVE_SILENT | ITEM_GIVE_RESOLVE; + +bool is_valid_check_name(const char* name) { + if (name == nullptr) { + return false; + } + const std::string_view view{name}; + return !view.empty() && view.size() <= kMaxCheckNameLength; +} + +ModResult item_set_check_override(ModContext* context, const char* name, uint8_t itemNo) { + auto* mod = mod_from_context(context); + if (mod == nullptr || !is_valid_check_name(name)) { + return MOD_INVALID_ARGUMENT; + } + return item_check_set_override(*mod, name, itemNo); +} + +ModResult item_clear_check_override(ModContext* context, const char* name) { + auto* mod = mod_from_context(context); + if (mod == nullptr || !is_valid_check_name(name)) { + return MOD_INVALID_ARGUMENT; + } + return item_check_clear_override(*mod, name); +} + +ModResult item_set_check_resolver(ModContext* context, const char* name, ItemCheckResolveFn fn, + void* userData, ItemCheckHandle* outHandle) { + if (outHandle != nullptr) { + *outHandle = 0; + } + + auto* mod = mod_from_context(context); + if (mod == nullptr || fn == nullptr || (name != nullptr && !is_valid_check_name(name))) { + return MOD_INVALID_ARGUMENT; + } + + ItemCheckHandle handle = 0; + const auto result = item_check_add_resolver(*mod, name, fn, userData, handle); + if (outHandle != nullptr) { + *outHandle = handle; + } + return result; +} + +ModResult item_clear_check_resolver(ModContext* context, ItemCheckHandle handle) { + auto* mod = mod_from_context(context); + if (mod == nullptr || handle == 0) { + return MOD_INVALID_ARGUMENT; + } + return item_check_remove_resolver(*mod, handle); +} + +ModResult item_resolve_check( + ModContext* context, const char* name, uint8_t originalItemNo, uint8_t* outItem) { + if (mod_from_context(context) == nullptr || !is_valid_check_name(name) || outItem == nullptr) { + return MOD_INVALID_ARGUMENT; + } + *outItem = item_check(name, originalItemNo, nullptr); + return MOD_OK; +} + +ModResult item_give_item( + ModContext* context, const char* checkName, uint8_t itemNo, uint32_t flags) { + auto* mod = mod_from_context(context); + if (mod == nullptr || (checkName != nullptr && !is_valid_check_name(checkName)) || + (flags & ~kGiveFlagMask) != 0) + { + return MOD_INVALID_ARGUMENT; + } + if ((flags & ITEM_GIVE_RESOLVE) != 0) { + if (checkName == nullptr) { + return MOD_INVALID_ARGUMENT; + } + } else if (itemNo == dItemNo_NONE_e) { + return MOD_INVALID_ARGUMENT; + } + return item_give_enqueue(*mod, checkName, itemNo, flags); +} + +ModResult item_observe_gives( + ModContext* context, ItemGiveObserveFn fn, void* userData, ItemGiveHandle* outHandle) { + if (outHandle != nullptr) { + *outHandle = 0; + } + + auto* mod = mod_from_context(context); + if (mod == nullptr || fn == nullptr) { + return MOD_INVALID_ARGUMENT; + } + + ItemGiveHandle handle = 0; + const auto result = item_give_add_observer(*mod, fn, userData, handle); + if (outHandle != nullptr) { + *outHandle = handle; + } + return result; +} + +ModResult item_unobserve_gives(ModContext* context, ItemGiveHandle handle) { + auto* mod = mod_from_context(context); + if (mod == nullptr || handle == 0) { + return MOD_INVALID_ARGUMENT; + } + return item_give_remove_observer(*mod, handle); +} + +constexpr ItemService s_itemService{ + .header = SERVICE_HEADER(ItemService, ITEM_SERVICE_MAJOR, ITEM_SERVICE_MINOR), + .set_check_override = item_set_check_override, + .clear_check_override = item_clear_check_override, + .set_check_resolver = item_set_check_resolver, + .clear_check_resolver = item_clear_check_resolver, + .resolve_check = item_resolve_check, + .give_item = item_give_item, + .observe_gives = item_observe_gives, + .unobserve_gives = item_unobserve_gives, +}; + +} // namespace + +constinit const ServiceModule g_itemModule{ + .id = ITEM_SERVICE_ID, + .majorVersion = ITEM_SERVICE_MAJOR, + .minorVersion = ITEM_SERVICE_MINOR, + .service = &s_itemService, + .modDetached = + [](LoadedMod& mod) { + item_checks_remove_mod(mod); + item_gives_remove_mod(mod); + }, + .frameEnd = item_gives_tick, + .shutdown = item_gives_clear, +}; + +} // namespace dusk::mods::svc diff --git a/src/dusk/mods/svc/item.hpp b/src/dusk/mods/svc/item.hpp new file mode 100644 index 0000000000..355278a3af --- /dev/null +++ b/src/dusk/mods/svc/item.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "mods/svc/item.h" + +#include + +namespace dusk::mods { + +struct LoadedMod; + +namespace svc { + +ModResult item_check_set_override(LoadedMod& mod, const char* name, uint8_t itemNo); +ModResult item_check_clear_override(LoadedMod& mod, const char* name); +ModResult item_check_add_resolver(LoadedMod& mod, const char* name, ItemCheckResolveFn fn, + void* userData, ItemCheckHandle& outHandle); +ModResult item_check_remove_resolver(LoadedMod& mod, ItemCheckHandle handle); +void item_checks_remove_mod(LoadedMod& mod); + +ModResult item_give_enqueue(LoadedMod& mod, const char* checkName, uint8_t itemNo, uint32_t flags); +ModResult item_give_add_observer( + LoadedMod& mod, ItemGiveObserveFn fn, void* userData, ItemGiveHandle& outHandle); +ModResult item_give_remove_observer(LoadedMod& mod, ItemGiveHandle handle); +void item_gives_remove_mod(LoadedMod& mod); +void item_gives_tick(); +void item_gives_clear(); + +} // namespace svc +} // namespace dusk::mods diff --git a/src/dusk/mods/svc/registry.cpp b/src/dusk/mods/svc/registry.cpp index 34134c90c9..1c8368720f 100644 --- a/src/dusk/mods/svc/registry.cpp +++ b/src/dusk/mods/svc/registry.cpp @@ -213,6 +213,7 @@ void ModLoader::init_services() { &svc::g_gfxModule, &svc::g_saveModule, &svc::g_stageModule, + &svc::g_itemModule, }) { svc::register_module(*module); diff --git a/src/dusk/mods/svc/registry.hpp b/src/dusk/mods/svc/registry.hpp index df0ca7bdbb..d3df44ccc3 100644 --- a/src/dusk/mods/svc/registry.hpp +++ b/src/dusk/mods/svc/registry.hpp @@ -75,5 +75,6 @@ extern const ServiceModule g_windowModule; extern const ServiceModule g_gfxModule; extern const ServiceModule g_saveModule; extern const ServiceModule g_stageModule; +extern const ServiceModule g_itemModule; } // namespace dusk::mods::svc diff --git a/src/dusk/mods/svc/save.cpp b/src/dusk/mods/svc/save.cpp index 9930129be5..360cdf6dca 100644 --- a/src/dusk/mods/svc/save.cpp +++ b/src/dusk/mods/svc/save.cpp @@ -1,5 +1,6 @@ #include "save.hpp" +#include "item.hpp" #include "registry.hpp" #include "aurora/lib/logging.hpp" @@ -164,6 +165,7 @@ void save_slot_new(uint32_t slot) { store.mods.clear(); store.snapshotValid = false; s_currentSlot = static_cast(slot); + item_gives_clear(); Log.info("new save in slot {}; mod blob store cleared", slot); notify(slot, &SaveObserverRecord::onNewSave, "new-save"); } @@ -183,6 +185,7 @@ void save_slot_loaded(uint32_t slot, const void* slotData) { } } s_currentSlot = static_cast(slot); + item_gives_clear(); notify(slot, &SaveObserverRecord::onLoaded, "save-loaded"); } @@ -222,6 +225,7 @@ void save_slot_erased(uint32_t slot) { void save_no_slot() { s_currentSlot = -1; + item_gives_clear(); } namespace { diff --git a/src/f_op/f_op_actor.cpp b/src/f_op/f_op_actor.cpp index 9cf34de7ff..4e5940eec4 100644 --- a/src/f_op/f_op_actor.cpp +++ b/src/f_op/f_op_actor.cpp @@ -454,6 +454,10 @@ static int fopAc_Create(void* i_this) { actor->cullType = profile->cullType; fopAcM_prm_class* append = fopAcM_GetAppend(actor); +#if TARGET_PC + actor->mItemGiveTag = append != NULL ? append->mItemGiveTag : 0; + actor->mItemGiveOriginalNo = append != NULL ? append->mItemGiveOriginalNo : 0xFF; +#endif if (append != NULL) { fopAcM_SetParam(actor, append->base.parameters); actor->home.pos = append->base.position; diff --git a/src/f_op/f_op_actor_mng.cpp b/src/f_op/f_op_actor_mng.cpp index 6bd19762af..e02bec33fe 100644 --- a/src/f_op/f_op_actor_mng.cpp +++ b/src/f_op/f_op_actor_mng.cpp @@ -184,19 +184,22 @@ fopAcM_prm_class* fopAcM_CreateAppend() { append->scale.z = 10; append->parent_id = fpcM_ERROR_PROCESS_ID_e; append->argument = -1; + IF_DUSK(append->mItemGiveOriginalNo = 0xFF;) } return append; } 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) IF_DUSK_ARG(u8 i_itemOriginalNo)) { fopAcM_prm_class* append = fopAcM_CreateAppend(); if (append == NULL) { return NULL; } append->base.setID = i_setId; + IF_DUSK(append->mItemGiveTag = i_itemGiveTag;) + IF_DUSK(append->mItemGiveOriginalNo = i_itemOriginalNo;) if (i_pos != NULL) { append->base.position = *i_pos; @@ -253,10 +256,10 @@ 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)) { fopAcM_prm_class* append = createAppend(i_setId, i_parameters, i_pos, i_roomNo, i_angle, - i_scale, i_argument, fpcM_ERROR_PROCESS_ID_e); + i_scale, i_argument, fpcM_ERROR_PROCESS_ID_e IF_DUSK_ARG(i_itemGiveTag)); if (append == NULL) { return fpcM_ERROR_PROCESS_ID_e; } @@ -265,16 +268,17 @@ fpc_ProcID fopAcM_create(s16 i_procName, u16 i_setId, u32 i_parameters, const cX } 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)) { return fopAcM_create(i_procName, 0xFFFF, i_parameters, i_pos, i_roomNo, i_angle, i_scale, - i_argument, NULL); + i_argument, NULL IF_DUSK_ARG(i_itemGiveTag)); } 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) { - fopAcM_prm_class* append = createAppend(0xFFFF, i_parameters, i_pos, i_roomNo, i_angle, i_scale, - i_argument, fpcM_ERROR_PROCESS_ID_e); + const csXyz* i_angle, const cXyz* i_scale, s8 i_argument, createFunc i_createFunc, + void* i_createFuncData IF_DUSK_ARG(u32 i_itemGiveTag) IF_DUSK_ARG(u8 i_itemOriginalNo)) { + fopAcM_prm_class* append = + createAppend(0xFFFF, i_parameters, i_pos, i_roomNo, i_angle, i_scale, i_argument, + fpcM_ERROR_PROCESS_ID_e IF_DUSK_ARG(i_itemGiveTag) IF_DUSK_ARG(i_itemOriginalNo)); if (append == NULL) { return NULL; } @@ -1385,8 +1389,8 @@ fopAc_ac_c* fopAcM_getEventPartner(fopAc_ac_c const* i_actor) { } 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)) { JUT_ASSERT(3214, 0 <= i_itemNo && i_itemNo < 256); dComIfGp_event_setGtItm(i_itemNo); @@ -1395,14 +1399,14 @@ fpc_ProcID fopAcM_createItemForPresentDemo(cXyz const* i_pos, int i_itemNo, u8 p return fpcM_ERROR_PROCESS_ID_e; } - return fopAcM_createDemoItem(i_pos, i_itemNo, i_itemBitNo, i_angle, i_roomNo, i_scale, param_2); + return fopAcM_createDemoItem(i_pos, i_itemNo, i_itemBitNo, i_angle, i_roomNo, i_scale, + param_2 IF_DUSK_ARG(i_itemGiveTag)); } 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) { - - JUT_ASSERT(3259, 0 <= i_itemNo && i_itemNo < 256); - dComIfGp_event_setGtItm(i_itemNo); + int i_roomNo, csXyz const* i_angle, cXyz const* i_scale IF_DUSK_ARG(u32 i_itemGiveTag)) { + JUT_ASSERT(3259, 0 <= i_itemNo && i_itemNo < 256); + dComIfGp_event_setGtItm(i_itemNo); if (i_itemNo == dItemNo_NONE_e) { OS_REPORT("ゲットデモ用なのに「ハズレ」です![%d]\n", i_itemNo); // Even though it is for a Get Demo, it is a 'Miss'! @@ -1410,7 +1414,8 @@ fpc_ProcID fopAcM_createItemForTrBoxDemo(cXyz const* i_pos, int i_itemNo, int i_ } u8 param_7 = 0; - return fopAcM_createDemoItem(i_pos, i_itemNo, i_itemBitNo, i_angle, i_roomNo, i_scale, param_7); + return fopAcM_createDemoItem(i_pos, i_itemNo, i_itemBitNo, i_angle, i_roomNo, i_scale, + param_7 IF_DUSK_ARG(i_itemGiveTag)); } struct ItemTableList { @@ -1566,8 +1571,8 @@ fpc_ProcID fopAcM_createItemFromTable(cXyz const* i_pos, int i_itemNo, int i_ite } 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)) { // clang-format off JUT_ASSERT(3824, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on @@ -1577,17 +1582,29 @@ fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitN } u32 params = (i_itemNo & 0xFF) << 0x0 | (i_itemBitNo & 0x7F) << 0x8 | (param_7 & 0xFF) << 0x10; - return fopAcM_create(fpcNm_Demo_Item_e, params, i_pos, i_roomNo, i_angle, scale, -1); + return fopAcM_create( + fpcNm_Demo_Item_e, params, i_pos, i_roomNo, i_angle, scale, -1 IF_DUSK_ARG(i_itemGiveTag)); } 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)) { int _ = -1; +#if TARGET_PC + const u8 originalItemNo = i_itemNo & 0xFF; + u32 giveTag; + if (i_itemCheckName != NULL) { + i_itemNo = dusk::mods::item_check(i_itemCheckName, originalItemNo, NULL); + giveTag = dusk::mods::item_give_tag(i_itemCheckName); + } else { + i_itemNo = dusk::mods::item_check_boss(originalItemNo, NULL); + giveTag = dusk::mods::item_give_tag_boss(); + } +#endif u32 params = 0xFFFF0000 | param_8 << 8 | (i_itemNo & 0xFF); - fopAc_ac_c* actor = fopAcM_fastCreate(fpcNm_Obj_LifeContainer_e, params, i_pos, i_roomNo, i_angle, - i_scale, -1, NULL, NULL); + fopAc_ac_c* actor = fopAcM_fastCreate(fpcNm_Obj_LifeContainer_e, params, i_pos, i_roomNo, + i_angle, i_scale, -1, NULL, NULL IF_DUSK_ARG(giveTag) IF_DUSK_ARG(originalItemNo)); if (actor != NULL) { actor->speedF = i_speedF; actor->speed.y = i_speedY; From 0fc05028ccfe809c569b1b84c0bb87f382b0bf34 Mon Sep 17 00:00:00 2001 From: baxlek <115663151+baxlek@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:14:17 -0500 Subject: [PATCH 03/15] Add Aiming Reticle for Bow and Slingshot (#2290) * Change sight drawing flag to on in arrow logic * Refactor setBowSight for PC targeting and reticle logic * Add Aiming Reticle option to settings Add option for Aiming Reticle in settings. * Add aiming reticle setting to game preferences * Add aiming reticle to user settings registration * Add aimingReticle configuration variable * Fix syntax error in settings.cpp * Add aiming reticle check to bow charge condition * Refactor bow charge sight position logic Removed redundant sight position handling when aiming reticle is not displayed. * Refactor setBowSight for Dusk targeting * Clarify aiming reticle option description * Refactor bow sight logic in setBowSight function Removed dusk condition from bow sight check and adjusted sight drawing logic. * Refactor sight drawing logic in d_a_alink_bow.inc Remove redundant call to offDrawFlg() when aiming reticle is enabled. --- src/d/actor/d_a_alink_bow.inc | 2 +- src/dusk/settings.cpp | 2 ++ src/dusk/settings.h | 1 + src/dusk/ui/settings.cpp | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/d/actor/d_a_alink_bow.inc b/src/d/actor/d_a_alink_bow.inc index 600dcae2f4..aae80e8b88 100644 --- a/src/d/actor/d_a_alink_bow.inc +++ b/src/d/actor/d_a_alink_bow.inc @@ -551,7 +551,7 @@ void daAlink_c::setBowSight() { getArrowFlyData(&dist, &speed, TRUE); checkSightLine(dist, &sight_pos); mSight.setPos(&sight_pos); - mSight.offDrawFlg(); + DUSK_IF_ELSE(dusk::getSettings().game.aimingReticle ? mSight.onDrawFlg() : mSight.offDrawFlg(), mSight.offDrawFlg()); } else { mSight.offDrawFlg(); } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index e2a2169485..00191c3015 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -51,6 +51,7 @@ UserSettings g_userSettings = { .sunsSong {"game.sunsSong", false}, .autoSave {"game.autoSave", false}, .enhancedMapMenus {"game.enhancedMapMenus", false}, + .aimingReticle {"game.aimingReticle", false}, // Preferences .enableMirrorMode {"game.enableMirrorMode", false}, @@ -255,6 +256,7 @@ void registerSettings() { Register(g_userSettings.game.sunsSong); Register(g_userSettings.game.autoSave); Register(g_userSettings.game.enhancedMapMenus); + Register(g_userSettings.game.aimingReticle); Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); Register(g_userSettings.game.invertCameraYAxis); diff --git a/src/dusk/settings.h b/src/dusk/settings.h index 326f7f321c..19aad6ee09 100644 --- a/src/dusk/settings.h +++ b/src/dusk/settings.h @@ -183,6 +183,7 @@ struct UserSettings { ConfigVar sunsSong; ConfigVar autoSave; ConfigVar enhancedMapMenus; + ConfigVar aimingReticle; // Preferences ConfigVar enableMirrorMode; diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 43321d4879..f425fb5425 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -1225,6 +1225,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Allows Wolf Link to howl and change the time of day."); addOption("Quick Transform (R+Y)", getSettings().game.enableQuickTransform, "Transform instantly by pressing R and Y simultaneously."); + addOption("Aiming Reticle", getSettings().game.aimingReticle, + "Shows the aiming reticle for bow and slingshot."); leftPane.add_section("Speedrunning"); config_bool_select(leftPane, rightPane, getSettings().game.speedrunMode, From 0c3bbbaba03fc5b043aafda0838c24cce17d4e94 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Mon, 10 Aug 2026 12:35:08 -0700 Subject: [PATCH 04/15] standardize some check names --- src/d/actor/d_a_e_rdb.cpp | 2 +- src/d/actor/d_a_e_th_ball.cpp | 4 ++-- src/d/actor/d_a_npc_aru.cpp | 4 ++-- src/d/actor/d_a_npc_ashB.cpp | 4 ++-- src/d/actor/d_a_npc_chin.cpp | 4 ++-- src/d/actor/d_a_npc_fairy.cpp | 4 ++-- src/d/actor/d_a_npc_gra.cpp | 2 +- src/d/actor/d_a_npc_gro.cpp | 4 ++-- src/d/actor/d_a_npc_grr.cpp | 4 ++-- src/d/actor/d_a_npc_grs.cpp | 4 ++-- src/d/actor/d_a_npc_impal.cpp | 8 ++++---- src/d/actor/d_a_npc_kkri.cpp | 2 +- src/d/actor/d_a_npc_len.cpp | 4 ++-- src/d/actor/d_a_npc_maro.cpp | 4 ++-- src/d/actor/d_a_npc_myna2.cpp | 2 +- src/d/actor/d_a_npc_pouya.cpp | 4 ++-- src/d/actor/d_a_npc_prayer.cpp | 2 +- src/d/actor/d_a_npc_rafrel.cpp | 8 ++++---- src/d/actor/d_a_npc_the.cpp | 4 ++-- src/d/actor/d_a_npc_uri.cpp | 4 ++-- src/d/actor/d_a_npc_ykw.cpp | 8 ++++---- src/d/actor/d_a_npc_zra.inc | 4 ++-- src/d/actor/d_a_npc_zrc.cpp | 4 ++-- src/d/actor/d_a_npc_zrz.cpp | 4 ++-- src/d/actor/d_a_obj_shield.cpp | 6 +++--- src/d/actor/d_a_obj_sword.cpp | 6 +++--- src/d/actor/d_a_obj_wood_statue.cpp | 4 ++-- src/d/actor/d_a_obj_zcloth.cpp | 2 +- src/d/d_event_manager.cpp | 2 +- src/d/d_shop_system.cpp | 2 +- 30 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/d/actor/d_a_e_rdb.cpp b/src/d/actor/d_a_e_rdb.cpp index ed0459432f..c467f38fd6 100644 --- a/src/d/actor/d_a_e_rdb.cpp +++ b/src/d/actor/d_a_e_rdb.cpp @@ -1251,7 +1251,7 @@ static void demo_camera(e_rdb_class* i_this) { if (iVar1 != 0) { daPy_getPlayerActorClass()->changeDemoMode(11, - DUSK_ITEM_CHECK_EXPR("Hyrule Castle King Bulblin Key", dItemNo_SMALL_KEY_e, &i_this->enemy), 0, + 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; diff --git a/src/d/actor/d_a_e_th_ball.cpp b/src/d/actor/d_a_e_th_ball.cpp index 4a4c322cd1..fcb87ada24 100644 --- a/src/d/actor/d_a_e_th_ball.cpp +++ b/src/d/actor/d_a_e_th_ball.cpp @@ -932,8 +932,8 @@ static void get_demo(e_th_ball_class* i_this) { break; case 1: demo_id = fopAcM_createItemForTrBoxDemo(&i_this->current.pos, - DUSK_ITEM_CHECK_EXPR("Snowpeak Ruins Ball and Chain", dItemNo_IRONBALL_e, i_this), -1, - fopAcM_GetRoomNo(i_this), NULL, NULL DUSK_GIVE_TAG("Snowpeak Ruins Ball and Chain")); + 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; diff --git a/src/d/actor/d_a_npc_aru.cpp b/src/d/actor/d_a_npc_aru.cpp index 9d9356a698..5b6444123c 100644 --- a/src/d/actor/d_a_npc_aru.cpp +++ b/src/d/actor/d_a_npc_aru.cpp @@ -1744,9 +1744,9 @@ int daNpc_Aru_c::cutSpeakTo(int i_staffID) { switch (eventId) { case 1: if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { - DUSK_ITEM_CHECK("Herding Goats Reward", itemNo, this); + DUSK_ITEM_CHECK("goats_reward", itemNo, this); mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, itemNo, - 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("Herding Goats Reward")); + 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("goats_reward")); } if (fopAcM_IsExecuting(mItemPartnerId)) { diff --git a/src/d/actor/d_a_npc_ashB.cpp b/src/d/actor/d_a_npc_ashB.cpp index 29e349ba55..f9c7df2cc2 100644 --- a/src/d/actor/d_a_npc_ashB.cpp +++ b/src/d/actor/d_a_npc_ashB.cpp @@ -1001,9 +1001,9 @@ BOOL daNpcAshB_c::EvCut_Appear(int i_staffID) { case '0008': local_30[0] = 0; if (mFlow.getEventId(local_30) == 1) { - DUSK_ITEM_CHECK("Ashei Sketch", local_30[0], this); + DUSK_ITEM_CHECK("ashei_sketch", local_30[0], this); mItemPartnerId = fopAcM_createItemForPresentDemo( - ¤t.pos, local_30[0], 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Ashei Sketch")); + ¤t.pos, local_30[0], 0, -1, -1, 0, 0 DUSK_GIVE_TAG("ashei_sketch")); dComIfGp_event_setItemPartnerId(mItemPartnerId); mItemPartnerId = -1; } diff --git a/src/d/actor/d_a_npc_chin.cpp b/src/d/actor/d_a_npc_chin.cpp index 826cb27b53..fb91bd79d8 100644 --- a/src/d/actor/d_a_npc_chin.cpp +++ b/src/d/actor/d_a_npc_chin.cpp @@ -1807,9 +1807,9 @@ int daNpcChin_c::_Evt_GameSucceed_CutMain(const int& param_0) { #if TARGET_PC const char* itemCheckName = nullptr; if (itemId1 == dItemNo_ARROW_LV2_e) { - itemCheckName = "STAR Prize 1"; + itemCheckName = "star_reward_1"; } else if (itemId1 == dItemNo_ARROW_LV3_e) { - itemCheckName = "STAR Prize 2"; + itemCheckName = "star_reward_2"; } if (itemCheckName != nullptr) { itemId1 = dusk::mods::item_check(itemCheckName, itemId1, this); diff --git a/src/d/actor/d_a_npc_fairy.cpp b/src/d/actor/d_a_npc_fairy.cpp index 62d5367708..0b911973e4 100644 --- a/src/d/actor/d_a_npc_fairy.cpp +++ b/src/d/actor/d_a_npc_fairy.cpp @@ -1333,9 +1333,9 @@ void daNpc_Fairy_c::PresentDemoCall() { item_no = 0; } - DUSK_ITEM_CHECK("Cave of Ordeals Great Fairy Reward", item_no, this); + DUSK_ITEM_CHECK("fairy_reward:D_SB01", item_no, this); fpc_ProcID id = fopAcM_createItemForPresentDemo(¤t.pos, item_no, 0, -1, -1, NULL, - NULL DUSK_GIVE_TAG("Cave of Ordeals Great Fairy Reward")); + NULL DUSK_GIVE_TAG("fairy_reward:D_SB01")); if (id != fpcM_ERROR_PROCESS_ID_e) { dComIfGp_event_setItemPartnerId(id); } diff --git a/src/d/actor/d_a_npc_gra.cpp b/src/d/actor/d_a_npc_gra.cpp index ab0ce4aae8..4300348ff9 100644 --- a/src/d/actor/d_a_npc_gra.cpp +++ b/src/d/actor/d_a_npc_gra.cpp @@ -4021,7 +4021,7 @@ BOOL daNpc_grA_c::talk(void*) { #if TARGET_PC const char* itemCheckName = nullptr; if (sp8 == dItemNo_BOMB_IN_BAG_e) { - itemCheckName = "Zoras Domain Underwater Goron"; + itemCheckName = "goron_reward:F_SP113"; sp8 = dusk::mods::item_check(itemCheckName, sp8, this); } #endif diff --git a/src/d/actor/d_a_npc_gro.cpp b/src/d/actor/d_a_npc_gro.cpp index bb9ec6068c..e1b9e457de 100644 --- a/src/d/actor/d_a_npc_gro.cpp +++ b/src/d/actor/d_a_npc_gro.cpp @@ -1681,9 +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) { - DUSK_ITEM_CHECK("Goron Mines Gor Ebizo Key Shard", itemId, this); + DUSK_ITEM_CHECK("key_shard_2:D_MN04", itemId, this); mItemID = fopAcM_createItemForPresentDemo(¤t.pos, itemId, 0, -1, -1, - NULL, NULL DUSK_GIVE_TAG("Goron Mines Gor Ebizo Key Shard")); + 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); diff --git a/src/d/actor/d_a_npc_grr.cpp b/src/d/actor/d_a_npc_grr.cpp index 529686bf05..4a978c586e 100644 --- a/src/d/actor/d_a_npc_grr.cpp +++ b/src/d/actor/d_a_npc_grr.cpp @@ -1338,9 +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) { - DUSK_ITEM_CHECK("Goron Mines Gor Liggs Key Shard", i_itemNo, this); + DUSK_ITEM_CHECK("key_shard_3:D_MN04", i_itemNo, this); mItemID = fopAcM_createItemForPresentDemo(¤t.pos, i_itemNo, 0, -1, -1, - NULL, NULL DUSK_GIVE_TAG("Goron Mines Gor Liggs Key Shard")); + 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); diff --git a/src/d/actor/d_a_npc_grs.cpp b/src/d/actor/d_a_npc_grs.cpp index 9b3a08f432..462f77b8a7 100644 --- a/src/d/actor/d_a_npc_grs.cpp +++ b/src/d/actor/d_a_npc_grs.cpp @@ -1190,9 +1190,9 @@ int daNpc_grS_c::talk(void* param_0) { if (unkFlag1 && talkProc(NULL, 1, NULL)) { if (mType == 0) { if (mFlow.getEventId(&unkInt2) == 1) { - DUSK_ITEM_CHECK("Goron Mines Gor Amato Key Shard", unkInt2, this); + DUSK_ITEM_CHECK("key_shard_1:D_MN04", unkInt2, this); mPresentItemId = fopAcM_createItemForPresentDemo(¤t.pos, unkInt2, 0, -1, - -1, 0, 0 DUSK_GIVE_TAG("Goron Mines Gor Amato Key Shard")); + -1, 0, 0 DUSK_GIVE_TAG("key_shard_1:D_MN04")); if (mPresentItemId != fpcM_ERROR_PROCESS_ID_e) { s16 eventIdx = diff --git a/src/d/actor/d_a_npc_impal.cpp b/src/d/actor/d_a_npc_impal.cpp index 3d483a1bd9..171c36c289 100644 --- a/src/d/actor/d_a_npc_impal.cpp +++ b/src/d/actor/d_a_npc_impal.cpp @@ -971,9 +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) { - DUSK_ITEM_CHECK("Ilia Charm", evt_id, this); + DUSK_ITEM_CHECK("ilia_charm", evt_id, this); mItemPartnerId = fopAcM_createItemForPresentDemo( - ¤t.pos, evt_id, 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Ilia Charm")); + ¤t.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); @@ -1061,9 +1061,9 @@ BOOL daNpcImpal_c::EvCut_CopyRod(int i_cut_index) { case '0003': int evt_id = 0; if (mFlow.getEventId(&evt_id) == 1) { - DUSK_ITEM_CHECK("Skybook From Impaz", evt_id, this); + DUSK_ITEM_CHECK("skybook", evt_id, this); mItemPartnerId = fopAcM_createItemForPresentDemo( - ¤t.pos, evt_id, 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Skybook From Impaz")); + ¤t.pos, evt_id, 0, -1, -1, 0, 0 DUSK_GIVE_TAG("skybook")); dComIfGp_event_setItemPartnerId(mItemPartnerId); mItemPartnerId = -1; } diff --git a/src/d/actor/d_a_npc_kkri.cpp b/src/d/actor/d_a_npc_kkri.cpp index 55e0e551c4..4d9c882d03 100644 --- a/src/d/actor/d_a_npc_kkri.cpp +++ b/src/d/actor/d_a_npc_kkri.cpp @@ -1184,7 +1184,7 @@ int daNpc_Kkri_c::talk(void*) { #if TARGET_PC const char* itemCheckName = nullptr; if (item_no == dItemNo_OIL_BOTTLE3_e) { - itemCheckName = "Coro Bottle"; + itemCheckName = "coro_bottle"; item_no = dusk::mods::item_check(itemCheckName, item_no, this); } #endif diff --git a/src/d/actor/d_a_npc_len.cpp b/src/d/actor/d_a_npc_len.cpp index 1e3ce2c1c1..2568462e8b 100644 --- a/src/d/actor/d_a_npc_len.cpp +++ b/src/d/actor/d_a_npc_len.cpp @@ -1236,9 +1236,9 @@ int daNpc_Len_c::talk(void* param_0) { switch (evt_id) { case 1: if (mItemPartnerId == -1) { - DUSK_ITEM_CHECK("Renados Letter", local_18, this); + DUSK_ITEM_CHECK("renado_letter", local_18, this); mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, local_18, - 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Renados Letter")); + 0, -1, -1, 0, 0 DUSK_GIVE_TAG("renado_letter")); } if (fopAcM_IsExecuting(mItemPartnerId)) { mEvtNo = 1; diff --git a/src/d/actor/d_a_npc_maro.cpp b/src/d/actor/d_a_npc_maro.cpp index 7b538e8514..d985349a48 100644 --- a/src/d/actor/d_a_npc_maro.cpp +++ b/src/d/actor/d_a_npc_maro.cpp @@ -2393,10 +2393,10 @@ int daNpc_Maro_c::cutArrowTutorial(int arg0) { switch (evt_ret) { case 1: { if (mItemPartnerId == -1) { - DUSK_ITEM_CHECK("Talo Sharpshooting", evt_id, this); + DUSK_ITEM_CHECK("archery_reward:F_SP109", evt_id, this); mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, evt_id, 0, -1, -1, - NULL, NULL DUSK_GIVE_TAG("Talo Sharpshooting")); + NULL, NULL DUSK_GIVE_TAG("archery_reward:F_SP109")); } if (fopAcM_IsExecuting(mItemPartnerId)) { diff --git a/src/d/actor/d_a_npc_myna2.cpp b/src/d/actor/d_a_npc_myna2.cpp index b7c3ea1eaa..faa5301bb8 100644 --- a/src/d/actor/d_a_npc_myna2.cpp +++ b/src/d/actor/d_a_npc_myna2.cpp @@ -1194,7 +1194,7 @@ int daNpc_myna2_c::ECut_gameGoalSuccess(int i_staffId) { #if TARGET_PC const char* itemCheckName = nullptr; if (itemNo == dItemNo_KAKERA_HEART_e) { - itemCheckName = "Plumm Fruit Balloon Minigame"; + itemCheckName = "plumm_minigame_reward"; itemNo = dusk::mods::item_check(itemCheckName, itemNo, this); } #endif diff --git a/src/d/actor/d_a_npc_pouya.cpp b/src/d/actor/d_a_npc_pouya.cpp index d7ad2036f9..6101b64a93 100644 --- a/src/d/actor/d_a_npc_pouya.cpp +++ b/src/d/actor/d_a_npc_pouya.cpp @@ -961,9 +961,9 @@ int daNpc_Pouya_c::cutHaveFavorToAsk(int param_0) { #if TARGET_PC const char* itemCheckName = nullptr; if (local_64 == dItemNo_DROP_BOTTLE_e) { - itemCheckName = "Jovani 20 Poe Soul Reward"; + itemCheckName = "jovani_reward_1"; } else if (local_64 == dItemNo_SILVER_RUPEE_e) { - itemCheckName = "Jovani 60 Poe Soul Reward"; + itemCheckName = "jovani_reward_2"; } if (itemCheckName != nullptr) { local_64 = dusk::mods::item_check(itemCheckName, local_64, this); diff --git a/src/d/actor/d_a_npc_prayer.cpp b/src/d/actor/d_a_npc_prayer.cpp index e27eba137e..1e10121f56 100644 --- a/src/d/actor/d_a_npc_prayer.cpp +++ b/src/d/actor/d_a_npc_prayer.cpp @@ -721,7 +721,7 @@ fpc_ProcID daNpcPray_c::createHeart() { 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 IF_DUSK_ARG("Charlo Donation Blessing")); + &size, 0.0f, 0.0f, 0 IF_DUSK_ARG("prayer_reward")); } BOOL daNpcPray_c::_Evt_GetHeart(int i_staffID) { diff --git a/src/d/actor/d_a_npc_rafrel.cpp b/src/d/actor/d_a_npc_rafrel.cpp index ca2cc7ede3..d51d514785 100644 --- a/src/d/actor/d_a_npc_rafrel.cpp +++ b/src/d/actor/d_a_npc_rafrel.cpp @@ -1310,9 +1310,9 @@ bool daNpcRafrel_c::talk(void* param_0) { OS_REPORT("会話終了時 イベントID=%d アイテムNo=%d\n", eventId, itemNo); if (eventId == 1) { - DUSK_ITEM_CHECK("Auru Gift To Fyer", itemNo, this); + DUSK_ITEM_CHECK("auru_memo", itemNo, this); field_0xe00 = fopAcM_createItemForPresentDemo(¤t.pos, itemNo, 0, -1, -1, - NULL, NULL DUSK_GIVE_TAG("Auru Gift To Fyer")); + 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); @@ -1564,9 +1564,9 @@ int daNpcRafrel_c::EvCut_Appear(int i_staffId) { int itemNo = 0; u16 eventId = mFlow.getEventId(&itemNo); if (eventId == 1) { - DUSK_ITEM_CHECK("Auru Gift To Fyer", itemNo, this); + DUSK_ITEM_CHECK("auru_memo", itemNo, this); field_0xe00 = fopAcM_createItemForPresentDemo( - ¤t.pos, itemNo, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("Auru Gift To Fyer")); + ¤t.pos, itemNo, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("auru_memo")); dComIfGp_event_setItemPartnerId(field_0xe00); field_0xe00 = fpcM_ERROR_PROCESS_ID_e; } diff --git a/src/d/actor/d_a_npc_the.cpp b/src/d/actor/d_a_npc_the.cpp index 8e113a33aa..7291e14715 100644 --- a/src/d/actor/d_a_npc_the.cpp +++ b/src/d/actor/d_a_npc_the.cpp @@ -823,9 +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); + DUSK_ITEM_CHECK("telma_invoice", item_no, this); mItemID = fopAcM_createItemForPresentDemo(¤t.pos, item_no, 0, -1, -1, - NULL, NULL DUSK_GIVE_TAG("Telma Invoice")); + NULL, NULL DUSK_GIVE_TAG("telma_invoice")); if (mItemID != -1) { s16 event_id = dComIfGp_getEventManager().getEventIdx( this, "DEFAULT_GETITEM", 0xff); diff --git a/src/d/actor/d_a_npc_uri.cpp b/src/d/actor/d_a_npc_uri.cpp index aed64d4c3a..afb1d11b1b 100644 --- a/src/d/actor/d_a_npc_uri.cpp +++ b/src/d/actor/d_a_npc_uri.cpp @@ -1165,9 +1165,9 @@ int daNpc_Uri_c::cutEndCarryTutorial(int param_1) { (s32)mFlow.getEventId(&local_48) == 1) { if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { - DUSK_ITEM_CHECK("Uli Cradle Delivery", local_48, this); + DUSK_ITEM_CHECK("uli_cradle_reward", local_48, this); mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, local_48, 0, -1, -1, - NULL, NULL DUSK_GIVE_TAG("Uli Cradle Delivery")); + NULL, NULL DUSK_GIVE_TAG("uli_cradle_reward")); } if (fopAcM_IsExecuting(mItemPartnerId)) { /* T_0007 - Ordon Village - During Uli's pick-up tutorial */ diff --git a/src/d/actor/d_a_npc_ykw.cpp b/src/d/actor/d_a_npc_ykw.cpp index 38d7bdb412..7b84e1a1bb 100644 --- a/src/d/actor/d_a_npc_ykw.cpp +++ b/src/d/actor/d_a_npc_ykw.cpp @@ -2213,9 +2213,9 @@ int daNpc_ykW_c::cutEndSnowboardRace(int param_0) { switch (eventId) { case 1: if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { - DUSK_ITEM_CHECK("Snowboard Racing Prize", itemId, this); + DUSK_ITEM_CHECK("snowboard_race_reward", itemId, this); mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, itemId, 0, -1, - -1, 0, 0 DUSK_GIVE_TAG("Snowboard Racing Prize")); + -1, 0, 0 DUSK_GIVE_TAG("snowboard_race_reward")); } if (fopAcM_IsExecuting(mItemPartnerId)) { @@ -2912,9 +2912,9 @@ int daNpc_ykW_c::talk(void* param_0) { switch (eventId) { case 1: if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) { - DUSK_ITEM_CHECK("Snowpeak Ruins Mansion Map", itemNo, this); + DUSK_ITEM_CHECK("dungeon_map:D_MN11", itemNo, this); mItemPartnerId = fopAcM_createItemForPresentDemo(¤t.pos, itemNo, - 0, -1, -1, 0, 0 DUSK_GIVE_TAG("Snowpeak Ruins Mansion Map")); + 0, -1, -1, 0, 0 DUSK_GIVE_TAG("dungeon_map:D_MN11")); } if (fopAcM_IsExecuting(mItemPartnerId)) { diff --git a/src/d/actor/d_a_npc_zra.inc b/src/d/actor/d_a_npc_zra.inc index 3846051bbe..70426d5f99 100644 --- a/src/d/actor/d_a_npc_zra.inc +++ b/src/d/actor/d_a_npc_zra.inc @@ -2612,9 +2612,9 @@ BOOL daNpc_zrA_c::ECut_thanksBlast(int i_staffID) { #if TARGET_PC const char* itemCheckName = nullptr; if (item_id == dItemNo_BOMB_IN_BAG_e) { - itemCheckName = "Iza Helping Hand"; + itemCheckName = "iza_reward_1"; } else if (item_id == dItemNo_BOMB_BAG_LV2_e) { - itemCheckName = "Iza Raging Rapids Minigame"; + itemCheckName = "iza_reward_2"; } if (itemCheckName != nullptr) { item_id = dusk::mods::item_check(itemCheckName, item_id, this); diff --git a/src/d/actor/d_a_npc_zrc.cpp b/src/d/actor/d_a_npc_zrc.cpp index b800d13f80..f94dbaf087 100644 --- a/src/d/actor/d_a_npc_zrc.cpp +++ b/src/d/actor/d_a_npc_zrc.cpp @@ -1681,9 +1681,9 @@ BOOL daNpc_zrC_c::ECut_earringGet(int i_staffID) { case 40: { int item_no = 0; if (mFlow.getEventId(&item_no) == 1) { - DUSK_ITEM_CHECK("Gift From Ralis", item_no, this); + DUSK_ITEM_CHECK("coral_earring", item_no, this); mItemID = fopAcM_createItemForPresentDemo( - ¤t.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("Gift From Ralis")); + ¤t.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("coral_earring")); } break; } diff --git a/src/d/actor/d_a_npc_zrz.cpp b/src/d/actor/d_a_npc_zrz.cpp index 62312a5b81..0662a290cd 100644 --- a/src/d/actor/d_a_npc_zrz.cpp +++ b/src/d/actor/d_a_npc_zrz.cpp @@ -1729,9 +1729,9 @@ BOOL daNpc_zrZ_c::ECut_clothesGet(int i_staffID) { } item_no = 0; if (mFlow.getEventId(&item_no) == 1) { - DUSK_ITEM_CHECK("Rutelas Blessing", item_no, this); + DUSK_ITEM_CHECK("zora_armor", item_no, this); mItemID = fopAcM_createItemForPresentDemo( - ¤t.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("Rutelas Blessing")); + ¤t.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("zora_armor")); } break; diff --git a/src/d/actor/d_a_obj_shield.cpp b/src/d/actor/d_a_obj_shield.cpp index aad1594107..abe56ad6c8 100644 --- a/src/d/actor/d_a_obj_shield.cpp +++ b/src/d/actor/d_a_obj_shield.cpp @@ -109,7 +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); + DUSK_ITEM_CHECK("ordon_shield", m_itemNo, this); if (fopAcM_isSwitch(this, getSwBit2())) { OS_REPORT("木の盾:もう取ったので出ません\n"); return cPhs_ERROR_e; @@ -242,8 +242,8 @@ int daItemShield_c::initActionOrderGetDemo() { fopAcM_orderItemEvent(this, 0, 0); eventInfo.onCondition(dEvtCnd_CANGETITEM_e); mItemId = fopAcM_createItemForTrBoxDemo(¤t.pos, - DUSK_ITEM_CHECK_EXPR("Ordon Shield", dItemNo_WOOD_SHIELD_e, this), -1, - fopAcM_GetRoomNo(this), 0, 0 DUSK_GIVE_TAG("Ordon Shield")); + 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; diff --git a/src/d/actor/d_a_obj_sword.cpp b/src/d/actor/d_a_obj_sword.cpp index c393edb33b..d7bf0cfaab 100644 --- a/src/d/actor/d_a_obj_sword.cpp +++ b/src/d/actor/d_a_obj_sword.cpp @@ -37,7 +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); + DUSK_ITEM_CHECK("ordon_sword", m_itemNo, this); if (fopAcM_isItem(this, getItemBit())) { return cPhs_ERROR_e; } @@ -73,8 +73,8 @@ int daObjSword_c::initActionOrderGetDemo() { fopAcM_orderItemEvent(this, 0, 0); eventInfo.onCondition(8); mProcID = - fopAcM_createItemForTrBoxDemo(¤t.pos, DUSK_ITEM_CHECK_EXPR("Ordon Sword", 0x28, this), - -1, fopAcM_GetRoomNo(this), NULL, NULL DUSK_GIVE_TAG("Ordon Sword")); + fopAcM_createItemForTrBoxDemo(¤t.pos, DUSK_ITEM_CHECK_EXPR("ordon_sword", 0x28, this), + -1, fopAcM_GetRoomNo(this), NULL, NULL DUSK_GIVE_TAG("ordon_sword")); setStatus(1); return 1; } diff --git a/src/d/actor/d_a_obj_wood_statue.cpp b/src/d/actor/d_a_obj_wood_statue.cpp index 626f36adae..9656036235 100644 --- a/src/d/actor/d_a_obj_wood_statue.cpp +++ b/src/d/actor/d_a_obj_wood_statue.cpp @@ -256,8 +256,8 @@ int daObjWStatue_c::initActionOrderGetDemo() { dComIfGp_getEvent()->reset(this); fopAcM_orderChangeEventId(this, eventIdx, 1, 0xffff); mItemId = fopAcM_createItemForTrBoxDemo(¤t.pos, - DUSK_ITEM_CHECK_EXPR("Wooden Statue", m_itemNo, this), 0xffffffff, fopAcM_GetRoomNo(this), - 0, 0 DUSK_GIVE_TAG("Wooden Statue")); + 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; diff --git a/src/d/actor/d_a_obj_zcloth.cpp b/src/d/actor/d_a_obj_zcloth.cpp index 46561b2471..7d9ca8fc51 100644 --- a/src/d/actor/d_a_obj_zcloth.cpp +++ b/src/d/actor/d_a_obj_zcloth.cpp @@ -32,7 +32,7 @@ int daObjZCloth_c::Create() { int daObjZCloth_c::create() { fopAcM_ct(this, daObjZCloth_c); m_itemNo = 0x31; - DUSK_ITEM_CHECK("Rutelas Blessing", m_itemNo, this); + 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)) { diff --git a/src/d/d_event_manager.cpp b/src/d/d_event_manager.cpp index 28464aceaf..da50ec7d96 100644 --- a/src/d/d_event_manager.cpp +++ b/src/d/d_event_manager.cpp @@ -340,7 +340,7 @@ bool dEvent_manager_c::setObjectArchive(DUSK_CONST char* arcname) { // pointer to Prayer event `011get_item` prm0 in it's event_list.dat u8* itemNo = static_cast(rt) + 0x927; *itemNo = - dusk::mods::item_check("Charlo Donation Blessing", dItemNo_KAKERA_HEART_e, NULL); + dusk::mods::item_check("prayer_reward", dItemNo_KAKERA_HEART_e, NULL); } #endif int base_status = mEventList[BASE_ACTOR].init((char*)rt, -1); diff --git a/src/d/d_shop_system.cpp b/src/d/d_shop_system.cpp index d15004b656..bf2d3650af 100644 --- a/src/d/d_shop_system.cpp +++ b/src/d/d_shop_system.cpp @@ -908,7 +908,7 @@ int dShopSystem_c::seq_start(fopAc_ac_c* actor, dMsgFlow_c* i_flow) { #if TARGET_PC const char* itemCheckName = nullptr; if (itemNo == dItemNo_HALF_MILK_BOTTLE_e) { - itemCheckName = "Ordon Cat Rescue"; + itemCheckName = "sera_reward"; itemNo = dusk::mods::item_check(itemCheckName, itemNo, actor); } #endif From 5abc956448f4935f854a49135ffc0b3b06f49582 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Tue, 11 Aug 2026 14:45:53 -0700 Subject: [PATCH 05/15] daTbox2 handling --- include/d/actor/d_a_tbox2.h | 3 +++ src/d/actor/d_a_tbox2.cpp | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/d/actor/d_a_tbox2.h b/include/d/actor/d_a_tbox2.h index 3f176ade5e..56a5a48411 100644 --- a/include/d/actor/d_a_tbox2.h +++ b/include/d/actor/d_a_tbox2.h @@ -83,6 +83,9 @@ private: /* 0xAC0 */ u8 field_0xAC0[0xAC4 - 0xAC0]; /* 0xAC4 */ int mStaffIdx; /* 0xAC8 */ dBgW* mBoxBgW; +#if TARGET_PC + u8 mOriginalItemNo; +#endif }; STATIC_ASSERT(sizeof(daTbox2_c) == 0xACC); diff --git a/src/d/actor/d_a_tbox2.cpp b/src/d/actor/d_a_tbox2.cpp index 2f2a3a8276..4b83317092 100644 --- a/src/d/actor/d_a_tbox2.cpp +++ b/src/d/actor/d_a_tbox2.cpp @@ -138,6 +138,14 @@ int daTbox2_c::create1st() { fopAcM_ct(this, daTbox2_c); mModelType = getModelType(); +#if TARGET_PC + 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); +#endif + int phase_state = dComIfG_resLoad(&mPhase, l_arcName); if (phase_state == cPhs_COMPLEATE_e) { u32 heap_size; @@ -370,12 +378,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(¤t.pos, item_no, 1, -1, -1, NULL, NULL); + partner_id = fopAcM_createItemForPresentDemo(¤t.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag)); } else { - partner_id = fopAcM_createItemForTrBoxDemo(¤t.pos, item_no, -1, -1, NULL, NULL); + partner_id = fopAcM_createItemForTrBoxDemo(¤t.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag)); } if (partner_id != -1) { From 635901d3bdb388b7bb52cebb701c860f0805d342 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Wed, 12 Aug 2026 13:59:01 -0700 Subject: [PATCH 06/15] update game service major version --- sdk/include/mods/svc/game.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/include/mods/svc/game.h b/sdk/include/mods/svc/game.h index ecb8bcfeb7..32de49903f 100644 --- a/sdk/include/mods/svc/game.h +++ b/sdk/include/mods/svc/game.h @@ -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 { From 5b4def820a9f481c0a7c2a9cc544fdd6a13b9192 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Wed, 12 Aug 2026 15:02:33 -0700 Subject: [PATCH 07/15] couple fixes --- include/d/actor/d_a_tbox2.h | 1 + src/d/actor/d_a_tbox2.cpp | 13 ++++++++----- src/dusk/mods/item_gives.cpp | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/d/actor/d_a_tbox2.h b/include/d/actor/d_a_tbox2.h index 56a5a48411..9b06e685a3 100644 --- a/include/d/actor/d_a_tbox2.h +++ b/include/d/actor/d_a_tbox2.h @@ -84,6 +84,7 @@ private: /* 0xAC4 */ int mStaffIdx; /* 0xAC8 */ dBgW* mBoxBgW; #if TARGET_PC + bool mParamsInit; u8 mOriginalItemNo; #endif }; diff --git a/src/d/actor/d_a_tbox2.cpp b/src/d/actor/d_a_tbox2.cpp index 4b83317092..54c3e3135e 100644 --- a/src/d/actor/d_a_tbox2.cpp +++ b/src/d/actor/d_a_tbox2.cpp @@ -139,11 +139,14 @@ int daTbox2_c::create1st() { mModelType = getModelType(); #if TARGET_PC - 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); + 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); diff --git a/src/dusk/mods/item_gives.cpp b/src/dusk/mods/item_gives.cpp index 7610adc276..bfe92d3cff 100644 --- a/src/dusk/mods/item_gives.cpp +++ b/src/dusk/mods/item_gives.cpp @@ -171,7 +171,7 @@ void dispatch_demo_give() { daAlink_c* link = daAlink_getAlinkActorClass(); dComIfGp_getEvent()->setGtItm(s_inFlightItem); - link->mProcID = daAlink_c::PROC_GET_ITEM; + link->procCoGetItemInit(); const s16 eventIndex = dComIfGp_getEventManager().getEventIdx(link, "DEFAULT_GETITEM", 0xFF); fopAcM_orderChangeEventId(link, eventIndex, 1, 0xFFFF); } From 46da33be37198a5ea65c721aef04fe4d4cfb9d3c Mon Sep 17 00:00:00 2001 From: doop <56421834+dooplecks@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:44:36 -0400 Subject: [PATCH 08/15] Use texture replacement API for HQ minimap textures (#2303) --- files.cmake | 1 + src/d/d_map_path.cpp | 131 +------------------------ src/dusk/hq_minimap.cpp | 210 ++++++++++++++++++++++++++++++++++++++++ src/dusk/hq_minimap.hpp | 21 ++++ src/dusk/settings.cpp | 2 + src/dusk/settings.h | 1 + src/m_Do/m_Do_main.cpp | 5 + 7 files changed, 243 insertions(+), 128 deletions(-) create mode 100644 src/dusk/hq_minimap.cpp create mode 100644 src/dusk/hq_minimap.hpp diff --git a/files.cmake b/files.cmake index 6fc4762be3..675ba70fdc 100644 --- a/files.cmake +++ b/files.cmake @@ -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 diff --git a/src/d/d_map_path.cpp b/src/d/d_map_path.cpp index 08591007ec..41d5108e32 100644 --- a/src/d/d_map_path.cpp +++ b/src/d/d_map_path.cpp @@ -16,23 +16,14 @@ #ifdef TARGET_PC #include "dusk/settings.h" +#include "dusk/hq_minimap.hpp" #include "m_Do/m_Do_graphic.h" #include #include #include -#include #include -#include #include -#include -#include - -constexpr u16 kMapIconResolutionMultiplier = 4; -constexpr u16 kMapImageSide = 16 * kMapIconResolutionMultiplier; -constexpr u32 kMapImageTotalPixels = kMapImageSide * kMapImageSide; - -typedef std::function PaintI8Fn; u16 scaled_map_axis(u16 value, f32 scale) { const auto scaledValue = @@ -59,27 +50,6 @@ aurora::Vec2 map_render_size_for(u16 width, u16 height) { scaled_map_axis(height, irScaleY * hudScale), }; } - -void paint_i8(std::span 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(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(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) - - 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 * 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 >({ - {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() { diff --git a/src/dusk/hq_minimap.cpp b/src/dusk/hq_minimap.cpp new file mode 100644 index 0000000000..a4d73cbcf1 --- /dev/null +++ b/src/dusk/hq_minimap.cpp @@ -0,0 +1,210 @@ +#include "aurora/texture.hpp" +#include "dusk/texture_replacements.hpp" +#include "fmt/format.h" + +#include +#include +#include +#include + +namespace { + +constexpr u16 kMapIconResolutionMultiplier = 4; +constexpr u16 kMapImageSide = 16 * kMapIconResolutionMultiplier; +constexpr u32 kMapImageTotalPixels = kMapImageSide * kMapImageSide; + +// give higher priority to user and mod replacements +constexpr auto kInternalTextureReplacementPriority = dusk::texture_replacements::kUserTextureReplacementPriority - 1; + +typedef std::function PaintI8Fn; + +enum class ArcIndex : int { + Circle16 = 82, // map_icon_circle16x16_4i.bti - simple circle + Circle = 76, // im_map_icon_circle_4i.bti - outlined circle + Nijumaru = 78, // im_map_icon_nijumaru_4i.bti - concentric rings + Enter = 77, // im_map_icon_enter_4i.bti - outlined octagram + TryForce = 81, // im_map_icon_try_force_4i.bti - outlined circle with triangle +}; + +struct Replacement { + ArcIndex index; + PaintI8Fn painter; +}; + +struct Icon { + u8* origData = nullptr; + std::unique_ptr newData; + std::string label; + std::optional reg; +}; + +bool s_initialized = false; +bool s_active = false; +std::unordered_map s_icons; + +void paint_i8(std::span dst, size_t width, PaintI8Fn paint) { + assert(width % 8 == 0 && dst.size() % 32 == 0); + + 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); + } +} + +void draw_all_replacements() { + constexpr auto center = kMapImageSide / 2.0f; + constexpr auto radiusSq = center * center; + + // clang-format off + const auto replacements = std::to_array({ + { + ArcIndex::Circle16, + [=](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; + } + }, + { + ArcIndex::Circle, + [=](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; + } + }, + { + ArcIndex::Nijumaru, + [=](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) { + auto ringIndex = static_cast(std::trunc(std::sqrt(dSq) / kMapImageSide * 12)); + ringIndex = std::min(ringIndex, sizeof(nijumaruRings) - 1); + return nijumaruRings[ringIndex]; + } + return u8{0}; + } + }, + { + ArcIndex::Enter, + [=](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) - + kMapImageSide / 2.0f; + + return dist > 0.0f ? 0 : (dist > -outlineWidth ? 0x22 : 0x33); + } + }, + { + ArcIndex::TryForce, + [=](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 * std::abs(dx) - dy) * 0.5f; + const auto insideTri = std::max(dy, triSideDist) < triRadius; + + return insideTri ? 0x22 : (dSq < radiusSq ? (dSq < innerRadiusSq ? 0x33 : 0x22) : 0); + } + } + }); + // clang-format on + + for (const auto r : replacements) { + auto pixels = std::make_unique_for_overwrite(kMapImageTotalPixels); + paint_i8(std::span{pixels.get(), kMapImageTotalPixels}, kMapImageSide, r.painter); + + auto& icon = s_icons[static_cast(r.index)]; + icon.newData = std::move(pixels); + icon.label = fmt::format("hq minimap icon {}", static_cast(r.index)); + } +} + +} // namespace + +namespace dusk::hq_minimap { + +void register_pointer(int idx, u8* ptr) { + if (s_initialized) { + return; + } + + s_icons[idx].origData = ptr; +} + +void set_active(bool active) { + s_active = active; +} + +void update() { + if (!s_initialized) { + return; + } + + for (auto& [idx, icon] : s_icons) { + const bool shouldBeRegistered = s_active && icon.origData && icon.newData; + + if (shouldBeRegistered && !icon.reg) { + aurora::texture::ReplacementKey key{aurora::texture::TexturePointerKey{icon.origData}}; + aurora::texture::RawTextureReplacement repl{ + .bytes = std::span{icon.newData.get(), kMapImageTotalPixels}, + .width = kMapImageSide, + .height = kMapImageSide, + .mipCount = 1, + .gxFormat = GX_TF_I8, + .label = icon.label, + }; + icon.reg = aurora::texture::register_replacement( + key, repl, {.priority = kInternalTextureReplacementPriority}); + } else if (!shouldBeRegistered && icon.reg) { + aurora::texture::unregister_replacement(*icon.reg); + icon.reg.reset(); + } + } +} + +void initialize_if_needed() { + if (s_initialized) { + return; + } + + draw_all_replacements(); + s_initialized = true; + + update(); +} + +} // namespace dusk::hq_minimap diff --git a/src/dusk/hq_minimap.hpp b/src/dusk/hq_minimap.hpp new file mode 100644 index 0000000000..5ca89ac7d1 --- /dev/null +++ b/src/dusk/hq_minimap.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace dusk::hq_minimap { + +/// Adds a mapping of an image resource index (into `Always.arc`) to the pointer to its image data. +/// If `initialize_if_needed` has been called, this is a no-op. Pointers are expected to be stable +/// and valid for the program's entire lifetime. +void register_pointer(int idx, u8* ptr); + +/// Sets whether HQ minimap texture replacements should be active or not. Does not manage +/// replacement registrations itself; see `update`. +void set_active(bool active); + +/// Registers or unregisters texture replacements depending on active state. +void update(); + +/// Called once after registering image pointers, in which their HQ replacements are procedurally +/// drawn and `update` is called. Further calls are no-ops. +void initialize_if_needed(); + +} diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 00191c3015..a6c3149983 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -76,6 +76,7 @@ UserSettings g_userSettings = { .resampler {"game.resampler", Resampler::Bilinear}, .enableMapBackground {"game.enableMapBackground", true}, .disableCutscenePillarboxing {"game.disableCutscenePillarboxing", false}, + .enableHighQualityMinimapTextures {"game.enableHighQualityMinimapTextures", true}, // Audio .noLowHpSound {"game.noLowHpSound", false}, @@ -283,6 +284,7 @@ void registerSettings() { Register(g_userSettings.game.shadowResolutionMultiplier); Register(g_userSettings.game.enableMapBackground); Register(g_userSettings.game.disableCutscenePillarboxing); + Register(g_userSettings.game.enableHighQualityMinimapTextures); Register(g_userSettings.game.enableFastIronBoots); Register(g_userSettings.game.canTransformAnywhere); Register(g_userSettings.game.fastRoll); diff --git a/src/dusk/settings.h b/src/dusk/settings.h index 19aad6ee09..e31dc5eb42 100644 --- a/src/dusk/settings.h +++ b/src/dusk/settings.h @@ -208,6 +208,7 @@ struct UserSettings { ConfigVar resampler; ConfigVar enableMapBackground; ConfigVar disableCutscenePillarboxing; + ConfigVar enableHighQualityMinimapTextures; // Audio ConfigVar noLowHpSound; diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index ccb6c35c1c..6a6d38c890 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -65,6 +65,7 @@ #include "dusk/iso_validate.hpp" #include "dusk/logging.h" #include "dusk/main.h" +#include "dusk/hq_minimap.hpp" #include "dusk/mod_loader.hpp" #include "dusk/mods/svc/window.hpp" #include "dusk/mouse.h" @@ -711,6 +712,10 @@ int game_main(int argc, char* argv[]) { return 0; } + if (dusk::getSettings().game.enableHighQualityMinimapTextures.getValue()) { + dusk::hq_minimap::set_active(true); + } + dusk::texture_replacements::reload(); dusk::ui::initialize(); dusk::ui::push_document(std::make_unique(), true, true); From 045fbd9b469d6e02f4eac6d7d7e17410f50359a8 Mon Sep 17 00:00:00 2001 From: matthewdavidrichardanderson Date: Fri, 14 Aug 2026 06:54:29 +0100 Subject: [PATCH 09/15] request presentation sync for all captures (#2293) Co-authored-by: matthewdavidrichardanderson --- src/d/d_menu_window.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/d/d_menu_window.cpp b/src/d/d_menu_window.cpp index 7873632265..eb67822899 100644 --- a/src/d/d_menu_window.cpp +++ b/src/d/d_menu_window.cpp @@ -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() { From 0f5a248401a76883c5a5011f9c424fc00fa9f894 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Fri, 14 Aug 2026 00:07:54 -0600 Subject: [PATCH 10/15] 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> --- extern/aurora | 2 +- libs/JSystem/src/JFramework/JFWDisplay.cpp | 19 +-- libs/JSystem/src/JUtility/JUTVideo.cpp | 4 +- src/d/d_kankyo.cpp | 2 +- src/d/d_kyeff.cpp | 2 +- src/d/d_menu_ring.cpp | 2 +- src/d/d_save.cpp | 4 +- src/dusk/action_bindings.cpp | 9 ++ src/dusk/action_bindings.h | 1 + src/dusk/frame_interpolation.cpp | 4 + src/dusk/game_clock.cpp | 141 +++++++++++++-------- src/dusk/game_clock.h | 31 +++-- src/dusk/imgui/ImGuiConsole.cpp | 36 +++++- src/dusk/imgui/ImGuiMenuTools.cpp | 5 - src/dusk/presentation.cpp | 4 + src/dusk/settings.cpp | 2 +- src/dusk/settings.h | 2 +- src/dusk/speedrun.h | 18 ++- src/dusk/ui/overlay.cpp | 37 ++++-- src/f_op/f_op_overlap_req.cpp | 7 +- src/f_pc/f_pc_manager.cpp | 6 +- src/m_Do/m_Do_MemCardRWmng.cpp | 2 +- src/m_Do/m_Do_machine.cpp | 2 +- src/m_Do/m_Do_main.cpp | 54 +++++--- 24 files changed, 248 insertions(+), 148 deletions(-) diff --git a/extern/aurora b/extern/aurora index 8005d336ab..b2d7b78767 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 8005d336ab02f0c617fc27fabbea4af42ab04caa +Subproject commit b2d7b78767085f28f4f2174ee32b39b59a80d7e3 diff --git a/libs/JSystem/src/JFramework/JFWDisplay.cpp b/libs/JSystem/src/JFramework/JFWDisplay.cpp index 0c26e77a53..466335eae9 100644 --- a/libs/JSystem/src/JFramework/JFWDisplay.cpp +++ b/libs/JSystem/src/JFramework/JFWDisplay.cpp @@ -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(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(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(sleepTime) / static_cast(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) { - dusk::frameUsagePct = 0.f; - return; + 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(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((RETRACE_PERIOD * uVar1).count())); #else static u32 nextCount = VIGetRetraceCount(); diff --git a/libs/JSystem/src/JUtility/JUTVideo.cpp b/libs/JSystem/src/JUtility/JUTVideo.cpp index 2135c8fc22..b9a51ab6f3 100644 --- a/libs/JSystem/src/JUtility/JUTVideo.cpp +++ b/libs/JSystem/src/JUtility/JUTVideo.cpp @@ -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(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(OSGetNativeTime()), OSGetTick()); sVideoInterval = tick - sVideoLastTick; sVideoLastTick = tick; diff --git a/src/d/d_kankyo.cpp b/src/d/d_kankyo.cpp index e6f155a989..f9d77837f9 100644 --- a/src/d/d_kankyo.cpp +++ b/src/d/d_kankyo.cpp @@ -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, diff --git a/src/d/d_kyeff.cpp b/src/d/d_kyeff.cpp index 984478f74d..1ba4ffa96f 100644 --- a/src/d/d_kyeff.cpp +++ b/src/d/d_kyeff.cpp @@ -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; diff --git a/src/d/d_menu_ring.cpp b/src/d/d_menu_ring.cpp index 3fe55c2b6c..f0f44db166 100644 --- a/src/d/d_menu_ring.cpp +++ b/src/d/d_menu_ring.cpp @@ -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 { diff --git a/src/d/d_save.cpp b/src/d/d_save.cpp index cdd5c467ef..d634c7b21d 100644 --- a/src/d/d_save.cpp +++ b/src/d/d_save.cpp @@ -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]; diff --git a/src/dusk/action_bindings.cpp b/src/dusk/action_bindings.cpp index 68b07d6c0a..c919c9d4fe 100644 --- a/src/dusk/action_bindings.cpp +++ b/src/dusk/action_bindings.cpp @@ -41,6 +41,15 @@ bool isActionBound(ActionBinds action, u32 port) { return getActionBindButton(action, port) != PAD_NATIVE_BUTTON_INVALID; } +bool isActionBoundAnyPort(ActionBinds action) { + for (u32 port = 0; port < PAD_CHANMAX; ++port) { + if (isActionBound(action, port)) { + return true; + } + } + return false; +} + void updateActionBindings() { for (u32 port = 0; port < PAD_CHANMAX; ++port) { // Move the current press to the previous frame diff --git a/src/dusk/action_bindings.h b/src/dusk/action_bindings.h index a71dac5dfe..77ce9cb815 100644 --- a/src/dusk/action_bindings.h +++ b/src/dusk/action_bindings.h @@ -31,6 +31,7 @@ using ActionBindsMap = std::unordered_map; ActionBindsMap& getActionBinds(); bool isActionBound(ActionBinds action, u32 port); +bool isActionBoundAnyPort(ActionBinds action); void updateActionBindings(); diff --git a/src/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp index be03d51e96..ca30ec0db5 100644 --- a/src/dusk/frame_interpolation.cpp +++ b/src/dusk/frame_interpolation.cpp @@ -147,6 +147,10 @@ void begin_frame(FrameInterpMode mode, bool is_sim_frame, float step) { g_enabled = mode != FrameInterpMode::Off; g_is_sim_frame = is_sim_frame; g_step = std::clamp(step, 0.0f, 1.0f); + if (!g_enabled) { + g_interpolating = false; + clear_replacements(); + } } bool is_enabled() { diff --git a/src/dusk/game_clock.cpp b/src/dusk/game_clock.cpp index 1dc7fbf322..18ee24d542 100644 --- a/src/dusk/game_clock.cpp +++ b/src/dusk/game_clock.cpp @@ -1,104 +1,135 @@ #include "dusk/game_clock.h" #include +#include #include #include -#include #include +#include namespace dusk::game_clock { -using clock = std::chrono::steady_clock; +using native_clock = aurora::time::native_clock; +using game_clock = aurora::time::game_clock; +FrameTiming g_frameTiming; + +namespace { bool s_initialized = false; -clock::time_point s_previous_sample{}; -clock::time_point s_current_snapshot_time{}; +bool s_fixedStepActive = false; +bool s_simTickActive = false; +native_clock::time_point s_previousNativeSample{}; +game_clock::time_point s_latestGameSample{}; +game_clock::time_point s_currentSnapshotTime{}; +game_clock::time_point s_pendingSimTime{}; -std::unordered_map s_interval_last_sample; +std::unordered_map s_intervalLastSample; -constexpr clock::duration kSimPeriodDuration = - std::chrono::duration_cast(std::chrono::duration(sim_pace())); -constexpr clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250); -constexpr int kMaxSimTicksPerFrame = 2; +constexpr game_clock::duration kSimPeriodDuration = + std::chrono::duration_cast(std::chrono::duration(kSimPeriod)); +constexpr native_clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250); +constexpr int kMaxSimTicksPerFrame = static_cast(aurora::time::kMaximumTimeScale) * 4; +} // namespace -void ensure_initialized() { +void initialize() { if (s_initialized) { return; } - s_previous_sample = clock::now(); - s_current_snapshot_time = s_previous_sample; + s_previousNativeSample = native_clock::now(); + s_latestGameSample = game_clock::now(); + s_currentSnapshotTime = s_latestGameSample; + s_pendingSimTime = s_latestGameSample; s_initialized = true; } -void reset_frame_timer() { - s_previous_sample = clock::now(); - s_current_snapshot_time = s_previous_sample - kSimPeriodDuration; +void reset() { + s_previousNativeSample = native_clock::now(); + s_latestGameSample = game_clock::now(); + s_currentSnapshotTime = s_latestGameSample - kSimPeriodDuration; + s_pendingSimTime = s_currentSnapshotTime; + s_simTickActive = false; } -MainLoopPacer advance_main_loop() { - ensure_initialized(); +const FrameTiming& advance() { + const auto nativeNow = native_clock::now(); + const auto gameNow = game_clock::now(); + const auto nativeFrameGap = nativeNow - s_previousNativeSample; + s_previousNativeSample = nativeNow; + s_latestGameSample = gameNow; - const clock::time_point now = clock::now(); - const clock::duration frame_gap = now - s_previous_sample; - const float presentation_dt = std::chrono::duration(frame_gap).count(); - s_previous_sample = now; + auto& out = g_frameTiming; + out = {.dt = std::chrono::duration().count()}; - MainLoopPacer out{}; - out.presentation_dt_seconds = presentation_dt; + const float timeScale = aurora::time::scale(); + const bool interpolating = + getSettings().game.enableFrameInterpolation.getValue() != FrameInterpMode::Off; + const bool separatePresentation = interpolating || timeScale != 1.0f; + out.interpolating = interpolating; + out.separatePresentation = separatePresentation; + s_fixedStepActive = separatePresentation; - const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation.getValue() != - dusk::FrameInterpMode::Off && - !dusk::getTransientSettings().skipFrameRateLimit; - out.is_interpolating = should_interpolate; - out.sim_pace = sim_pace(); - - if (!should_interpolate) { - s_current_snapshot_time = now; - out.sim_ticks_to_run = 1; + if (!separatePresentation) { + s_currentSnapshotTime = gameNow; + out.numSimTicks = 1; return out; } - if (frame_gap > kAbnormalGapResetThreshold) { - s_current_snapshot_time = now - kSimPeriodDuration; - out.sim_ticks_to_run = 0; + const auto simulationTarget = interpolating ? gameNow - kSimPeriodDuration : gameNow; + if (timeScale == 0.f || nativeFrameGap > kAbnormalGapResetThreshold) { + s_currentSnapshotTime = simulationTarget; + out.numSimTicks = 0; return out; } - int sim_ticks_to_run = 0; - clock::time_point projected_snapshot_time = s_current_snapshot_time; - const clock::time_point render_time = now - kSimPeriodDuration; - while (sim_ticks_to_run < kMaxSimTicksPerFrame && projected_snapshot_time < render_time) { - projected_snapshot_time += kSimPeriodDuration; - sim_ticks_to_run++; + int numSimTicks = 0; + auto projectedSnapshotTime = s_currentSnapshotTime; + while (numSimTicks < kMaxSimTicksPerFrame) { + const bool tickDue = interpolating ? + projectedSnapshotTime < simulationTarget : + projectedSnapshotTime + kSimPeriodDuration <= simulationTarget; + if (!tickDue) { + break; + } + projectedSnapshotTime += kSimPeriodDuration; + numSimTicks++; } - out.sim_ticks_to_run = sim_ticks_to_run; + out.numSimTicks = numSimTicks; return out; } +void begin_sim_tick() { + s_pendingSimTime = + s_fixedStepActive ? s_currentSnapshotTime + kSimPeriodDuration : s_latestGameSample; + s_simTickActive = true; +} + void commit_sim_tick() { - ensure_initialized(); - s_current_snapshot_time += kSimPeriodDuration; + if (s_simTickActive) { + s_currentSnapshotTime = s_pendingSimTime; + s_simTickActive = false; + } else { + s_currentSnapshotTime += kSimPeriodDuration; + } } float sample_interpolation_step() { - ensure_initialized(); const float step = - std::chrono::duration(clock::now() - s_current_snapshot_time).count() / sim_pace(); + std::chrono::duration(game_clock::now() - s_currentSnapshotTime).count() / + kSimPeriod; return std::clamp(step, 0.0f, 1.0f); } float consume_interval(const void* consumer) { - ensure_initialized(); - const uintptr_t key = reinterpret_cast(consumer); - const clock::time_point now = clock::now(); - - float dt = ui_initial_dt(); - const auto it = s_interval_last_sample.find(key); - if (it != s_interval_last_sample.end()) { + const auto key = reinterpret_cast(consumer); + const auto now = s_simTickActive ? s_pendingSimTime : game_clock::now(); + const float timeScale = aurora::time::scale(); + float dt = kUiInitialDt * timeScale; + if (const auto it = s_intervalLastSample.find(key); it != s_intervalLastSample.end()) { dt = std::chrono::duration(now - it->second).count(); - dt = std::min(dt, ui_maximum_dt()); + const float maximumDt = std::max(kUiMaximumDt * timeScale, kSimPeriod); + dt = std::min(dt, maximumDt); } - s_interval_last_sample[key] = now; + s_intervalLastSample[key] = now; return dt; } diff --git a/src/dusk/game_clock.h b/src/dusk/game_clock.h index 8bb277e070..b300cf28d7 100644 --- a/src/dusk/game_clock.h +++ b/src/dusk/game_clock.h @@ -2,22 +2,27 @@ namespace dusk::game_clock { -void ensure_initialized(); -void reset_frame_timer(); +// Amount of time that a simulation tick advances +constexpr float kSimPeriod = 1.0f / 30.0f; +constexpr float kUiMaximumDt = 0.05f; +constexpr float kUiInitialDt = 1.0f / 60.0f; -constexpr float sim_pace() { return 1.0f / 30.0f; } -constexpr float period_for_original_frames(float frame_count) { return frame_count * sim_pace(); } -constexpr float ui_maximum_dt() { return 0.05f; } -constexpr float ui_initial_dt() { return 1.0f / 60.0f; } - -struct MainLoopPacer { - float presentation_dt_seconds; - bool is_interpolating; - int sim_ticks_to_run; - float sim_pace; +struct FrameTiming { + // Amount of time elapsed in seconds since the last advance + float dt; + // Whether interpolation is active + bool interpolating; + // Run simulation and presentation separately (for interpolation or time scaling) + bool separatePresentation; + // Number of simulation ticks to run + int numSimTicks; }; +extern FrameTiming g_frameTiming; -MainLoopPacer advance_main_loop(); +void initialize(); +void reset(); +const FrameTiming& advance(); +void begin_sim_tick(); void commit_sim_tick(); float sample_interpolation_step(); diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 3c0131c729..a102b0ca11 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -1,8 +1,9 @@ #include #include +#include +#include #include #include -#include #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui.h" @@ -20,6 +21,7 @@ #include "dusk/frame_interpolation.h" #include "dusk/livesplit.h" #include "dusk/main.h" +#include "dusk/presentation.hpp" #include "dusk/settings.h" #include "dusk/ui/ui.hpp" #include "f_pc/f_pc_manager.h" @@ -37,6 +39,8 @@ using namespace std::string_literals; using namespace std::string_view_literals; namespace { +constexpr float kTurboTimeScale = 4.f; + ImGuiWindow* FindDragScrollWindow(ImGuiWindow* window) { while (window != nullptr) { const bool canScrollX = window->ScrollMax.x > 0.0f; @@ -235,10 +239,32 @@ namespace dusk { } void ImGuiConsole::UpdateSettings() { - getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && - (ImGui::IsKeyDown(ImGuiKey_Tab) || getActionBindHoldAnyPort(ActionBinds::TURBO_SPEED_BUTTON)); + static bool previousTurboActive = false; + static bool previousSlowActive = false; + static float previousTimeScale = 1.0f; - if (dusk::frame_interp::get_ui_tick_pending() && mDoMain::developmentMode == 1 && (mDoCPd_c::getHold(PAD_1) & (PAD_TRIGGER_R | PAD_TRIGGER_L)) == (PAD_TRIGGER_R | PAD_TRIGGER_L) && mDoCPd_c::getTrigY(PAD_1)) { + const bool turboBound = isActionBoundAnyPort(ActionBinds::TURBO_SPEED_BUTTON); + const bool turboActive = + getSettings().game.enableTurboKeybind && + (turboBound ? getActionBindHoldAnyPort(ActionBinds::TURBO_SPEED_BUTTON) : + ImGui::IsKeyDown(ImGuiKey_Tab)); + const bool slowDown = turboActive && ImGui::GetIO().KeyShift; + if (turboActive != previousTurboActive) { + getTransientSettings().turboMode = turboActive; + presentation::update_frame_rate_preference(); + if (turboActive) { + previousTimeScale = aurora_get_timescale(); + aurora_set_timescale(slowDown ? 1.f / kTurboTimeScale : kTurboTimeScale); + } else { + aurora_set_timescale(previousTimeScale); + } + } else if (turboActive && slowDown != previousSlowActive) { + aurora_set_timescale(slowDown ? 1.f / kTurboTimeScale : kTurboTimeScale); + } + previousTurboActive = turboActive; + previousSlowActive = slowDown; + + if (frame_interp::get_ui_tick_pending() && mDoMain::developmentMode == 1 && (mDoCPd_c::getHold(PAD_1) & (PAD_TRIGGER_R | PAD_TRIGGER_L)) == (PAD_TRIGGER_R | PAD_TRIGGER_L) && mDoCPd_c::getTrigY(PAD_1)) { getTransientSettings().moveLinkActive = !getTransientSettings().moveLinkActive; } if (mDoMain::developmentMode != 1) { @@ -270,7 +296,7 @@ namespace dusk { m_isHidden = true; } } - + bool showMenu = !m_isHidden; // The menu bar renders with ImGuiCol_WindowBg behind it. We just want ImGuiCol_MenuBarBg, diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index b9ad5eb021..c2f5e45754 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -25,10 +25,6 @@ #include #endif -namespace aurora::gx { -extern bool enableLodBias; -} - namespace dusk { ImGuiMenuTools::ImGuiMenuTools() {} @@ -76,7 +72,6 @@ namespace dusk { getSettings().game.disableWaterRefraction.setValue(disableWaterRefraction); config::save(); } - ImGui::Checkbox("Enable LOD Bias", &aurora::gx::enableLodBias); ImGui::EndMenu(); } diff --git a/src/dusk/presentation.cpp b/src/dusk/presentation.cpp index b55c11d93b..1443ab85f9 100644 --- a/src/dusk/presentation.cpp +++ b/src/dusk/presentation.cpp @@ -10,6 +10,10 @@ namespace dusk::presentation { namespace { float preferred_frame_rate() { + if (getTransientSettings().turboMode) { + return 0.0f; + } + switch (getSettings().game.enableFrameInterpolation.getValue()) { case FrameInterpMode::Off: return 30.0f; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index a6c3149983..1af01faa81 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -396,7 +396,7 @@ static TransientSettings g_transientSettings = { .colliderViewOpacity = 50.0f, .drawRange = 100.0f, }, - .skipFrameRateLimit = false, + .turboMode = false, }; TransientSettings& getTransientSettings() { diff --git a/src/dusk/settings.h b/src/dusk/settings.h index e31dc5eb42..914ef37efc 100644 --- a/src/dusk/settings.h +++ b/src/dusk/settings.h @@ -327,7 +327,7 @@ struct CollisionViewSettings { struct TransientSettings { CollisionViewSettings collisionView; - bool skipFrameRateLimit; + bool turboMode; bool moveLinkActive; bool stateShareLoadActive; }; diff --git a/src/dusk/speedrun.h b/src/dusk/speedrun.h index d9e43c3ed7..887d9e6842 100644 --- a/src/dusk/speedrun.h +++ b/src/dusk/speedrun.h @@ -6,18 +6,23 @@ namespace dusk { struct SpeedrunInfo { void startRun() { m_isRunStarted = true; - m_startTimestamp = OSGetTime(); + m_rtaStartTimestamp = OSGetNativeTime(); + m_igtStartTimestamp = OSGetTime(); } void stopRun() { m_isRunStarted = false; - m_endTimestamp = OSGetTime() - m_startTimestamp; + m_rtaTimer = OSGetNativeTime() - m_rtaStartTimestamp; + if (!m_isPauseIGT) { + m_igtTimer = OSGetTime() - m_igtStartTimestamp - m_totalLoadTime; + } } void reset() { m_isRunStarted = false; - m_startTimestamp = 0; - m_endTimestamp = 0; + m_rtaStartTimestamp = 0; + m_rtaTimer = 0; + m_igtStartTimestamp = 0; m_isPauseIGT = false; m_loadStartTimestamp = 0; m_totalLoadTime = 0; @@ -25,8 +30,9 @@ struct SpeedrunInfo { } bool m_isRunStarted = false; - OSTime m_startTimestamp = 0; - OSTime m_endTimestamp = 0; + OSTime m_rtaStartTimestamp = 0; + OSTime m_rtaTimer = 0; + OSTime m_igtStartTimestamp = 0; bool m_isPauseIGT = false; OSTime m_loadStartTimestamp = 0; diff --git a/src/dusk/ui/overlay.cpp b/src/dusk/ui/overlay.cpp index 740b4febf0..c271cccfdb 100644 --- a/src/dusk/ui/overlay.cpp +++ b/src/dusk/ui/overlay.cpp @@ -203,10 +203,19 @@ void remove_element(Rml::Element*& elem) noexcept { } // namespace -static std::string FormatTime(OSTime ticks) { - OSCalendarTime t; - OSTicksToCalendarTime(ticks, &t); - return fmt::format("{0:02}:{1:02}:{2:02}.{3:03}", t.hour, t.min, t.sec, t.msec); +static std::string FormatElapsedTime(OSTime ticksElapsed) { + using namespace std::chrono; + + milliseconds ms{OSTicksToMilliseconds(ticksElapsed)}; + + const hours hr = duration_cast(ms); + ms -= hr; + const minutes min = duration_cast(ms); + ms -= min; + const seconds sec = duration_cast(ms); + ms -= sec; + + return fmt::format("{0:02}:{1:02}:{2:02}.{3:03}", hr.count(), min.count(), sec.count(), ms.count()); } Overlay::Overlay() : Document(kDocumentSource, true, DocumentScope::Overlay) { @@ -317,33 +326,33 @@ void Overlay::update() { mDoCPd_c::getHoldA(PAD_1) && mDoCPd_c::getTrigY(PAD_1)) { if (m_speedrunInfo.m_isRunStarted) { - m_speedrunInfo.m_endTimestamp = OSGetTime() - m_speedrunInfo.m_startTimestamp; - m_speedrunInfo.m_isRunStarted = false; + m_speedrunInfo.stopRun(); } } - OSTime elapsedTime = 0; + OSTime rtaElapsedTime = 0; if (m_speedrunInfo.m_isRunStarted) { - elapsedTime = OSGetTime() - m_speedrunInfo.m_startTimestamp; - } else if (m_speedrunInfo.m_endTimestamp != 0) { - elapsedTime = m_speedrunInfo.m_endTimestamp; + rtaElapsedTime = OSGetNativeTime() - m_speedrunInfo.m_rtaStartTimestamp; + } else if (m_speedrunInfo.m_rtaTimer != 0) { + rtaElapsedTime = m_speedrunInfo.m_rtaTimer; } - if (!m_speedrunInfo.m_isPauseIGT) { - m_speedrunInfo.m_igtTimer = elapsedTime - m_speedrunInfo.m_totalLoadTime; + if (m_speedrunInfo.m_isRunStarted && !m_speedrunInfo.m_isPauseIGT) { + m_speedrunInfo.m_igtTimer = OSGetTime() - m_speedrunInfo.m_igtStartTimestamp - + m_speedrunInfo.m_totalLoadTime; } mSpeedrunTimer->SetAttribute("open", ""); if (getSettings().game.showSpeedrunRTATimer) { mSpeedrunRta->SetAttribute("open", ""); - mSpeedrunRta->SetInnerRML(escape(fmt::format("RTA {}", FormatTime(elapsedTime)))); + mSpeedrunRta->SetInnerRML(escape(fmt::format("RTA {}", FormatElapsedTime(rtaElapsedTime)))); } else { mSpeedrunRta->RemoveAttribute("open"); } mSpeedrunIgt->SetInnerRML( - escape(fmt::format("IGT {}", FormatTime(m_speedrunInfo.m_igtTimer)))); + escape(fmt::format("IGT {}", FormatElapsedTime(m_speedrunInfo.m_igtTimer)))); } else { mSpeedrunTimer->RemoveAttribute("open"); } diff --git a/src/f_op/f_op_overlap_req.cpp b/src/f_op/f_op_overlap_req.cpp index 37b901c706..7d7f855c26 100644 --- a/src/f_op/f_op_overlap_req.cpp +++ b/src/f_op/f_op_overlap_req.cpp @@ -19,15 +19,16 @@ static int fopOvlpReq_phase_Done(overlap_request_class* i_overlapReq) { i_overlapReq->field_0x8 = 0; i_overlapReq->field_0xc = 0; - #if TARGET_PC +#if TARGET_PC if (dusk::getSettings().game.speedrunMode) { if (dusk::m_speedrunInfo.m_isRunStarted) { dusk::m_speedrunInfo.m_isPauseIGT = false; - dusk::m_speedrunInfo.m_totalLoadTime += OSGetTime() - dusk::m_speedrunInfo.m_loadStartTimestamp; + dusk::m_speedrunInfo.m_totalLoadTime += + OSGetTime() - dusk::m_speedrunInfo.m_loadStartTimestamp; dusk::m_speedrunInfo.m_loadStartTimestamp = OSGetTime(); } } - #endif +#endif return cPhs_NEXT_e; } diff --git a/src/f_pc/f_pc_manager.cpp b/src/f_pc/f_pc_manager.cpp index 3cda826854..22c705c720 100644 --- a/src/f_pc/f_pc_manager.cpp +++ b/src/f_pc/f_pc_manager.cpp @@ -22,7 +22,7 @@ #include "m_Do/m_Do_controller_pad.h" #if TARGET_PC -#include "dusk/frame_interpolation.h" +#include "dusk/game_clock.h" #endif #include "tracy/Tracy.hpp" @@ -68,8 +68,8 @@ void fpcM_Management(fpcM_ManagementFunc i_preExecuteFn, fpcM_ManagementFunc i_p } #ifdef TARGET_PC - // FRAME INTERP NOTE: Called in m_Do_main when interp is enabled - if (!dusk::frame_interp::is_enabled()) + // The main loop manages painting when simulation and presentation are separated. + if (!dusk::game_clock::g_frameTiming.separatePresentation) #endif { cAPIGph_Painter(); diff --git a/src/m_Do/m_Do_MemCardRWmng.cpp b/src/m_Do/m_Do_MemCardRWmng.cpp index 59688043a5..bb0326f623 100644 --- a/src/m_Do/m_Do_MemCardRWmng.cpp +++ b/src/m_Do/m_Do_MemCardRWmng.cpp @@ -320,7 +320,7 @@ static void mDoMemCdRWm_BuildHeader(mDoMemCdRWm_HeaderData* header) { #endif OSCalendarTime time; - OSTicksToCalendarTime(OSGetTime(), &time); + OSTicksToCalendarTime(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()), &time); #if TARGET_PC if (dusk::version::isRegionPal()) { diff --git a/src/m_Do/m_Do_machine.cpp b/src/m_Do/m_Do_machine.cpp index a18657a2ad..3d0acca1a9 100644 --- a/src/m_Do/m_Do_machine.cpp +++ b/src/m_Do/m_Do_machine.cpp @@ -527,7 +527,7 @@ void myExceptionCallback(u16, OSContext*, u32, u32) { u32 btnHold; u32 btnTrig; - mDoMain::sHungUpTime = OSGetTime(); + mDoMain::sHungUpTime = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()); OSReportEnable(); cAPICPad_recalibrate(); // "Vibration stopping & resetting to default\n" diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 6a6d38c890..d169a7ad98 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -233,7 +233,7 @@ void main01(void) { OSReport("Entering Main Loop (main01)...\n"); - dusk::game_clock::ensure_initialized(); + dusk::game_clock::initialize(); do { // 1. Update Window Events @@ -248,7 +248,7 @@ void main01(void) { break; case AURORA_UNPAUSED: dusk::audio::SetPaused(false); - dusk::game_clock::reset_frame_timer(); + dusk::game_clock::reset(); dusk::mouse::on_focus_gained(); break; case AURORA_SDL_EVENT: @@ -290,46 +290,56 @@ void main01(void) { dusk::ui::update(); - const auto pacing = dusk::game_clock::advance_main_loop(); - if (pacing.is_interpolating) { - if (pacing.sim_ticks_to_run > 0) { - dusk::frame_interp::begin_frame(dusk::getSettings().game.enableFrameInterpolation, true, 0.0f); + const auto timing = dusk::game_clock::advance(); + const auto interpolationMode = dusk::getSettings().game.enableFrameInterpolation.getValue(); + if (timing.separatePresentation) { + if (timing.numSimTicks > 0) { + dusk::frame_interp::begin_frame(interpolationMode, true, 0.0f); dusk::frame_interp::set_ui_tick_pending(true); - - for (int sim_tick = 0; sim_tick < pacing.sim_ticks_to_run; ++sim_tick) { - dusk::frame_interp::begin_sim_tick(); + for (int i = 0; i < timing.numSimTicks; ++i) { + if (timing.interpolating) { + dusk::frame_interp::begin_sim_tick(); + } + dusk::game_clock::begin_sim_tick(); mDoCPd_c::read(); dusk::mouse::read(); - dusk::gyro::read(pacing.sim_pace); + dusk::gyro::read(dusk::game_clock::kSimPeriod); fapGm_Execute(); mDoAud_Execute(); dusk::game_clock::commit_sim_tick(); } } - dusk::frame_interp::begin_frame(dusk::getSettings().game.enableFrameInterpolation, false, - dusk::game_clock::sample_interpolation_step()); - dusk::frame_interp::interpolate(); - dusk::frame_interp::begin_presentation_camera(); - // run draw functions for anything specially marked to handle interp + const float interpolationStep = + timing.interpolating ? dusk::game_clock::sample_interpolation_step() : 1.0f; + dusk::frame_interp::begin_frame(interpolationMode, false, interpolationStep); + if (timing.interpolating) { + dusk::frame_interp::interpolate(); + dusk::frame_interp::begin_presentation_camera(); + } + fpcM_DrawIterater((fpcM_DrawIteraterFunc)fpcM_Draw); cAPIGph_Painter(); - dusk::frame_interp::end_presentation_camera(); + if (timing.interpolating) { + dusk::frame_interp::end_presentation_camera(); + } dusk::frame_interp::set_ui_tick_pending(false); } else { dusk::frame_interp::begin_frame(dusk::FrameInterpMode::Off, true, 0.0f); dusk::frame_interp::set_ui_tick_pending(true); + dusk::game_clock::begin_sim_tick(); // Game Inputs mDoCPd_c::read(); dusk::mouse::read(); - dusk::gyro::read(pacing.presentation_dt_seconds); + dusk::gyro::read(timing.dt); // EXECUTE GAME LOGIC & RENDER // This calls mDoGph_Painter -> JFWDisplay -> GX Functions fapGm_Execute(); mDoAud_Execute(); + dusk::game_clock::commit_sim_tick(); } aurora_end_frame(); @@ -345,7 +355,10 @@ void main01(void) { static double last_fps_setting = 0.0; static Limiter::duration_t target_ns = 0; - if (dusk::getSettings().game.enableFrameInterpolation.getValue() == dusk::FrameInterpMode::Capped && !dusk::getTransientSettings().skipFrameRateLimit) { + if (dusk::getSettings().game.enableFrameInterpolation.getValue() == + dusk::FrameInterpMode::Capped && + !dusk::getTransientSettings().turboMode) + { ZoneScopedN("Frame limiter"); double current_fps = dusk::getSettings().video.maxFrameRate.getValue(); if (current_fps != last_fps_setting) { @@ -354,7 +367,8 @@ void main01(void) { } Limiter::duration_t sleepTime = main_loop_limiter.Sleep(target_ns); - dusk::frameUsagePct = 100.0f * (1.0f - static_cast(sleepTime) / static_cast(target_ns)); + dusk::frameUsagePct = + 100.0f * (1.0f - static_cast(sleepTime) / static_cast(target_ns)); } else { main_loop_limiter.Reset(); } @@ -845,7 +859,7 @@ int game_main(int argc, char* argv[]) { OSInit(); - mDoMain::sPowerOnTime = OSGetTime(); + mDoMain::sPowerOnTime = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()); // Reset Data static mDoRstData sResetData = {0}; From c722cb1be01eb534656919d580bbe647bf7e0a95 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Fri, 14 Aug 2026 19:12:14 -0700 Subject: [PATCH 11/15] update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index b2d7b78767..1f84646de4 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit b2d7b78767085f28f4f2174ee32b39b59a80d7e3 +Subproject commit 1f84646de4c35483e580b07ab5c537c681a55e5e From 9d38af22ee1003d9b48fa2d8f1ae7a6896c60d11 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 15 Aug 2026 11:50:13 -0600 Subject: [PATCH 12/15] GfxService v1.2 + fixes (#2305) * GfxService v1.2 + fixes * gfx::synchronize before mod_shutdown --- extern/aurora | 2 +- libs/JSystem/src/JUtility/JUTVideo.cpp | 5 ++ mods/ao_mod/src/mod.cpp | 22 ++--- mods/shadow_mod/src/mod.cpp | 24 ++--- mods/window_demo/src/mod.cpp | 6 -- sdk/include/mods/svc/gfx.h | 74 +++++++++++++++- src/dusk/mods/loader/loader.cpp | 1 + src/dusk/mods/svc/gfx.cpp | 118 +++++++++++++++++++------ src/dusk/mods/svc/registry.cpp | 8 ++ src/dusk/mods/svc/registry.hpp | 6 +- 10 files changed, 208 insertions(+), 58 deletions(-) diff --git a/extern/aurora b/extern/aurora index 1f84646de4..cf3ffc9886 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 1f84646de4c35483e580b07ab5c537c681a55e5e +Subproject commit cf3ffc98865ab209b02cb3eefe778a3f057c6db4 diff --git a/libs/JSystem/src/JUtility/JUTVideo.cpp b/libs/JSystem/src/JUtility/JUTVideo.cpp index b9a51ab6f3..663c357e17 100644 --- a/libs/JSystem/src/JUtility/JUTVideo.cpp +++ b/libs/JSystem/src/JUtility/JUTVideo.cpp @@ -70,6 +70,11 @@ void JUTVideo::preRetraceProc(u32 retrace_count) { OSTick tick = DUSK_IF_ELSE(static_cast(OSGetNativeTime()), OSGetTick()); sVideoInterval = tick - sVideoLastTick; +#if TARGET_PC + if (sVideoInterval <= 0) { + sVideoInterval = 1; + } +#endif sVideoLastTick = tick; JUTXfb* xfb = JUTXfb::getManager(); diff --git a/mods/ao_mod/src/mod.cpp b/mods/ao_mod/src/mod.cpp index fb36ddb40c..8a96ae3c95 100644 --- a/mods/ao_mod/src/mod.cpp +++ b/mods/ao_mod/src/mod.cpp @@ -53,6 +53,7 @@ ResourceBuffer g_denoiseSource = RESOURCE_BUFFER_INIT; ResourceBuffer g_compositeSource = RESOURCE_BUFFER_INIT; GfxDeviceInfo g_deviceInfo = GFX_DEVICE_INFO_INIT; +GfxRenderTargetLayout g_sceneTargetLayout = GFX_RENDER_TARGET_LAYOUT_INIT; WGPUComputePipeline g_preprocessPipeline = nullptr; WGPUComputePipeline g_mip4Pipeline = nullptr; WGPUComputePipeline g_gtaoPipeline = nullptr; @@ -226,19 +227,17 @@ bool build_composite_pipeline( .dstFactor = WGPUBlendFactor_One, }, }; - WGPUColorTargetState colorTarget = WGPU_COLOR_TARGET_STATE_INIT; - colorTarget.format = g_deviceInfo.color_format; - if (blend) { - colorTarget.blend = &blendState; - } + WGPUColorTargetState colorTargets[GFX_MAX_COLOR_ATTACHMENTS]; + const uint32_t colorTargetCount = gfx_init_color_target_states( + &g_sceneTargetLayout, colorTargets, blend ? &blendState : nullptr, WGPUColorWriteMask_All); WGPUFragmentState fragment = WGPU_FRAGMENT_STATE_INIT; fragment.module = module; fragment.entryPoint = {"fs_main", WGPU_STRLEN}; - fragment.targetCount = 1; - fragment.targets = &colorTarget; + fragment.targetCount = colorTargetCount; + fragment.targets = colorTargets; // Depth state must match the EFB pass despite never touching depth. WGPUDepthStencilState depthStencil = WGPU_DEPTH_STENCIL_STATE_INIT; - depthStencil.format = g_deviceInfo.depth_format; + depthStencil.format = g_sceneTargetLayout.depth_stencil_format; depthStencil.depthWriteEnabled = WGPUOptionalBool_False; depthStencil.depthCompare = WGPUCompareFunction_Always; @@ -248,7 +247,7 @@ bool build_composite_pipeline( pipelineDesc.vertex.entryPoint = {"vs_main", WGPU_STRLEN}; pipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList; pipelineDesc.depthStencil = &depthStencil; - pipelineDesc.multisample.count = g_deviceInfo.sample_count; + pipelineDesc.multisample.count = g_sceneTargetLayout.sample_count; pipelineDesc.fragment = &fragment; outPipeline = wgpuDeviceCreateRenderPipeline(g_deviceInfo.device, &pipelineDesc); wgpuShaderModuleRelease(module); @@ -504,7 +503,7 @@ void on_compute( // Render worker thread: composite the AO over the scene (or show it, in debug view). void on_draw( ModContext*, const GfxDrawContext* ctx, const void* payload, size_t payloadSize, void*) { - if (payloadSize != sizeof(CompositePayload)) { + if (payloadSize != sizeof(CompositePayload) || ctx->layout.key != g_sceneTargetLayout.key) { return; } CompositePayload data; @@ -816,6 +815,9 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { if (svc_gfx->get_device_info(mod_ctx, &g_deviceInfo) != MOD_OK) { return mods::set_error(error, MOD_ERROR, "failed to query device info"); } + if (svc_gfx->get_scene_target_layout(mod_ctx, &g_sceneTargetLayout) != MOD_OK) { + return mods::set_error(error, MOD_ERROR, "failed to query scene target layout"); + } if (!build_compute_pipeline("AO preprocess depth", g_preprocessSource, "preprocess_depth", g_preprocessPipeline, g_preprocessLayout) || !build_compute_pipeline("AO downsample mip4", g_preprocessSource, "downsample_mip4", diff --git a/mods/shadow_mod/src/mod.cpp b/mods/shadow_mod/src/mod.cpp index 36f33de41d..fa4056040b 100644 --- a/mods/shadow_mod/src/mod.cpp +++ b/mods/shadow_mod/src/mod.cpp @@ -20,12 +20,12 @@ #include "dolphin/gx/GXPixel.h" #include "dolphin/gx/GXTransform.h" #include "m_Do/m_Do_mtx.h" -#include "mods/svc/hook.hpp" #include "mods/service.hpp" #include "mods/svc/camera.h" #include "mods/svc/config.h" #include "mods/svc/gfx.h" #include "mods/svc/hook.h" +#include "mods/svc/hook.hpp" #include "mods/svc/log.h" #include "mods/svc/resource.h" #include "mods/svc/ui.h" @@ -69,6 +69,7 @@ GfxStageHookHandle g_frameBeforeHudHook = 0; UiWindowHandle g_controlsWindow = 0; ResourceBuffer g_shaderSource = RESOURCE_BUFFER_INIT; GfxDeviceInfo g_deviceInfo = GFX_DEVICE_INFO_INIT; +GfxRenderTargetLayout g_sceneTargetLayout = GFX_RENDER_TARGET_LAYOUT_INIT; WGPURenderPipeline g_compositePipeline = nullptr; // multiply blend WGPURenderPipeline g_compositeDebugPipeline = nullptr; // no blend (debug views) WGPUBindGroupLayout g_compositeLayout = nullptr; @@ -399,18 +400,16 @@ bool build_composite_pipeline( .srcFactor = WGPUBlendFactor_Zero, .dstFactor = WGPUBlendFactor_One}, }; - WGPUColorTargetState colorTarget = WGPU_COLOR_TARGET_STATE_INIT; - colorTarget.format = g_deviceInfo.color_format; - if (blend) { - colorTarget.blend = &blendState; - } + WGPUColorTargetState colorTargets[GFX_MAX_COLOR_ATTACHMENTS]; + const uint32_t colorTargetCount = gfx_init_color_target_states( + &g_sceneTargetLayout, colorTargets, blend ? &blendState : nullptr, WGPUColorWriteMask_All); WGPUFragmentState fragment = WGPU_FRAGMENT_STATE_INIT; fragment.module = module; fragment.entryPoint = {"fs_main", WGPU_STRLEN}; - fragment.targetCount = 1; - fragment.targets = &colorTarget; + fragment.targetCount = colorTargetCount; + fragment.targets = colorTargets; WGPUDepthStencilState depthStencil = WGPU_DEPTH_STENCIL_STATE_INIT; - depthStencil.format = g_deviceInfo.depth_format; + depthStencil.format = g_sceneTargetLayout.depth_stencil_format; depthStencil.depthWriteEnabled = WGPUOptionalBool_False; depthStencil.depthCompare = WGPUCompareFunction_Always; @@ -420,7 +419,7 @@ bool build_composite_pipeline( pipelineDesc.vertex.entryPoint = {"vs_main", WGPU_STRLEN}; pipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList; pipelineDesc.depthStencil = &depthStencil; - pipelineDesc.multisample.count = g_deviceInfo.sample_count; + pipelineDesc.multisample.count = g_sceneTargetLayout.sample_count; pipelineDesc.fragment = &fragment; outPipeline = wgpuDeviceCreateRenderPipeline(g_deviceInfo.device, &pipelineDesc); wgpuShaderModuleRelease(module); @@ -518,7 +517,7 @@ WGPUBindGroup create_composite_bind_group(WGPUDevice device, WGPUBindGroupLayout // Render worker thread: fullscreen deferred-shadow composite. void on_draw( ModContext*, const GfxDrawContext* ctx, const void* payload, size_t payloadSize, void*) { - if (payloadSize != sizeof(DrawPayload)) { + if (payloadSize != sizeof(DrawPayload) || ctx->layout.key != g_sceneTargetLayout.key) { return; } DrawPayload data; @@ -1243,6 +1242,9 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { if (svc_gfx->get_device_info(mod_ctx, &g_deviceInfo) != MOD_OK) { return mods::set_error(error, MOD_ERROR, "failed to query device info"); } + if (svc_gfx->get_scene_target_layout(mod_ctx, &g_sceneTargetLayout) != MOD_OK) { + return mods::set_error(error, MOD_ERROR, "failed to query scene target layout"); + } if (!build_composite_pipeline(true, g_compositePipeline, g_compositeLayout) || !build_composite_pipeline(false, g_compositeDebugPipeline, g_compositeDebugLayout)) { diff --git a/mods/window_demo/src/mod.cpp b/mods/window_demo/src/mod.cpp index fb3cc31809..00d6f1240b 100644 --- a/mods/window_demo/src/mod.cpp +++ b/mods/window_demo/src/mod.cpp @@ -189,12 +189,6 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { return mods::set_error(error, MOD_ERROR, "failed to register mod panel"); } - if (open_window() != MOD_OK) { - svc_gfx->unregister_stage_hook(mod_ctx, g_stageHook); - g_stageHook = 0; - return mods::set_error(error, MOD_ERROR, "failed to open auxiliary window"); - } - mods::log::info("auxiliary WebGPU window ready"); return MOD_OK; } diff --git a/sdk/include/mods/svc/gfx.h b/sdk/include/mods/svc/gfx.h index 5041c611e3..0eee026984 100644 --- a/sdk/include/mods/svc/gfx.h +++ b/sdk/include/mods/svc/gfx.h @@ -33,10 +33,68 @@ #define GFX_SERVICE_ID "dev.twilitrealm.dusklight.gfx" #define GFX_SERVICE_MAJOR 1u -#define GFX_SERVICE_MINOR 1u +#define GFX_SERVICE_MINOR 2u /* Maximum size for push_draw payload */ #define GFX_INLINE_DRAW_PAYLOAD_SIZE 128u +#define GFX_MAX_COLOR_ATTACHMENTS 8u +#define GFX_SCENE_COLOR_ATTACHMENT_INDEX 0u + +typedef enum GfxAttachmentSemantic { + GFX_ATTACHMENT_SCENE_COLOR, + GFX_ATTACHMENT_NORMAL, + GFX_ATTACHMENT_AUXILIARY, +} GfxAttachmentSemantic; + +typedef struct GfxColorAttachmentLayout { + GfxAttachmentSemantic semantic; + WGPUTextureFormat format; + uint32_t width; + uint32_t height; +} GfxColorAttachmentLayout; + +typedef struct GfxRenderTargetLayout { + uint32_t struct_size; + uint64_t key; + /* At least one; scene color is at GFX_SCENE_COLOR_ATTACHMENT_INDEX. */ + uint32_t color_attachment_count; + GfxColorAttachmentLayout color_attachments[GFX_MAX_COLOR_ATTACHMENTS]; + WGPUTextureFormat depth_stencil_format; + uint32_t sample_count; +} GfxRenderTargetLayout; + +#define GFX_RENDER_TARGET_LAYOUT_INIT \ + {sizeof(GfxRenderTargetLayout), 0u, 0u, \ + {{GFX_ATTACHMENT_AUXILIARY, WGPUTextureFormat_Undefined}}, WGPUTextureFormat_Undefined, \ + 1u} + +/* + * Initializes pipeline color targets for a render-target layout. Only scene color is writable; + * callers that write another semantic should override that target afterward. + */ +static uint32_t gfx_init_color_target_states(const GfxRenderTargetLayout* layout, + WGPUColorTargetState targets[GFX_MAX_COLOR_ATTACHMENTS], const WGPUBlendState* scene_blend, + WGPUColorWriteMask scene_write_mask) { + if (layout == NULL || targets == NULL) { + return 0; + } + const uint32_t count = layout->color_attachment_count < GFX_MAX_COLOR_ATTACHMENTS ? + layout->color_attachment_count : + GFX_MAX_COLOR_ATTACHMENTS; + for (uint32_t i = 0; i < GFX_MAX_COLOR_ATTACHMENTS; ++i) { + WGPUColorTargetState target = WGPU_COLOR_TARGET_STATE_INIT; + if (i < count) { + target.format = layout->color_attachments[i].format; + target.writeMask = WGPUColorWriteMask_None; + } + targets[i] = target; + } + if (count != 0) { + targets[GFX_SCENE_COLOR_ATTACHMENT_INDEX].blend = scene_blend; + targets[GFX_SCENE_COLOR_ATTACHMENT_INDEX].writeMask = scene_write_mask; + } + return count; +} /* 0 is never a valid handle. */ typedef uint64_t GfxDrawTypeHandle; @@ -51,8 +109,8 @@ typedef struct GfxRange { } GfxRange; /* - * Device and scene pass configuration. Valid from mod_initialize onward and stable for the - * session. Offscreen passes from create_pass are always single-sample. + * Device and legacy primary scene-pass configuration. Use get_scene_target_layout when creating + * scene pipelines. Offscreen passes from create_pass are always single-sample. */ typedef struct GfxDeviceInfo { uint32_t struct_size; @@ -83,12 +141,18 @@ typedef struct GfxDrawContext { WGPUBuffer index_buffer; WGPUBuffer uniform_buffer; WGPUBuffer storage_buffer; + /* deprecated: use layout.color_attachments[GFX_SCENE_COLOR_ATTACHMENT_INDEX].format */ WGPUTextureFormat color_format; + /* deprecated: use layout.depth_stencil_format */ WGPUTextureFormat depth_format; + /* deprecated: use layout.sample_count */ uint32_t sample_count; + /* deprecated: use layout.color_attachments[GFX_SCENE_COLOR_ATTACHMENT_INDEX].width */ uint32_t target_width; + /* deprecated: use layout.color_attachments[GFX_SCENE_COLOR_ATTACHMENT_INDEX].height */ uint32_t target_height; bool uses_reversed_z; + GfxRenderTargetLayout layout; /* added in GfxService 1.2 */ } GfxDrawContext; typedef void (*GfxDrawFn)(ModContext* ctx, const GfxDrawContext* draw_ctx, const void* payload, @@ -269,6 +333,10 @@ typedef struct GfxService { */ ModResult (*push_present)( ModContext* ctx, GfxPresentTargetHandle handle, const void* payload, size_t payload_size); + + /* Minor version 2 */ + + ModResult (*get_scene_target_layout)(ModContext* ctx, GfxRenderTargetLayout* out_layout); } GfxService; MOD_DECLARE_SERVICE(GfxService, svc_gfx, GFX_SERVICE_ID, GFX_SERVICE_MAJOR, GFX_SERVICE_MINOR); diff --git a/src/dusk/mods/loader/loader.cpp b/src/dusk/mods/loader/loader.cpp index 6c3a01bd7a..83c7660410 100644 --- a/src/dusk/mods/loader/loader.cpp +++ b/src/dusk/mods/loader/loader.cpp @@ -899,6 +899,7 @@ bool ModLoader::activate_mod(LoadedMod& mod) { } void ModLoader::deactivate_mod(LoadedMod& mod) { + svc::modules_mod_deactivating(mod); if (mod.initialized && mod.native && mod.native->fn_shutdown) { log::write(mod.metadata.id, LOG_LEVEL_TRACE, "calling mod_shutdown"); try { diff --git a/src/dusk/mods/svc/gfx.cpp b/src/dusk/mods/svc/gfx.cpp index 900640d4fb..098894007a 100644 --- a/src/dusk/mods/svc/gfx.cpp +++ b/src/dusk/mods/svc/gfx.cpp @@ -9,8 +9,10 @@ #include #include +#include #include +#include #include #include #include @@ -126,23 +128,34 @@ GfxSlot* resolve_owned_slot_locked(LoadedMod& mod, uint64_t handle, GfxSlotKind return &entry->value; } -void collect_mod_types_locked(LoadedMod& owner, std::vector& drawIds, +void take_mod_types_locked(LoadedMod& owner, std::vector& drawIds, std::vector& taskIds) { - s_slots.for_each([&](uint64_t, const auto& entry) { + std::vector drawHandles; + std::vector taskHandles; + s_slots.for_each([&](uint64_t handle, const auto& entry) { if (entry.owner != &owner) { return; } const auto& slot = entry.value; if (slot.kind == GfxSlotKind::DrawType && slot.auroraDrawId != aurora::gfx::InvalidDrawType) { - drawIds.push_back(slot.auroraDrawId); + drawHandles.push_back(handle); } else if ((slot.kind == GfxSlotKind::ComputeType || slot.kind == GfxSlotKind::PresentTarget) && slot.auroraTaskId != aurora::gfx::InvalidEncoderTask) { - taskIds.push_back(slot.auroraTaskId); + taskHandles.push_back(handle); } }); + for (const auto handle : drawHandles) { + auto* entry = s_slots.find(handle); + drawIds.push_back(std::exchange(entry->value.auroraDrawId, aurora::gfx::InvalidDrawType)); + } + for (const auto handle : taskHandles) { + auto* entry = s_slots.find(handle); + taskIds.push_back( + std::exchange(entry->value.auroraTaskId, aurora::gfx::InvalidEncoderTask)); + } } void unregister_aurora_types(const std::vector& drawIds, @@ -155,6 +168,49 @@ void unregister_aurora_types(const std::vector& drawIds } } +void gfx_mod_deactivating(LoadedMod& mod) { + std::vector drawIds; + std::vector taskIds; + { + std::lock_guard lock{s_mutex}; + take_mod_types_locked(mod, drawIds, taskIds); + } + unregister_aurora_types(drawIds, taskIds); + if (!drawIds.empty() || !taskIds.empty()) { + aurora::gfx::synchronize(); + } +} + +GfxAttachmentSemantic gfx_attachment_semantic(aurora::gfx::ColorAttachmentSemantic semantic) { + switch (semantic) { + case aurora::gfx::ColorAttachmentSemantic::SceneColor: + return GFX_ATTACHMENT_SCENE_COLOR; + case aurora::gfx::ColorAttachmentSemantic::Normal: + return GFX_ATTACHMENT_NORMAL; + case aurora::gfx::ColorAttachmentSemantic::Auxiliary: + return GFX_ATTACHMENT_AUXILIARY; + } + return GFX_ATTACHMENT_AUXILIARY; +} + +GfxRenderTargetLayout gfx_render_target_layout(const aurora::gfx::RenderTargetLayout& layout) { + GfxRenderTargetLayout result = GFX_RENDER_TARGET_LAYOUT_INIT; + result.key = layout.key; + result.color_attachment_count = + std::min(layout.colorAttachmentCount, GFX_MAX_COLOR_ATTACHMENTS); + for (uint32_t i = 0; i < result.color_attachment_count; ++i) { + result.color_attachments[i] = { + .semantic = gfx_attachment_semantic(layout.colorAttachments[i].semantic), + .format = static_cast(layout.colorAttachments[i].format), + .width = layout.colorAttachments[i].width, + .height = layout.colorAttachments[i].height, + }; + } + result.depth_stencil_format = static_cast(layout.depthStencilFormat); + result.sample_count = layout.sampleCount; + return result; +} + void draw_trampoline(const aurora::gfx::DrawContext& ctx, const wgpu::RenderPassEncoder& pass, const void* payload, size_t payloadSize, void* userdata) { const auto handle = static_cast(reinterpret_cast(userdata)); @@ -184,12 +240,12 @@ void draw_trampoline(const aurora::gfx::DrawContext& ctx, const wgpu::RenderPass .index_buffer = ctx.indexBuffer.Get(), .uniform_buffer = ctx.uniformBuffer.Get(), .storage_buffer = ctx.storageBuffer.Get(), - .color_format = static_cast(ctx.colorFormat), - .depth_format = static_cast(ctx.depthFormat), - .sample_count = ctx.sampleCount, - .target_width = ctx.targetWidth, - .target_height = ctx.targetHeight, + .color_format = static_cast( + ctx.layout.colorAttachments[GFX_SCENE_COLOR_ATTACHMENT_INDEX].format), + .depth_format = static_cast(ctx.layout.depthStencilFormat), + .sample_count = ctx.layout.sampleCount, .uses_reversed_z = aurora::gfx::uses_reversed_z(), + .layout = gfx_render_target_layout(ctx.layout), }; std::string failure; @@ -778,8 +834,10 @@ ModResult gfx_unregister_present_target(LoadedMod& mod, uint64_t handle) { auroraId = slot->auroraTaskId; } - aurora::gfx::unregister_encoder_task_type(auroraId); - aurora::gfx::synchronize(); + if (auroraId != aurora::gfx::InvalidEncoderTask) { + aurora::gfx::unregister_encoder_task_type(auroraId); + aurora::gfx::synchronize(); + } std::optional removed; { @@ -903,6 +961,7 @@ void gfx_run_stage( .game_viewport = gameViewport, }; + AuroraGXSync(); for (const auto& entry : entries) { { std::lock_guard lock{s_mutex}; @@ -924,6 +983,7 @@ void gfx_run_stage( fail_mod(*entry.owner, MOD_ERROR, "unknown exception in gfx stage callback"); } + AuroraGXSync(); if (aurora::gfx::is_offscreen() != wasOffscreen) { aurora::gfx::ResolvedTargets discarded; aurora::gfx::resolve_pass( @@ -935,17 +995,8 @@ void gfx_run_stage( } } -void gfx_remove_mod(LoadedMod& mod) { - std::vector drawIds; - std::vector taskIds; - { - std::lock_guard lock{s_mutex}; - collect_mod_types_locked(mod, drawIds, taskIds); - } - unregister_aurora_types(drawIds, taskIds); - if (!drawIds.empty() || !taskIds.empty()) { - aurora::gfx::synchronize(); - } +void gfx_mod_detached(LoadedMod& mod) { + gfx_mod_deactivating(mod); std::vector entries; { @@ -966,7 +1017,7 @@ void gfx_remove_mod(LoadedMod& mod) { } } -void gfx_drain_worker_failures() { +void gfx_frame_begin() { std::vector failures; { std::lock_guard lock{s_mutex}; @@ -979,7 +1030,7 @@ void gfx_drain_worker_failures() { for (const auto& failure : failures) { for (auto& mod : ModLoader::instance().mods()) { if (mod.metadata.id == failure.modId && mod.active) { - gfx_remove_mod(mod); + gfx_mod_detached(mod); fail_mod(mod, MOD_ERROR, failure.message); break; } @@ -1030,6 +1081,19 @@ ModResult gfx_get_device_info(ModContext* context, GfxDeviceInfo* outInfo) { return MOD_OK; } +ModResult gfx_get_scene_target_layout(ModContext* context, GfxRenderTargetLayout* outLayout) { + if (outLayout == nullptr || outLayout->struct_size < sizeof(GfxRenderTargetLayout) || + mod_from_context(context) == nullptr) + { + return MOD_INVALID_ARGUMENT; + } + + const uint32_t structSize = outLayout->struct_size; + *outLayout = gfx_render_target_layout(aurora::gfx::scene_render_target_layout()); + outLayout->struct_size = structSize; + return MOD_OK; +} + void* gfx_get_proc_address(ModContext* context, const char* name) { if (mod_from_context(context) == nullptr || name == nullptr) { return nullptr; @@ -1327,6 +1391,7 @@ constexpr GfxService s_gfxService{ .resize_present_target = gfx_resize_present_target_impl, .unregister_present_target = gfx_unregister_present_target_impl, .push_present = gfx_push_present_impl, + .get_scene_target_layout = gfx_get_scene_target_layout, }; } // namespace @@ -1336,8 +1401,9 @@ constinit const ServiceModule g_gfxModule{ .majorVersion = GFX_SERVICE_MAJOR, .minorVersion = GFX_SERVICE_MINOR, .service = &s_gfxService, - .modDetached = gfx_remove_mod, - .frameBegin = gfx_drain_worker_failures, + .modDeactivating = gfx_mod_deactivating, + .modDetached = gfx_mod_detached, + .frameBegin = gfx_frame_begin, }; } // namespace dusk::mods::svc diff --git a/src/dusk/mods/svc/registry.cpp b/src/dusk/mods/svc/registry.cpp index 1c8368720f..051d7ad977 100644 --- a/src/dusk/mods/svc/registry.cpp +++ b/src/dusk/mods/svc/registry.cpp @@ -150,6 +150,14 @@ ModResult register_module(const ServiceModule& module) { return MOD_OK; } +void modules_mod_deactivating(LoadedMod& mod) { + for (const auto* module : s_modules | std::views::reverse) { + if (module->modDeactivating != nullptr) { + module->modDeactivating(mod); + } + } +} + void modules_mod_detached(LoadedMod& mod) { for (const auto* module : s_modules | std::views::reverse) { if (module->modDetached != nullptr) { diff --git a/src/dusk/mods/svc/registry.hpp b/src/dusk/mods/svc/registry.hpp index d3df44ccc3..c88ef5509c 100644 --- a/src/dusk/mods/svc/registry.hpp +++ b/src/dusk/mods/svc/registry.hpp @@ -19,7 +19,7 @@ struct ServiceRecord { }; // A host service and its lifecycle hooks. Every hook is optional. Frame and lifecycle hooks run in -// registration order, modDetached in reverse registration order. +// registration order, teardown hooks in reverse registration order. struct ServiceModule { const char* id = nullptr; uint16_t majorVersion = 0; @@ -28,6 +28,9 @@ struct ServiceModule { // One-time setup, at registration (ModLoader::init_services). void (*initialize)() = nullptr; + // A mod is beginning deactivation: stop callbacks that may execute concurrently. Service state + // remains registered so mod_shutdown may release it normally. + void (*modDeactivating)(LoadedMod& mod) = nullptr; // A mod is going away (deactivation or failed activation): drop all state held for it. // Runs after the mod's mod_shutdown and before its library unloads, so pointers into // the mod are still valid but must not be called. @@ -55,6 +58,7 @@ const ServiceRecord* find_service( const ServiceRecord* find_service_record(const char* serviceId, uint16_t majorVersion); ModResult register_module(const ServiceModule& module); +void modules_mod_deactivating(LoadedMod& mod); void modules_mod_detached(LoadedMod& mod); void modules_lifecycle_applied(); void modules_frame_begin(); From ac45cac56e6a33a2fc24549b6b359bf0448ccf2e Mon Sep 17 00:00:00 2001 From: Irastris Date: Sat, 15 Aug 2026 13:52:54 -0400 Subject: [PATCH 13/15] Fix Graphics Backend option value (#2295) * Graphics backend setting bugfix * Better solution --- src/dusk/ui/settings.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index f425fb5425..7aa70ea860 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -220,6 +220,18 @@ AuroraBackend configured_backend() { return configuredBackend; } +bool is_graphics_backend_restart_pending() { + return getSettings().backend.graphicsBackend.getValue() != + prelaunch_state().initialGraphicsBackend; +} + +Rml::String graphics_backend_display_name() { + if (is_graphics_backend_restart_pending()) { + return Rml::String{backend_name(configured_backend())}; + } + return Rml::String{backend_name(aurora_get_backend())}; +} + Rml::String configured_data_path_display_name() { const auto path = data::abbreviated_path_string(data::configured_data_path()); if (path.empty()) { @@ -602,12 +614,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { leftPane.register_control( leftPane.add_select_button({ .key = "Graphics Backend", - .getValue = [] { return Rml::String{backend_name(aurora_get_backend())}; }, - .isModified = - [] { - return getSettings().backend.graphicsBackend.getValue() != - prelaunch_state().initialGraphicsBackend; - }, + .getValue = [] { return graphics_backend_display_name(); }, + .isModified = [] { return is_graphics_backend_restart_pending(); }, }), rightPane, [](Pane& pane) { const auto availableBackends = available_backends(); From 27d27e6d9495bf0381f6d3afb852e2b89adf939f Mon Sep 17 00:00:00 2001 From: Irastris Date: Sat, 15 Aug 2026 13:58:09 -0400 Subject: [PATCH 14/15] Add support for playing Dusklight using Wii disc images (#2296) * Add offsets for Wii USA R0/R2, JPN, and PAL Also fix an oopsie where l_pos and l_normal's offsets were swapped * Allow loading Wii discs * Change GameVersion::GcnJpn checks to isRegionJpn() * Change GameVersion::GcnPal checks to isRegionPal() * Add platformSelect and regionSelect templates, simplify a ton of code * Add *_get() macros to simplify REL loads * Actually accept Wii USA Rev. 2 discs * Show the Wii USA revision in prelaunch's disc status * l_normal isn't in Wii RELs, so I'll just hardcode it * Account for various BLO differences Fix crash when opening the map on Wii USA Rev 0 and Wii PAL * Use the streamed version of the file select music * Update language selection * Fix the d-pad/cross button text for Wii PAL Resizing panes that are already 120.0f wide is harmless, so I didn't bother with a GameVersion check * Fix brightness check discrepancy, use IF_DUSK_BLOCK macros when ideal * Update docs and the issue template --- .github/ISSUE_TEMPLATE/bug-report.yml | 15 ++- README.md | 5 +- docs/ios-install-altstore.md | 8 +- files.cmake | 2 + libs/JSystem/src/JUtility/JUTResFont.cpp | 2 +- src/Z2AudioLib/Z2SeqMgr.cpp | 5 +- src/d/actor/d_a_mant.cpp | 92 +++++++++++++---- src/d/actor/d_a_player.cpp | 12 ++- src/d/actor/d_a_title.cpp | 2 +- src/d/actor/d_flower.inc | 124 ++++++++++++++++++++--- src/d/actor/d_grass.inc | 101 ++++++++++++++++-- src/d/d_bright_check.cpp | 12 ++- src/d/d_menu_dmap.cpp | 28 ++--- src/d/d_menu_fishing.cpp | 2 + src/d/d_menu_fmap2D.cpp | 104 +++++++++---------- src/d/d_menu_option.cpp | 8 +- src/d/d_menu_ring.cpp | 76 ++++++++------ src/d/d_meter2_draw.cpp | 16 +++ src/d/d_meter_button.cpp | 6 +- src/d/d_msg_object.cpp | 35 +------ src/d/d_s_logo.cpp | 104 ++++++++++--------- src/d/d_save.cpp | 2 +- src/dusk/dvd_asset.hpp | 11 ++ src/dusk/iso_validate.cpp | 20 +++- src/dusk/iso_validate.hpp | 1 + src/dusk/language.cpp | 79 +++++++++++++++ src/dusk/language.hpp | 15 +++ src/dusk/settings.h | 3 +- src/dusk/ui/prelaunch.cpp | 57 ++++++++++- src/dusk/ui/prelaunch.hpp | 3 + src/dusk/ui/settings.cpp | 39 ++++--- src/dusk/version.cpp | 18 +++- src/dusk/version.hpp | 27 +++-- src/m_Do/m_Do_ext.cpp | 4 +- 34 files changed, 755 insertions(+), 283 deletions(-) create mode 100644 src/dusk/language.cpp create mode 100644 src/dusk/language.hpp diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index ab91aca242..467849f761 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -68,13 +68,18 @@ body: required: true - type: dropdown - id: game-region + id: game-disc attributes: - label: Game Region - description: The game region you are playing on + label: Game Disc + description: The game disc you are playing on options: - - NTSC-U (North America) - - PAL (Europe) + - GameCube NTSC-U (North America) + - GameCube PAL (Europe) + - GameCube NTSC-J (Japan) + - Wii NTSC-U (North America) Revision 0 + - Wii NTSC-U (North America) Revision 2 + - Wii PAL (Europe) + - Wii NTSC-J (Japan) validations: required: true diff --git a/README.md b/README.md index b5a9ecae72..473efd8b68 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,10 @@ It aims to be as accurate as possible to the original while also providing new o You must dump your own copy of the game. Please see [this article](https://wiki.dolphin-emu.org/index.php?title=Ripping_Games) for instructions. After dumping, you can use a program like [Dolphin](https://dolphin-emu.org/) or [nodtool](https://github.com/encounter/nod/releases) to convert the `.iso` to `.rvz` to save space. -Currently, only the GameCube releases are supported. Support for other versions of the game is planned in the future. +Dusklight currently supports all commercial discs except for Wii's Korean release. + +> [!NOTE] +> Dusklight is based on the [Twilight Princess decompilation](https://github.com/zeldaret/tp), which is currently only matching for GameCube. As a result, even when playing Dusklight with a Wii disc, you will be presented with the GameCube version's HUD and certain other specificities. ### 2. Install Dusklight diff --git a/docs/ios-install-altstore.md b/docs/ios-install-altstore.md index 5c0f071878..0c2e9e36a6 100644 --- a/docs/ios-install-altstore.md +++ b/docs/ios-install-altstore.md @@ -5,7 +5,13 @@ - A Windows, Linux, or macOS device - iOS device connected to computer via USB - Dusklight IPA file (download the latest `Dusklight-vX.X.X-ios-arm64.ipa` from the [releases page](https://github.com/TwilitRealm/dusklight/releases)) -- Legally acquired game disc - `GZ2E01` (Gamecube USA) or `GZ2PE01` (Gamecube PAL) +- Legally acquired game disc: + - `GZ2E01` (GameCube USA) + - `GZ2P01` (GameCube PAL) + - `GZ2J01` (GameCube JPN) + - `RZDE01` (Wii USA Rev. 0 *or* Rev. 2) + - `RZDP01` (Wii PAL) + - `RZDJ01` (Wii JPN) ## 1. Install iloader diff --git a/files.cmake b/files.cmake index 675ba70fdc..60c862053e 100644 --- a/files.cmake +++ b/files.cmake @@ -1457,6 +1457,8 @@ set(DUSK_FILES src/dusk/imgui/ImGuiStubLog.cpp src/dusk/io.cpp src/dusk/iso_validate.cpp + src/dusk/language.cpp + src/dusk/language.hpp src/dusk/layout.cpp src/dusk/livesplit.cpp src/dusk/logging.cpp diff --git a/libs/JSystem/src/JUtility/JUTResFont.cpp b/libs/JSystem/src/JUtility/JUTResFont.cpp index 4ec23dff44..3f31961d1f 100644 --- a/libs/JSystem/src/JUtility/JUTResFont.cpp +++ b/libs/JSystem/src/JUtility/JUTResFont.cpp @@ -71,7 +71,7 @@ void JUTResFont::initJoinedTexture() { int pageCount = 0; u32 pageNumCells = block.numRows * block.numColumns; - if (dusk::version::getGameVersion() == dusk::version::GameVersion::GcnJpn) { + if (dusk::version::isRegionJpn()) { pageCount = 1; if (pageNumCells > 0 && block.endCode > block.startCode) { pageCount = (block.endCode - block.startCode + pageNumCells - 1) / pageNumCells; diff --git a/src/Z2AudioLib/Z2SeqMgr.cpp b/src/Z2AudioLib/Z2SeqMgr.cpp index a3e45d8acd..0658f7eb54 100644 --- a/src/Z2AudioLib/Z2SeqMgr.cpp +++ b/src/Z2AudioLib/Z2SeqMgr.cpp @@ -11,7 +11,10 @@ #include "Z2AudioLib/SpotName.h" #include "os_report.h" +#if TARGET_PC #include "dusk/audio.h" +#include "dusk/version.hpp" +#endif Z2SeqMgr::Z2SeqMgr() : JASGlobalInstance(true) { mMainBgmMaster.forceIn(); @@ -584,7 +587,7 @@ void Z2SeqMgr::bgmStreamPlay() { } #if !PLATFORM_SHIELD - else if (getStreamBgmID() == 0x2000000) { + else if (getStreamBgmID() == 0x2000000 IF_DUSK(&& dusk::version::isGcn())) { if (mStreamBgmHandle) { mStreamBgmHandle->stop(); } diff --git a/src/d/actor/d_a_mant.cpp b/src/d/actor/d_a_mant.cpp index 449efcd672..cc83028b9c 100644 --- a/src/d/actor/d_a_mant.cpp +++ b/src/d/actor/d_a_mant.cpp @@ -15,12 +15,50 @@ #include "dusk/dvd_asset.hpp" #include "dusk/frame_interpolation.h" -using GameVersion = dusk::version::GameVersion; +#include + +using namespace dusk::version; + +#define MANT_REL_PATH platformSelect("/rel/Final/Release/d_a_mant.rel", "/rel/Rfinal/Release/d_a_mant.rel") + +#define DEFINE_MANT_ASSET(name, type, count, ...) \ + static type* name##_get() { \ + alignas(32) static std::remove_cv_t buf[count]; \ + static bool _ = (dusk::LoadRelAsset(buf, MANT_REL_PATH, __VA_ARGS__), true); \ + return buf; \ + } // keep the original version of the cape texture const so we don't need to reload the file -static u8 const * l_Egnd_mantTEX_get() { alignas(32) static u8 buf[0x4000]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0x1C00}, {GameVersion::GcnPal, 0x1C00}, {GameVersion::GcnJpn, 0x1C00}}, 0x4000), true); return buf; } -static u8* l_Egnd_mantTEX_U_get() { alignas(32) static u8 buf[0x4000]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0x5C00}, {GameVersion::GcnPal, 0x5C00}, {GameVersion::GcnJpn, 0x5C00}}, 0x4000), true); return buf; } -static u8* l_Egnd_mantPAL_get() { alignas(32) static u8 buf[0x60]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0x9C00}, {GameVersion::GcnPal, 0x9C00}, {GameVersion::GcnJpn, 0x9C00}}, 0x60), true); return buf; } +DEFINE_MANT_ASSET(l_Egnd_mantTEX, u8 const, 0x4000, { + {GameVersion::GcnUsa, 0x1C00}, + {GameVersion::GcnPal, 0x1C00}, + {GameVersion::GcnJpn, 0x1C00}, + {GameVersion::WiiUsaRev0, 0x1B00}, + {GameVersion::WiiUsa, 0x1900}, + {GameVersion::WiiPal, 0x1900}, + {GameVersion::WiiJpn, 0x1900}, +}); + +DEFINE_MANT_ASSET(l_Egnd_mantTEX_U, u8, 0x4000, { + {GameVersion::GcnUsa, 0x5C00}, + {GameVersion::GcnPal, 0x5C00}, + {GameVersion::GcnJpn, 0x5C00}, + {GameVersion::WiiUsaRev0, 0x5B00}, + {GameVersion::WiiUsa, 0x5900}, + {GameVersion::WiiPal, 0x5900}, + {GameVersion::WiiJpn, 0x5900}, +}); + +DEFINE_MANT_ASSET(l_Egnd_mantPAL, u8, 0x60, { + {GameVersion::GcnUsa, 0x9C00}, + {GameVersion::GcnPal, 0x9C00}, + {GameVersion::GcnJpn, 0x9C00}, + {GameVersion::WiiUsaRev0, 0x9B00}, + {GameVersion::WiiUsa, 0x9900}, + {GameVersion::WiiPal, 0x9900}, + {GameVersion::WiiJpn, 0x9900}, +}); + #define l_Egnd_mantTEX (l_Egnd_mantTEX_get()) #define l_Egnd_mantTEX_U (l_Egnd_mantTEX_U_get()) #define l_Egnd_mantPAL (l_Egnd_mantPAL_get()) @@ -35,14 +73,39 @@ static TGXTexObj mainTexObj; static TGXTexObj undersideTexObj; // l_pos is unused -//static f32* l_pos_get() { alignas(32) static f32 buf[507]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0xA44C}, {GameVersion::GcnPal, 0xA44C}}, sizeof(buf)), true); return buf; } -static f32* l_normal_get() { alignas(32) static f32 buf[3]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0x9C60}, {GameVersion::GcnPal, 0x9C60}, {GameVersion::GcnJpn, 0x9C60}}, sizeof(buf)), true); return buf; } -static f32* l_texCoord_get() { alignas(32) static f32 buf[338]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0xA458}, {GameVersion::GcnPal, 0xA458}, {GameVersion::GcnJpn, 0xA458}}, sizeof(buf)), true); return buf; } -//#define l_pos (l_pos_get()) -#define l_normal (l_normal_get()) +// DEFINE_MANT_ASSET(l_pos, f32, 507, { +// {GameVersion::GcnUsa, 0x9C60}, +// {GameVersion::GcnPal, 0x9C60}, +// }); + +alignas(32) static f32 const l_normal[3] = {0.0f, 1.0f, 0.0f}; + +DEFINE_MANT_ASSET(l_texCoord, f32, 338, { + {GameVersion::GcnUsa, 0xA458}, + {GameVersion::GcnPal, 0xA458}, + {GameVersion::GcnJpn, 0xA458}, + {GameVersion::WiiUsaRev0, 0x9B60}, + {GameVersion::WiiUsa, 0x9960}, + {GameVersion::WiiPal, 0x9960}, + {GameVersion::WiiJpn, 0x9960}, +}); + +// #define l_pos (l_pos_get()) #define l_texCoord (l_texCoord_get()) static bool l_Egnd_mantTEX_hasReplacement = false; + +DEFINE_MANT_ASSET(l_Egnd_mantDL, u8, 0x3EC, { + {GameVersion::GcnUsa, 0xA9A0}, + {GameVersion::GcnPal, 0xA9A0}, + {GameVersion::GcnJpn, 0xA9A0}, + {GameVersion::WiiUsaRev0, 0xA0C0}, + {GameVersion::WiiUsa, 0x9EC0}, + {GameVersion::WiiPal, 0x9EC0}, + {GameVersion::WiiJpn, 0x9EC0}, +}); + +#define l_Egnd_mantDL (l_Egnd_mantDL_get()) #else #include "assets/l_Egnd_mantTEX.h" @@ -52,16 +115,9 @@ static bool l_Egnd_mantTEX_hasReplacement = false; #endif #include "d/d_s_play.h" -#if TARGET_PC -using GameVersion = dusk::version::GameVersion; - -static u8* l_Egnd_mantDL_get() { alignas(32) static u8 buf[0x3EC]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0xA9A0}, {GameVersion::GcnPal, 0xA9A0}, {GameVersion::GcnJpn, 0xA9A0}}, 0x3EC), true); return buf; } -#define l_Egnd_mantDL (l_Egnd_mantDL_get()) -#else -#include "assets/l_Egnd_mantDL.h" -#endif - #if !TARGET_PC +#include "assets/l_Egnd_mantDL.h" + static void* pal_d = (void*)&l_Egnd_mantPAL; static void* tex_d[2] = { diff --git a/src/d/actor/d_a_player.cpp b/src/d/actor/d_a_player.cpp index 8ab70d8907..48ad37e2ec 100644 --- a/src/d/actor/d_a_player.cpp +++ b/src/d/actor/d_a_player.cpp @@ -375,10 +375,14 @@ static const u8* l_sightDL_get() { static bool _ = ( dusk::LoadDolAsset( buf, -{ - {GameVersion::GcnUsa, 0x803BA0C0}, - {GameVersion::GcnPal, 0x803BBDA0}, - {GameVersion::GcnJpn, 0x803B4220} + { + {GameVersion::GcnUsa, 0x803BA0C0}, + {GameVersion::GcnPal, 0x803BBDA0}, + {GameVersion::GcnJpn, 0x803B4220}, + {GameVersion::WiiUsaRev0, 0x803F63C0}, + {GameVersion::WiiUsa, 0x803E1640}, + {GameVersion::WiiPal, 0x803E23A0}, + {GameVersion::WiiJpn, 0x803DF600} }, 0x89 ), diff --git a/src/d/actor/d_a_title.cpp b/src/d/actor/d_a_title.cpp index 5ff7dad881..b9edd72fa1 100644 --- a/src/d/actor/d_a_title.cpp +++ b/src/d/actor/d_a_title.cpp @@ -52,7 +52,7 @@ static u8 const lit_3772[12] = { #if TARGET_PC using namespace dusk::version; -#define l_arcName versionSelect({{GameVersion::GcnPal, "TitlePal"}}, "Title") +#define l_arcName regionSelect("Title", "TitlePal", "Title") #elif VERSION == VERSION_GCN_PAL static char const l_arcName[] = "TitlePal"; #else diff --git a/src/d/actor/d_flower.inc b/src/d/actor/d_flower.inc index f8fe0bed10..9e3d77efd0 100644 --- a/src/d/actor/d_flower.inc +++ b/src/d/actor/d_flower.inc @@ -11,9 +11,16 @@ const u16 l_J_Ohana00_64TEX__width = 63; const u16 l_J_Ohana00_64TEX__height = 63; #if TARGET_PC -#include "dusk/dvd_asset.hpp" -using GameVersion = dusk::version::GameVersion; -static u8* l_J_Ohana00_64TEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x9060}, {GameVersion::GcnPal, 0x9060}, {GameVersion::GcnJpn, 0x9060}}, 0x800), true); return buf; } +DEFINE_GRASS_ASSET(l_J_Ohana00_64TEX, u8, 0x800, { + {GameVersion::GcnUsa, 0x9060}, + {GameVersion::GcnPal, 0x9060}, + {GameVersion::GcnJpn, 0x9060}, + {GameVersion::WiiUsaRev0, 0x7B60}, + {GameVersion::WiiUsa, 0x7BC0}, + {GameVersion::WiiPal, 0x7BC0}, + {GameVersion::WiiJpn, 0x7BC0}, +}); + #define l_J_Ohana00_64TEX (l_J_Ohana00_64TEX_get()) // from d_grass.inc @@ -115,12 +122,46 @@ static u8 l_flowerTexCoord[] = { 0x3E, 0xA7, 0x72, 0xD6, 0xBD, 0x2F, 0x46, 0xAA}; #if TARGET_PC -using GameVersion = dusk::version::GameVersion; +DEFINE_GRASS_ASSET(l_J_hana00DL, u8, 0x150, { + {GameVersion::GcnUsa, 0x9D20}, + {GameVersion::GcnPal, 0x9D20}, + {GameVersion::GcnJpn, 0x9D20}, + {GameVersion::WiiUsaRev0, 0x8820}, + {GameVersion::WiiUsa, 0x8880}, + {GameVersion::WiiPal, 0x8880}, + {GameVersion::WiiJpn, 0x8880}, +}); + +DEFINE_GRASS_ASSET(l_J_hana00_cDL, u8, 0xDE, { + {GameVersion::GcnUsa, 0x9E80}, + {GameVersion::GcnPal, 0x9E80}, + {GameVersion::GcnJpn, 0x9E80}, + {GameVersion::WiiUsaRev0, 0x8980}, + {GameVersion::WiiUsa, 0x89E0}, + {GameVersion::WiiPal, 0x89E0}, + {GameVersion::WiiJpn, 0x89E0}, +}); + +DEFINE_GRASS_ASSET(l_matDL, u8, 0x99, { + {GameVersion::GcnUsa, 0x9F60}, + {GameVersion::GcnPal, 0x9F60}, + {GameVersion::GcnJpn, 0x9F60}, + {GameVersion::WiiUsaRev0, 0x8A60}, + {GameVersion::WiiUsa, 0x8AC0}, + {GameVersion::WiiPal, 0x8AC0}, + {GameVersion::WiiJpn, 0x8AC0}, +}); + +DEFINE_GRASS_ASSET(l_matLight4DL, u8, 0x99, { + {GameVersion::GcnUsa, 0xA000}, + {GameVersion::GcnPal, 0xA000}, + {GameVersion::GcnJpn, 0xA000}, + {GameVersion::WiiUsaRev0, 0x8B00}, + {GameVersion::WiiUsa, 0x8B60}, + {GameVersion::WiiPal, 0x8B60}, + {GameVersion::WiiJpn, 0x8B60}, +}); -static u8* l_J_hana00DL_get() { static u8 buf[0x150]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x9D20}, {GameVersion::GcnPal, 0x9D20}, {GameVersion::GcnJpn, 0x9D20}}, 0x150), true); return buf; } -static u8* l_J_hana00_cDL_get() { static u8 buf[0xDE]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x9E80}, {GameVersion::GcnPal, 0x9E80}, {GameVersion::GcnJpn, 0x9E80}}, 0xDE), true); return buf; } -static u8* l_matDL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x9F60}, {GameVersion::GcnPal, 0x9F60}, {GameVersion::GcnJpn, 0x9F60}}, 0x99), true); return buf; } -static u8* l_matLight4DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xA000}, {GameVersion::GcnPal, 0xA000}, {GameVersion::GcnJpn, 0xA000}}, 0x99), true); return buf; } #define l_J_hana00DL (l_J_hana00DL_get()) #define l_J_hana00_cDL (l_J_hana00_cDL_get()) #define l_matDL (l_matDL_get()) @@ -141,8 +182,16 @@ const u16 l_J_Ohana01_64128_0419TEX__width = 63; const u16 l_J_Ohana01_64128_0419TEX__height = 127; #if TARGET_PC -using GameVersion = dusk::version::GameVersion; -static u8* l_J_Ohana01_64128_0419TEX_get() { static u8 buf[0x1000]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xA0A0}, {GameVersion::GcnPal, 0xA0A0}, {GameVersion::GcnJpn, 0xA0A0}}, 0x1000), true); return buf; } +DEFINE_GRASS_ASSET(l_J_Ohana01_64128_0419TEX, u8, 0x1000, { + {GameVersion::GcnUsa, 0xA0A0}, + {GameVersion::GcnPal, 0xA0A0}, + {GameVersion::GcnJpn, 0xA0A0}, + {GameVersion::WiiUsaRev0, 0x8BA0}, + {GameVersion::WiiUsa, 0x8C00}, + {GameVersion::WiiPal, 0x8C00}, + {GameVersion::WiiJpn, 0x8C00}, +}); + #define l_J_Ohana01_64128_0419TEX (l_J_Ohana01_64128_0419TEX_get()) #else #include "assets/l_J_Ohana01_64128_0419TEX.h" @@ -274,13 +323,56 @@ static u8 l_flowerTexCoord2[] = { 0x40, 0x1B, 0x7D, 0x52, 0x3F, 0x80, 0x3F, 0x79, 0x40, 0x1B, 0x7D, 0x52, 0x3F, 0x51, 0x10, 0x6F}; #if TARGET_PC -using GameVersion = dusk::version::GameVersion; +DEFINE_GRASS_ASSET(l_J_hana01DL, u8, 0x138, { + {GameVersion::GcnUsa, 0xB7C0}, + {GameVersion::GcnPal, 0xB7C0}, + {GameVersion::GcnJpn, 0xB7C0}, + {GameVersion::WiiUsaRev0, 0xA2C0}, + {GameVersion::WiiUsa, 0xA320}, + {GameVersion::WiiPal, 0xA320}, + {GameVersion::WiiJpn, 0xA320}, +}); + +DEFINE_GRASS_ASSET(l_J_hana01_c_00DL, u8, 0xDE, { + {GameVersion::GcnUsa, 0xB900}, + {GameVersion::GcnPal, 0xB900}, + {GameVersion::GcnJpn, 0xB900}, + {GameVersion::WiiUsaRev0, 0xA400}, + {GameVersion::WiiUsa, 0xA460}, + {GameVersion::WiiPal, 0xA460}, + {GameVersion::WiiJpn, 0xA460}, +}); + +DEFINE_GRASS_ASSET(l_J_hana01_c_01DL, u8, 0x128, { + {GameVersion::GcnUsa, 0xB9E0}, + {GameVersion::GcnPal, 0xB9E0}, + {GameVersion::GcnJpn, 0xB9E0}, + {GameVersion::WiiUsaRev0, 0xA4E0}, + {GameVersion::WiiUsa, 0xA540}, + {GameVersion::WiiPal, 0xA540}, + {GameVersion::WiiJpn, 0xA540}, +}); + +DEFINE_GRASS_ASSET(l_mat2DL, u8, 0x99, { + {GameVersion::GcnUsa, 0xBB20}, + {GameVersion::GcnPal, 0xBB20}, + {GameVersion::GcnJpn, 0xBB20}, + {GameVersion::WiiUsaRev0, 0xA620}, + {GameVersion::WiiUsa, 0xA680}, + {GameVersion::WiiPal, 0xA680}, + {GameVersion::WiiJpn, 0xA680}, +}); + +DEFINE_GRASS_ASSET(l_mat2Light4DL, u8, 0x99, { + {GameVersion::GcnUsa, 0xBBC0}, + {GameVersion::GcnPal, 0xBBC0}, + {GameVersion::GcnJpn, 0xBBC0}, + {GameVersion::WiiUsaRev0, 0xA6C0}, + {GameVersion::WiiUsa, 0xA720}, + {GameVersion::WiiPal, 0xA720}, + {GameVersion::WiiJpn, 0xA720}, +}); -static u8* l_J_hana01DL_get() { static u8 buf[0x138]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xB7C0}, {GameVersion::GcnPal, 0xB7C0}, {GameVersion::GcnJpn, 0xB7C0}}, 0x138), true); return buf; } -static u8* l_J_hana01_c_00DL_get() { static u8 buf[0xDE]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xB900}, {GameVersion::GcnPal, 0xB900}, {GameVersion::GcnJpn, 0xB900}}, 0xDE), true); return buf; } -static u8* l_J_hana01_c_01DL_get() { static u8 buf[0x128]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xB9E0}, {GameVersion::GcnPal, 0xB9E0}, {GameVersion::GcnJpn, 0xB9E0}}, 0x128), true); return buf; } -static u8* l_mat2DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xBB20}, {GameVersion::GcnPal, 0xBB20}, {GameVersion::GcnJpn, 0xBB20}}, 0x99), true); return buf; } -static u8* l_mat2Light4DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xBBC0}, {GameVersion::GcnPal, 0xBBC0}, {GameVersion::GcnJpn, 0xBBC0}}, 0x99), true); return buf; } #define l_J_hana01DL (l_J_hana01DL_get()) #define l_J_hana01_c_00DL (l_J_hana01_c_00DL_get()) #define l_J_hana01_c_01DL (l_J_hana01_c_01DL_get()) diff --git a/src/d/actor/d_grass.inc b/src/d/actor/d_grass.inc index 2e20394620..6c7a898c56 100644 --- a/src/d/actor/d_grass.inc +++ b/src/d/actor/d_grass.inc @@ -21,8 +21,39 @@ const u16 l_M_kusa05_RGBATEX__height = 31; #if TARGET_PC #include "dusk/dvd_asset.hpp" using GameVersion = dusk::version::GameVersion; -static u8* l_M_kusa05_RGBATEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x7680}, {GameVersion::GcnPal, 0x7680}, {GameVersion::GcnJpn, 0x7680}}, 0x800), true); return buf; } -static u8* l_M_Hijiki00TEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x7E80}, {GameVersion::GcnPal, 0x7E80}, {GameVersion::GcnJpn, 0x7E80}}, 0x800), true); return buf; } + +template +static bool LoadGrassAsset(T (&dst)[N], std::initializer_list offset) { + return dusk::LoadArchivedRelAsset(dst, 'AMEM', "d_a_grass.rel", offset); +} + +#define DEFINE_GRASS_ASSET(name, type, count, ...) \ + static type* name##_get() { \ + static type buf[count]; \ + static bool _ = (LoadGrassAsset(buf, __VA_ARGS__), true); \ + return buf; \ + } + +DEFINE_GRASS_ASSET(l_M_kusa05_RGBATEX, u8, 0x800, { + {GameVersion::GcnUsa, 0x7680}, + {GameVersion::GcnPal, 0x7680}, + {GameVersion::GcnJpn, 0x7680}, + {GameVersion::WiiUsaRev0, 0x6180}, + {GameVersion::WiiUsa, 0x61E0}, + {GameVersion::WiiPal, 0x61E0}, + {GameVersion::WiiJpn, 0x61E0}, +}); + +DEFINE_GRASS_ASSET(l_M_Hijiki00TEX, u8, 0x800, { + {GameVersion::GcnUsa, 0x7E80}, + {GameVersion::GcnPal, 0x7E80}, + {GameVersion::GcnJpn, 0x7E80}, + {GameVersion::WiiUsaRev0, 0x6980}, + {GameVersion::WiiUsa, 0x69E0}, + {GameVersion::WiiPal, 0x69E0}, + {GameVersion::WiiJpn, 0x69E0}, +}); + #define l_M_kusa05_RGBATEX (l_M_kusa05_RGBATEX_get()) #define l_M_Hijiki00TEX (l_M_Hijiki00TEX_get()) #else @@ -114,14 +145,66 @@ static u8 l_texCoord[160] = { }; #if TARGET_PC -using GameVersion = dusk::version::GameVersion; +DEFINE_GRASS_ASSET(l_M_Kusa_9qDL, u8, 0xCB, { + {GameVersion::GcnUsa, 0x8B00}, + {GameVersion::GcnPal, 0x8B00}, + {GameVersion::GcnJpn, 0x8B00}, + {GameVersion::WiiUsaRev0, 0x7600}, + {GameVersion::WiiUsa, 0x7660}, + {GameVersion::WiiPal, 0x7660}, + {GameVersion::WiiJpn, 0x7660}, +}); + +DEFINE_GRASS_ASSET(l_M_Kusa_9q_cDL, u8, 0xCB, { + {GameVersion::GcnUsa, 0x8BE0}, + {GameVersion::GcnPal, 0x8BE0}, + {GameVersion::GcnJpn, 0x8BE0}, + {GameVersion::WiiUsaRev0, 0x76E0}, + {GameVersion::WiiUsa, 0x7740}, + {GameVersion::WiiPal, 0x7740}, + {GameVersion::WiiJpn, 0x7740}, +}); + +DEFINE_GRASS_ASSET(l_M_TenGusaDL, u8, 0xD4, { + {GameVersion::GcnUsa, 0x8CC0}, + {GameVersion::GcnPal, 0x8CC0}, + {GameVersion::GcnJpn, 0x8CC0}, + {GameVersion::WiiUsaRev0, 0x77C0}, + {GameVersion::WiiUsa, 0x7820}, + {GameVersion::WiiPal, 0x7820}, + {GameVersion::WiiJpn, 0x7820}, +}); + +DEFINE_GRASS_ASSET(l_Tengusa_matDL, u8, 0xA8, { + {GameVersion::GcnUsa, 0x8DA0}, + {GameVersion::GcnPal, 0x8DA0}, + {GameVersion::GcnJpn, 0x8DA0}, + {GameVersion::WiiUsaRev0, 0x78A0}, + {GameVersion::WiiUsa, 0x7900}, + {GameVersion::WiiPal, 0x7900}, + {GameVersion::WiiJpn, 0x7900}, +}); + +DEFINE_GRASS_ASSET(l_kusa9q_matDL, u8, 0xA8, { + {GameVersion::GcnUsa, 0x8E60}, + {GameVersion::GcnPal, 0x8E60}, + {GameVersion::GcnJpn, 0x8E60}, + {GameVersion::WiiUsaRev0, 0x7960}, + {GameVersion::WiiUsa, 0x79C0}, + {GameVersion::WiiPal, 0x79C0}, + {GameVersion::WiiJpn, 0x79C0}, +}); + +DEFINE_GRASS_ASSET(l_kusa9q_l4_matDL, u8, 0xA8, { + {GameVersion::GcnUsa, 0x8F20}, + {GameVersion::GcnPal, 0x8F20}, + {GameVersion::GcnJpn, 0x8F20}, + {GameVersion::WiiUsaRev0, 0x7A20}, + {GameVersion::WiiUsa, 0x7A80}, + {GameVersion::WiiPal, 0x7A80}, + {GameVersion::WiiJpn, 0x7A80}, +}); -static u8* l_M_Kusa_9qDL_get() { static u8 buf[0xCB]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8B00}, {GameVersion::GcnPal, 0x8B00}, {GameVersion::GcnJpn, 0x8B00}}, 0xCB), true); return buf; } -static u8* l_M_Kusa_9q_cDL_get() { static u8 buf[0xCB]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8BE0}, {GameVersion::GcnPal, 0x8BE0}, {GameVersion::GcnJpn, 0x8BE0}}, 0xCB), true); return buf; } -static u8* l_M_TenGusaDL_get() { static u8 buf[0xD4]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8CC0}, {GameVersion::GcnPal, 0x8CC0}, {GameVersion::GcnJpn, 0x8CC0}}, 0xD4), true); return buf; } -static u8* l_Tengusa_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8DA0}, {GameVersion::GcnPal, 0x8DA0}, {GameVersion::GcnJpn, 0x8DA0}}, 0xA8), true); return buf; } -static u8* l_kusa9q_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8E60}, {GameVersion::GcnPal, 0x8E60}, {GameVersion::GcnJpn, 0x8E60}}, 0xA8), true); return buf; } -static u8* l_kusa9q_l4_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8F20}, {GameVersion::GcnPal, 0x8F20}, {GameVersion::GcnJpn, 0x8F20}}, 0xA8), true); return buf; } #define l_M_Kusa_9qDL (l_M_Kusa_9qDL_get()) #define l_M_Kusa_9q_cDL (l_M_Kusa_9q_cDL_get()) #define l_M_TenGusaDL (l_M_TenGusaDL_get()) diff --git a/src/d/d_bright_check.cpp b/src/d/d_bright_check.cpp index cbea55100a..3d36e72232 100644 --- a/src/d/d_bright_check.cpp +++ b/src/d/d_bright_check.cpp @@ -83,7 +83,9 @@ void dBrightCheck_c::screenSet() { JUT_ASSERT(0, mBrightCheck.Scr != NULL); mBrightCheck.Scr->setPriority("zelda_option_check.blo", 0x1100000, mArchive); + IF_DUSK_BLOCK(dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) mBrightCheck.Scr->search(MULTI_CHAR('g_abtn_n'))->hide(); + IF_DUSK_BLOCK_END #if TARGET_PC J2DTextBox* settings_text; @@ -113,9 +115,11 @@ void dBrightCheck_c::screenSet() { J2DTextBox* btna_text[5]; for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { btna_text[i] = (J2DTextBox*)mBrightCheck.Scr->search(tv_btnA[i]); - mBrightCheck.Scr->search(ftv_btnA[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mBrightCheck.Scr->search(ftv_btnA[i])->hide(); + } } else { btna_text[i] = (J2DTextBox*)mBrightCheck.Scr->search(ftv_btnA[i]); mBrightCheck.Scr->search(tv_btnA[i])->hide(); @@ -222,7 +226,9 @@ void dBrightCheck_c::brightCheckWide() { // Confirm A Button mBrightCheck.Scr->search(MULTI_CHAR('abtn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mBrightCheck.Scr->search(MULTI_CHAR('gcabtn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mBrightCheck.Scr->search(MULTI_CHAR('gcabtn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + } // Text mBrightCheck.Scr->search(MULTI_CHAR('menu_6n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); diff --git a/src/d/d_menu_dmap.cpp b/src/d/d_menu_dmap.cpp index 6feb191562..4557edfb4c 100644 --- a/src/d/d_menu_dmap.cpp +++ b/src/d/d_menu_dmap.cpp @@ -363,13 +363,15 @@ void dMenu_DmapBg_c::buttonIconScreenInit() { for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { ((J2DTextBox*)mButtonScreen->search(cont_at[i]))->setFont(mDoExt_getMesgFont()); ((J2DTextBox*)mButtonScreen->search(cont_bt[i]))->setFont(mDoExt_getMesgFont()); ((J2DTextBox*)mButtonScreen->search(cont_at[i]))->setString(32, ""); ((J2DTextBox*)mButtonScreen->search(cont_bt[i]))->setString(32, ""); - ((J2DTextBox*)mButtonScreen->search(font_at[i]))->hide(); - ((J2DTextBox*)mButtonScreen->search(font_bt[i]))->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + ((J2DTextBox*)mButtonScreen->search(font_at[i]))->hide(); + ((J2DTextBox*)mButtonScreen->search(font_bt[i]))->hide(); + } } else { ((J2DTextBox*)mButtonScreen->search(font_at[i]))->setFont(mDoExt_getMesgFont()); ((J2DTextBox*)mButtonScreen->search(font_bt[i]))->setFont(mDoExt_getMesgFont()); @@ -400,7 +402,7 @@ void dMenu_DmapBg_c::buttonIconScreenInit() { J2DTextBox* textBox; for (int i = 0; i < 2; i++) { - textBox = ((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? c_tag_jpn[i] : c_tag[i], c_tag[i]))); + textBox = (J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? c_tag_jpn[i] : c_tag[i], c_tag[i])); textBox->setFont(mDoExt_getMesgFont()); textBox->setString(32, ""); } @@ -436,9 +438,9 @@ void dMenu_DmapBg_c::setAButtonString(u32 i_msgNo) { for (int i = 0; i < 5; i++) { if (i_msgNo == 0) { - SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? cont_at_jpn[i] : cont_at[i], cont_at[i])))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? cont_at_jpn[i] : cont_at[i], cont_at[i])))->getStringPtr(), ""); } else { - dMeter2Info_getStringKanji(i_msgNo, ((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? cont_at_jpn[i] : cont_at[i], cont_at[i])))->getStringPtr(), NULL); + dMeter2Info_getStringKanji(i_msgNo, ((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? cont_at_jpn[i] : cont_at[i], cont_at[i])))->getStringPtr(), NULL); } } } @@ -463,9 +465,9 @@ void dMenu_DmapBg_c::setBButtonString(u32 i_msgNo) { for (int i = 0; i < 5; i++) { if (i_msgNo == 0) { - SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? cont_bt_jpn[i] : cont_bt[i], cont_bt[i])))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? cont_bt_jpn[i] : cont_bt[i], cont_bt[i])))->getStringPtr(), ""); } else { - dMeter2Info_getStringKanji(i_msgNo, ((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? cont_bt_jpn[i] : cont_bt[i], cont_bt[i])))->getStringPtr(), NULL); + dMeter2Info_getStringKanji(i_msgNo, ((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? cont_bt_jpn[i] : cont_bt[i], cont_bt[i])))->getStringPtr(), NULL); } } } @@ -504,12 +506,12 @@ void dMenu_DmapBg_c::setCButtonString(u32 i_msgNo) { if (msgNo == 0) { for (i = 0; i < 2; i++) { - SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? c_tag_jpn[i] : c_tag[i], c_tag[i])))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? c_tag_jpn[i] : c_tag[i], c_tag[i])))->getStringPtr(), ""); } mpCButton->setAlphaRate(0.5f); } else { for (i = 0; i < 2; i++) { - dMeter2Info_getStringKanji(msgNo, ((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? c_tag_jpn[i] : c_tag[i], c_tag[i])))->getStringPtr(), NULL); + dMeter2Info_getStringKanji(msgNo, ((J2DTextBox*)mButtonScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? c_tag_jpn[i] : c_tag[i], c_tag[i])))->getStringPtr(), NULL); } mpCButton->setAlphaRate(1.0f); } @@ -979,8 +981,10 @@ void dMenu_DmapBg_c::dMapBgWide() { mButtonScreen->search(MULTI_CHAR('c_btn'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); mButtonScreen->search(MULTI_CHAR('c_text_s'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); mButtonScreen->search(MULTI_CHAR('c_text'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mButtonScreen->search(MULTI_CHAR('f_text_s'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mButtonScreen->search(MULTI_CHAR('f_text'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mButtonScreen->search(MULTI_CHAR('f_text_s'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mButtonScreen->search(MULTI_CHAR('f_text'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + } // Decorations mButtonScreen->search(MULTI_CHAR('kazari_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); diff --git a/src/d/d_menu_fishing.cpp b/src/d/d_menu_fishing.cpp index eb24852160..2579d328fb 100644 --- a/src/d/d_menu_fishing.cpp +++ b/src/d/d_menu_fishing.cpp @@ -343,7 +343,9 @@ void dMenu_Fishing_c::screenSetBase() { field_0x19c[1][i]->setString(0x20, ""); mpFishNameString[i] = (J2DTextBox*)mpScreen->search(name_0[i]); + IF_DUSK_BLOCK(dusk::version::isGcn()) mpScreen->search(fname_0[i])->hide(); + IF_DUSK_BLOCK_END mpFishNameString[i]->setFont(mDoExt_getSubFont()); mpFishNameString[i]->setString(0x20, ""); dMeter2Info_getStringKanji(name_id[i], mpFishNameString[i]->getStringPtr(), NULL); diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index 9d1d17ca0e..cd2e09c6d7 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -2420,10 +2420,12 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { static const u64 farea_name[3] = {MULTI_CHAR('f_name_1'), MULTI_CHAR('f_name3'), MULTI_CHAR('f_name2')}; for (int i = 0; i < 3; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { static_cast(mpTitleScreen->search(area_name[i]))->setFont(mDoExt_getRubyFont()); static_cast(mpTitleScreen->search(area_name[i]))->setString(0x40, ""); - mpTitleScreen->search(farea_name[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpTitleScreen->search(farea_name[i])->hide(); + } } else { static_cast(mpTitleScreen->search(farea_name[i]))->setFont(mDoExt_getRubyFont()); static_cast(mpTitleScreen->search(farea_name[i]))->setString(0x40, ""); @@ -2458,10 +2460,12 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { #endif for (int i = 0; i < 7; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { static_cast(mpTitleScreen->search(sfont_name[i]))->setFont(mDoExt_getRubyFont()); static_cast(mpTitleScreen->search(sfont_name[i]))->setString(0x40, ""); - mpTitleScreen->search(ffont_name[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpTitleScreen->search(ffont_name[i])->hide(); + } } else { static_cast(mpTitleScreen->search(ffont_name[i]))->setFont(mDoExt_getRubyFont()); static_cast(mpTitleScreen->search(ffont_name[i]))->setString(0x40, ""); @@ -2485,10 +2489,12 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { static const u64 font_zt[5] = {MULTI_CHAR('font_zt1'), MULTI_CHAR('font_zt2'), MULTI_CHAR('font_zt3'), MULTI_CHAR('font_zt4'), MULTI_CHAR('font_zt5')}; for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { static_cast(mpTitleScreen->search(cont_zt[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(cont_zt[i]))->setString(0x20, ""); - mpTitleScreen->search(font_zt[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpTitleScreen->search(font_zt[i])->hide(); + } } else { static_cast(mpTitleScreen->search(font_zt[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(font_zt[i]))->setString(0x20, ""); @@ -2516,10 +2522,12 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { #endif for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { static_cast(mpTitleScreen->search(cont_bt[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(cont_bt[i]))->setString(0x20, ""); - mpTitleScreen->search(font_bt[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpTitleScreen->search(font_bt[i])->hide(); + } } else { static_cast(mpTitleScreen->search(font_bt[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(font_bt[i]))->setString(0x20, ""); @@ -2543,10 +2551,12 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { static const u64 font_at[5] = {MULTI_CHAR('font_at1'), MULTI_CHAR('font_at2'), MULTI_CHAR('font_at3'), MULTI_CHAR('font_at4'), MULTI_CHAR('font_at5')}; for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { static_cast(mpTitleScreen->search(cont_at[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(cont_at[i]))->setString(0x20, ""); - mpTitleScreen->search(font_at[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpTitleScreen->search(font_at[i])->hide(); + } } else { static_cast(mpTitleScreen->search(font_at[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(font_at[i]))->setString(0x20, ""); @@ -2571,10 +2581,12 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { static const u64 fuji_c[5] = {MULTI_CHAR('fuji_c00'), MULTI_CHAR('fuji_c01'), MULTI_CHAR('fuji_c02'), MULTI_CHAR('fuji_c03'), MULTI_CHAR('fuji_c04')}; for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { static_cast(mpTitleScreen->search(juji_c[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(juji_c[i]))->setString(0x20, ""); - mpTitleScreen->search(fuji_c[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpTitleScreen->search(fuji_c[i])->hide(); + } } else { static_cast(mpTitleScreen->search(fuji_c[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(fuji_c[i]))->setString(0x20, ""); @@ -2598,10 +2610,12 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { static const u64 fst_c[5] = {MULTI_CHAR('fst_00'), MULTI_CHAR('fst_01'), MULTI_CHAR('fst_02'), MULTI_CHAR('fst_03'), MULTI_CHAR('fst_04')}; for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { static_cast(mpTitleScreen->search(ast_c[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(ast_c[i]))->setString(0x20, ""); - mpTitleScreen->search(fst_c[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpTitleScreen->search(fst_c[i])->hide(); + } } else { static_cast(mpTitleScreen->search(fst_c[i]))->setFont(mDoExt_getMesgFont()); static_cast(mpTitleScreen->search(fst_c[i]))->setString(0x20, ""); @@ -2863,8 +2877,7 @@ void dMenu_Fmap2DTop_c::setTitleNameString(u32 param_0) { static const u64 ffont_name[7] = { MULTI_CHAR('ffont00'), MULTI_CHAR('ffontl0'), MULTI_CHAR('ffontl1'), MULTI_CHAR('ffontl2'), MULTI_CHAR('ffontb0'), MULTI_CHAR('ffontb3'), MULTI_CHAR('ffontb4') }; - - auto setTitleNameString_font_name = dusk::version::isRegionJpn() ? sfont_name : ffont_name; + auto setTitleNameString_font_name = dusk::version::isJpnOrLessThanWiiJpn() ? sfont_name : ffont_name; #elif VERSION == VERSION_GCN_JPN static const u64 sfont_name[7] = { MULTI_CHAR('sfont00'), MULTI_CHAR('sfontl0'), MULTI_CHAR('sfontl1'), MULTI_CHAR('sfontl2'), MULTI_CHAR('sfontb0'), MULTI_CHAR('sfontb1'), MULTI_CHAR('sfontb2') @@ -2883,15 +2896,9 @@ void dMenu_Fmap2DTop_c::setTitleNameString(u32 param_0) { #endif for (int i = 0; i < 7; i++) { if (param_0 == 0) { - SAFE_STRCPY(((J2DTextBox*)(mpTitleScreen->search(setTitleNameString_font_name[i]))) - ->getStringPtr(), - ""); + SAFE_STRCPY(((J2DTextBox*)(mpTitleScreen->search(setTitleNameString_font_name[i])))->getStringPtr(), ""); } else { - dMeter2Info_getStringKanji( - param_0, - ((J2DTextBox*)(mpTitleScreen->search(setTitleNameString_font_name[i]))) - ->getStringPtr(), - NULL); + dMeter2Info_getStringKanji(param_0, ((J2DTextBox*)(mpTitleScreen->search(setTitleNameString_font_name[i])))->getStringPtr(), NULL); } } } @@ -2900,7 +2907,7 @@ void dMenu_Fmap2DTop_c::setAreaNameString(u32 param_0) { #if TARGET_PC static const u64 iarea_name[3] = {MULTI_CHAR('i_name_s'), MULTI_CHAR('i_name'), MULTI_CHAR('i_name1')}; static const u64 farea_name[3] = {MULTI_CHAR('f_name_1'), MULTI_CHAR('f_name3'), MULTI_CHAR('f_name2')}; - auto setAreaNameString_area_name = dusk::version::isRegionJpn() ? iarea_name : farea_name; + auto setAreaNameString_area_name = dusk::version::isJpnOrLessThanWiiJpn() ? iarea_name : farea_name; #elif VERSION == VERSION_GCN_JPN static const u64 iarea_name[3] = {MULTI_CHAR('i_name_s'), MULTI_CHAR('i_name'), MULTI_CHAR('i_name1')}; #define setAreaNameString_area_name iarea_name @@ -2910,15 +2917,9 @@ void dMenu_Fmap2DTop_c::setAreaNameString(u32 param_0) { #endif for (int i = 0; i < 3; i++) { if (param_0 == 0) { - SAFE_STRCPY(((J2DTextBox*)(mpTitleScreen->search(setAreaNameString_area_name[i]))) - ->getStringPtr(), - ""); + SAFE_STRCPY(((J2DTextBox*)(mpTitleScreen->search(setAreaNameString_area_name[i])))->getStringPtr(), ""); } else { - dMeter2Info_getStringKanji( - param_0, - ((J2DTextBox*)(mpTitleScreen->search(setAreaNameString_area_name[i]))) - ->getStringPtr(), - NULL); + dMeter2Info_getStringKanji(param_0, ((J2DTextBox*)(mpTitleScreen->search(setAreaNameString_area_name[i])))->getStringPtr(), NULL); } } } @@ -2937,7 +2938,7 @@ void dMenu_Fmap2DTop_c::setZButtonString(u32 param_0, u8 i_alpha) { #if TARGET_PC static const u64 cont_zt[5] = {MULTI_CHAR('cont_zt'), MULTI_CHAR('cont_zt1'), MULTI_CHAR('cont_zt2'), MULTI_CHAR('cont_zt3'), MULTI_CHAR('cont_zt4')}; static const u64 font_zt[5] = {MULTI_CHAR('font_zt1'), MULTI_CHAR('font_zt2'), MULTI_CHAR('font_zt3'), MULTI_CHAR('font_zt4'), MULTI_CHAR('font_zt5')}; - auto setZButtonString_font_zt = dusk::version::isRegionJpn() ? cont_zt : font_zt; + auto setZButtonString_font_zt = dusk::version::isJpnOrLessThanWiiJpn() ? cont_zt : font_zt; #elif VERSION == VERSION_GCN_JPN static const u64 cont_zt[5] = {MULTI_CHAR('cont_zt'), MULTI_CHAR('cont_zt1'), MULTI_CHAR('cont_zt2'), MULTI_CHAR('cont_zt3'), MULTI_CHAR('cont_zt4')}; #define setZButtonString_font_zt cont_zt @@ -2953,10 +2954,7 @@ void dMenu_Fmap2DTop_c::setZButtonString(u32 param_0, u8 i_alpha) { #endif } else { for (int i = 0; i < 5; i++) { - dMeter2Info_getStringKanji( - param_0, - ((J2DTextBox*)(mpTitleScreen->search(setZButtonString_font_zt[i])))->getStringPtr(), - NULL); + dMeter2Info_getStringKanji(param_0, ((J2DTextBox*)(mpTitleScreen->search(setZButtonString_font_zt[i])))->getStringPtr(), NULL); } if (i_alpha == ALPHA_DEFAULT) { @@ -2975,7 +2973,7 @@ void dMenu_Fmap2DTop_c::setBButtonString(u32 param_0, u8 i_alpha) { #if TARGET_PC static const u64 cont_bt[5] = {MULTI_CHAR('cont_bt1'), MULTI_CHAR('cont_bt2'), MULTI_CHAR('cont_bt3'), MULTI_CHAR('cont_bt4'), MULTI_CHAR('cont_bt')}; static const u64 font_bt[5] = {MULTI_CHAR('font_bt1'), MULTI_CHAR('font_bt2'), MULTI_CHAR('font_bt3'), MULTI_CHAR('font_bt4'), MULTI_CHAR('font_bt5')}; - auto setBButtonString_font_bt = dusk::version::isRegionJpn() ? cont_bt : font_bt; + auto setBButtonString_font_bt = dusk::version::isJpnOrLessThanWiiJpn() ? cont_bt : font_bt; #elif VERSION == VERSION_GCN_JPN static const u64 cont_bt[5] = {MULTI_CHAR('cont_bt1'), MULTI_CHAR('cont_bt2'), MULTI_CHAR('cont_bt3'), MULTI_CHAR('cont_bt4'), MULTI_CHAR('cont_bt')}; #define setBButtonString_font_bt cont_bt @@ -2987,10 +2985,7 @@ void dMenu_Fmap2DTop_c::setBButtonString(u32 param_0, u8 i_alpha) { mAlphaButtonB = ALPHA_MIN; } else { for (int i = 0; i < 5; i++) { - dMeter2Info_getStringKanji( - param_0, - ((J2DTextBox*)(mpTitleScreen->search(setBButtonString_font_bt[i])))->getStringPtr(), - NULL); + dMeter2Info_getStringKanji(param_0, ((J2DTextBox*)(mpTitleScreen->search(setBButtonString_font_bt[i])))->getStringPtr(), NULL); } if (i_alpha == ALPHA_DEFAULT) { @@ -3005,7 +3000,7 @@ void dMenu_Fmap2DTop_c::setAButtonString(u32 param_0, u8 i_alpha) { #if TARGET_PC static const u64 cont_at[5] = {MULTI_CHAR('cont_at'), MULTI_CHAR('cont_at1'), MULTI_CHAR('cont_at2'), MULTI_CHAR('cont_at3'), MULTI_CHAR('cont_at4')}; static const u64 font_at[5] = {MULTI_CHAR('font_at1'), MULTI_CHAR('font_at2'), MULTI_CHAR('font_at3'), MULTI_CHAR('font_at4'), MULTI_CHAR('font_at5')}; - auto setAButtonString_font_at = dusk::version::isRegionJpn() ? cont_at : font_at; + auto setAButtonString_font_at = dusk::version::isJpnOrLessThanWiiJpn() ? cont_at : font_at; #elif VERSION == VERSION_GCN_JPN static const u64 cont_at[5] = {MULTI_CHAR('cont_at'), MULTI_CHAR('cont_at1'), MULTI_CHAR('cont_at2'), MULTI_CHAR('cont_at3'), MULTI_CHAR('cont_at4')}; #define setAButtonString_font_at cont_at @@ -3017,10 +3012,7 @@ void dMenu_Fmap2DTop_c::setAButtonString(u32 param_0, u8 i_alpha) { mAlphaButtonA = ALPHA_MIN; } else { for (int i = 0; i < 5; i++) { - dMeter2Info_getStringKanji( - param_0, - ((J2DTextBox*)(mpTitleScreen->search(setAButtonString_font_at[i])))->getStringPtr(), - NULL); + dMeter2Info_getStringKanji(param_0, ((J2DTextBox*)(mpTitleScreen->search(setAButtonString_font_at[i])))->getStringPtr(), NULL); } if (i_alpha == ALPHA_DEFAULT) { @@ -3034,8 +3026,8 @@ void dMenu_Fmap2DTop_c::setAButtonString(u32 param_0, u8 i_alpha) { void dMenu_Fmap2DTop_c::setCrossLRString(u32 param_0) { #if PLATFORM_GCN || (VERSION == VERSION_SHIELD) #if TARGET_PC - static const u64 juji_c_jpn[5] = {MULTI_CHAR('juji_c00'), MULTI_CHAR('juji_c01'), MULTI_CHAR('juji_c02'), MULTI_CHAR('juji_c03'), MULTI_CHAR('juji_c04')}; - static const u64 juji_c[5] = {MULTI_CHAR('fuji_c00'), MULTI_CHAR('fuji_c01'), MULTI_CHAR('fuji_c02'), MULTI_CHAR('fuji_c03'), MULTI_CHAR('fuji_c04')}; + static const u64 juji_c[5] = {MULTI_CHAR('juji_c00'), MULTI_CHAR('juji_c01'), MULTI_CHAR('juji_c02'), MULTI_CHAR('juji_c03'), MULTI_CHAR('juji_c04')}; + static const u64 fuji_c[5] = {MULTI_CHAR('fuji_c00'), MULTI_CHAR('fuji_c01'), MULTI_CHAR('fuji_c02'), MULTI_CHAR('fuji_c03'), MULTI_CHAR('fuji_c04')}; #elif VERSION == VERSION_GCN_JPN static const u64 juji_c[5] = {MULTI_CHAR('juji_c00'), MULTI_CHAR('juji_c01'), MULTI_CHAR('juji_c02'), MULTI_CHAR('juji_c03'), MULTI_CHAR('juji_c04')}; #else @@ -3043,14 +3035,14 @@ void dMenu_Fmap2DTop_c::setCrossLRString(u32 param_0) { #endif if (param_0 == 0) { for (int i = 0; i < 5; i++) { - J2DTextBox* text_box = static_cast(mpTitleScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? juji_c_jpn[i] : juji_c[i], juji_c[i]))); + J2DTextBox* text_box = static_cast(mpTitleScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? juji_c[i] : fuji_c[i], juji_c[i]))); SAFE_STRCPY(text_box->getStringPtr(), ""); } mpTitleScreen->search(MULTI_CHAR('juy_sha0'))->show(); mAlphaDpad = 1; } else { for (int i = 0; i < 5; i++) { - J2DTextBox* text_box = static_cast(mpTitleScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? juji_c_jpn[i] : juji_c[i], juji_c[i]))); + J2DTextBox* text_box = static_cast(mpTitleScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? juji_c[i] : fuji_c[i], juji_c[i]))); dMeter2Info_getStringKanji(param_0, text_box->getStringPtr(), NULL); } mpTitleScreen->search(MULTI_CHAR('juy_sha0'))->show(); @@ -3062,8 +3054,8 @@ void dMenu_Fmap2DTop_c::setCrossLRString(u32 param_0) { void dMenu_Fmap2DTop_c::set3DStickString(u32 param_0) { #if PLATFORM_GCN || (VERSION == VERSION_SHIELD) #if TARGET_PC - static const u64 ast_c_jpn[5] = {MULTI_CHAR('ast_00'), MULTI_CHAR('ast_01'), MULTI_CHAR('ast_02'), MULTI_CHAR('ast_03'), MULTI_CHAR('ast_04')}; - static const u64 ast_c[5] = {MULTI_CHAR('fst_00'), MULTI_CHAR('fst_01'), MULTI_CHAR('fst_02'), MULTI_CHAR('fst_03'), MULTI_CHAR('fst_04')}; + static const u64 ast_c[5] = {MULTI_CHAR('ast_00'), MULTI_CHAR('ast_01'), MULTI_CHAR('ast_02'), MULTI_CHAR('ast_03'), MULTI_CHAR('ast_04')}; + static const u64 fst_c[5] = {MULTI_CHAR('fst_00'), MULTI_CHAR('fst_01'), MULTI_CHAR('fst_02'), MULTI_CHAR('fst_03'), MULTI_CHAR('fst_04')}; #elif VERSION == VERSION_GCN_JPN static const u64 ast_c[5] = {MULTI_CHAR('ast_00'), MULTI_CHAR('ast_01'), MULTI_CHAR('ast_02'), MULTI_CHAR('ast_03'), MULTI_CHAR('ast_04')}; #else @@ -3071,14 +3063,14 @@ void dMenu_Fmap2DTop_c::set3DStickString(u32 param_0) { #endif if (param_0 == 0) { for (int i = 0; i < 5; i++) { - J2DTextBox* text_box = static_cast(mpTitleScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? ast_c_jpn[i] : ast_c[i], ast_c[i]))); + J2DTextBox* text_box = static_cast(mpTitleScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? ast_c[i] : fst_c[i], ast_c[i]))); SAFE_STRCPY(text_box->getStringPtr(), ""); } mpTitleScreen->search(MULTI_CHAR('as_sha0'))->show(); mAlphaAnalogStick = 1; } else { for (int i = 0; i < 5; i++) { - J2DTextBox* text_box = static_cast(mpTitleScreen->search(DUSK_IF_ELSE(dusk::version::isRegionJpn() ? ast_c_jpn[i] : ast_c[i], ast_c[i]))); + J2DTextBox* text_box = static_cast(mpTitleScreen->search(DUSK_IF_ELSE(dusk::version::isJpnOrLessThanWiiJpn() ? ast_c[i] : fst_c[i], ast_c[i]))); dMeter2Info_getStringKanji(param_0, text_box->getStringPtr(), NULL); } mpTitleScreen->search(MULTI_CHAR('as_sha0'))->show(); diff --git a/src/d/d_menu_option.cpp b/src/d/d_menu_option.cpp index cb9af850c0..3845acb436 100644 --- a/src/d/d_menu_option.cpp +++ b/src/d/d_menu_option.cpp @@ -159,7 +159,9 @@ void dMenu_Option_c::_create() { mpTVButtonText = JKR_NEW CPaneMgr(mpTVScreen, MULTI_CHAR('a_text_n'), 0, NULL); JUT_ASSERT(298, mpTVButtonText != NULL); + IF_DUSK_BLOCK(dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) mpTVScreen->search(MULTI_CHAR('g_abtn_n'))->hide(); + IF_DUSK_BLOCK_END mpScreenIcon = JKR_NEW J2DScreen(); JUT_ASSERT(325, mpScreenIcon != NULL); @@ -1710,9 +1712,11 @@ void dMenu_Option_c::screenSet() { mpString->getString(0x55C, field_0x270[2], NULL, NULL, NULL, 0); for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { field_0x25c[i] = (J2DTextBox*)mpTVScreen->search(tv_btnA[i]); - mpTVScreen->search(ftv_btnA[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpTVScreen->search(ftv_btnA[i])->hide(); + } } else { field_0x25c[i] = (J2DTextBox*)mpTVScreen->search(ftv_btnA[i]); mpTVScreen->search(tv_btnA[i])->hide(); diff --git a/src/d/d_menu_ring.cpp b/src/d/d_menu_ring.cpp index f0f44db166..ed79d40524 100644 --- a/src/d/d_menu_ring.cpp +++ b/src/d/d_menu_ring.cpp @@ -333,11 +333,13 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i mpScreen->search(MULTI_CHAR('yx_te_s3'))->hide(); mpScreen->search(MULTI_CHAR('yx_te_s4'))->hide(); mpScreen->search(MULTI_CHAR('yx_text'))->hide(); + IF_DUSK_BLOCK(dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) mpScreen->search(MULTI_CHAR('fyx_te_1'))->hide(); mpScreen->search(MULTI_CHAR('fyx_te_2'))->hide(); mpScreen->search(MULTI_CHAR('fyx_te_3'))->hide(); mpScreen->search(MULTI_CHAR('fyx_te_4'))->hide(); mpScreen->search(MULTI_CHAR('fyx_tex'))->hide(); + IF_DUSK_BLOCK_END mpScreen->search(MULTI_CHAR('x_btn_n'))->hide(); mpScreen->search(MULTI_CHAR('y_btn_n'))->hide(); } @@ -345,9 +347,11 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i for (i = 0; i < 5; i++) { #if TARGET_PC J2DTextBox* fxy_TextBox; - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { fxy_TextBox = (J2DTextBox*)mpScreen->search(xy_text[i]); - mpScreen->search(fxy_text[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpScreen->search(fxy_text[i])->hide(); + } } else { fxy_TextBox = (J2DTextBox*)mpScreen->search(fxy_text[i]); mpScreen->search(xy_text[i])->hide(); @@ -366,13 +370,17 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i for (i = 0; i < 5; i++) { #if TARGET_PC J2DTextBox* fc_TextBox; - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { if (dusk::getSettings().game.swapDirectSelect) { fc_TextBox = (J2DTextBox*)mpScreen->search(c_text1[i]); - mpScreen->search(fc_text1[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpScreen->search(fc_text1[i])->hide(); + } } else { fc_TextBox = (J2DTextBox*)mpScreen->search(c_text[i]); - mpScreen->search(fc_text[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpScreen->search(fc_text[i])->hide(); + } } } else { if (dusk::getSettings().game.swapDirectSelect) { @@ -397,13 +405,17 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i for (i = 0; i < 5; i++) { #if TARGET_PC J2DTextBox* fc1_TextBox; - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { if (dusk::getSettings().game.swapDirectSelect) { fc1_TextBox = (J2DTextBox*)mpScreen->search(c_text[i]); - mpScreen->search(fc_text[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpScreen->search(fc_text[i])->hide(); + } } else { fc1_TextBox = (J2DTextBox*)mpScreen->search(c_text1[i]); - mpScreen->search(fc_text1[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpScreen->search(fc_text1[i])->hide(); + } } } else { if (dusk::getSettings().game.swapDirectSelect) { @@ -427,9 +439,11 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i } for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { mpComboOffString[i] = (J2DTextBox*)mpScreen->search(t_on[i]); - mpScreen->search(ft_on[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpScreen->search(ft_on[i])->hide(); + } } else { mpComboOffString[i] = (J2DTextBox*)mpScreen->search(ft_on[i]); mpScreen->search(t_on[i])->hide(); @@ -447,9 +461,11 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i } for (int i = 0; i < 5; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { mpBowArrowComboString[i] = (J2DTextBox*)mpScreen->search(t_off[i]); - mpScreen->search(ft_off[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpScreen->search(ft_off[i])->hide(); + } } else { mpBowArrowComboString[i] = (J2DTextBox*)mpScreen->search(ft_off[i]); mpScreen->search(t_off[i])->hide(); @@ -486,19 +502,21 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i mpCircle = JKR_NEW CPaneMgr(mpCenterScreen, MULTI_CHAR('circle_n'), 2, NULL); J2DTextBox* textBox[4]; #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { textBox[0] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n04')); textBox[1] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n05')); textBox[2] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n06')); textBox[3] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n07')); - J2DPane* pane = mpCenterScreen->search(MULTI_CHAR('fitem_n1')); - pane->mVisible = false; - pane = mpCenterScreen->search(MULTI_CHAR('fitem_n2')); - pane->mVisible = false; - pane = mpCenterScreen->search(MULTI_CHAR('fitem_n3')); - pane->mVisible = false; - pane = mpCenterScreen->search(MULTI_CHAR('fitem_n4')); - pane->mVisible = false; + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + J2DPane* pane = mpCenterScreen->search(MULTI_CHAR('fitem_n1')); + pane->mVisible = false; + pane = mpCenterScreen->search(MULTI_CHAR('fitem_n2')); + pane->mVisible = false; + pane = mpCenterScreen->search(MULTI_CHAR('fitem_n3')); + pane->mVisible = false; + pane = mpCenterScreen->search(MULTI_CHAR('fitem_n4')); + pane->mVisible = false; + } } else { textBox[0] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('fitem_n1')); textBox[1] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('fitem_n2')); @@ -1292,17 +1310,11 @@ void dMenu_Ring_c::setScale() { void dMenu_Ring_c::setNameString(u32 i_stringID) { J2DTextBox* textBox[4]; #if TARGET_PC - if (dusk::version::isRegionJpn()) { - textBox[0] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n04')); - textBox[1] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n05')); - textBox[2] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n06')); - textBox[3] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n07')); - } else { - textBox[0] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('fitem_n1')); - textBox[1] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('fitem_n2')); - textBox[2] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('fitem_n3')); - textBox[3] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('fitem_n4')); - } + const bool useDomestic = dusk::version::isJpnOrLessThanWiiJpn(); + textBox[0] = (J2DTextBox*)mpCenterScreen->search(useDomestic ? MULTI_CHAR('item_n04') : MULTI_CHAR('fitem_n1')); + textBox[1] = (J2DTextBox*)mpCenterScreen->search(useDomestic ? MULTI_CHAR('item_n05') : MULTI_CHAR('fitem_n2')); + textBox[2] = (J2DTextBox*)mpCenterScreen->search(useDomestic ? MULTI_CHAR('item_n06') : MULTI_CHAR('fitem_n3')); + textBox[3] = (J2DTextBox*)mpCenterScreen->search(useDomestic ? MULTI_CHAR('item_n07') : MULTI_CHAR('fitem_n4')); #elif VERSION == VERSION_GCN_JPN textBox[0] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n04')); textBox[1] = (J2DTextBox*)mpCenterScreen->search(MULTI_CHAR('item_n05')); diff --git a/src/d/d_meter2_draw.cpp b/src/d/d_meter2_draw.cpp index 09963e8cc7..3e5f55916e 100644 --- a/src/d/d_meter2_draw.cpp +++ b/src/d/d_meter2_draw.cpp @@ -1386,6 +1386,22 @@ void dMeter2Draw_c::initButtonCross() { dMeter2Info_getString( 0x62, static_cast(mpScreen->search(MULTI_CHAR('cont_ju9')))->getStringPtr(), NULL); +#if TARGET_PC + // These panes are not wide enough for French text (and possibly other languages) + // on Wii PAL. Resize them to always match their counterparts in later releases. + static u64 const juTags[] = { + MULTI_CHAR('cont_ju0'), MULTI_CHAR('cont_ju1'), MULTI_CHAR('cont_ju2'), + MULTI_CHAR('cont_ju3'), MULTI_CHAR('cont_ju4'), MULTI_CHAR('cont_ju5'), + MULTI_CHAR('cont_ju6'), MULTI_CHAR('cont_ju7'), MULTI_CHAR('cont_ju8'), + MULTI_CHAR('cont_ju9'), + }; + + for (u64 tag : juTags) { + J2DPane* pane = mpScreen->search(tag); + pane->resize(120.0f, pane->getHeight()); + } +#endif + mpButtonCrossParent->setAlphaRate(0.0f); drawButtonCross(g_drawHIO.mButtonCrossOFFPosX, g_drawHIO.mButtonCrossOFFPosY); } diff --git a/src/d/d_meter_button.cpp b/src/d/d_meter_button.cpp index 573147f888..56d756ef44 100644 --- a/src/d/d_meter_button.cpp +++ b/src/d/d_meter_button.cpp @@ -1208,9 +1208,11 @@ void dMeterButton_c::screenInitButton() { for (int i = 0; i < 10; i++) { #if TARGET_PC - if (dusk::version::isRegionJpn()) { + if (dusk::version::isJpnOrLessThanWiiJpn()) { mpTextBox[i] = (J2DTextBox*)mpButtonScreen->search(text_tag[i]); - mpButtonScreen->search(ftext_tag[i])->hide(); + if (dusk::version::getGameVersion() >= dusk::version::GameVersion::WiiJpn) { + mpButtonScreen->search(ftext_tag[i])->hide(); + } } else { mpTextBox[i] = (J2DTextBox*)mpButtonScreen->search(ftext_tag[i]); mpButtonScreen->search(text_tag[i])->hide(); diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index fc6b03d6df..f39467222f 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -30,6 +30,7 @@ #include "m_Do/m_Do_lib.h" #if TARGET_PC +#include "dusk/language.hpp" #include "dusk/menu_pointer.h" #include "dusk/settings.h" #include "dusk/version.hpp" @@ -1731,7 +1732,9 @@ void dMsgObject_c::readMessageGroupLocal(mDoDvdThd_mountXArchive_c** p_arcMount) #endif int msgGroup = dStage_stagInfo_GetMsgGroup(dComIfGp_getStage()->getStagInfo()); - #if REGION_PAL +#if TARGET_PC + snprintf(arcName, sizeof(arcName), "/res/%s/bmgres%d.arc", dusk::language::msg_folder(), msgGroup); +#elif REGION_PAL switch (dComIfGs_getPalLanguage()) { case dSv_player_config_c::LANGUAGE_GERMAN: sprintf(arcName, "/res/Msgde/bmgres%d.arc", msgGroup); @@ -1748,39 +1751,11 @@ void dMsgObject_c::readMessageGroupLocal(mDoDvdThd_mountXArchive_c** p_arcMount) default: sprintf(arcName, "/res/Msguk/bmgres%d.arc", msgGroup); } - #elif REGION_JPN +#elif REGION_JPN sprintf(arcName, "/res/Msgjp/bmgres%d.arc", msgGroup); - #else -#if TARGET_PC - // Original game UB - - if (dusk::version::isRegionPal()) { - switch (dComIfGs_getPalLanguage()) { - case dSv_player_config_c::LANGUAGE_GERMAN: - snprintf(arcName, sizeof(arcName), "/res/Msgde/bmgres%d.arc", msgGroup); - break; - case dSv_player_config_c::LANGUAGE_FRENCH: - snprintf(arcName, sizeof(arcName), "/res/Msgfr/bmgres%d.arc", msgGroup); - break; - case dSv_player_config_c::LANGUAGE_SPANISH: - snprintf(arcName, sizeof(arcName), "/res/Msgsp/bmgres%d.arc", msgGroup); - break; - case dSv_player_config_c::LANGUAGE_ITALIAN: - snprintf(arcName, sizeof(arcName), "/res/Msgit/bmgres%d.arc", msgGroup); - break; - default: - snprintf(arcName, sizeof(arcName), "/res/Msguk/bmgres%d.arc", msgGroup); - } - } else if (dusk::version::isRegionJpn()) { - snprintf(arcName, sizeof(arcName), "/res/Msgjp/bmgres%d.arc", msgGroup); - } else { - snprintf(arcName, sizeof(arcName), "/res/Msgus/bmgres%d.arc", msgGroup); - } - #else sprintf(arcName, "/res/Msgus/bmgres%d.arc", msgGroup); #endif - #endif *p_arcMount = mDoDvdThd_mountXArchive_c::create(arcName, 0, JKRArchive::MOUNT_MEM, NULL); diff --git a/src/d/d_s_logo.cpp b/src/d/d_s_logo.cpp index 366d87c02b..bd5b1f7783 100644 --- a/src/d/d_s_logo.cpp +++ b/src/d/d_s_logo.cpp @@ -24,6 +24,7 @@ #include "JSystem/JUtility/JUTConsole.h" #ifdef TARGET_PC +#include "dusk/language.hpp" #include "dusk/logging.h" #include "dusk/main.h" #include "dusk/mods/svc/save.hpp" @@ -53,8 +54,8 @@ struct homeBtnData { #if TARGET_PC using namespace dusk::version; -#define LOGO_ARC versionSelect({{GameVersion::GcnJpn, "Logo"}, {GameVersion::GcnPal, "LogoPal"}}, "LogoUs") -#define MSG_PATH versionSelect({{GameVersion::GcnJpn, "/res/Msgjp/bmgres.arc"}}, "/res/Msgus/bmgres.arc") +#define LOGO_ARC regionSelect("LogoUs", "LogoPal", "Logo") +#define MSG_PATH regionSelect("/res/Msgus/bmgres.arc", "/res/Msgus/bmgres.arc", "/res/Msgjp/bmgres.arc") #elif VERSION == VERSION_SHIELD #define LOGO_ARC "LogoUs" #define MSG_PATH "/res/Msgcn/bmgres.arc" @@ -77,7 +78,20 @@ using namespace dusk::version; #define PROGRESSIVE_MODE_ON OS_PROGRESSIVE_MODE_ON #endif -#if PLATFORM_WII || VERSION == VERSION_SHIELD_DEBUG +// TODO: Probably shouldn't actually load LayoutRevo, so I disabled it for now +#if TARGET_PC && 0 +using namespace dusk::version; + +#define FMAP_RES_PATH platformSelect("/res/Layout/fmapres.arc", "/res/LayoutRevo/fmapresR.arc") +#define DMAP_RES_PATH platformSelect("/res/Layout/dmapres.arc", "/res/LayoutRevo/dmapresR.arc") +#define COLLECT_RES_PATH platformSelect("/res/Layout/clctres.arc", "/res/LayoutRevo/clctresR.arc") + +#define MSG_COM_PATH platformSelect("/res/Layout/msgcom.arc", "/res/LayoutRevo/msgcomR.arc") +#define MSG_RES0_PATH platformSelect("/res/Layout/msgres00.arc", "/res/LayoutRevo/msgres00R.arc") +#define MSG_RES1_PATH platformSelect("/res/Layout/msgres01.arc", "/res/LayoutRevo/msgres01R.arc") +#define MSG_RES2_PATH platformSelect("/res/Layout/msgres02.arc", "/res/LayoutRevo/msgres02R.arc") +#define MSG_RES3_PATH platformSelect("/res/Layout/msgres03.arc", "/res/LayoutRevo/msgres03R.arc") +#elif PLATFORM_WII || VERSION == VERSION_SHIELD_DEBUG #define FMAP_RES_PATH "/res/LayoutRevo/fmapresR.arc" #define DMAP_RES_PATH "/res/LayoutRevo/dmapresR.arc" #define COLLECT_RES_PATH "/res/LayoutRevo/clctresR.arc" @@ -99,7 +113,10 @@ using namespace dusk::version; #define MSG_RES3_PATH "/res/Layout/msgres03.arc" #endif -#if PLATFORM_WII || PLATFORM_SHIELD +#if TARGET_PC +#define ICON_RES_PATH "/res/CardIcon/cardicon.arc" +#define PARTICLE_COM_PATH platformSelect("/res/Particle/common.jpc", "/res/Particle/common-r.jpc") +#elif PLATFORM_WII || PLATFORM_SHIELD #define ICON_RES_PATH "/res/WiiBannerIcon/bannerIcon.arc" #define PARTICLE_COM_PATH "/res/Particle/common-r.jpc" #else @@ -107,7 +124,13 @@ using namespace dusk::version; #define PARTICLE_COM_PATH "/res/Particle/common.jpc" #endif -#if PLATFORM_WII +// TODO: Probably shouldn't actually load LayoutRevo, so I disabled it for now +#if TARGET_PC && 0 +#define RING_RES_PATH platformSelect("/res/Layout/ringres.arc", "/res/LayoutRevo/ringresR.arc") +#define ITEM_INF_RES_PATH platformSelect("/res/Layout/itmInfRes.arc", "/res/LayoutRevo/itmInfResR.arc") +#define BUTTON_RES_PATH platformSelect("/res/Layout/button.arc", "/res/LayoutRevo/buttonR.arc") +#define MAIN2D_PATH platformSelect("/res/Layout/main2D.arc", "/res/LayoutRevo/main2DR.arc") +#elif PLATFORM_WII #define RING_RES_PATH "/res/LayoutRevo/ringresR.arc" #define ITEM_INF_RES_PATH "/res/LayoutRevo/itmInfResR.arc" #define BUTTON_RES_PATH "/res/LayoutRevo/buttonR.arc" @@ -862,7 +885,7 @@ dScnLogo_c::~dScnLogo_c() { JKR_DELETE(mProgressiveSel); #if TARGET_PC - if (getGameVersion() == GameVersion::GcnPal) { + if (isRegionPal()) { mpPalLogoResCommand->getArchive()->removeResourceAll(); mpPalLogoResCommand->getArchive()->unmount(); mpPalLogoResCommand->destroy(); @@ -974,7 +997,7 @@ dScnLogo_c::~dScnLogo_c() { mDoExt_setAraCacheSize(free_size - aram_heap->getTotalFreeSize()); #if TARGET_PC - if (getGameVersion() == GameVersion::GcnJpn) { + if (isRegionJpn()) { if (dComIfGp_getFontArchive() != NULL) { dComIfGp_getFontArchive()->unmount(); dComIfGp_setFontArchive(NULL); @@ -1037,7 +1060,7 @@ static int phase_0(dScnLogo_c* i_this) { JKRHEAP_NAME(i_this->mLogo01Heap, "Logo01"); #if TARGET_PC || VERSION == VERSION_GCN_PAL - IF_DUSK_BLOCK(getGameVersion() == GameVersion::GcnPal) + IF_DUSK_BLOCK(isRegionPal()) switch (i_this->getPalLanguage()) { case 1: i_this->mpPalLogoResCommand = mDoDvdThd_mountArchive_c::create("/res/Layout/LogoPalGm.arc", 0, NULL); @@ -1078,7 +1101,7 @@ static int phase_1(dScnLogo_c* i_this) { #endif #if TARGET_PC || VERSION == VERSION_GCN_PAL - IF_DUSK_BLOCK(getGameVersion() == GameVersion::GcnPal) + IF_DUSK_BLOCK(isRegionPal()) if (!mDoDvdThd::SyncWidthSound) { return cPhs_INIT_e; } @@ -1313,7 +1336,7 @@ void dScnLogo_c::logoInitGC() { mDolbyLogo = JKR_NEW dDlst_2D_c(dolbyImg, 189, 150, 232, 112, 255); #if TARGET_PC - if (getGameVersion() == GameVersion::GcnPal) { + if (isRegionPal()) { u8 language = getPalLanguage(); if (language >= 5) { language = 0; @@ -1576,30 +1599,15 @@ void dScnLogo_c::dvdDataLoad() { mpButtonCommand = aramMount(BUTTON_RES_PATH, mDoExt_getJ2dHeap()); mpCardIconCommand = aramMount(ICON_RES_PATH, mDoExt_getJ2dHeap()); - #if TARGET_PC - if (getGameVersion() == GameVersion::GcnPal) { - switch (getPalLanguage()) { - case 1: - mpBmgResCommand = onMemMount("/res/Msgde/bmgres.arc"); - break; - case 2: - mpBmgResCommand = onMemMount("/res/Msgfr/bmgres.arc"); - break; - case 3: - mpBmgResCommand = onMemMount("/res/Msgsp/bmgres.arc"); - break; - case 4: - mpBmgResCommand = onMemMount("/res/Msgit/bmgres.arc"); - break; - case 0: - default: - mpBmgResCommand = onMemMount("/res/Msguk/bmgres.arc"); - break; - } - } else { - mpBmgResCommand = onMemMount(MSG_PATH); - } - #elif VERSION == VERSION_GCN_PAL +#if TARGET_PC +#if AVOID_UB + static char bmgPath[32]; +#else + static char bmgPath[22]; +#endif + snprintf(bmgPath, sizeof(bmgPath), "/res/%s/bmgres.arc", dusk::language::msg_folder()); + mpBmgResCommand = onMemMount(bmgPath); +#elif VERSION == VERSION_GCN_PAL switch (getPalLanguage()) { case 1: mpBmgResCommand = onMemMount("/res/Msgde/bmgres.arc"); @@ -1618,7 +1626,7 @@ void dScnLogo_c::dvdDataLoad() { mpBmgResCommand = onMemMount("/res/Msguk/bmgres.arc"); break; } - #elif VERSION == VERSION_SHIELD_DEBUG +#elif VERSION == VERSION_SHIELD_DEBUG switch (getPalLanguage()) { case 2: mpBmgResCommand = onMemMount("/res/Msgfr/bmgres.arc"); @@ -1630,9 +1638,9 @@ void dScnLogo_c::dvdDataLoad() { mpBmgResCommand = onMemMount("/res/Msgus/bmgres.arc"); break; } - #else +#else mpBmgResCommand = onMemMount(MSG_PATH); - #endif +#endif mpMsgComCommand = aramMount(MSG_COM_PATH, mDoExt_getJ2dHeap()); mpMsgResCommand[0] = aramMount(MSG_RES0_PATH, mDoExt_getJ2dHeap()); @@ -1640,8 +1648,10 @@ void dScnLogo_c::dvdDataLoad() { mpMsgResCommand[2] = aramMount(MSG_RES2_PATH, mDoExt_getJ2dHeap()); mpMsgResCommand[3] = aramMount(MSG_RES3_PATH, mDoExt_getJ2dHeap()); #if TARGET_PC - const auto res4Path = versionSelect({{GameVersion::GcnJpn, "/res/Layout/msgres04.arc"}}, "/res/Layout/msgres04F.arc"); - mpMsgResCommand[4] = aramMount( res4Path, mDoExt_getJ2dHeap()); + const auto res4Path = regionSelect("/res/Layout/msgres04F.arc", + "/res/Layout/msgres04F.arc", + "/res/Layout/msgres04.arc"); + mpMsgResCommand[4] = aramMount(res4Path, mDoExt_getJ2dHeap()); #elif VERSION == VERSION_GCN_JPN mpMsgResCommand[4] = aramMount("/res/Layout/msgres04.arc", mDoExt_getJ2dHeap()); #else @@ -1653,16 +1663,12 @@ void dScnLogo_c::dvdDataLoad() { mpMain2DCommand = onMemMount(MAIN2D_PATH); #if TARGET_PC - const auto fontResPath = versionSelect( - { - {GameVersion::GcnJpn, "/res/Fontjp/fontres.arc"}, - {GameVersion::GcnPal, "/res/Fonteu/fontres.arc"}, - }, "/res/Fontus/fontres.arc"); - const auto fontRubyPath = versionSelect( - { - {GameVersion::GcnJpn, "/res/Fontjp/rubyres.arc"}, - {GameVersion::GcnPal, "/res/Fonteu/rubyres.arc"}, - }, "/res/Fontus/rubyres.arc"); + const auto fontResPath = regionSelect("/res/Fontus/fontres.arc", + "/res/Fonteu/fontres.arc", + "/res/Fontjp/fontres.arc"); + const auto fontRubyPath = regionSelect("/res/Fontus/rubyres.arc", + "/res/Fonteu/rubyres.arc", + "/res/Fontjp/rubyres.arc"); // Note: GCN_JPN mounts this archive as tail instead of head. // I'm guessing this is fine since we have more RAM. diff --git a/src/d/d_save.cpp b/src/d/d_save.cpp index d634c7b21d..e638d23ab1 100644 --- a/src/d/d_save.cpp +++ b/src/d/d_save.cpp @@ -1086,7 +1086,7 @@ void dSv_player_config_c::setVibration(u8 i_status) { u8 dSv_player_config_c::getPalLanguage() const { #if TARGET_PC || VERSION == VERSION_GCN_PAL - IF_DUSK_BLOCK(dusk::version::getGameVersion() == dusk::version::GameVersion::GcnPal) + IF_DUSK_BLOCK(dusk::version::isRegionPal()) switch (OSGetLanguage()) { case 0: return LANGUAGE_ENGLISH; diff --git a/src/dusk/dvd_asset.hpp b/src/dusk/dvd_asset.hpp index 8594d4631c..d1ce5f3e39 100644 --- a/src/dusk/dvd_asset.hpp +++ b/src/dusk/dvd_asset.hpp @@ -23,9 +23,20 @@ bool LoadDolAsset(void* dst, std::initializer_list virtualAddress */ bool LoadRelAsset(void* dst, const char* dvdPath, std::initializer_list offset, s32 size); +template +bool LoadRelAsset(T (&dst)[N], const char* dvdPath, std::initializer_list offset) { + return LoadRelAsset(static_cast(dst), dvdPath, offset, static_cast(sizeof(dst))); +} + /** * Load bytes from a REL inside RELS.arc */ bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, std::initializer_list offset, s32 size); +template +bool LoadArchivedRelAsset(T (&dst)[N], u32 memType, const char* relFileName, + std::initializer_list offset) { + return LoadArchivedRelAsset(static_cast(dst), memType, relFileName, offset, static_cast(sizeof(dst))); +} + } // namespace dusk diff --git a/src/dusk/iso_validate.cpp b/src/dusk/iso_validate.cpp index 380c1860a1..26fdef11e9 100644 --- a/src/dusk/iso_validate.cpp +++ b/src/dusk/iso_validate.cpp @@ -40,10 +40,25 @@ constexpr auto AcceptedDiscs = std::to_array({ .gameId = "GZ2P01", .expectedHash = borealis::disc::parse_xxh3_128("9ef597588b0035ca9e91b333fa9a8a7e"), }, + { + .gameId = "RZDE01", .revision = 0, + .expectedHash = borealis::disc::parse_xxh3_128("b3d91fbea59e5c66934d04c01566728e"), + }, + { + .gameId = "RZDE01", .revision = 2, + .expectedHash = borealis::disc::parse_xxh3_128("c3ec420921a1b36d6ae43f576491d25c"), + }, + { + .gameId = "RZDJ01", + .expectedHash = borealis::disc::parse_xxh3_128("d3866821c7fc6999e6e8bbef8b6875aa"), + }, + { + .gameId = "RZDP01", + .expectedHash = borealis::disc::parse_xxh3_128("6095a924a57e5fb4294ac96fb85a09a1"), + }, }); -constexpr auto RecognizedGameIds = - std::to_array({"RZDE01", "RZDJ01", "RZDK01", "RZDP01"}); +constexpr auto RecognizedGameIds = std::to_array({"RZDK01"}); constexpr borealis::disc::Catalog DiscCatalog{ .acceptedDiscs = AcceptedDiscs, @@ -92,6 +107,7 @@ void update_info(const borealis::disc::Result& result, DiscInfo& info) noexcept if (!result.metadata.gameId.empty()) { info.platform = result.metadata.platform; info.region = region_from_game_id(result.metadata.gameId); + info.revision = result.metadata.revision; } } diff --git a/src/dusk/iso_validate.hpp b/src/dusk/iso_validate.hpp index b9267a6a64..7b376815bd 100644 --- a/src/dusk/iso_validate.hpp +++ b/src/dusk/iso_validate.hpp @@ -37,6 +37,7 @@ using VerificationStatus = borealis::disc::Progress; struct DiscInfo { Platform platform = Platform::Unknown; Region region = Region::NorthAmerica; + std::uint8_t revision = 0; }; ValidationError inspect(const char* path, DiscInfo& info); diff --git a/src/dusk/language.cpp b/src/dusk/language.cpp new file mode 100644 index 0000000000..62d30df816 --- /dev/null +++ b/src/dusk/language.cpp @@ -0,0 +1,79 @@ +#include "dusk/language.hpp" + +#include "dusk/version.hpp" + +namespace dusk::language { +namespace { + +constexpr GameLanguage kEnglishOnly[] = {GameLanguage::English}; +constexpr GameLanguage kJapaneseOnly[] = {GameLanguage::Japanese}; +constexpr GameLanguage kPalLanguages[] = { + GameLanguage::English, + GameLanguage::German, + GameLanguage::French, + GameLanguage::Spanish, + GameLanguage::Italian, +}; +constexpr GameLanguage kWiiUsaLanguages[] = { + GameLanguage::English, + GameLanguage::French, + GameLanguage::Spanish, +}; + +} // namespace + +std::span available_languages(const iso::DiscInfo& info) noexcept { + switch (info.region) { + case iso::Region::Japan: + return kJapaneseOnly; + case iso::Region::Europe: + return kPalLanguages; + case iso::Region::NorthAmerica: + if (info.platform == iso::Platform::Wii && info.revision == 2) { + return kWiiUsaLanguages; + } + return kEnglishOnly; + default: + return kEnglishOnly; + } +} + +const char* language_name(GameLanguage language) noexcept { + switch (language) { + case GameLanguage::English: + return "English"; + case GameLanguage::German: + return "German"; + case GameLanguage::French: + return "French"; + case GameLanguage::Spanish: + return "Spanish"; + case GameLanguage::Italian: + return "Italian"; + case GameLanguage::Japanese: + return "Japanese"; + } + return "English"; +} + +const char* msg_folder() noexcept { + using namespace version; + + switch (getSettings().game.language.getValue()) { + case GameLanguage::German: + return "Msgde"; + case GameLanguage::French: + return "Msgfr"; + case GameLanguage::Spanish: + return "Msgsp"; + case GameLanguage::Italian: + return "Msgit"; + case GameLanguage::Japanese: + return "Msgjp"; + case GameLanguage::English: + default: + return isRegionPal() ? "Msguk" : "Msgus"; + } +} + +} // namespace dusk::language diff --git a/src/dusk/language.hpp b/src/dusk/language.hpp new file mode 100644 index 0000000000..3da488b538 --- /dev/null +++ b/src/dusk/language.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "dusk/iso_validate.hpp" +#include "dusk/settings.h" + +#include + +namespace dusk::language { + +std::span available_languages(const iso::DiscInfo& info) noexcept; + +const char* language_name(GameLanguage language) noexcept; +const char* msg_folder() noexcept; + +} // namespace dusk::language diff --git a/src/dusk/settings.h b/src/dusk/settings.h index 914ef37efc..3d29c0ec02 100644 --- a/src/dusk/settings.h +++ b/src/dusk/settings.h @@ -33,6 +33,7 @@ enum class GameLanguage : u8 { French = OS_LANGUAGE_FRENCH, Spanish = OS_LANGUAGE_SPANISH, Italian = OS_LANGUAGE_ITALIAN, + Japanese = 6, }; enum class DiscVerificationState : u8 { @@ -89,7 +90,7 @@ struct ConfigEnumRange { template <> struct ConfigEnumRange { static constexpr auto min = GameLanguage::English; - static constexpr auto max = GameLanguage::Italian; + static constexpr auto max = GameLanguage::Japanese; }; template <> diff --git a/src/dusk/ui/prelaunch.cpp b/src/dusk/ui/prelaunch.cpp index 487e40ab48..d392242140 100644 --- a/src/dusk/ui/prelaunch.cpp +++ b/src/dusk/ui/prelaunch.cpp @@ -4,6 +4,7 @@ #include "dusk/config.hpp" #include "dusk/data.hpp" #include "dusk/iso_validate.hpp" +#include "dusk/language.hpp" #include "dusk/main.h" #include "dusk/settings.h" #include "modal.hpp" @@ -291,7 +292,7 @@ std::string get_error_msg(iso::ValidationError error) { case iso::ValidationError::WrongGame: return "The selected game is not supported by Dusklight."; case iso::ValidationError::WrongVersion: - return "Dusklight currently supports GameCube USA and PAL disc images only."; + return "Dusklight does not currently support the Wii's Korean version."; case iso::ValidationError::Canceled: return "Disc verification was canceled. Dusklight cannot guarantee the selected disc " "image is compatible."; @@ -317,6 +318,24 @@ void persist_disc_choice(const std::string& path, iso::ValidationError validatio } } +void apply_language_for_disc(const iso::DiscInfo& info) { + const auto langs = language::available_languages(info); + auto& language = getSettings().game.language; + const GameLanguage previous = language.getValue(); + if (std::ranges::find(langs, previous) != langs.end()) { + return; + } + + const GameLanguage fallback = langs.front(); + language.setValue(fallback); + config::save(); + + auto& state = prelaunch_state(); + state.initialLanguage = fallback; + state.unavailableLanguage = previous; + state.pendingLanguageUnavailableNotice = true; +} + void apply_valid_disc_result( const std::string& path, const iso::DiscInfo& info, iso::ValidationError validation) { auto& state = prelaunch_state(); @@ -329,6 +348,7 @@ void apply_valid_disc_result( state.activeDiscInfo = info; } persist_disc_choice(path, validation); + apply_language_for_disc(info); } void apply_disc_verification_result(const DiscVerificationResult& result) { @@ -548,6 +568,7 @@ void refresh_configured_disc_state() noexcept { if (state.configuredDiscPath == state.activeDiscPath) { state.activeDiscInfo = info; } + apply_language_for_disc(info); return; } @@ -632,6 +653,36 @@ void try_push_verification_modal(Document& host) { })); } +void try_push_language_unavailable_modal(Document& host) { + auto& state = prelaunch_state(); + + if (!state.pendingLanguageUnavailableNotice) { + return; + } + state.pendingLanguageUnavailableNotice = false; + + const Rml::String bodyRml = fmt::format( + "{} is not available on this disc. Language has been reset to {}.", + language::language_name(state.unavailableLanguage), + language::language_name(getSettings().game.language.getValue())); + + auto dismiss = [](Modal& modal) { modal.pop(); }; + + host.push(std::make_unique(Modal::Props{ + .title = "Language unavailable", + .bodyRml = bodyRml, + .actions = + { + ModalAction{ + .label = "OK", + .onPressed = dismiss, + }, + }, + .onDismiss = dismiss, + .icon = "warning", + })); +} + void ensure_initialized() noexcept { auto& state = prelaunch_state(); if (state.initialized) { @@ -844,6 +895,7 @@ void Prelaunch::update() { if (top_document() == this) { try_push_verification_modal(*this); + try_push_language_unavailable_modal(*this); } const auto& state = prelaunch_state(); @@ -917,6 +969,9 @@ void Prelaunch::update() { break; case iso::Region::NorthAmerica: innerRML += "USA"; + if (state.activeDiscInfo.platform == iso::Platform::Wii) { + innerRML += fmt::format(" Rev. {}", state.activeDiscInfo.revision); + } break; case iso::Region::Korea: innerRML += "KOR"; diff --git a/src/dusk/ui/prelaunch.hpp b/src/dusk/ui/prelaunch.hpp index a5be868f36..45d158912a 100644 --- a/src/dusk/ui/prelaunch.hpp +++ b/src/dusk/ui/prelaunch.hpp @@ -49,6 +49,8 @@ struct PrelaunchState { std::string activeDiscPath; iso::DiscInfo activeDiscInfo{}; GameLanguage initialLanguage = GameLanguage::English; + GameLanguage unavailableLanguage = GameLanguage::English; + bool pendingLanguageUnavailableNotice = false; std::string initialGraphicsBackend; int initialCardFileType = 0; std::string errorString; @@ -63,5 +65,6 @@ void refresh_configured_disc_state() noexcept; void open_iso_picker() noexcept; bool is_restart_pending() noexcept; void try_push_verification_modal(Document& host); +void try_push_language_unavailable_modal(Document& host); } // namespace dusk::ui diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 7aa70ea860..423909f821 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -11,6 +11,7 @@ #include "dusk/data.hpp" #include "dusk/imgui/ImGuiEngine.hpp" #include "dusk/io.hpp" +#include "dusk/language.hpp" #include "dusk/presentation.hpp" #include #include @@ -53,14 +54,6 @@ namespace dusk::ui { namespace { -constexpr std::array kLanguageNames = { - "English", - "German", - "French", - "Spanish", - "Italian", -}; - constexpr std::array kCardFileTypes = { "Card Image", "GCI Folder", @@ -574,18 +567,15 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { .key = "Language", .getValue = [] { - const auto& state = prelaunch_state(); - if (!state.configuredDiscCanLaunch || state.configuredDiscInfo.region != iso::Region::Europe) { - return kLanguageNames[0]; - } - const u8 idx = static_cast(getSettings().game.language.getValue()); - return kLanguageNames[idx]; + return language::language_name(getSettings().game.language.getValue()); }, .isDisabled = [] { const auto& state = prelaunch_state(); - return !state.configuredDiscCanLaunch || - state.configuredDiscInfo.region != iso::Region::Europe; + if (!state.configuredDiscCanLaunch) { + return true; + } + return language::available_languages(state.configuredDiscInfo).size() <= 1; }, .isModified = [] { @@ -594,18 +584,22 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { }, }), rightPane, [](Pane& pane) { - for (int i = 0; i < kLanguageNames.size(); i++) { + const auto& state = prelaunch_state(); + const auto languages = state.configuredDiscCanLaunch + ? language::available_languages(state.configuredDiscInfo) + : language::available_languages({}); + for (const GameLanguage language : languages) { pane.add_button({ - .text = kLanguageNames[i], + .text = language::language_name(language), .isSelected = - [i] { + [language] { return getSettings().game.language.getValue() == - static_cast(i); + language; }, }) - .on_pressed([i] { + .on_pressed([language] { mDoAud_seStartMenu(kSoundItemChange); - getSettings().game.language.setValue(static_cast(i)); + getSettings().game.language.setValue(language); config::save(); }); } @@ -1551,6 +1545,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { void SettingsWindow::update() { if (mPrelaunch && top_document() == this) { try_push_verification_modal(*this); + try_push_language_unavailable_modal(*this); } Window::update(); diff --git a/src/dusk/version.cpp b/src/dusk/version.cpp index 1655031cdb..5d0856aed1 100644 --- a/src/dusk/version.cpp +++ b/src/dusk/version.cpp @@ -26,10 +26,18 @@ void init() { if (game == "GZ2E"sv) { gameVersion = GameVersion::GcnUsa; - } else if (game == "GZ2P") { + } else if (game == "GZ2P"sv) { gameVersion = GameVersion::GcnPal; - } else if (game == "GZ2J") { + } else if (game == "GZ2J"sv) { gameVersion = GameVersion::GcnJpn; + } else if (game == "RZDE"sv && diskId.gameVersion == 0) { + gameVersion = GameVersion::WiiUsaRev0; + } else if (game == "RZDE"sv && diskId.gameVersion == 2) { + gameVersion = GameVersion::WiiUsa; + } else if (game == "RZDP"sv) { + gameVersion = GameVersion::WiiPal; + } else if (game == "RZDJ"sv) { + gameVersion = GameVersion::WiiJpn; } else { // TODO: Handle remaining valid versions. DuskLog.fatal("Unknown/unsupported game version in disc: {}", game); @@ -52,8 +60,12 @@ bool isWii() { || getGameVersion() == GameVersion::WiiKor; } +bool isJpnOrLessThanWiiJpn() { + return isRegionJpn() || getGameVersion() < GameVersion::WiiJpn; +} + bool isPalOrAtLeastWiiR2() { - return getGameVersion() == GameVersion::GcnPal || getGameVersion() >= GameVersion::WiiUsa; + return isRegionPal() || (isWii() && getGameVersion() != GameVersion::WiiUsaRev0); } bool isRegionJpn() { diff --git a/src/dusk/version.hpp b/src/dusk/version.hpp index 4fd6395059..59e2895f77 100644 --- a/src/dusk/version.hpp +++ b/src/dusk/version.hpp @@ -5,18 +5,19 @@ */ namespace dusk::version { enum class GameVersion : u8 { - GcnUsa = VERSION_GCN_USA, - GcnPal = VERSION_GCN_PAL, - GcnJpn = VERSION_GCN_JPN, - WiiUsaRev0 = VERSION_WII_USA_R0, - WiiUsa = VERSION_WII_USA_R2, - WiiPal = VERSION_WII_PAL, - WiiJpn = VERSION_WII_JPN, - WiiKor = VERSION_WII_KOR, + WiiUsaRev0, + WiiPal, + WiiJpn, + GcnUsa, + GcnPal, + GcnJpn, + WiiUsa, + WiiKor, }; bool isGcn(); bool isWii(); + bool isJpnOrLessThanWiiJpn(); bool isPalOrAtLeastWiiR2(); bool isRegionPal(); @@ -61,4 +62,14 @@ namespace dusk::version { return defaultValue; } + + template + T platformSelect(const T& gcn, const T& wii) { + return isGcn() ? gcn : wii; + } + + template + T regionSelect(const T& usa, const T& pal, const T& jpn) { + return isRegionUsa() ? usa : isRegionPal() ? pal : jpn; + } } // namespace dusk::version diff --git a/src/m_Do/m_Do_ext.cpp b/src/m_Do/m_Do_ext.cpp index 381061e079..2d7cc86bbd 100644 --- a/src/m_Do/m_Do_ext.cpp +++ b/src/m_Do/m_Do_ext.cpp @@ -3713,7 +3713,7 @@ static ResFONT* mDoExt_resfont0; static void mDoExt_initFont0() { static char const fontdata[] = "rodan_b_24_22.bfn"; #if TARGET_PC - if (dusk::version::getGameVersion() == dusk::version::GameVersion::GcnJpn) { + if (dusk::version::isRegionJpn()) { mDoExt_initFontCommon(&mDoExt_font0, &mDoExt_resfont0, mDoExt_getZeldaHeap(), fontdata, dComIfGp_getFontArchive(), 0, 200, 512); } else { @@ -3748,7 +3748,7 @@ void mDoExt_removeMesgFont() { mDoExt_font0 = NULL; if (mDoExt_resfont0 != NULL) { #if TARGET_PC - if (dusk::version::getGameVersion() == dusk::version::GameVersion::GcnJpn) { + if (dusk::version::isRegionJpn()) { JKRFileLoader::removeResource(mDoExt_resfont0, NULL); } else { JKRFree(mDoExt_resfont0); From c880d46fb57ec81f9380422728b3f7d7ba56a4ef Mon Sep 17 00:00:00 2001 From: Irastris Date: Sat, 15 Aug 2026 13:58:24 -0400 Subject: [PATCH 15/15] d_menu_option bugfix (#2294) --- src/d/d_menu_option.cpp | 211 ++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 118 deletions(-) diff --git a/src/d/d_menu_option.cpp b/src/d/d_menu_option.cpp index 3845acb436..9d6f0816e2 100644 --- a/src/d/d_menu_option.cpp +++ b/src/d/d_menu_option.cpp @@ -73,6 +73,26 @@ static calibrationFunc calibration_process[] = { static dusk::menu_pointer::TargetId option_yes_no_target(u8 index) noexcept { return static_cast(0x100 + index); } + +static u8 option_to_proc(u8 select) noexcept { + if (!dusk::version::isRegionJpn() && select >= 1) { + return static_cast(select + 1); + } + return select; +} + +static u8 option_to_select(u8 proc) noexcept { + if (!dusk::version::isRegionJpn() && proc >= 2) { + return static_cast(proc - 1); + } + return proc; +} + +#define OPTION_SELECT(proc_e) (option_to_select(proc_e)) +#define OPTION_PROC(select) (option_to_proc(select)) +#else +#define OPTION_SELECT(proc_e) (proc_e) +#define OPTION_PROC(select) (select) #endif dMenu_Option_c::dMenu_Option_c(JKRArchive* i_archive, STControl* i_stick) { @@ -259,11 +279,11 @@ void dMenu_Option_c::_create() { field_0x3e1 = 10; field_0x3e2 = 0xff; field_0x3e3 = 0xc0; - field_0x3ef = PROC_ATTEN_e; + field_0x3ef = OPTION_SELECT(PROC_ATTEN_e); field_0x3f0 = 0xff; field_0x3f1 = 0xff; field_0x3f2 = 0; - field_0x3f5 = PROC_ATTEN_e; + field_0x3f5 = OPTION_SELECT(PROC_ATTEN_e); field_0x3f3 = 5; field_0x3f4 = 5; field_0x334 = 0.0f; @@ -487,9 +507,9 @@ void dMenu_Option_c::_move() { } if (mDoGph_gInf_c::getFader()->getStatus() == 1) { - if (mDoCPd_c::getTrigA(PAD_1) != 0 && field_0x3ef != PROC_CHANGE_MOVE_e && field_0x3f3 == 5) { - if (field_0x3f4 == 5 && field_0x3ef != PROC_CONFIRM_OPEN_MOVE_e && field_0x3ef != PROC_CONFIRM_MOVE_MOVE_e && field_0x3ef != PROC_CONFIRM_SELECT_MOVE_e && - field_0x3ef != PROC_CONFIRM_CLOSE_MOVE_e) + if (mDoCPd_c::getTrigA(PAD_1) != 0 && field_0x3ef != OPTION_SELECT(PROC_CHANGE_MOVE_e) && field_0x3f3 == 5) { + if (field_0x3f4 == 5 && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_MOVE_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_SELECT_MOVE_e) && + field_0x3ef != OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e)) { if (mDoCPd_c::getTrigStart(PAD_1) == 0 && mDoCPd_c::getTrigB(PAD_1) == 0) { if (mDoCPd_c::getTrigUp(PAD_1) == 0 && mDoCPd_c::getTrigDown(PAD_1) == 0 && @@ -497,17 +517,17 @@ void dMenu_Option_c::_move() { { field_0x3f7 = 1; field_0x3f5 = field_0x3ef; - field_0x3ef = PROC_CONFIRM_OPEN_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e); dMeter2Info_set2DVibration(); - (this->*init[field_0x3ef])(); + (this->*init[OPTION_PROC(field_0x3ef)])(); goto skip; } } } } - if (mDoCPd_c::getTrigB(PAD_1) != 0 && field_0x3ef != PROC_CHANGE_MOVE_e && field_0x3f3 == 5 && - field_0x3ef != PROC_CONFIRM_OPEN_MOVE_e && field_0x3ef != PROC_CONFIRM_MOVE_MOVE_e && field_0x3ef != PROC_CONFIRM_SELECT_MOVE_e && field_0x3ef != PROC_CONFIRM_CLOSE_MOVE_e) + if (mDoCPd_c::getTrigB(PAD_1) != 0 && field_0x3ef != OPTION_SELECT(PROC_CHANGE_MOVE_e) && field_0x3f3 == 5 && + field_0x3ef != OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_MOVE_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_SELECT_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e)) { if (field_0x3f4 == 5 && mDoCPd_c::getTrigStart(PAD_1) == 0 && mDoCPd_c::getTrigA(PAD_1) == 0 && mDoCPd_c::getTrigUp(PAD_1) == 0 && @@ -516,16 +536,16 @@ void dMenu_Option_c::_move() { { field_0x3f7 = 0; field_0x3f5 = field_0x3ef; - field_0x3ef = PROC_CONFIRM_OPEN_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e); dMeter2Info_set2DVibration(); - (this->*init[field_0x3ef])(); + (this->*init[OPTION_PROC(field_0x3ef)])(); } } #if TARGET_PC - if (field_0x3f4 == 5 && field_0x3ef != PROC_CHANGE_MOVE_e && field_0x3f3 == 5 && - field_0x3ef != PROC_CONFIRM_OPEN_MOVE_e && field_0x3ef != PROC_CONFIRM_MOVE_MOVE_e && field_0x3ef != PROC_CONFIRM_SELECT_MOVE_e && - field_0x3ef != PROC_CONFIRM_CLOSE_MOVE_e && pointerConfirmSelect()) + if (field_0x3f4 == 5 && field_0x3ef != OPTION_SELECT(PROC_CHANGE_MOVE_e) && field_0x3f3 == 5 && + field_0x3ef != OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_MOVE_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_SELECT_MOVE_e) && + field_0x3ef != OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e) && pointerConfirmSelect()) { goto skip; } @@ -533,7 +553,7 @@ void dMenu_Option_c::_move() { } skip: u8 oldValue = field_0x3ef; - if (field_0x3f3 == 5 && oldValue != PROC_CONFIRM_OPEN_MOVE_e && oldValue != PROC_CONFIRM_MOVE_MOVE_e && oldValue != PROC_CONFIRM_SELECT_MOVE_e && oldValue != PROC_CONFIRM_CLOSE_MOVE_e) { + if (field_0x3f3 == 5 && oldValue != OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e) && oldValue != OPTION_SELECT(PROC_CONFIRM_MOVE_MOVE_e) && oldValue != OPTION_SELECT(PROC_CONFIRM_SELECT_MOVE_e) && oldValue != OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e)) { dpdMenuMove(); } @@ -547,10 +567,10 @@ skip: field_0x3f0 = 0xff; } - (this->*process[field_0x3ef])(); + (this->*process[OPTION_PROC(field_0x3ef)])(); mpSelectScreen->animation(); if (oldValue != field_0x3ef) { - (this->*init[field_0x3ef])(); + (this->*init[OPTION_PROC(field_0x3ef)])(); } setHIO(false); @@ -612,8 +632,8 @@ void dMenu_Option_c::drawHaihai() { field_0x3f6 = 0; field_0x3f6 |= 1; field_0x3f6 |= 4; - if (selectType < PROC_CONFIRM_OPEN_MOVE_e && field_0x3f6 != 0 && field_0x3f3 == 5 && field_0x3ef != PROC_CONFIRM_OPEN_MOVE_e && - field_0x3ef != PROC_CONFIRM_MOVE_MOVE_e && field_0x3ef != PROC_CONFIRM_SELECT_MOVE_e && field_0x3ef != PROC_CONFIRM_CLOSE_MOVE_e) + if (selectType < OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e) && field_0x3f6 != 0 && field_0x3f3 == 5 && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e) && + field_0x3ef != OPTION_SELECT(PROC_CONFIRM_MOVE_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_SELECT_MOVE_e) && field_0x3ef != OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e)) { mpMeterHaihai->_execute(0); Vec haihaiPosL = @@ -754,13 +774,7 @@ void dMenu_Option_c::atten_move() { if (field_0x3f3 != 5) { (this->*tv_process[field_0x3f3])(); } else if (downTrigger) { -#if TARGET_PC - field_0x3ef = dusk::version::isRegionJpn() ? PROC_RUBY_e : PROC_VIB_e; -#elif VERSION == VERSION_GCN_JPN - field_0x3ef = PROC_RUBY_e; -#else - field_0x3ef = PROC_VIB_e; -#endif + field_0x3ef = 1; Z2GetAudioMgr()->seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (leftTrigger) { if (field_0x3e4 == 0) { @@ -770,8 +784,8 @@ void dMenu_Option_c::atten_move() { field_0x3e4 = 0; field_0x3da = -5; } - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_ATTEN_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_ATTEN_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (rightTrigger) { if (field_0x3e4 == 0) { @@ -781,8 +795,8 @@ void dMenu_Option_c::atten_move() { field_0x3e4 = 0; field_0x3da = 5; } - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_ATTEN_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_ATTEN_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else { changeTVCheck(); @@ -820,10 +834,10 @@ void dMenu_Option_c::ruby_move() { if (field_0x3f3 != 5) { (this->*tv_process[field_0x3f3])(); } else if (upTrigger) { - field_0x3ef = PROC_ATTEN_e; + field_0x3ef = OPTION_SELECT(PROC_ATTEN_e); Z2GetAudioMgr()->seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (downTrigger) { - field_0x3ef = PROC_VIB_e; + field_0x3ef = OPTION_SELECT(PROC_VIB_e); Z2GetAudioMgr()->seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (leftTrigger) { if (field_0x3e5_JPN == 0) { @@ -833,8 +847,8 @@ void dMenu_Option_c::ruby_move() { field_0x3e5_JPN = 0; field_0x3da = -5; } - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_RUBY_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_RUBY_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (rightTrigger) { if (field_0x3e5_JPN == 0) { @@ -844,8 +858,8 @@ void dMenu_Option_c::ruby_move() { field_0x3e5_JPN = 0; field_0x3da = 5; } - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_RUBY_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_RUBY_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else { changeTVCheck(); @@ -855,7 +869,7 @@ void dMenu_Option_c::ruby_move() { void dMenu_Option_c::vib_init() { mpDrawCursor->setAlphaRate(1.0f); - setCursorPos(PROC_VIB_e); + setCursorPos(OPTION_SELECT(PROC_VIB_e)); setAButtonString(0x40C); setBButtonString(0x3F9); } @@ -870,7 +884,7 @@ void dMenu_Option_c::vib_move() { (this->*tv_process[field_0x3f3])(); } else if (upTrigger) { #if TARGET_PC - field_0x3ef = dusk::version::isRegionJpn() ? PROC_RUBY_e : PROC_ATTEN_e; + field_0x3ef = OPTION_SELECT(dusk::version::isRegionJpn() ? PROC_RUBY_e : PROC_ATTEN_e); #elif VERSION == VERSION_GCN_JPN field_0x3ef = PROC_RUBY_e; #else @@ -878,7 +892,7 @@ void dMenu_Option_c::vib_move() { #endif Z2GetAudioMgr()->seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (downTrigger) { - field_0x3ef = PROC_SOUND_e; + field_0x3ef = OPTION_SELECT(PROC_SOUND_e); Z2GetAudioMgr()->seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (leftTrigger) { if (isRumbleSupported()) { @@ -890,8 +904,8 @@ void dMenu_Option_c::vib_move() { field_0x3ea = 0; field_0x3da = -5; } - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_VIB_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_VIB_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } @@ -905,8 +919,8 @@ void dMenu_Option_c::vib_move() { field_0x3ea = 0; field_0x3da = 5; } - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_VIB_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_VIB_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } @@ -917,7 +931,7 @@ void dMenu_Option_c::vib_move() { void dMenu_Option_c::sound_init() { mpDrawCursor->setAlphaRate(1.0f); - setCursorPos(PROC_SOUND_e); + setCursorPos(OPTION_SELECT(PROC_SOUND_e)); setAButtonString(0x40C); setBButtonString(0x3F9); } @@ -931,7 +945,7 @@ void dMenu_Option_c::sound_move() { if (field_0x3f3 != 5) { (this->*tv_process[field_0x3f3])(); } else if (upTrigger) { - field_0x3ef = PROC_VIB_e; + field_0x3ef = OPTION_SELECT(PROC_VIB_e); Z2GetAudioMgr()->seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (leftTrigger) { if (field_0x3e9 == 2) { @@ -956,8 +970,8 @@ void dMenu_Option_c::sound_move() { } mDoAud_setOutputMode(dMo_soundMode[field_0x3e9]); setSoundMode(dMo_soundMode[field_0x3e9]); - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_SOUND_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_SOUND_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else if (rightTrigger) { if (field_0x3e9 == 0) { @@ -982,8 +996,8 @@ void dMenu_Option_c::sound_move() { } mDoAud_setOutputMode(dMo_soundMode[field_0x3e9]); setSoundMode(dMo_soundMode[field_0x3e9]); - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_SOUND_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_SOUND_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } else { changeTVCheck(); @@ -1004,15 +1018,15 @@ void dMenu_Option_c::change_move() { field_0x3da++; } u8 index; - switch (field_0x3f5) { + switch (OPTION_PROC(field_0x3f5)) { case PROC_ATTEN_e: - index = PROC_ATTEN_e; + index = field_0x3f5; if (field_0x3da == 0) { setAttenString(); } break; case PROC_RUBY_e: - index = PROC_RUBY_e; + index = field_0x3f5; if (field_0x3da == 0) { IF_DUSK_BLOCK(dusk::version::isRegionJpn()) setRubyString(); @@ -1020,13 +1034,13 @@ void dMenu_Option_c::change_move() { } break; case PROC_VIB_e: - index = DUSK_IF_ELSE(dusk::version::isRegionJpn() ? 2 : 1, PROC_VIB_e); + index = field_0x3f5; if (field_0x3da == 0) { setVibString(); } break; case PROC_SOUND_e: - index = DUSK_IF_ELSE(dusk::version::isRegionJpn() ? 3 : 2, PROC_SOUND_e); + index = field_0x3f5; if (field_0x3da == 0) { setSoundString(); } @@ -1092,7 +1106,7 @@ void dMenu_Option_c::confirm_open_move() { } if (status == 1 && yesNoMenuMove == 1 && field_0x374 == 1.0f) { yesnoCursorShow(); - field_0x3ef = PROC_CONFIRM_MOVE_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_MOVE_MOVE_e); } mpWarning->_move(); setAnimation(); @@ -1122,21 +1136,21 @@ void dMenu_Option_c::confirm_move_move() { field_0x3f9 = i; if (clicked) { yesNoSelectStart(); - field_0x3ef = PROC_CONFIRM_CLOSE_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e); dMeter2Info_set2DVibrationM(); mpWarning->_move(); setAnimation(); return; } yesnoSelectAnmSet(); - field_0x3ef = PROC_CONFIRM_SELECT_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_SELECT_MOVE_e); mpWarning->_move(); setAnimation(); return; } if (clicked) { yesNoSelectStart(); - field_0x3ef = PROC_CONFIRM_CLOSE_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e); dMeter2Info_set2DVibrationM(); mpWarning->_move(); setAnimation(); @@ -1147,12 +1161,12 @@ void dMenu_Option_c::confirm_move_move() { if (mDoCPd_c::getTrigA(PAD_1) != 0) { yesNoSelectStart(); - field_0x3ef = PROC_CONFIRM_CLOSE_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e); dMeter2Info_set2DVibrationM(); } else if (mDoCPd_c::getTrigB(PAD_1) != 0) { field_0x3f9 = 0; yesnoCancelAnmSet(); - field_0x3ef = PROC_CONFIRM_CLOSE_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e); dMeter2Info_set2DVibrationM(); } else if (rightTrigger != 0) { if (field_0x3f9 != 0) { @@ -1161,7 +1175,7 @@ void dMenu_Option_c::confirm_move_move() { field_0x3fa = field_0x3f9; field_0x3f9 = 0; yesnoSelectAnmSet(); - field_0x3ef = PROC_CONFIRM_SELECT_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_SELECT_MOVE_e); } } else if (leftTrigger != 0) { if (field_0x3f9 != 1) { @@ -1170,7 +1184,7 @@ void dMenu_Option_c::confirm_move_move() { field_0x3fa = field_0x3f9; field_0x3f9 = 1; yesnoSelectAnmSet(); - field_0x3ef = PROC_CONFIRM_SELECT_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_SELECT_MOVE_e); } } mpWarning->_move(); @@ -1204,14 +1218,14 @@ void dMenu_Option_c::confirm_select_move() { dusk::menu_pointer::Context::Options, option_yes_no_target(field_0x3f9))) { yesNoSelectStart(); - field_0x3ef = PROC_CONFIRM_CLOSE_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_CLOSE_MOVE_e); dMeter2Info_set2DVibrationM(); mpWarning->_move(); setAnimation(); return; } #endif - field_0x3ef = PROC_CONFIRM_MOVE_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_MOVE_MOVE_e); } mpWarning->_move(); setAnimation(); @@ -1355,13 +1369,7 @@ void dMenu_Option_c::calibration_close2_move() { void dMenu_Option_c::menuVisible() { for (int i = 0; i < 6; i++) { -#if TARGET_PC - if ((dusk::version::isRegionJpn() && i < 4) || i < 3) -#elif VERSION == VERSION_GCN_JPN - if (i < 4) -#else - if (i < 3) -#endif + if (i < OPTION_SELECT(PROC_CHANGE_MOVE_e)) { menuShow(i); } else { @@ -2214,19 +2222,6 @@ void dMenu_Option_c::setSoundString() { } void dMenu_Option_c::setCursorPos(u8 i_index) { -#if TARGET_PC - if (!dusk::version::isRegionJpn()) { - switch (i_index) { - case PROC_VIB_e: - i_index = 1; - break; - case PROC_SOUND_e: - i_index = 2; - break; - } - } -#endif - #if TARGET_PC || VERSION != VERSION_GCN_JPN IF_DUSK_BLOCK(!dusk::version::isRegionJpn()) if (i_index == 4) { @@ -2283,33 +2278,13 @@ void dMenu_Option_c::setSelectColor(u8 param_0, bool param_1) { } u8 dMenu_Option_c::getSelectType() { -#if TARGET_PC - u8 proc = 0; - if (field_0x3ef < PROC_CHANGE_MOVE_e) { - proc = field_0x3ef; - } else if (field_0x3f5 < PROC_CHANGE_MOVE_e) { - proc = field_0x3f5; - } - - if (!dusk::version::isRegionJpn()) { - switch (proc) { - case PROC_VIB_e: - return 1; - case PROC_SOUND_e: - return 2; - } - } - - return proc; -#else - if (field_0x3ef < PROC_CHANGE_MOVE_e) { + if (field_0x3ef < OPTION_SELECT(PROC_CHANGE_MOVE_e)) { return field_0x3ef; } - if (field_0x3f5 < PROC_CHANGE_MOVE_e) { + if (field_0x3f5 < OPTION_SELECT(PROC_CHANGE_MOVE_e)) { return field_0x3f5; } return 0; -#endif } void dMenu_Option_c::changeBarColor(bool i_changeColor) { @@ -2517,9 +2492,9 @@ bool dMenu_Option_c::pointerConfirmSelect() { field_0x3f7 = 1; field_0x3f5 = field_0x3ef; - field_0x3ef = PROC_CONFIRM_OPEN_MOVE_e; + field_0x3ef = OPTION_SELECT(PROC_CONFIRM_OPEN_MOVE_e); dMeter2Info_set2DVibration(); - (this->*init[field_0x3ef])(); + (this->*init[OPTION_PROC(field_0x3ef)])(); return true; } #endif @@ -2564,8 +2539,8 @@ bool dMenu_Option_c::dpdMenuMove() { } if (getSelectType() != i) { - field_0x3ef = selectProc; - setCursorPos(field_0x3ef); + field_0x3ef = OPTION_SELECT(selectProc); + setCursorPos(i); Z2GetAudioMgr()->seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } @@ -2577,8 +2552,8 @@ bool dMenu_Option_c::dpdMenuMove() { case PROC_ATTEN_e: field_0x3e4 ^= 1; field_0x3da = 5; - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_ATTEN_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_ATTEN_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); return true; @@ -2590,8 +2565,8 @@ bool dMenu_Option_c::dpdMenuMove() { field_0x3e5_JPN = 0; field_0x3da = 5; } - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_RUBY_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_RUBY_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); return true; case PROC_VIB_e: @@ -2601,8 +2576,8 @@ bool dMenu_Option_c::dpdMenuMove() { mDoCPd_c::startMotorWave(0, &field_0x3e0, JUTGamePad::CRumble::VAL_0, 0x3c); } field_0x3da = 5; - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_VIB_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_VIB_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } @@ -2616,8 +2591,8 @@ bool dMenu_Option_c::dpdMenuMove() { field_0x3da = 5; mDoAud_setOutputMode(dMo_soundMode[field_0x3e9]); setSoundMode(dMo_soundMode[field_0x3e9]); - field_0x3ef = PROC_CHANGE_MOVE_e; - field_0x3f5 = PROC_SOUND_e; + field_0x3ef = OPTION_SELECT(PROC_CHANGE_MOVE_e); + field_0x3f5 = OPTION_SELECT(PROC_SOUND_e); Z2GetAudioMgr()->seStart(Z2SE_SY_OPTION_SWITCH, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); return true;