From 71b2eedc826464de4714b0c0fc257d057a57648e Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Sun, 9 Aug 2026 16:40:26 -0700 Subject: [PATCH 01/26] 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 0c3bbbaba03fc5b043aafda0838c24cce17d4e94 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Mon, 10 Aug 2026 12:35:08 -0700 Subject: [PATCH 02/26] 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 5830c41547f6c66aa02274684ff910f6cd3a3d82 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Mon, 10 Aug 2026 20:33:25 -0700 Subject: [PATCH 03/26] couple ui fixes --- mods/randomizer/src/ui/rando_config.cpp | 75 ++++++++++++++++++++----- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/mods/randomizer/src/ui/rando_config.cpp b/mods/randomizer/src/ui/rando_config.cpp index 994612b5e8..58253075ac 100644 --- a/mods/randomizer/src/ui/rando_config.cpp +++ b/mods/randomizer/src/ui/rando_config.cpp @@ -273,18 +273,58 @@ ModResult buildSeedManagementTab(ModContext* ctx, UiWindowHandle, UiElementHandl SaveRandomizerConfig(); }); - add_button(leftPane, - "Delete Seeds", - "Delete any seed not currently being used.", - [](ModContext*, void*) { - // TODO - }); + { + std::filesystem::path seed_dir = paths::GetRandomizerSeedsPath(); + if (!std::filesystem::exists(seed_dir)) + std::filesystem::create_directory(seed_dir); + + std::string help_rml = "Delete any seed not currently being used."; + + std::vector seedHashes; + for (const auto& entry : std::filesystem::directory_iterator(seed_dir)) { + if (entry.is_directory()) { + seedHashes.push_back(entry.path().filename().string()); + } + } + + std::vector availableSeeds; + for (const auto& hash : seedHashes) { + availableSeeds.push_back(hash.c_str()); + } + + add_select(leftPane, + "Delete Seeds", + help_rml.c_str(), + availableSeeds.data(), + availableSeeds.size(), + [](ModContext*, void*, UiControlValue* out_value) { + out_value->int_value = 0; + }, + [](ModContext*, void*, const UiControlValue* value) { + int idx = 0; + for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) { + if (entry.is_directory()) { + if (idx == value->int_value) { + std::string hash = entry.path().filename().string(); + if (randomizer_GetContext().mHash == hash) { + randomizer_GetContext() = RandomizerContext{}; + } + std::filesystem::remove_all(entry); + break; + } + idx++; + } + } + }); + } add_section(leftPane, "Permalink"); { - std::string help_rml = "Copy your current settings permalink to share with others."; - help_rml += fmt::format("
Current Permalink: {}", GetRandomizerConfig().GetPermalink()); + std::string help_rml = "Copy your current settings permalink to share with others.
"; + help_rml += fmt::format( + "
Current Permalink:
{}", + GetRandomizerConfig().GetPermalink()); add_button(leftPane, "Copy Permalink", help_rml.c_str(), @@ -538,15 +578,22 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane } }); - add_button(leftPane, - "Start Randomizer", - "", - [](ModContext*, void* userdata) { + { + UiControlDesc desc = UI_CONTROL_DESC_INIT; + desc.kind = UI_CONTROL_BUTTON; + desc.label = "Start Randomizer"; + desc.help_rml = ""; + desc.on_pressed = [](ModContext*, void* userdata) { // set flag to move to name screen after window close g_file_select_window_ctx.is_proceed = true; session::svc_mng.ui->window_close(session::svc_mng.mod_ctx, *static_cast(userdata)); - }, - &g_file_select_window_ctx.window_handle); + }; + desc.user_data = &g_file_select_window_ctx.window_handle; + desc.is_disabled = [](ModContext*, void*) { + return randomizer_GetContext().mHash.empty(); + }; + session::svc_mng.ui->pane_add_control(session::svc_mng.mod_ctx, leftPane, &desc, nullptr); + } return MOD_OK; } From 5abc956448f4935f854a49135ffc0b3b06f49582 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Tue, 11 Aug 2026 14:45:53 -0700 Subject: [PATCH 04/26] 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 05/26] 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 06/26] 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 11055282ffb79a9311dade500bac81b64485021f Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Wed, 12 Aug 2026 19:42:57 -0700 Subject: [PATCH 07/26] hook up item check overriding --- mods/randomizer/CMakeLists.txt | 1 + mods/randomizer/src/hooks.cpp | 51 +- mods/randomizer/src/item.cpp | 1597 +++++++++++++++++++++++++++++++ mods/randomizer/src/item.hpp | 11 + mods/randomizer/src/mod.cpp | 6 +- mods/randomizer/src/session.cpp | 123 ++- mods/randomizer/src/session.hpp | 2 + src/d/d_item.cpp | 5 +- 8 files changed, 1772 insertions(+), 24 deletions(-) create mode 100644 mods/randomizer/src/item.cpp create mode 100644 mods/randomizer/src/item.hpp diff --git a/mods/randomizer/CMakeLists.txt b/mods/randomizer/CMakeLists.txt index 3496312a59..300539ce2e 100644 --- a/mods/randomizer/CMakeLists.txt +++ b/mods/randomizer/CMakeLists.txt @@ -86,6 +86,7 @@ set(RANDOMIZER_SOURCES src/paths.cpp src/session.cpp src/hooks.cpp + src/item.cpp src/ui/config_store.cpp src/ui/rando_seed_generation.cpp src/ui/rando_config.cpp diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index f5197d59bd..7b2a906ca1 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -5,6 +5,7 @@ #include "flags.h" #include "stages.h" #include "tools.h" +#include "item.hpp" #include "item_ids.h" #include @@ -50,7 +51,13 @@ DEFINE_HOOK(&CheckFieldItemCreateHeap, dItemData_CheckFieldItemCreateHeap); DEFINE_HOOK(&dEvt_control_c::talkEnd, dEvt_control_c__talkEnd); DEFINE_HOOK(&dComIfG_play_c::getLayerNo_common_common, dComIfG_play_c__getLayerNo_common_common); +DEFINE_HOOK(&dComIfGs_onStageSwitch, onStageSwitch); +extern void getItemFunc(u8); +DEFINE_HOOK(&getItemFunc, dItem_getItemFunc); + +extern int checkItemGet(u8 i_itemNo, int i_default); +DEFINE_HOOK(&checkItemGet, dItem_checkItemGet); namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; @@ -482,24 +489,16 @@ HookAction hookPreSaveInfoOnSwitch(ModContext*, void* args, void*, void*) { return HOOK_CONTINUE; } -// TODO: item service -/* HookAction hookPreExecItemGet(ModContext*, void* args, void*, void*) { - if (randomizer_IsActive()) { - const u8 item = mods::arg(args, 0); - item_funcs::exec_item_get(item); - dusk::mods::item_granted(item, mods::arg(args, 1), mods::arg(args, 2)); - return HOOK_SKIP_ORIGINAL; - } - return HOOK_CONTINUE; -} */ +HookAction hookPreGetItemFunc(ModContext*, void* args, void*, void*) { + const u8 item = mods::arg(args, 0); + item::exec_item_get(item); + return HOOK_SKIP_ORIGINAL; +} -/* HookAction hookPreCheckItemGet(ModContext*, void* args, void* retval, void*) { - if (randomizer_IsActive()) { - *static_cast(retval) = item_funcs::check_item_get(mods::arg(args, 0), mods::arg(args, 1)); - return HOOK_SKIP_ORIGINAL; - } - return HOOK_CONTINUE; -} */ + HookAction hookPreCheckItemGet(ModContext*, void* args, void* retval, void*) { + *static_cast(retval) = item::check_item_get(mods::arg(args, 0), mods::arg(args, 1)); + return HOOK_SKIP_ORIGINAL; +} HookAction hookPreSetNextStage(ModContext*, void* args, void*, void*) { randomizer_checkAndOverrideEntranceData( @@ -1161,6 +1160,19 @@ HookAction hookPreGetLayerNo(ModContext*, void* args, void* retval, void*) { return HOOK_CONTINUE; } +HookAction hookPreOnStageSwitch(ModContext*, void* args, void* retval, void*) { + const int i_stageNo = mods::arg(args, 0); + const int i_no = mods::arg(args, 1); + + // Avoid trying to get the save table if stag info is NULL + if (dComIfGp_getStageStagInfo() == NULL) { + dComIfGs_onSaveSwitch(i_stageNo, i_no); + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + } ModResult initialize() { @@ -1211,6 +1223,11 @@ ModResult initialize() { ADD_HOOK_PRE(dComIfG_play_c__getLayerNo_common_common, hookPreGetLayerNo); + ADD_HOOK_PRE(dItem_getItemFunc, hookPreGetItemFunc); + ADD_HOOK_PRE(dItem_checkItemGet, hookPreCheckItemGet); + + ADD_HOOK_PRE(onStageSwitch, hookPreOnStageSwitch); + return MOD_OK; } } \ No newline at end of file diff --git a/mods/randomizer/src/item.cpp b/mods/randomizer/src/item.cpp new file mode 100644 index 0000000000..4004b83378 --- /dev/null +++ b/mods/randomizer/src/item.cpp @@ -0,0 +1,1597 @@ +#include "item.hpp" + +#include "d/d_com_inf_game.h" +#include "d/d_item.h" +#include "d/d_item_data.h" + +#include "flags.h" +#include "item_ids.h" +#include "randomizer_context.hpp" +#include "stages.h" +#include "tools.h" +#include "verify_item_functions.h" + +static void item_func_FOOLISH_ITEM() { + // Failsafe: Make sure the count does not somehow exceed 100 + if (g_randomizerState.mFoolishItemCount < 100) + { + g_randomizerState.mFoolishItemCount += 1; + } +} + +static void item_func_ORDON_PORTAL() { + dComIfGs_onStageSwitch(0x0, 0x34); // Unlock Ordon Portal +} + +static void item_func_SOUTH_FARON_PORTAL() { + dComIfGs_onStageSwitch(0x2, 0x47); // Unlock S Faron Portal +} + +static void item_func_UPPER_ZORAS_RIVER_PORTAL() { + dComIfGs_onStageSwitch(0x4, 0x17); // Talked to Iza before portal + dComIfGs_onStageSwitch(0x4, 0x37); // Talked to Iza after portal + dComIfGs_onStageSwitch(0x4, 0x15); // Unlock UZR Portal + dComIfGs_onEventBit(0xB80); // Declined to help Iza + dComIfGs_onEventBit(0x1304); // Talked to Iza before UZR portal + dComIfGs_onEventBit(0xB02); // Iza 1 Minigame Unlocked +} + +static void item_func_CASTLE_TOWN_PORTAL() { + dComIfGs_onStageSwitch(0x6, 0x3); // Unlock Castle Town Portal +} + +static void item_func_GERUDO_DESERT_PORTAL() { + dComIfGs_onStageSwitch(0xA, 0x15); // Unlock Desert Portal +} + +static void item_func_NORTH_FARON_PORTAL() { + dComIfGs_onStageSwitch(0x2, 0x2); // Unlock N Faron Portal +} + +static void item_func_KAKARIKO_GORGE_PORTAL() { + dComIfGs_onStageSwitch(0x6, 0x15); // Unlock Gorge Portal +} + +static void item_func_KAKARIKO_VILLAGE_PORTAL() { + dComIfGs_onStageSwitch(0x3, 0x1F); // Unlock Kak Portal +} + +static void item_func_DEATH_MOUNTAIN_PORTAL() { + dComIfGs_onStageSwitch(0x3, 0x15); // Unlock DM Portal +} + +static void item_func_ZORAS_DOMAIN_PORTAL() { + dComIfGs_onStageSwitch(0x4, 0x2); // Unlock ZD Portal +} + +static void item_func_FOREST_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x10); + dComIfGs_setKeyNum(0x10, currentKeys + 1); +} + +static void item_func_MINES_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x11); + dComIfGs_setKeyNum(0x11, currentKeys + 1); +} + +static void item_func_LAKEBED_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x12); + dComIfGs_setKeyNum(0x12, currentKeys + 1); +} + +static void item_func_ARBITERS_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x13); + dComIfGs_setKeyNum(0x13, currentKeys + 1); +} + +static void item_func_SNOWPEAK_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x14); + dComIfGs_setKeyNum(0x14, currentKeys + 1); +} + +static void item_func_TEMPLE_OF_TIME_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x15); + dComIfGs_setKeyNum(0x15, currentKeys + 1); +} + +static void item_func_CITY_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x16); + dComIfGs_setKeyNum(0x16, currentKeys + 1); +} + +static void item_func_PALACE_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x17); + dComIfGs_setKeyNum(0x17, currentKeys + 1); +} + +static void item_func_HYRULE_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0x18); + dComIfGs_setKeyNum(0x18, currentKeys + 1); +} + +static void item_func_CAMP_SMALL_KEY() { + u8 currentKeys = getAreaKeyNum(0xA); + dComIfGs_setKeyNum(0xA, currentKeys + 1); +} + +static void item_func_LAKE_HYLIA_PORTAL() { + dComIfGs_onStageSwitch(0x4, 0xA); // Unlock Lake Portal +} + +static void item_func_FOREST_BOSS_KEY() { + dComIfGs_onDungeonItemBossKey(0x10); +} + +static void item_func_LAKEBED_BOSS_KEY() { + dComIfGs_onDungeonItemBossKey(0x12); +} + +static void item_func_ARBITERS_BOSS_KEY() { + dComIfGs_onDungeonItemBossKey(0x13); +} + +static void item_func_TEMPLE_OF_TIME_BOSS_KEY() { + dComIfGs_onDungeonItemBossKey(0x15); +} + +static void item_func_CITY_BOSS_KEY() { + dComIfGs_onDungeonItemBossKey(0x16); +} + +static void item_func_PALACE_BOSS_KEY() { + dComIfGs_onDungeonItemBossKey(0x17); +} + +static void item_func_HYRULE_BOSS_KEY() { + dComIfGs_onDungeonItemBossKey(0x18); +} + +static void item_func_FOREST_COMPASS() { + dComIfGs_onDungeonItemCompass(0x10); +} + +static void item_func_MINES_COMPASS() { + dComIfGs_onDungeonItemCompass(0x11); +} + +static void item_func_LAKEBED_COMPASS() { + dComIfGs_onDungeonItemCompass(0x12); +} + +static void item_func_ARBITERS_COMPASS() { + dComIfGs_onDungeonItemCompass(0x13); +} + +static void item_func_SNOWPEAK_COMPASS() { + dComIfGs_onDungeonItemCompass(0x14); +} + +static void item_func_TEMPLE_OF_TIME_COMPASS() { + dComIfGs_onDungeonItemCompass(0x15); +} + +static void item_func_CITY_COMPASS() { + dComIfGs_onDungeonItemCompass(0x16); +} + +static void item_func_PALACE_COMPASS() { + dComIfGs_onDungeonItemCompass(0x17); +} + +static void item_func_HYRULE_COMPASS() { + dComIfGs_onDungeonItemCompass(0x18); +} + +static void item_func_MIRROR_CHAMBER_PORTAL() { + dComIfGs_onStageSwitch(0xA, 0x28); // Unlock MC Portal +} + +static void item_func_SNOWPEAK_PORTAL() { + dComIfGs_onStageSwitch(0x8, 0x15); // Unlock Snowpeak Portal +} + +static void item_func_FOREST_MAP() { + dComIfGs_onDungeonItemMap(0x10); +} + +static void item_func_MINES_MAP() { + dComIfGs_onDungeonItemMap(0x11); +} + +static void item_func_LAKEBED_MAP() { + dComIfGs_onDungeonItemMap(0x12); +} + +static void item_func_ARBITERS_MAP() { + dComIfGs_onDungeonItemMap(0x13); +} + +static void item_func_SNOWPEAK_MAP() { + dComIfGs_onDungeonItemMap(0x14); +} + +static void item_func_TEMPLE_OF_TIME_MAP() { + dComIfGs_onDungeonItemMap(0x15); +} + +static void item_func_CITY_MAP() { + dComIfGs_onDungeonItemMap(0x16); +} + +static void item_func_PALACE_MAP() { + dComIfGs_onDungeonItemMap(0x17); +} + +static void item_func_HYRULE_MAP() { + dComIfGs_onDungeonItemMap(0x18); +} + +static void item_func_SACRED_GROVE_PORTAL() { + dComIfGs_onStageSwitch(0x7, 0x64); // Unlock Grove Portal +} + +static void item_func_FUSED_SHADOW_1() { + dComIfGs_onCollectCrystal(0); + /* + Adding rando code until framework is implemented + // Check if the requirement for the HC barrier is set to shadows, and if so, set the flag + rando::gRandomizer->checkSetHCBarrierFlag(rando::HC_Fused_Shadows, 1); + + // Check if the requirement for the HC BK is set to shadows, and if so, set the flag + rando::gRandomizer->checkSetHCBkFlag(rando::HC_BK_Fused_Shadows, 1); + */ +} + +static void item_func_FUSED_SHADOW_2() { + dComIfGs_onCollectCrystal(1); + /* + Adding rando code until framework is implemented + // Check if the requirement for the HC barrier is set to shadows, and if so, set the flag + rando::gRandomizer->checkSetHCBarrierFlag(rando::HC_Fused_Shadows, 2); + + // Check if the requirement for the HC BK is set to shadows, and if so, set the flag + rando::gRandomizer->checkSetHCBkFlag(rando::HC_BK_Fused_Shadows, 2); + */ +} + +static void item_func_FUSED_SHADOW_3() { + dComIfGs_onCollectCrystal(2); + /* + Adding rando code until framework is implemented + // If the player has the palace requirement set to Fused Shadows. + if (headerPtr->getPalaceRequirements() == rando::PalaceEntryRequirements::PoT_Fused_Shadows) + { + events::setSaveFileEventFlag(libtp::data::flags::FIXED_THE_MIRROR_OF_TWILIGHT); + } + + // Check if the requirement for the HC barrier is set to shadows, and if so, set the flag + rando::gRandomizer->checkSetHCBarrierFlag(rando::HC_Fused_Shadows, 3); + + // Check if the requirement for the HC BK is set to shadows, and if so, set the flag + rando::gRandomizer->checkSetHCBkFlag(rando::HC_BK_Fused_Shadows, 3); + */ +} + +static void item_func_MIRROR_PIECE_1() { + dComIfGs_onCollectMirror(0); +} + +static void item_func_ENDING_BLOW() { + dComIfGs_onEventBit(0x2904); +} + +static void item_func_SHIELD_ATTACK() { + dComIfGs_onEventBit(0x2908); +} + +static void item_func_BACK_SLICE() { + dComIfGs_onEventBit(0x2902); +} + +static void item_func_HELM_SPLITTER() { + dComIfGs_onEventBit(0x2901); +} + +static void item_func_MORTAL_DRAW() { + dComIfGs_onEventBit(0x2A80); +} + +static void item_func_JUMP_STRIKE() { + dComIfGs_onEventBit(0x2A40); +} + +static void item_func_GREAT_SPIN() { + dComIfGs_onEventBit(0x2A20); +} + +static void item_func_ELDIN_BRIDGE_PORTAL() { + dComIfGs_onStageSwitch(0x6, 0x63); // Unlock Eldin Bridge Portal +} + +static int item_getcheck_func_ORDON_PORTAL() { + return dComIfGs_isStageSwitch(0x0, 0x34); // Unlock Ordon Portal +} + +static int item_getcheck_func_SOUTH_FARON_PORTAL() { + return dComIfGs_isStageSwitch(0x2, 0x47); // Unlock S Faron Portal +} + +static int item_getcheck_func_UPPER_ZORAS_RIVER_PORTAL() { + return dComIfGs_isStageSwitch(0x4, 0x15); // Unlock UZR Portal +} + +static int item_getcheck_func_CASTLE_TOWN_PORTAL() { + return dComIfGs_isStageSwitch(0x6, 0x3); // Unlock Castle Town Portal +} + +static int item_getcheck_func_GERUDO_DESERT_PORTAL() { + return dComIfGs_isStageSwitch(0xA, 0x15); // Unlock Desert Portal +} + +static int item_getcheck_func_NORTH_FARON_PORTAL() { + return dComIfGs_isStageSwitch(0x2, 0x2); // Unlock N Faron Portal +} + +static int item_getcheck_func_KAKARIKO_GORGE_PORTAL() { + return dComIfGs_isStageSwitch(0x6, 0x15); // Unlock Gorge Portal +} + +static int item_getcheck_func_KAKARIKO_VILLAGE_PORTAL() { + return dComIfGs_isStageSwitch(0x3, 0x1F); // Unlock Kak Portal +} + +static int item_getcheck_func_DEATH_MOUNTAIN_PORTAL() { + return dComIfGs_isStageSwitch(0x3, 0x15); // Unlock DM Portal +} + +static int item_getcheck_func_ZORAS_DOMAIN_PORTAL() { + return dComIfGs_isStageSwitch(0x4, 0x2); // Unlock ZD Portal +} + +static int item_getcheck_func_CAMP_SMALL_KEY() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_CAMP_SMALL_KEY_e); +} + +static int item_getcheck_func_LAKE_HYLIA_PORTAL() { + return dComIfGs_isStageSwitch(0x4, 0xA); // Unlock Lake Portal +} + +static int item_getcheck_func_MIRROR_CHAMBER_PORTAL() { + return dComIfGs_isStageSwitch(0xA, 0x28); // Unlock MC Portal +} + +static int item_getcheck_func_SNOWPEAK_PORTAL() { + return dComIfGs_isStageSwitch(0x8, 0x15); // Unlock Snowpeak Portal +} + +static int item_getcheck_func_SACRED_GROVE_PORTAL() { + return dComIfGs_isStageSwitch(0x7, 0x64); // Unlock Grove Portal +} + +static int item_getcheck_func_FUSED_SHADOW_1() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_FUSED_SHADOW_1_e); +} + +static int item_getcheck_func_FUSED_SHADOW_2() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_FUSED_SHADOW_2_e); +} + +static int item_getcheck_func_FUSED_SHADOW_3() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_FUSED_SHADOW_3_e); +} + +static int item_getcheck_func_MIRROR_PIECE_1() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_MIRROR_PIECE_1_e); +} + +static int item_getcheck_func_ENDING_BLOW() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_ENDING_BLOW_e); +} + +static int item_getcheck_func_SHIELD_ATTACK() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_SHIELD_ATTACK_e); +} + +static int item_getcheck_func_BACK_SLICE() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_BACK_SLICE_e); +} + +static int item_getcheck_func_HELM_SPLITTER() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_HELM_SPLITTER_e); +} + +static int item_getcheck_func_MORTAL_DRAW() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_MORTAL_DRAW_e); +} + +static int item_getcheck_func_JUMP_STRIKE() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_JUMP_STRIKE_e); +} + +static int item_getcheck_func_GREAT_SPIN() { + return dComIfGs_isItemFirstBit(dItemNo_Randomizer_GREAT_SPIN_e); +} + +static int item_getcheck_func_ELDIN_BRIDGE_PORTAL() { + return dComIfGs_isStageSwitch(0x6, 0x63); // Unlock Eldin Bridge Portal +} + +static void (*item_func_ptr_randomizer[256])() = { + /* 0x00 */ item_func_HEART, + /* 0x01 */ item_func_GREEN_RUPEE, + /* 0x02 */ item_func_BLUE_RUPEE, + /* 0x03 */ item_func_YELLOW_RUPEE, + /* 0x04 */ item_func_RED_RUPEE, + /* 0x05 */ item_func_PURPLE_RUPEE, + /* 0x06 */ item_func_ORANGE_RUPEE, + /* 0x07 */ item_func_SILVER_RUPEE, + /* 0x08 */ item_func_S_MAGIC, + /* 0x09 */ item_func_L_MAGIC, + /* 0x0A */ item_func_BOMB_5, + /* 0x0B */ item_func_BOMB_10, + /* 0x0C */ item_func_BOMB_20, + /* 0x0D */ item_func_BOMB_30, + /* 0x0E */ item_func_ARROW_10, + /* 0x0F */ item_func_ARROW_20, + /* 0x10 */ item_func_ARROW_30, + /* 0x11 */ item_func_ARROW_1, + /* 0x12 */ item_func_PACHINKO_SHOT, + /* 0x13 */ item_func_FOOLISH_ITEM, + /* 0x14 */ item_func_ORDON_PORTAL, + /* 0x15 */ item_func_SOUTH_FARON_PORTAL, + /* 0x16 */ item_func_WATER_BOMB_5, + /* 0x17 */ item_func_WATER_BOMB_10, + /* 0x18 */ item_func_WATER_BOMB_20, + /* 0x19 */ item_func_WATER_BOMB_30, + /* 0x1A */ item_func_BOMB_INSECT_5, + /* 0x1B */ item_func_BOMB_INSECT_10, + /* 0x1C */ item_func_BOMB_INSECT_20, + /* 0x1D */ item_func_BOMB_INSECT_30, + /* 0x1E */ item_func_RECOVER_FAILY, + /* 0x1F */ item_func_TRIPLE_HEART, + /* 0x20 */ item_func_SMALL_KEY, + /* 0x21 */ item_func_KAKERA_HEART, + /* 0x22 */ item_func_UTUWA_HEART, + /* 0x23 */ item_func_MAP, + /* 0x24 */ item_func_COMPUS, + /* 0x25 */ item_func_DUNGEON_EXIT, + /* 0x26 */ item_func_BOSS_KEY, + /* 0x27 */ item_func_DUNGEON_BACK, + /* 0x28 */ item_func_SWORD, + /* 0x29 */ item_func_MASTER_SWORD, + /* 0x2A */ item_func_WOOD_SHIELD, + /* 0x2B */ item_func_SHIELD, + /* 0x2C */ item_func_HYLIA_SHIELD, + /* 0x2D */ item_func_TKS_LETTER, + /* 0x2E */ item_func_WEAR_CASUAL, + /* 0x2F */ item_func_WEAR_KOKIRI, + /* 0x30 */ item_func_ARMOR, + /* 0x31 */ item_func_WEAR_ZORA, + /* 0x32 */ item_func_MAGIC_LV1, + /* 0x33 */ item_func_DUNGEON_EXIT_2, + /* 0x34 */ item_func_WALLET_LV1, + /* 0x35 */ item_func_WALLET_LV2, + /* 0x36 */ item_func_WALLET_LV3, + /* 0x37 */ item_func_noentry, + /* 0x38 */ item_func_noentry, + /* 0x39 */ item_func_UPPER_ZORAS_RIVER_PORTAL, + /* 0x3A */ item_func_CASTLE_TOWN_PORTAL, + /* 0x3B */ item_func_GERUDO_DESERT_PORTAL, + /* 0x3C */ item_func_NORTH_FARON_PORTAL, + /* 0x3D */ item_func_ZORAS_JEWEL, + /* 0x3E */ item_func_HAWK_EYE, + /* 0x3F */ item_func_WOOD_STICK, + /* 0x40 */ item_func_BOOMERANG, + /* 0x41 */ item_func_SPINNER, + /* 0x42 */ item_func_IRONBALL, + /* 0x43 */ item_func_BOW, + /* 0x44 */ item_func_HOOKSHOT, + /* 0x45 */ item_func_HVY_BOOTS, + /* 0x46 */ item_func_COPY_ROD, + /* 0x47 */ item_func_W_HOOKSHOT, + /* 0x48 */ item_func_KANTERA, + /* 0x49 */ item_func_LIGHT_SWORD, + /* 0x4A */ item_func_FISHING_ROD_1, + /* 0x4B */ item_func_PACHINKO, + /* 0x4C */ item_func_COPY_ROD_2, + /* 0x4D */ item_func_KAKARIKO_GORGE_PORTAL, + /* 0x4E */ item_func_KAKARIKO_VILLAGE_PORTAL, + /* 0x4F */ item_func_BOMB_BAG_LV2, + /* 0x50 */ item_func_BOMB_BAG_LV1, + /* 0x51 */ item_func_BOMB_IN_BAG, + /* 0x52 */ item_func_DEATH_MOUNTAIN_PORTAL, + /* 0x53 */ item_func_LIGHT_ARROW, + /* 0x54 */ item_func_ARROW_LV1, + /* 0x55 */ item_func_ARROW_LV2, + /* 0x56 */ item_func_ARROW_LV3, + /* 0x57 */ item_func_ZORAS_DOMAIN_PORTAL, + /* 0x58 */ item_func_LURE_ROD, + /* 0x59 */ item_func_BOMB_ARROW, + /* 0x5A */ item_func_HAWK_ARROW, + /* 0x5B */ item_func_BEE_ROD, + /* 0x5C */ item_func_JEWEL_ROD, + /* 0x5D */ item_func_WORM_ROD, + /* 0x5E */ item_func_JEWEL_BEE_ROD, + /* 0x5F */ item_func_JEWEL_WORM_ROD, + /* 0x60 */ item_func_EMPTY_BOTTLE, + /* 0x61 */ item_func_RED_BOTTLE, + /* 0x62 */ item_func_GREEN_BOTTLE, + /* 0x63 */ item_func_BLUE_BOTTLE, + /* 0x64 */ item_func_MILK_BOTTLE, + /* 0x65 */ item_func_HALF_MILK_BOTTLE, + /* 0x66 */ item_func_OIL_BOTTLE, + /* 0x67 */ item_func_WATER_BOTTLE, + /* 0x68 */ item_func_OIL_BOTTLE2, + /* 0x69 */ item_func_RED_BOTTLE2, + /* 0x6A */ item_func_UGLY_SOUP, + /* 0x6B */ item_func_HOT_SPRING, + /* 0x6C */ item_func_FAIRY_BOTTLE, + /* 0x6D */ item_func_HOT_SPRING2, + /* 0x6E */ item_func_OIL2, + /* 0x6F */ item_func_OIL, + /* 0x70 */ item_func_NORMAL_BOMB, + /* 0x71 */ item_func_WATER_BOMB, + /* 0x72 */ item_func_POKE_BOMB, + /* 0x73 */ item_func_FAIRY_DROP, + /* 0x74 */ item_func_WORM, + /* 0x75 */ item_func_DROP_BOTTLE, + /* 0x76 */ item_func_BEE_CHILD, + /* 0x77 */ item_func_CHUCHU_RARE, + /* 0x78 */ item_func_CHUCHU_RED, + /* 0x79 */ item_func_CHUCHU_BLUE, + /* 0x7A */ item_func_CHUCHU_GREEN, + /* 0x7B */ item_func_CHUCHU_YELLOW, + /* 0x7C */ item_func_CHUCHU_PURPLE, + /* 0x7D */ item_func_LV1_SOUP, + /* 0x7E */ item_func_LV2_SOUP, + /* 0x7F */ item_func_LV3_SOUP, + /* 0x80 */ item_func_LETTER, + /* 0x81 */ item_func_BILL, + /* 0x82 */ item_func_WOOD_STATUE, + /* 0x83 */ item_func_IRIAS_PENDANT, + /* 0x84 */ item_func_HORSE_FLUTE, + /* 0x85 */ item_func_FOREST_SMALL_KEY, + /* 0x86 */ item_func_MINES_SMALL_KEY, + /* 0x87 */ item_func_LAKEBED_SMALL_KEY, + /* 0x88 */ item_func_ARBITERS_SMALL_KEY, + /* 0x89 */ item_func_SNOWPEAK_SMALL_KEY, + /* 0x8A */ item_func_TEMPLE_OF_TIME_SMALL_KEY, + /* 0x8B */ item_func_CITY_SMALL_KEY, + /* 0x8C */ item_func_PALACE_SMALL_KEY, + /* 0x8D */ item_func_HYRULE_SMALL_KEY, + /* 0x8E */ item_func_CAMP_SMALL_KEY, + /* 0x8F */ item_func_LAKE_HYLIA_PORTAL, + /* 0x90 */ item_func_RAFRELS_MEMO, + /* 0x91 */ item_func_ASHS_SCRIBBLING, + /* 0x92 */ item_func_FOREST_BOSS_KEY, + /* 0x93 */ item_func_LAKEBED_BOSS_KEY, + /* 0x94 */ item_func_ARBITERS_BOSS_KEY, + /* 0x95 */ item_func_TEMPLE_OF_TIME_BOSS_KEY, + /* 0x96 */ item_func_CITY_BOSS_KEY, + /* 0x97 */ item_func_PALACE_BOSS_KEY, + /* 0x98 */ item_func_HYRULE_BOSS_KEY, + /* 0x99 */ item_func_FOREST_COMPASS, + /* 0x9A */ item_func_MINES_COMPASS, + /* 0x9B */ item_func_LAKEBED_COMPASS, + /* 0x9C */ item_func_CHUCHU_YELLOW2, + /* 0x9D */ item_func_OIL_BOTTLE3, + /* 0x9E */ item_func_SHOP_BEE_CHILD, + /* 0x9F */ item_func_CHUCHU_BLACK, + /* 0xA0 */ item_func_LIGHT_DROP, + /* 0xA1 */ item_func_DROP_CONTAINER, + /* 0xA2 */ item_func_DROP_CONTAINER02, + /* 0xA3 */ item_func_DROP_CONTAINER03, + /* 0xA4 */ item_func_FILLED_CONTAINER, + /* 0xA5 */ item_func_MIRROR_PIECE_2, + /* 0xA6 */ item_func_MIRROR_PIECE_3, + /* 0xA7 */ item_func_MIRROR_PIECE_4, + /* 0xA8 */ item_func_ARBITERS_COMPASS, + /* 0xA9 */ item_func_SNOWPEAK_COMPASS, + /* 0xAA */ item_func_TEMPLE_OF_TIME_COMPASS, + /* 0xAB */ item_func_CITY_COMPASS, + /* 0xAC */ item_func_PALACE_COMPASS, + /* 0xAD */ item_func_HYRULE_COMPASS, + /* 0xAE */ item_func_MIRROR_CHAMBER_PORTAL, + /* 0xAF */ item_func_SNOWPEAK_PORTAL, + /* 0xB0 */ item_func_SMELL_YELIA_POUCH, + /* 0xB1 */ item_func_SMELL_PUMPKIN, + /* 0xB2 */ item_func_SMELL_POH, + /* 0xB3 */ item_func_SMELL_FISH, + /* 0xB4 */ item_func_SMELL_CHILDREN, + /* 0xB5 */ item_func_SMELL_MEDICINE, + /* 0xB6 */ item_func_FOREST_MAP, + /* 0xB7 */ item_func_MINES_MAP, + /* 0xB8 */ item_func_LAKEBED_MAP, + /* 0xB9 */ item_func_ARBITERS_MAP, + /* 0xBA */ item_func_SNOWPEAK_MAP, + /* 0xBB */ item_func_TEMPLE_OF_TIME_MAP, + /* 0xBC */ item_func_CITY_MAP, + /* 0xBD */ item_func_PALACE_MAP, + /* 0xBE */ item_func_HYRULE_MAP, + /* 0xBF */ item_func_SACRED_GROVE_PORTAL, + /* 0xC0 */ item_func_M_BEETLE, + /* 0xC1 */ item_func_F_BEETLE, + /* 0xC2 */ item_func_M_BUTTERFLY, + /* 0xC3 */ item_func_F_BUTTERFLY, + /* 0xC4 */ item_func_M_STAG_BEETLE, + /* 0xC5 */ item_func_F_STAG_BEETLE, + /* 0xC6 */ item_func_M_GRASSHOPPER, + /* 0xC7 */ item_func_F_GRASSHOPPER, + /* 0xC8 */ item_func_M_NANAFUSHI, + /* 0xC9 */ item_func_F_NANAFUSHI, + /* 0xCA */ item_func_M_DANGOMUSHI, + /* 0xCB */ item_func_F_DANGOMUSHI, + /* 0xCC */ item_func_M_MANTIS, + /* 0xCD */ item_func_F_MANTIS, + /* 0xCE */ item_func_M_LADYBUG, + /* 0xCF */ item_func_F_LADYBUG, + /* 0xD0 */ item_func_M_SNAIL, + /* 0xD1 */ item_func_F_SNAIL, + /* 0xD2 */ item_func_M_DRAGONFLY, + /* 0xD3 */ item_func_F_DRAGONFLY, + /* 0xD4 */ item_func_M_ANT, + /* 0xD5 */ item_func_F_ANT, + /* 0xD6 */ item_func_M_MAYFLY, + /* 0xD7 */ item_func_F_MAYFLY, + /* 0xD8 */ item_func_FUSED_SHADOW_1, + /* 0xD9 */ item_func_FUSED_SHADOW_2, + /* 0xDA */ item_func_FUSED_SHADOW_3, + /* 0xDB */ item_func_MIRROR_PIECE_1, + /* 0xDC */ item_func_noentry, + /* 0xDD */ item_func_noentry, + /* 0xDE */ item_func_noentry, + /* 0xDF */ item_func_noentry, + /* 0xE0 */ item_func_POU_SPIRIT, + /* 0xE1 */ item_func_ENDING_BLOW, + /* 0xE2 */ item_func_SHIELD_ATTACK, + /* 0xE3 */ item_func_BACK_SLICE, + /* 0xE4 */ item_func_HELM_SPLITTER, + /* 0xE5 */ item_func_MORTAL_DRAW, + /* 0xE6 */ item_func_JUMP_STRIKE, + /* 0xE7 */ item_func_GREAT_SPIN, + /* 0xE8 */ item_func_ELDIN_BRIDGE_PORTAL, + /* 0xE9 */ item_func_ANCIENT_DOCUMENT, + /* 0xEA */ item_func_AIR_LETTER, + /* 0xEB */ item_func_ANCIENT_DOCUMENT2, + /* 0xEC */ item_func_LV7_DUNGEON_EXIT, + /* 0xED */ item_func_LINKS_SAVINGS, + /* 0xEE */ item_func_SMALL_KEY2, + /* 0xEF */ item_func_POU_FIRE1, + /* 0xF0 */ item_func_POU_FIRE2, + /* 0xF1 */ item_func_POU_FIRE3, + /* 0xF2 */ item_func_POU_FIRE4, + /* 0xF3 */ item_func_BOSSRIDER_KEY, + /* 0xF4 */ item_func_TOMATO_PUREE, + /* 0xF5 */ item_func_TASTE, + /* 0xF6 */ item_func_LV5_BOSS_KEY, + /* 0xF7 */ item_func_SURFBOARD, + /* 0xF8 */ item_func_KANTERA2, + /* 0xF9 */ item_func_L2_KEY_PIECES1, + /* 0xFA */ item_func_L2_KEY_PIECES2, + /* 0xFB */ item_func_L2_KEY_PIECES3, + /* 0xFC */ item_func_KEY_OF_CARAVAN, + /* 0xFD */ item_func_LV2_BOSS_KEY, + /* 0xFE */ item_func_KEY_OF_FILONE, + /* 0xFF */ item_func_noentry, +}; + +static int (*item_getcheck_func_ptr_randomizer[256])() = { + /* 0x00 */ item_getcheck_func_HEART, + /* 0x01 */ item_getcheck_func_GREEN_RUPEE, + /* 0x02 */ item_getcheck_func_BLUE_RUPEE, + /* 0x03 */ item_getcheck_func_YELLOW_RUPEE, + /* 0x04 */ item_getcheck_func_RED_RUPEE, + /* 0x05 */ item_getcheck_func_PURPLE_RUPEE, + /* 0x06 */ item_getcheck_func_ORANGE_RUPEE, + /* 0x07 */ item_getcheck_func_SILVER_RUPEE, + /* 0x08 */ item_getcheck_func_S_MAGIC, + /* 0x09 */ item_getcheck_func_L_MAGIC, + /* 0x0A */ item_getcheck_func_BOMB_5, + /* 0x0B */ item_getcheck_func_BOMB_10, + /* 0x0C */ item_getcheck_func_BOMB_20, + /* 0x0D */ item_getcheck_func_BOMB_30, + /* 0x0E */ item_getcheck_func_ARROW_10, + /* 0x0F */ item_getcheck_func_ARROW_20, + /* 0x10 */ item_getcheck_func_ARROW_30, + /* 0x11 */ item_getcheck_func_ARROW_1, + /* 0x12 */ item_getcheck_func_PACHINKO_SHOT, + /* 0x13 */ item_getcheck_func_noentry, + /* 0x14 */ item_getcheck_func_ORDON_PORTAL, + /* 0x15 */ item_getcheck_func_SOUTH_FARON_PORTAL, + /* 0x16 */ item_getcheck_func_WATER_BOMB_5, + /* 0x17 */ item_getcheck_func_WATER_BOMB_10, + /* 0x18 */ item_getcheck_func_WATER_BOMB_20, + /* 0x19 */ item_getcheck_func_WATER_BOMB_30, + /* 0x1A */ item_getcheck_func_BOMB_INSECT_5, + /* 0x1B */ item_getcheck_func_BOMB_INSECT_10, + /* 0x1C */ item_getcheck_func_BOMB_INSECT_20, + /* 0x1D */ item_getcheck_func_BOMB_INSECT_30, + /* 0x1E */ item_getcheck_func_RECOVER_FAILY, + /* 0x1F */ item_getcheck_func_TRIPLE_HEART, + /* 0x20 */ item_getcheck_func_SMALL_KEY, + /* 0x21 */ item_getcheck_func_KAKERA_HEART, + /* 0x22 */ item_getcheck_func_UTUWA_HEART, + /* 0x23 */ item_getcheck_func_MAP, + /* 0x24 */ item_getcheck_func_COMPUS, + /* 0x25 */ item_getcheck_func_DUNGEON_EXIT, + /* 0x26 */ item_getcheck_func_BOSS_KEY, + /* 0x27 */ item_getcheck_func_DUNGEON_BACK, + /* 0x28 */ item_getcheck_func_SWORD, + /* 0x29 */ item_getcheck_func_MASTER_SWORD, + /* 0x2A */ item_getcheck_func_WOOD_SHIELD, + /* 0x2B */ item_getcheck_func_SHIELD, + /* 0x2C */ item_getcheck_func_HYLIA_SHIELD, + /* 0x2D */ item_getcheck_func_TKS_LETTER, + /* 0x2E */ item_getcheck_func_WEAR_CASUAL, + /* 0x2F */ item_getcheck_func_WEAR_KOKIRI, + /* 0x30 */ item_getcheck_func_ARMOR, + /* 0x31 */ item_getcheck_func_WEAR_ZORA, + /* 0x32 */ item_getcheck_func_MAGIC_LV1, + /* 0x33 */ item_getcheck_func_DUNGEON_EXIT_2, + /* 0x34 */ item_getcheck_func_WALLET_LV1, + /* 0x35 */ item_getcheck_func_WALLET_LV2, + /* 0x36 */ item_getcheck_func_WALLET_LV3, + /* 0x37 */ item_getcheck_func_noentry, + /* 0x38 */ item_getcheck_func_noentry, + /* 0x39 */ item_getcheck_func_UPPER_ZORAS_RIVER_PORTAL, + /* 0x3A */ item_getcheck_func_CASTLE_TOWN_PORTAL, + /* 0x3B */ item_getcheck_func_GERUDO_DESERT_PORTAL, + /* 0x3C */ item_getcheck_func_NORTH_FARON_PORTAL, + /* 0x3D */ item_getcheck_func_ZORAS_JEWEL, + /* 0x3E */ item_getcheck_func_HAWK_EYE, + /* 0x3F */ item_getcheck_func_WOOD_STICK, + /* 0x40 */ item_getcheck_func_BOOMERANG, + /* 0x41 */ item_getcheck_func_SPINNER, + /* 0x42 */ item_getcheck_func_IRONBALL, + /* 0x43 */ item_getcheck_func_BOW, + /* 0x44 */ item_getcheck_func_HOOKSHOT, + /* 0x45 */ item_getcheck_func_HVY_BOOTS, + /* 0x46 */ item_getcheck_func_COPY_ROD, + /* 0x47 */ item_getcheck_func_W_HOOKSHOT, + /* 0x48 */ item_getcheck_func_KANTERA, + /* 0x49 */ item_getcheck_func_LIGHT_SWORD, + /* 0x4A */ item_getcheck_func_FISHING_ROD_1, + /* 0x4B */ item_getcheck_func_PACHINKO, + /* 0x4C */ item_getcheck_func_COPY_ROD_2, + /* 0x4D */ item_getcheck_func_KAKARIKO_GORGE_PORTAL, + /* 0x4E */ item_getcheck_func_KAKARIKO_VILLAGE_PORTAL, + /* 0x4F */ item_getcheck_func_BOMB_BAG_LV2, + /* 0x50 */ item_getcheck_func_BOMB_BAG_LV1, + /* 0x51 */ item_getcheck_func_BOMB_IN_BAG, + /* 0x52 */ item_getcheck_func_DEATH_MOUNTAIN_PORTAL, + /* 0x53 */ item_getcheck_func_LIGHT_ARROW, + /* 0x54 */ item_getcheck_func_ARROW_LV1, + /* 0x55 */ item_getcheck_func_ARROW_LV2, + /* 0x56 */ item_getcheck_func_ARROW_LV3, + /* 0x57 */ item_getcheck_func_ZORAS_DOMAIN_PORTAL, + /* 0x58 */ item_getcheck_func_LURE_ROD, + /* 0x59 */ item_getcheck_func_BOMB_ARROW, + /* 0x5A */ item_getcheck_func_HAWK_ARROW, + /* 0x5B */ item_getcheck_func_BEE_ROD, + /* 0x5C */ item_getcheck_func_JEWEL_ROD, + /* 0x5D */ item_getcheck_func_WORM_ROD, + /* 0x5E */ item_getcheck_func_JEWEL_BEE_ROD, + /* 0x5F */ item_getcheck_func_JEWEL_WORM_ROD, + /* 0x60 */ item_getcheck_func_EMPTY_BOTTLE, + /* 0x61 */ item_getcheck_func_RED_BOTTLE, + /* 0x62 */ item_getcheck_func_GREEN_BOTTLE, + /* 0x63 */ item_getcheck_func_BLUE_BOTTLE, + /* 0x64 */ item_getcheck_func_MILK_BOTTLE, + /* 0x65 */ item_getcheck_func_HALF_MILK_BOTTLE, + /* 0x66 */ item_getcheck_func_OIL_BOTTLE, + /* 0x67 */ item_getcheck_func_WATER_BOTTLE, + /* 0x68 */ item_getcheck_func_OIL_BOTTLE2, + /* 0x69 */ item_getcheck_func_RED_BOTTLE2, + /* 0x6A */ item_getcheck_func_UGLY_SOUP, + /* 0x6B */ item_getcheck_func_HOT_SPRING, + /* 0x6C */ item_getcheck_func_FAIRY_BOTTLE, + /* 0x6D */ item_getcheck_func_HOT_SPRING2, + /* 0x6E */ item_getcheck_func_OIL2, + /* 0x6F */ item_getcheck_func_OIL, + /* 0x70 */ item_getcheck_func_NORMAL_BOMB, + /* 0x71 */ item_getcheck_func_WATER_BOMB, + /* 0x72 */ item_getcheck_func_POKE_BOMB, + /* 0x73 */ item_getcheck_func_FAIRY_DROP, + /* 0x74 */ item_getcheck_func_WORM, + /* 0x75 */ item_getcheck_func_DROP_BOTTLE, + /* 0x76 */ item_getcheck_func_BEE_CHILD, + /* 0x77 */ item_getcheck_func_CHUCHU_RARE, + /* 0x78 */ item_getcheck_func_CHUCHU_RED, + /* 0x79 */ item_getcheck_func_CHUCHU_BLUE, + /* 0x7A */ item_getcheck_func_CHUCHU_GREEN, + /* 0x7B */ item_getcheck_func_CHUCHU_YELLOW, + /* 0x7C */ item_getcheck_func_CHUCHU_PURPLE, + /* 0x7D */ item_getcheck_func_LV1_SOUP, + /* 0x7E */ item_getcheck_func_LV2_SOUP, + /* 0x7F */ item_getcheck_func_LV3_SOUP, + /* 0x80 */ item_getcheck_func_LETTER, + /* 0x81 */ item_getcheck_func_BILL, + /* 0x82 */ item_getcheck_func_WOOD_STATUE, + /* 0x83 */ item_getcheck_func_IRIAS_PENDANT, + /* 0x84 */ item_getcheck_func_HORSE_FLUTE, + /* 0x85 */ item_getcheck_func_noentry, + /* 0x86 */ item_getcheck_func_noentry, + /* 0x87 */ item_getcheck_func_noentry, + /* 0x88 */ item_getcheck_func_noentry, + /* 0x89 */ item_getcheck_func_noentry, + /* 0x8A */ item_getcheck_func_noentry, + /* 0x8B */ item_getcheck_func_noentry, + /* 0x8C */ item_getcheck_func_noentry, + /* 0x8D */ item_getcheck_func_noentry, + /* 0x8E */ item_getcheck_func_CAMP_SMALL_KEY, + /* 0x8F */ item_getcheck_func_LAKE_HYLIA_PORTAL, + /* 0x90 */ item_getcheck_func_RAFRELS_MEMO, + /* 0x91 */ item_getcheck_func_ASHS_SCRIBBLING, + /* 0x92 */ item_getcheck_func_noentry, + /* 0x93 */ item_getcheck_func_noentry, + /* 0x94 */ item_getcheck_func_noentry, + /* 0x95 */ item_getcheck_func_noentry, + /* 0x96 */ item_getcheck_func_noentry, + /* 0x97 */ item_getcheck_func_noentry, + /* 0x98 */ item_getcheck_func_noentry, + /* 0x99 */ item_getcheck_func_noentry, + /* 0x9A */ item_getcheck_func_noentry, + /* 0x9B */ item_getcheck_func_noentry, + /* 0x9C */ item_getcheck_func_CHUCHU_YELLOW2, + /* 0x9D */ item_getcheck_func_OIL_BOTTLE3, + /* 0x9E */ item_getcheck_func_SHOP_BEE_CHILD, + /* 0x9F */ item_getcheck_func_CHUCHU_BLACK, + /* 0xA0 */ item_getcheck_func_LIGHT_DROP, + /* 0xA1 */ item_getcheck_func_DROP_CONTAINER, + /* 0xA2 */ item_getcheck_func_DROP_CONTAINER02, + /* 0xA3 */ item_getcheck_func_DROP_CONTAINER03, + /* 0xA4 */ item_getcheck_func_FILLED_CONTAINER, + /* 0xA5 */ item_getcheck_func_MIRROR_PIECE_2, + /* 0xA6 */ item_getcheck_func_MIRROR_PIECE_3, + /* 0xA7 */ item_getcheck_func_MIRROR_PIECE_4, + /* 0xA8 */ item_getcheck_func_noentry, + /* 0xA9 */ item_getcheck_func_noentry, + /* 0xAA */ item_getcheck_func_noentry, + /* 0xAB */ item_getcheck_func_noentry, + /* 0xAC */ item_getcheck_func_noentry, + /* 0xAD */ item_getcheck_func_noentry, + /* 0xAE */ item_getcheck_func_MIRROR_CHAMBER_PORTAL, + /* 0xAF */ item_getcheck_func_SNOWPEAK_PORTAL, + /* 0xB0 */ item_getcheck_func_SMELL_YELIA_POUCH, + /* 0xB1 */ item_getcheck_func_SMELL_PUMPKIN, + /* 0xB2 */ item_getcheck_func_SMELL_POH, + /* 0xB3 */ item_getcheck_func_SMELL_FISH, + /* 0xB4 */ item_getcheck_func_SMELL_CHILDREN, + /* 0xB5 */ item_getcheck_func_SMELL_MEDICINE, + /* 0xB6 */ item_getcheck_func_noentry, + /* 0xB7 */ item_getcheck_func_noentry, + /* 0xB8 */ item_getcheck_func_noentry, + /* 0xB9 */ item_getcheck_func_noentry, + /* 0xBA */ item_getcheck_func_noentry, + /* 0xBB */ item_getcheck_func_noentry, + /* 0xBC */ item_getcheck_func_noentry, + /* 0xBD */ item_getcheck_func_noentry, + /* 0xBE */ item_getcheck_func_noentry, + /* 0xBF */ item_getcheck_func_SACRED_GROVE_PORTAL, + /* 0xC0 */ item_getcheck_func_M_BEETLE, + /* 0xC1 */ item_getcheck_func_F_BEETLE, + /* 0xC2 */ item_getcheck_func_M_BUTTERFLY, + /* 0xC3 */ item_getcheck_func_F_BUTTERFLY, + /* 0xC4 */ item_getcheck_func_M_STAG_BEETLE, + /* 0xC5 */ item_getcheck_func_F_STAG_BEETLE, + /* 0xC6 */ item_getcheck_func_M_GRASSHOPPER, + /* 0xC7 */ item_getcheck_func_F_GRASSHOPPER, + /* 0xC8 */ item_getcheck_func_M_NANAFUSHI, + /* 0xC9 */ item_getcheck_func_F_NANAFUSHI, + /* 0xCA */ item_getcheck_func_M_DANGOMUSHI, + /* 0xCB */ item_getcheck_func_F_DANGOMUSHI, + /* 0xCC */ item_getcheck_func_M_MANTIS, + /* 0xCD */ item_getcheck_func_F_MANTIS, + /* 0xCE */ item_getcheck_func_M_LADYBUG, + /* 0xCF */ item_getcheck_func_F_LADYBUG, + /* 0xD0 */ item_getcheck_func_M_SNAIL, + /* 0xD1 */ item_getcheck_func_F_SNAIL, + /* 0xD2 */ item_getcheck_func_M_DRAGONFLY, + /* 0xD3 */ item_getcheck_func_F_DRAGONFLY, + /* 0xD4 */ item_getcheck_func_M_ANT, + /* 0xD5 */ item_getcheck_func_F_ANT, + /* 0xD6 */ item_getcheck_func_M_MAYFLY, + /* 0xD7 */ item_getcheck_func_F_MAYFLY, + /* 0xD8 */ item_getcheck_func_FUSED_SHADOW_1, + /* 0xD9 */ item_getcheck_func_FUSED_SHADOW_2, + /* 0xDA */ item_getcheck_func_FUSED_SHADOW_3, + /* 0xDB */ item_getcheck_func_MIRROR_PIECE_1, + /* 0xDC */ item_getcheck_func_noentry, + /* 0xDD */ item_getcheck_func_noentry, + /* 0xDE */ item_getcheck_func_noentry, + /* 0xDF */ item_getcheck_func_noentry, + /* 0xE0 */ item_getcheck_func_POU_SPIRIT, + /* 0xE1 */ item_getcheck_func_ENDING_BLOW, + /* 0xE2 */ item_getcheck_func_SHIELD_ATTACK, + /* 0xE3 */ item_getcheck_func_BACK_SLICE, + /* 0xE4 */ item_getcheck_func_HELM_SPLITTER, + /* 0xE5 */ item_getcheck_func_MORTAL_DRAW, + /* 0xE6 */ item_getcheck_func_JUMP_STRIKE, + /* 0xE7 */ item_getcheck_func_GREAT_SPIN, + /* 0xE8 */ item_getcheck_func_ELDIN_BRIDGE_PORTAL, + /* 0xE9 */ item_getcheck_func_ANCIENT_DOCUMENT, + /* 0xEA */ item_getcheck_func_AIR_LETTER, + /* 0xEB */ item_getcheck_func_ANCIENT_DOCUMENT2, + /* 0xEC */ item_getcheck_func_LV7_DUNGEON_EXIT, + /* 0xED */ item_getcheck_func_LINKS_SAVINGS, + /* 0xEE */ item_getcheck_func_SMALL_KEY2, + /* 0xEF */ item_getcheck_func_POU_FIRE1, + /* 0xF0 */ item_getcheck_func_POU_FIRE2, + /* 0xF1 */ item_getcheck_func_POU_FIRE3, + /* 0xF2 */ item_getcheck_func_POU_FIRE4, + /* 0xF3 */ item_getcheck_func_BOSSRIDER_KEY, + /* 0xF4 */ item_getcheck_func_TOMATO_PUREE, + /* 0xF5 */ item_getcheck_func_TASTE, + /* 0xF6 */ item_getcheck_func_LV5_BOSS_KEY, + /* 0xF7 */ item_getcheck_func_SURFBOARD, + /* 0xF8 */ item_getcheck_func_KANTERA2, + /* 0xF9 */ item_getcheck_func_L2_KEY_PIECES1, + /* 0xFA */ item_getcheck_func_L2_KEY_PIECES2, + /* 0xFB */ item_getcheck_func_L2_KEY_PIECES3, + /* 0xFC */ item_getcheck_func_KEY_OF_CARAVAN, + /* 0xFD */ item_getcheck_func_LV2_BOSS_KEY, + /* 0xFE */ item_getcheck_func_KEY_OF_FILONE, + /* 0xFF */ item_getcheck_func_noentry, +}; + +namespace randomizer::item { +void exec_item_get(u8 item_no) { + item_no = verifyProgressiveItem(item_no); + g_randomizerState.mUpdateTracker = true; + dComIfGs_onItemFirstBit(item_no); + item_func_ptr_randomizer[item_no](); +} + +int check_item_get(u8 item_no, int param) { + switch (item_no) { + case dItemNo_Randomizer_HYLIA_SHIELD_e: + // Check if we are at Kakariko Malo mart and verify that we have not bought the shield. + if (playerIsInRoomStage(3, allStages[Kakariko_Village_Interiors]) && + !dComIfGs_isEventBit(BOUGHT_HYLIAN_SHIELD_AT_MALO_MART)) { + // Return false so we can buy the shield. + return 0; + } + break; + case dItemNo_Randomizer_HAWK_EYE_e: + // Check if we are at Kakariko Village and that the hawkeye is currently not for sale. + if (getStageID() == Kakariko_Village && !dComIfGs_isSwitch(0x3E, 0)) { + // Return false so we can buy the hawkeye. + return 0; + } + break; + case dItemNo_Randomizer_SHIELD_e: + case dItemNo_Randomizer_WOOD_SHIELD_e: + // Check if we are at Kakariko Malo mart and that the Wooden Shield has not been bought. + if (playerIsInRoomStage(3, allStages[Kakariko_Village_Interiors]) && + !dComIfGs_isSwitch(0x5, 3)) { + // Return false so we can buy the shield. + return 0; + } + break; + case dItemNo_Randomizer_TOMATO_PUREE_e: + case dItemNo_Randomizer_TASTE_e: + // Check to see if currently in Snowpeak Ruins + if (getStageID() == Snowpeak_Ruins) { + // Return false so that yeta will give the map item no matter what. + return 0; + } + break; + case dItemNo_Randomizer_IRONBALL_e: + // Check to see if currently in Snowpeak Ruins Darkhammer room + if (getStageID() == Darkhammer) { + return dComIfGs_isSwitch(0x5F, 51); // Picked up the Ball and Chain check. + } + } + + int result = item_getcheck_func_ptr_randomizer[item_no](); + + if (result == -1) { + result = param; + } + + return result; +} + +} // namespace randomizer::item + +namespace randomizer::item { + +dItem_itemResource item_resource_randomizer[] = { + /* 0x00 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x01 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x3C, 0x0000}, + /* 0x02 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x1, -0x1, 0x002D, 0x3C, 0x0000}, + /* 0x03 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x2, -0x1, 0x002D, 0x3C, 0x0000}, + /* 0x04 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x3, -0x1, 0x002D, 0x3C, 0x0000}, + /* 0x05 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x4, -0x1, 0x002D, 0x3C, 0x0000}, + /* 0x06 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x5, -0x1, 0x002D, 0x3C, 0x0000}, + /* 0x07 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x6, -0x1, 0x002D, 0x3C, 0x0000}, + /* 0x08 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x09 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x0A */ {"O_gD_bomb", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000C, 0x64, 0x019F}, + /* 0x0B */ {"O_gD_bomb", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000C, 0x64, 0x019F}, + /* 0x0C */ {"O_gD_bomb", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000C, 0x64, 0x019F}, + /* 0x0D */ {"O_gD_bomb", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000C, 0x64, 0x019F}, + /* 0x0E */ {"O_gD_arow", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0053, 0x64, 0x0000}, + /* 0x0F */ {"O_gD_arow", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0053, 0x64, 0x0000}, + /* 0x10 */ {"O_gD_arow", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0053, 0x64, 0x0000}, + /* 0x11 */ {"O_gD_arow", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0053, 0x64, 0x0000}, + /* 0x12 */ {"O_gD_tane", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x005C, 0x64, 0x0000}, + /* 0x13 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x14 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x15 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x16 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000D, 0x64, 0x0000}, + /* 0x17 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000D, 0x64, 0x0000}, + /* 0x18 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000D, 0x64, 0x0000}, + /* 0x19 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000D, 0x64, 0x0000}, + /* 0x1A */ {"O_gD_BI", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000B, 0x64, 0x0000}, + /* 0x1B */ {"O_gD_BI", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000B, 0x64, 0x0000}, + /* 0x1C */ {"O_gD_BI", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000B, 0x64, 0x0000}, + /* 0x1D */ {"O_gD_BI", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000B, 0x64, 0x0000}, + /* 0x1E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x1F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x20 */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x21 */ {"O_gD_hutk", 0x0008,-0x0001, 0x0005, 0x000B,-0x0001, -0x1, -0x1, 0x0050, 0x64, 0x0194}, + /* 0x22 */ {"O_gD_hutu", 0x0008,-0x0001, 0x0005, 0x000B,-0x0001, -0x1, -0x1, 0x0051, 0x64, 0x0000}, + /* 0x23 */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0x24 */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0x25 */ {"O_gD_TKS", 0x0008,-0x0001, 0x0005,-0x0001, 0x000B, -0x1, -0x1, 0x0028, 0x64, 0x0191}, + /* 0x26 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0021, 0x64,-0x0001}, + /* 0x27 */ {"O_gD_TKC", 0x0009, 0x000C, 0x0006,-0x0001, 0x000F, -0x1, -0x1, 0x0026, 0x64, 0x0191}, + /* 0x28 */ {"O_gD_SWA", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0070, 0x6E, 0x0195}, + /* 0x29 */ {"MstrSword", 0x0005,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0042, 0x6E, 0x0195}, + /* 0x2A */ {"T_gD_SHB", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0074, 0x6E, 0x01B5}, + /* 0x2B */ {"O_gD_SHC", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0040, 0x6E, 0x0196}, + /* 0x2C */ {"O_gD_SHA", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x003C, 0x64, 0x01B4}, + /* 0x2D */ {"O_gD_mem2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0022, 0x64, 0x01A8}, + /* 0x2E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x006B, 0x64, 0x0192}, + /* 0x2F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x006B, 0x64, 0x0000}, + /* 0x30 */ {"O_gD_marm", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0041, 0x64, 0x01A9}, + /* 0x31 */ {"O_gD_zora", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004D, 0x64, 0x01AA}, + /* 0x32 */ {"O_gD_Injy", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002E, 0x64, 0x0000}, + /* 0x33 */ {"O_gD_TKS", 0x0008,-0x0001, 0x0005,-0x0001,-0x0001, -0x1, -0x1, 0x0028, 0x64, 0x0000}, + /* 0x34 */ {"O_gD_puL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0047, 0x64, 0x0000}, + /* 0x35 */ {"O_gD_puL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0048, 0x64, 0x0000}, + /* 0x36 */ {"O_gD_puL3", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0049, 0x64, 0x01AD}, + /* 0x37 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0050, 0x64, 0x019F}, + /* 0x38 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0050, 0x64, 0x01AF}, + /* 0x39 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0050, 0x64, 0x01B0}, + /* 0x3A */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0050, 0x64, 0x0000}, + /* 0x3B */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0072, 0x6E, 0x0000}, + /* 0x3C */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x01B3}, + /* 0x3D */ {"O_gD_sang", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002A, 0x64, 0x0194}, + /* 0x3E */ {"O_gD_hawk", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0059, 0x64, 0x0000}, + /* 0x3F */ {"O_gD_SWB", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0024, 0x6E, 0x0000}, + /* 0x40 */ {"O_gD_boom", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0062, 0x6E, 0x01AB}, + /* 0x41 */ {"O_gD_SP", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002B, 0x5F, 0x01A6}, + /* 0x42 */ {"O_gD_IB", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0020, 0x78, 0x0000}, + /* 0x43 */ {"O_gD_bow", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0069, 0x64, 0x01A5}, + /* 0x44 */ {"O_gD_HS", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006A, 0x78, 0x01AC}, + /* 0x45 */ {"O_gD_boot", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0025, 0x64, 0x01A7}, + /* 0x46 */ {"O_gD_CROD", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0016, 0x6E, 0x019E}, + /* 0x47 */ {"O_gD_HS", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002C, 0x82, 0x01B5}, + /* 0x48 */ {"T_gD_kt", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006C, 0x6E, 0x0193}, + /* 0x49 */ {"MstrSword", 0x0005,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0042, 0x6E, 0x0195}, + /* 0x4A */ {"O_gD_uktr", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0018, 0x64, 0x01B7}, + /* 0x4B */ {"O_gD_pach", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x005B, 0x64, 0x01B8}, + /* 0x4C */ {"O_gD_CROD", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0016, 0x6E, 0x01B9}, + /* 0x4D */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x01BA}, + /* 0x4E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0197}, + /* 0x4F */ {"O_gD_bmL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0056, 0x64, 0x0198}, + /* 0x50 */ {"O_gD_bomc", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, 0x0, -0x1, 0x0055, 0x64, 0x01BD}, + /* 0x51 */ {"O_gD_bomc", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0055, 0x64, 0x0000}, + /* 0x52 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0056, 0x64, 0x0000}, + /* 0x53 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x54 */ {"O_gD_quL1", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004A, 0x64, 0x01B1}, + /* 0x55 */ {"O_gD_quL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004B, 0x64, 0x01F0}, + /* 0x56 */ {"O_gD_quL3", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004C, 0x64, 0x0000}, + /* 0x57 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x58 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x004E, 0x64, 0x0000}, + /* 0x59 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x000C, 0x64, 0x0000}, + /* 0x5A */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0059, 0x64, 0x0000}, + /* 0x5B */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0019, 0x64, 0x0000}, + /* 0x5C */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x001B, 0x64, 0x0000}, + /* 0x5D */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x001A, 0x64, 0x0000}, + /* 0x5E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x001C, 0x64, 0x0000}, + /* 0x5F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x001D, 0x64, 0x0000}, + /* 0x60 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x0, 0x2, 0x0011, 0x50, 0x01BE}, + /* 0x61 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x0, 0x0, 0x0011, 0x50, 0x01BF}, + /* 0x62 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x1, 0x0, 0x0011, 0x50, 0x01C0}, + /* 0x63 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x2, 0x0, 0x0011, 0x50, 0x01C1}, + /* 0x64 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x3, 0x0, 0x0011, 0x50, 0x0000}, + /* 0x65 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x3, 0x1, 0x0012, 0x50, 0x0000}, + /* 0x66 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x4, 0x0, 0x0011, 0x50, 0x01A1}, + /* 0x67 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x5, 0x0, 0x0011, 0x50, 0x01AE}, + /* 0x68 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x4, 0x0, 0x0011, 0x50, 0x01A1}, + /* 0x69 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x0, 0x0, 0x0011, 0x50, 0x01BF}, + /* 0x6A */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x8, 0x0, 0x0011, 0x50, 0x0000}, + /* 0x6B */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x5, 0x0, 0x0011, 0x50, 0x0000}, + /* 0x6C */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0011, 0x50, 0x0000}, + /* 0x6D */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x5, 0x0, 0x0011, 0x50, 0x0000}, + /* 0x6E */ {"Obj_kntr", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006C, 0x6E, 0x0000}, + /* 0x6F */ {"Obj_kntr", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006C, 0x6E, 0x0000}, + /* 0x70 */ {"O_gD_bmL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000C, 0x64, 0x0198}, + /* 0x71 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000D, 0x64, 0x0000}, + /* 0x72 */ {"O_gD_BI", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x000B, 0x64, 0x0000}, + /* 0x73 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0xB, 0x0, 0x0011, 0x50, 0x0000}, + /* 0x74 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0011, 0x50, 0x0000}, + /* 0x75 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0xB, 0x0, 0x0011, 0x50, 0x0000}, + /* 0x76 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0011, 0x50, 0x0000}, + /* 0x77 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x7, 0x0, 0x0011, 0x50, 0x01BF}, + /* 0x78 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x0, 0x0, 0x0011, 0x50, 0x01BF}, + /* 0x79 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x2, 0x0, 0x0011, 0x50, 0x01BF}, + /* 0x7A */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x1, 0x0, 0x0011, 0x50, 0x01BF}, + /* 0x7B */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x4, 0x0, 0x0011, 0x50, 0x01BF}, + /* 0x7C */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x6, 0x0, 0x0011, 0x50, 0x01BF}, + /* 0x7D */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0013, 0x50, 0x0000}, + /* 0x7E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0013, 0x50, 0x0000}, + /* 0x7F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0013, 0x50, 0x01CF}, + /* 0x80 */ {"O_gD_lttr", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x005A, 0x64, 0x0000}, + /* 0x81 */ {"O_gD_bill", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0054, 0x64, 0x0000}, + /* 0x82 */ {"O_gD_wood", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0058, 0x64, 0x0000}, + /* 0x83 */ {"O_gD_pend", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x005D, 0x64, 0x0000}, + /* 0x84 */ {"O_gD_pend", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x005D, 0x64, 0x0000}, + /* 0x85 */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x86 */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x87 */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x88 */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x89 */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x8A */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x8B */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x8C */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x8D */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x8E */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0079}, + /* 0x8F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0x90 */ {"O_gD_mem2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0022, 0x64, 0x0000}, + /* 0x91 */ {"O_gD_mem2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0022, 0x64, 0x0000}, + /* 0x92 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0021, 0x64,-0x0001}, + /* 0x93 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0021, 0x64,-0x0001}, + /* 0x94 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0021, 0x64,-0x0001}, + /* 0x95 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0021, 0x64,-0x0001}, + /* 0x96 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0021, 0x64,-0x0001}, + /* 0x97 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0021, 0x64,-0x0001}, + /* 0x98 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0021, 0x64,-0x0001}, + /* 0x99 */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0x9A */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0x9B */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0x9C */ {"Obj_kntr", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006C, 0x6E, 0x0000}, + /* 0x9D */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x4, 0x0, 0x0011, 0x50, 0x01A1}, + /* 0x9E */ {"O_gD_hk_s", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0011, 0x50, 0x0000}, + /* 0x9F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0011, 0x50, 0x0000}, + /* 0xA0 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xA1 */ {"N_gD_Lpod", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0052, 0x64, 0x0000}, + /* 0xA2 */ {"N_gD_Lpod", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0052, 0x64, 0x0000}, + /* 0xA3 */ {"N_gD_Lpod", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0052, 0x64, 0x0000}, + /* 0xA4 */ {"N_gD_Lpod", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0052, 0x64, 0x0000}, + /* 0xA5 */ {"MirrorB", 0x0009,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xA6 */ {"MirrorB", 0x0009,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xA7 */ {"MirrorB", 0x0009,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xA8 */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0xA9 */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0xAA */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0xAB */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0xAC */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0xAD */ {"T_gD_kmps", 0x0007,-0x0001, 0x0004,-0x0001,-0x0001, -0x1, -0x1, 0x006F, 0x64, 0x0198}, + /* 0xAE */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xAF */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xB0 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xB1 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xB2 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xB3 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xB4 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xB5 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xB6 */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xB7 */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xB8 */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xB9 */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xBA */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xBB */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xBC */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xBD */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xBE */ {"T_gD_map", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0071, 0x64, 0x0197}, + /* 0xBF */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x0011, 0x50, 0x0000}, + /* 0xC0 */ {"O_gD_kabo", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0030, 0x50, 0x0000}, + /* 0xC1 */ {"O_gD_kabm", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x002F, 0x50, 0x0000}, + /* 0xC2 */ {"O_gD_choo", 0x0009, 0x000F, 0x0006, 0x000C,-0x0001, -0x1, -0x1, 0x0008, 0x50, 0x0000}, + /* 0xC3 */ {"O_gD_chom", 0x0009, 0x000F, 0x0006, 0x000C,-0x0001, -0x1, -0x1, 0x0007, 0x50, 0x0000}, + /* 0xC4 */ {"O_gD_kuwo", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0038, 0x50, 0x0000}, + /* 0xC5 */ {"O_gD_kuwm", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0037, 0x50, 0x0000}, + /* 0xC6 */ {"O_gD_bato", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0006, 0x50, 0x0000}, + /* 0xC7 */ {"O_gD_batm", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0005, 0x50, 0x0000}, + /* 0xC8 */ {"O_gD_nano", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x003B, 0x50, 0x0000}, + /* 0xC9 */ {"O_gD_nanm", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x003A, 0x50, 0x0000}, + /* 0xCA */ {"O_gD_dano", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x000A, 0x50, 0x0000}, + /* 0xCB */ {"O_gD_danm", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0009, 0x50, 0x0000}, + /* 0xCC */ {"O_gD_kamo", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0034, 0x50, 0x0000}, + /* 0xCD */ {"O_gD_kamm", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0033, 0x50, 0x0000}, + /* 0xCE */ {"O_gD_teno", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x005F, 0x50, 0x0000}, + /* 0xCF */ {"O_gD_tenm", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x005E, 0x50, 0x0000}, + /* 0xD0 */ {"O_gD_kato", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0036, 0x50, 0x0000}, + /* 0xD1 */ {"O_gD_katm", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0035, 0x50, 0x0000}, + /* 0xD2 */ {"O_gD_tono", 0x0009, 0x000F, 0x0006, 0x000C,-0x0001, -0x1, -0x1, 0x0061, 0x50, 0x0000}, + /* 0xD3 */ {"O_gD_tonm", 0x0009, 0x000F, 0x0006, 0x000C,-0x0001, -0x1, -0x1, 0x0060, 0x50, 0x0000}, + /* 0xD4 */ {"O_gD_ario", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0004, 0x50, 0x0000}, + /* 0xD5 */ {"O_gD_arim", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x0003, 0x50, 0x0000}, + /* 0xD6 */ {"O_gD_kago", 0x0009, 0x000F, 0x0006, 0x000C,-0x0001, -0x1, -0x1, 0x0032, 0x50, 0x0000}, + /* 0xD7 */ {"O_gD_kagm", 0x0009, 0x000F, 0x0006, 0x000C,-0x0001, -0x1, -0x1, 0x0031, 0x50, 0x0000}, + /* 0xD8 */ {"N_gD_mskF", 0x0004,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xD9 */ {"N_gD_mskB", 0x0004,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xDA */ {"N_gD_mskT", 0x0004,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xDB */ {"MirrorB", 0x0009,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xDC */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xDD */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xDE */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xDF */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xE0 */ {"O_gD_tama", 0x0005, 0x000B,-0x0001, 0x0008,-0x0001, -0x1, -0x1, 0x003E, 0x64, 0x0000}, + /* 0xE1 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x003D, 0x64, 0x0000}, + /* 0xE2 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x003D, 0x64, 0x0000}, + /* 0xE3 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x003D, 0x64, 0x0000}, + /* 0xE4 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x003D, 0x64, 0x0000}, + /* 0xE5 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x003D, 0x64, 0x0000}, + /* 0xE6 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x003D, 0x64, 0x0000}, + /* 0xE7 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, -0x0001,-0x0001, 0x0, -0x1, 0x003D, 0x64, 0x0000}, + /* 0xE8 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x002D, 0x64, 0x0000}, + /* 0xE9 */ {"O_gD_komo", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004F, 0x64, 0x0000}, + /* 0xEA */ {"O_gD_komo", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004F, 0x64, 0x0000}, + /* 0xEB */ {"O_gD_komo", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004F, 0x64, 0x0000}, + /* 0xEC */ {"O_gD_TKC", 0x0009, 0x000C, 0x0006,-0x0001, 0x000F, -0x1, -0x1, 0x0026, 0x64, 0x0191}, + /* 0xED */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x4, -0x1, 0x002D, 0x3C, 0x0000}, + /* 0xEE */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0000}, + /* 0xEF */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x0, 0x2, 0x0011, 0x50, 0x0000}, + /* 0xF0 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x0, 0x2, 0x0011, 0x50, 0x0000}, + /* 0xF1 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x0, 0x2, 0x0011, 0x50, 0x0000}, + /* 0xF2 */ {"O_gD_bott", 0x0006, 0x000C,-0x0001, 0x0009, 0x000F, 0x0, 0x2, 0x0011, 0x50, 0x0000}, + /* 0xF3 */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0000}, + /* 0xF4 */ {"O_gD_pump", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0029, 0x64, 0x0000}, + /* 0xF5 */ {"O_gD_chee", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0015, 0x64, 0x0000}, + /* 0xF6 */ {"O_gD_bkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x003F, 0x64, 0x0000}, + /* 0xF7 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x003C, 0x64, 0x0000}, + /* 0xF8 */ {"T_gD_kt", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006C, 0x6E, 0x0000}, + /* 0xF9 */ {"D_MKey_01", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0043, 0x78, 0x0000}, + /* 0xFA */ {"D_MKey_02", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0045, 0x78, 0x0000}, + /* 0xFB */ {"D_MKey_03", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0046, 0x78, 0x0000}, + /* 0xFC */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0000}, + /* 0xFD */ {"O_gD_Mkey", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0017, 0x78, 0x0000}, + /* 0xFE */ {"T_gD_key", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x006E, 0x64, 0x0000}, +}; + +dItem_fieldItemResource field_item_res_randomizer[] = { + /* 0x00 */ {"Always", 0x0014,-0x0001, 0x0030, 0xFF, 0x1000}, + /* 0x01 */ {"Always", 0x0017,-0x0001, 0x0031, 0x0, 0x1000}, + /* 0x02 */ {"Always", 0x0017,-0x0001, 0x0031, 0x1, 0x1000}, + /* 0x03 */ {"Always", 0x0017,-0x0001, 0x0031, 0x2, 0x1000}, + /* 0x04 */ {"Always", 0x0017,-0x0001, 0x0031, 0x3, 0x1000}, + /* 0x05 */ {"Always", 0x0017,-0x0001, 0x0031, 0x4, 0x1000}, + /* 0x06 */ {"Always", 0x0017,-0x0001, 0x0031, 0x5, 0x1000}, + /* 0x07 */ {"Always", 0x0017,-0x0001, 0x0031, 0x6, 0x1000}, + /* 0x08 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x09 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x0A */ {"O_gD_bomb", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x0B */ {"O_gD_bomb", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x0C */ {"O_gD_bomb", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x0D */ {"O_gD_bomb", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x0E */ {"Always", 0x0023,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x0F */ {"Always", 0x0023,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x10 */ {"Always", 0x0023,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x11 */ {"Always", 0x0022,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x12 */ {"Always", 0x0024,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x13 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x14 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x15 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x16 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x17 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x18 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x19 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x1A */ {"O_gD_BI", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x1B */ {"O_gD_BI", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x1C */ {"O_gD_BI", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x1D */ {"O_gD_BI", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x1E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x1F */ {"Always", 0x0014,-0x0001, 0x0030, 0xFF, 0x1000}, + /* 0x20 */ {"T_g_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x21 */ {"Always", 0x0015, 0x000D, 0x0033, 0xFF, 0x1000}, + /* 0x22 */ {"Always", 0x0016, 0x000E, 0x0034, 0xFF, 0x1000}, + /* 0x23 */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x24 */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x25 */ {"O_gD_TKS", 0x0008,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x26 */ {"T_g_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x27 */ {"O_gD_TKC", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x28 */ {"O_g_SWA", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x29 */ {"MstrSword", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x2A */ {"T_gD_SHB", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x2B */ {"O_gD_SHC", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x2C */ {"O_gD_SHA", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x2D */ {"O_gD_mem2", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x2E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x2F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x30 */ {"O_gD_marm", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x31 */ {"O_gD_ZORA", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x32 */ {"O_gD_Injy", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x33 */ {"O_gD_TKS", 0x0008,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x34 */ {"O_gD_puL2", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x35 */ {"O_gD_puL2", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x36 */ {"O_gD_puL3", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x37 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x38 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x39 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x3A */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x3B */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x3C */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x3D */ {"O_gD_sang", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x3E */ {"O_gD_hawk", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x3F */ {"O_gD_SWB", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x40 */ {"O_gD_boom", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x41 */ {"O_gD_SP", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x42 */ {"O_gD_IB", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x43 */ {"O_gD_bow", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x44 */ {"O_gD_HS", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x45 */ {"O_gD_boot", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x46 */ {"O_gD_CROD", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x47 */ {"O_gD_HS", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x48 */ {"T_gD_kt", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x49 */ {"MstrSword", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x4A */ {"O_gD_uktr", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x4B */ {"O_gD_pach", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x4C */ {"O_gD_CROD", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x4D */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x4E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x4F */ {"O_gD_bmL2", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x50 */ {"O_gD_bomc", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x51 */ {"O_gD_bomc", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x52 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x53 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x54 */ {"O_gD_quL1", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x55 */ {"O_gD_quL2", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x56 */ {"O_gD_quL3", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x57 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x58 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x59 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x5A */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x5B */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x5C */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x5D */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x5E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x5F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x60 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x61 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x62 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x63 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x64 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x65 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x66 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x67 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x68 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x69 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x6A */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x6B */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x6C */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x6D */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x6E */ {"Obj_kntr", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x6F */ {"Obj_kntr", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x70 */ {"O_gD_bmL2", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x71 */ {"O_gD_PG", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x72 */ {"O_gD_BI", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x73 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x74 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x75 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x76 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x77 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x78 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x79 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x7A */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x7B */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x7C */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x7D */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x7E */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x7F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x80 */ {"O_gD_lttr", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x81 */ {"O_gD_bill", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x82 */ {"O_wood", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x83 */ {"O_gD_pend", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x84 */ {"O_gD_pend", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x85 */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x86 */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x87 */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x88 */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x89 */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x8A */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x8B */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x8C */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x8D */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x8E */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x8F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x90 */ {"O_gD_mem2", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x91 */ {"O_gD_mem2", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x92 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x93 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x94 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x95 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x96 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x97 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x98 */ {"T_gD_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x99 */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x9A */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x9B */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x9C */ {"Obj_kntr", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x9D */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x9E */ {"O_gD_hk_s", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0x9F */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA0 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA1 */ {"N_gD_Lpod", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA2 */ {"N_gD_Lpod", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA3 */ {"N_gD_Lpod", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA4 */ {"N_gD_Lpod", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA5 */ {"MirrorB", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA6 */ {"MirrorB", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA7 */ {"MirrorB", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA8 */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xA9 */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xAA */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xAB */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xAC */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xAD */ {"T_gD_kmps", 0x0007,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xAE */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xAF */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB0 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB1 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB2 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB3 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB4 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB5 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB6 */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB7 */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB8 */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xB9 */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xBA */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xBB */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xBC */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xBD */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xBE */ {"T_gD_map", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xBF */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC0 */ {"O_gD_kabo", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC1 */ {"O_gD_kabm", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC2 */ {"O_gD_choo", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC3 */ {"O_gD_chom", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC4 */ {"O_gD_kuwo", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC5 */ {"O_gD_kuwm", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC6 */ {"O_gD_bato", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC7 */ {"O_gD_batm", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC8 */ {"O_gD_nano", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xC9 */ {"O_gD_nanm", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xCA */ {"O_gD_dano", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xCB */ {"O_gD_danm", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xCC */ {"O_gD_kamo", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xCD */ {"O_gD_kamm", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xCE */ {"O_gD_teno", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xCF */ {"O_gD_tenm", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD0 */ {"O_gD_kato", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD1 */ {"O_gD_katm", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD2 */ {"O_gD_tono", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD3 */ {"O_gD_tonm", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD4 */ {"O_gD_ario", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD5 */ {"O_gD_arim", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD6 */ {"O_gD_kago", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD7 */ {"O_gD_kagm", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD8 */ {"N_gD_mskF", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xD9 */ {"N_gD_mskB", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xDA */ {"N_gD_mskT", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xDB */ {"MirrorB", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xDC */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xDD */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xDE */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xDF */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE0 */ {"O_gD_tama", 0x0005,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE1 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE2 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE3 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE4 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE5 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE6 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE7 */ {"O_gD_memo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE8 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xE9 */ {"O_gD_komo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xEA */ {"O_gD_komo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xEB */ {"O_gD_komo", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xEC */ {"O_gD_TKC", 0x0009,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xED */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xEE */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xEF */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF0 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF1 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF2 */ {"O_gD_bott", 0x0006,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF3 */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF4 */ {"O_gD_pump", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF5 */ {"O_gD_chee", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF6 */ {"O_gD_bkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF7 */ {"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF8 */ {"T_gD_kt", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xF9 */ {"D_MKey_01", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xFA */ {"D_MKey_02", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xFB */ {"D_MKey_03", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xFC */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xFD */ {"O_gD_Mkey", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, + /* 0xFE */ {"T_gD_key", 0x0003,-0x0001,-0x0001, 0xFF, 0x1000}, +}; + +dItem_itemInfo item_info_randomizer[] = { + {15, 40, 30, 4}, {20, 70, 30, 4}, {20, 70, 30, 4}, {20, 70, 30, 4}, {20, 70, 30, 4}, + {20, 70, 30, 4}, {20, 70, 30, 4}, {20, 70, 30, 4}, {20, 40, 30, 4}, {20, 40, 30, 4}, + {40, 60, 30, 0}, {40, 60, 30, 0}, {40, 60, 30, 0}, {40, 60, 30, 0}, {20, 80, 50, 0}, + {20, 80, 50, 0}, {20, 80, 50, 0}, {20, 80, 50, 0}, {35, 70, 30, 0}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,0}, + {0, 70, 30,4}, {0, 70, 30,4}, {150, 40, 30, 17}, {230, 100, 50, 21}, {230, 100, 50, 21}, + {0, 70, 30,64}, {0, 70, 30,68}, {0, 70, 30,0}, {150, 70, 30,85}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,4}, {150, 60, 60, 255}, {0, 70, 30,0}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, {0, 70, 30,0}, + {0, 70, 30,4}, {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, {0, 70, 30,0}, {20, 255, 255, 4}, + {0, 70, 30,64}, {0, 70, 30,4}, {0, 70, 30,64}, {0, 70, 30,64}, {0, 70, 30,64}, + {0, 70, 30,64}, {0, 70, 30,64}, {150, 50, 20, 21}, {0, 70, 30,4}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,0}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30, 0}, {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {30, 50, 40, 5}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,0}, {0, 70, 30,4}, + {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,4}, {0, 70, 30,0}, {0, 70, 30,4}, + {0, 70, 30,0}, {0, 70, 30,4}, {0, 70, 30,4}, {150, 50, 20, 21}, {0, 70, 30,0}, + {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,0}, {0, 70, 30,0}, +}; + +static dItem_itemResource s_vanilla_item_resource[255]; +static dItem_fieldItemResource s_vanilla_field_item_res[255]; +static dItem_itemInfo s_vanilla_item_info[255]; +static bool s_applied = false; + +void apply_item_data_tables() { + if (s_applied) { + return; + } + std::memcpy(s_vanilla_item_resource, dItem_data::item_resource, sizeof(s_vanilla_item_resource)); + std::memcpy(s_vanilla_field_item_res, dItem_data::field_item_res, sizeof(s_vanilla_field_item_res)); + std::memcpy(s_vanilla_item_info, dItem_data::item_info, sizeof(s_vanilla_item_info)); + std::memcpy(dItem_data::item_resource, item_resource_randomizer, sizeof(item_resource_randomizer)); + std::memcpy(dItem_data::field_item_res, field_item_res_randomizer, sizeof(field_item_res_randomizer)); + std::memcpy(dItem_data::item_info, item_info_randomizer, sizeof(item_info_randomizer)); + s_applied = true; +} + +void restore_item_data_tables() { + if (!s_applied) { + return; + } + std::memcpy(dItem_data::item_resource, s_vanilla_item_resource, sizeof(s_vanilla_item_resource)); + std::memcpy(dItem_data::field_item_res, s_vanilla_field_item_res, sizeof(s_vanilla_field_item_res)); + std::memcpy(dItem_data::item_info, s_vanilla_item_info, sizeof(s_vanilla_item_info)); + s_applied = false; +} + +} // namespace randomizer::item \ No newline at end of file diff --git a/mods/randomizer/src/item.hpp b/mods/randomizer/src/item.hpp new file mode 100644 index 0000000000..99b6eece67 --- /dev/null +++ b/mods/randomizer/src/item.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace randomizer::item { +void exec_item_get(u8 item_no); +int check_item_get(u8 item_no, int param); + +void apply_item_data_tables(); +void restore_item_data_tables(); +} // namespace randomizer::item diff --git a/mods/randomizer/src/mod.cpp b/mods/randomizer/src/mod.cpp index a770ccf44a..8a2eb32ccc 100644 --- a/mods/randomizer/src/mod.cpp +++ b/mods/randomizer/src/mod.cpp @@ -1,9 +1,10 @@ #include "mods/service.hpp" #include "mods/svc/log.h" +#include "hooks.hpp" +#include "item.hpp" #include "session.hpp" #include "ui/ui.hpp" -#include "hooks.hpp" DEFINE_MOD(); IMPORT_SERVICE(HostService, svc_host); @@ -14,6 +15,7 @@ IMPORT_SERVICE(ResourceService, svc_res); IMPORT_SERVICE(ConfigService, svc_config); IMPORT_SERVICE(SaveService, svc_save); IMPORT_SERVICE(StageService, svc_stage); +IMPORT_SERVICE(ItemService, svc_item); extern "C" { @@ -28,6 +30,7 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { svc_config, svc_save, svc_stage, + svc_item, }); if (result != MOD_OK) { return mods::set_error(error, result, "failed to initialize session"); @@ -54,6 +57,7 @@ MOD_EXPORT ModResult mod_update(ModError*) { MOD_EXPORT ModResult mod_shutdown(ModError*) { svc_log->info(mod_ctx, "randomizer unloaded"); + randomizer::item::restore_item_data_tables(); return MOD_OK; } } diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index 749fd32fad..c6691df8b1 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -7,6 +7,8 @@ #include "item_ids.h" #include "tools.h" #include "stages.h" +#include "item.hpp" +#include "verify_item_functions.h" #include "d/d_com_inf_game.h" #include "d/d_item.h" @@ -21,7 +23,118 @@ ModResult initialize(const ServiceManager& services) { return MOD_OK; } +ItemCheckHandle s_check_resolver{}; +ItemGiveHandle s_check_observer{}; + +struct DerivedKey { + int stage_id; + u16 key; +}; + +std::optional parse_derived(const char* name, std::string_view prefix) { + if (std::strncmp(name, prefix.data(), prefix.size()) != 0) { + return std::nullopt; + } + const char* stage_begin = name + prefix.size(); + const char* stage_end = std::strchr(stage_begin, ':'); + if (stage_end == nullptr) { + return std::nullopt; + } + const std::string stage{stage_begin, stage_end}; + const int stage_id = getStageID(stage.c_str()); + if (stage_id < 0) { + return std::nullopt; + } + const int n = std::atoi(stage_end + 1); + return DerivedKey{stage_id, static_cast((stage_id << 8) | (n & 0xFF))}; +} + +template +bool lookup_override(const Map& map, u16 key, uint8_t* out_item, bool progressive) { + const auto it = map.find(key); + if (it == map.end()) { + return false; + } + *out_item = progressive ? static_cast(verifyProgressiveItem(it->second)) : it->second; + return true; +} + +bool resolve_check(ModContext*, const ItemCheckInfo* info, uint8_t* out_item, void*) { + auto& ctx = randomizer_GetContext(); + + if (auto it = ctx.mItemLocations.find(info->name); it != ctx.mItemLocations.end()) { + *out_item = static_cast(it->second.itemId); + return true; + } + + if (auto key = parse_derived(info->name, "chest:")) { + return lookup_override(ctx.mTreasureChestOverrides, key->key, out_item, false); + } + if (auto key = parse_derived(info->name, "freestanding:")) { + if (key->stage_id == Ook) { + if (auto it = ctx.mItemLocations.find("Forest Temple Gale Boomerang"); + it != ctx.mItemLocations.end()) { + *out_item = static_cast(verifyProgressiveItem(it->second.itemId)); + return true; + } + return false; + } + return lookup_override(ctx.mFreestandingItemOverrides, key->key, out_item, true); + } + if (auto key = parse_derived(info->name, "poe:")) { + return lookup_override(ctx.mPoeOverrides, key->key, out_item, false); + } + if (auto key = parse_derived(info->name, "shop:")) { + return lookup_override(ctx.mShopOverrides, key->key, out_item, true); + } + if (auto key = parse_derived(info->name, "sky:")) { + return lookup_override(ctx.mSkyCharacterOverrides, key->key, out_item, true); + } + + if (std::strncmp(info->name, "bug:", 4) == 0) { + const u8 insect = static_cast(std::atoi(info->name + 4)); + if (auto it = ctx.mBugRewardOverrides.find(insect); it != ctx.mBugRewardOverrides.end()) { + *out_item = static_cast(verifyProgressiveItem(it->second)); + return true; + } + return false; + } + + return false; +} + +void observe_give(ModContext*, const ItemGiveInfo* info, void*) { + if (info->check_name == nullptr) { + return; + } + + auto& ctx = randomizer_GetContext(); + if (ctx.mItemLocations.contains(info->check_name)) { + randomizer_setTempFlagForLocation(info->check_name); + } + + if (std::strcmp(info->check_name, "Arbiters Grounds Dungeon Reward") == 0) { + dComIfGs_onItem(0x9E, -1); + } else if (auto key = parse_derived(info->check_name, "freestanding:"); + key && key->stage_id == Ook) + { + dComIfGs_onItem(0x9D, -1); + randomizer_setTempFlagForLocation("Forest Temple Gale Boomerang"); + } +} + +void activateSeed() { + auto& ctx = randomizer_GetContext(); + + item::apply_item_data_tables(); + + svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver); + svc_mng.item->observe_gives(mod_ctx, observe_give, nullptr, &s_check_observer); +} + void setupRandomizerFile() { + activateSeed(); + // Setup file based on randomizer data auto& randoData = randomizer_GetContext(); randoData.mCreatingSave = true; @@ -46,22 +159,22 @@ void setupRandomizerFile() { { dComIfGs_onDarkClearLV(0); dComIfGs_setLightDropNum(0, 0x10); - execItemGet(dItemNo_Randomizer_DROP_CONTAINER_e); - execItemGet(dItemNo_Randomizer_WEAR_KOKIRI_e); + item::exec_item_get(dItemNo_Randomizer_DROP_CONTAINER_e); + item::exec_item_get(dItemNo_Randomizer_WEAR_KOKIRI_e); } if (dComIfGs_isEventBit(CLEARED_ELDIN_TWILIGHT)) { dComIfGs_onDarkClearLV(1); dComIfGs_setLightDropNum(1, 0x10); - execItemGet(dItemNo_Randomizer_DROP_CONTAINER02_e); + item::exec_item_get(dItemNo_Randomizer_DROP_CONTAINER02_e); } if (dComIfGs_isEventBit(CLEARED_LANAYRU_TWILIGHT)) { dComIfGs_onDarkClearLV(2); dComIfGs_setLightDropNum(2, 0x10); - execItemGet(dItemNo_Randomizer_DROP_CONTAINER03_e); + item::exec_item_get(dItemNo_Randomizer_DROP_CONTAINER03_e); } if (randoData.mSettings[RandomizerContext::SKIP_MINOR_CUTSCENES] == RandomizerContext::ON) @@ -90,7 +203,7 @@ void setupRandomizerFile() { // Set starting inventory for (const auto& itemId: randoData.mStartingInventory) { - execItemGet(itemId); + item::exec_item_get(itemId); } g_randomizerState = RandomizerState(); diff --git a/mods/randomizer/src/session.hpp b/mods/randomizer/src/session.hpp index e033ebb961..3c58f15b26 100644 --- a/mods/randomizer/src/session.hpp +++ b/mods/randomizer/src/session.hpp @@ -8,6 +8,7 @@ #include "mods/svc/resource.h" #include "mods/svc/save.h" #include "mods/svc/stage.h" +#include "mods/svc/item.h" namespace randomizer::session { struct ServiceManager { @@ -20,6 +21,7 @@ struct ServiceManager { const ConfigService* config; const SaveService* save; const StageService* stage; + const ItemService* item; }; extern ServiceManager svc_mng; diff --git a/src/d/d_item.cpp b/src/d/d_item.cpp index 5484bd584a..b50ceb40c2 100644 --- a/src/d/d_item.cpp +++ b/src/d/d_item.cpp @@ -269,7 +269,10 @@ static void (*item_func_ptr[256])() = { item_func_noentry, }; -inline void getItemFunc(u8 i_itemNo) { +#if !TARGET_PC +inline +#endif +void getItemFunc(u8 i_itemNo) { dComIfGs_onItemFirstBit(i_itemNo); item_func_ptr[i_itemNo](); } From 8229879aabc760c479d22b9e83bd6d8a13c07eba Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 13 Aug 2026 02:04:41 -0700 Subject: [PATCH 08/26] refactor seed load/save and add file info hook --- include/d/d_file_sel_info.h | 2 +- mods/randomizer/src/hooks.cpp | 91 ++++++++++++++++++-- mods/randomizer/src/hooks.hpp | 1 + mods/randomizer/src/mod.cpp | 3 +- mods/randomizer/src/session.cpp | 107 +++++++++++++++++++++--- mods/randomizer/src/session.hpp | 4 + mods/randomizer/src/ui/rando_config.cpp | 9 +- 7 files changed, 191 insertions(+), 26 deletions(-) diff --git a/include/d/d_file_sel_info.h b/include/d/d_file_sel_info.h index 973349c469..37dadbeb1b 100644 --- a/include/d/d_file_sel_info.h +++ b/include/d/d_file_sel_info.h @@ -37,7 +37,7 @@ public: CPaneMgrAlpha* getNoDatBase() { return mNoDatBase; } void draw() { _draw(); } -private: +// private: /* 0x04 */ JKRArchive* mArchive; /* 0x08 */ dDlst_FileInfo_c mFileInfo; /* 0x1C */ u8 field_0x1c[4]; diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 7b2a906ca1..e8e0c3a2ac 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -13,13 +13,15 @@ #include "d/actor/d_a_alink.h" #include "d/d_file_select.h" +#include "d/d_file_sel_info.h" #include "d/d_meter2_info.h" #include "d/d_save.h" #include "d/d_shop_system.h" DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect); -DEFINE_HOOK(&dFile_select_c::nameInput2, dFile_select_c__nameInput2); + +DEFINE_HOOK(&dFile_info_c::setSaveData, dFile_info_c__setSaveData); DEFINE_HOOK(&dSv_event_c::isEventBit, dSv_event_c__isEventBit); DEFINE_HOOK(&dSv_event_c::onEventBit, dSv_event_c__onEventBit); @@ -142,12 +144,41 @@ HookAction hookPreSelectDataNameMove(ModContext*, void* args, void* retval, void return HOOK_SKIP_ORIGINAL; } -void hookPostNameInput2(ModContext*, void* args, void* retval, void* userdata) { - dFile_select_c* i_this = mods::arg(args, 0); +void hookPostSetSaveData(ModContext* ctx, void* args, void* retval, void* userdata) { + dFile_info_c* i_this = mods::arg(args, 0); + u8 i_dataNo = mods::arg(args, 3); - if (i_this->mIsSelectEnd) { - if (!randomizer_GetContext().mHash.empty()) { - session::setupRandomizerFile(); + if (*static_cast(retval) == 0) { + char hash[64]; + size_t size = sizeof(hash) - 1; + + ModResult rt = session::svc_mng.save->peek_blob(ctx, i_dataNo, "seed_hash", hash, &size); + if (rt != MOD_OK || size == 0) { + // leave file text vanilla if seed hash isn't found + mods::log::debug("no seed_hash found for file {}", i_dataNo); + return; + } + + hash[size] = 0; + const std::string curFileSeedHash = hash; + if (!curFileSeedHash.empty()) { + const auto setHBinding = [](J2DTextBox* tbox, J2DTextBoxHBinding bind) { + tbox->mFlags &= 0b0011; + tbox->mFlags |= ((bind & 3) << 2); + }; + + // Overwrite "Save time" text with "Randomizer" + auto saveTimeText = (J2DTextBox*)i_this->mFileInfo.Scr->search(MULTI_CHAR('f_s_t_02')); + SafeStringCopy(saveTimeText->getStringPtr(), "Randomizer"); + setHBinding(saveTimeText, J2DTextBoxHBinding::HBIND_LEFT); + + // Overwrite the "Total play time" text with the seed hash + auto playTimeText = (J2DTextBox*)i_this->mFileInfo.Scr->search(MULTI_CHAR('f_p_t_02')); + SafeStringCopy(playTimeText->getStringPtr(), curFileSeedHash.c_str()); + + // Give the text double the space on the menu incase the seed hash is long + setHBinding(playTimeText, J2DTextBoxHBinding::HBIND_LEFT); + playTimeText->resize(playTimeText->getWidth() * 2, playTimeText->getHeight()); } } } @@ -1191,6 +1222,8 @@ ModResult initialize() { ADD_HOOK_PRE(dFile_select_c__selectDataNameMove, hookPreSelectDataNameMove); ADD_HOOK_PRE(dFile_select_c__dataSelect, hookPreDataSelect); + ADD_HOOK_POST(dFile_info_c__setSaveData, hookPostSetSaveData); + ADD_HOOK_PRE(dSv_event_c__isEventBit, hookPreIsEventBit); ADD_HOOK_PRE(dSv_event_c__onEventBit, hookPreOnEventBit); @@ -1219,8 +1252,6 @@ ModResult initialize() { ADD_HOOK_POST(dEvt_control_c__talkEnd, hookPostTalkEnd); - ADD_HOOK_POST(dFile_select_c__nameInput2, hookPostNameInput2); - ADD_HOOK_PRE(dComIfG_play_c__getLayerNo_common_common, hookPreGetLayerNo); ADD_HOOK_PRE(dItem_getItemFunc, hookPreGetItemFunc); @@ -1230,4 +1261,48 @@ ModResult initialize() { return MOD_OK; } + +ModResult uninstall() { + auto svc_hook = session::svc_mng.hook; + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + return MOD_OK; +} } \ No newline at end of file diff --git a/mods/randomizer/src/hooks.hpp b/mods/randomizer/src/hooks.hpp index a27ea0aaba..b71f56bd35 100644 --- a/mods/randomizer/src/hooks.hpp +++ b/mods/randomizer/src/hooks.hpp @@ -4,4 +4,5 @@ namespace randomizer::hooks { ModResult initialize(); +ModResult uninstall(); } \ No newline at end of file diff --git a/mods/randomizer/src/mod.cpp b/mods/randomizer/src/mod.cpp index 8a2eb32ccc..219431f41a 100644 --- a/mods/randomizer/src/mod.cpp +++ b/mods/randomizer/src/mod.cpp @@ -56,8 +56,9 @@ MOD_EXPORT ModResult mod_update(ModError*) { } MOD_EXPORT ModResult mod_shutdown(ModError*) { + randomizer::session::deactivateSeed(); + randomizer::hooks::uninstall(); svc_log->info(mod_ctx, "randomizer unloaded"); - randomizer::item::restore_item_data_tables(); return MOD_OK; } } diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index c6691df8b1..5aedde25a9 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -16,15 +16,14 @@ namespace randomizer::session { ServiceManager svc_mng; +std::string g_pending_seed_hash{}; -ModResult initialize(const ServiceManager& services) { - svc_mng = services; - - return MOD_OK; -} - +SaveObserverHandle s_save_observer{}; ItemCheckHandle s_check_resolver{}; ItemGiveHandle s_check_observer{}; +std::vector s_stage_edits{}; + +constexpr const char* kSeedHashBlobName = "seed_hash"; struct DerivedKey { int stage_id; @@ -123,18 +122,46 @@ void observe_give(ModContext*, const ItemGiveInfo* info, void*) { } } -void activateSeed() { +bool activateSeed(const char* hash) { auto& ctx = randomizer_GetContext(); + ctx = RandomizerContext(); + if (auto err = ctx.LoadFromHash(hash); err.has_value() || ctx.mHash.empty()) { + mods::log::error("failed to load seed {}", hash); + return false; + } item::apply_item_data_tables(); svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver); svc_mng.item->observe_gives(mod_ctx, observe_give, nullptr, &s_check_observer); + + registerStageEdits(); + mods::log::info("activated seed {}", ctx.mHash); + return true; +} + +void deactivateSeed() { + if (s_check_resolver != 0) { + svc_mng.item->clear_check_resolver(mod_ctx, s_check_resolver); + s_check_resolver = 0; + } + + if (s_check_observer != 0) { + svc_mng.item->unobserve_gives(mod_ctx, s_check_observer); + s_check_observer = 0; + } + + for (auto handle : s_stage_edits) { + svc_mng.stage->remove_actor_edit(mod_ctx, handle); + } + s_stage_edits.clear(); + + item::restore_item_data_tables(); + randomizer_GetContext() = RandomizerContext{}; + g_randomizerState = RandomizerState{}; } void setupRandomizerFile() { - activateSeed(); - // Setup file based on randomizer data auto& randoData = randomizer_GetContext(); randoData.mCreatingSave = true; @@ -239,6 +266,10 @@ void registerStageEdits() { res = svc_mng.stage->patch_actor( mod_ctx, stage, room, layer, crc, bytes.data(), bytes.size(), &handle); } + + if (res == MOD_OK) { + s_stage_edits.push_back(handle); + } } } @@ -252,9 +283,65 @@ void registerStageEdits() { const s8 layer = static_cast(key & 0xFF); for (const auto& bytes : additions) { StageActorHandle handle{}; - svc_mng.stage->add_actor(mod_ctx, stage, room, layer, bytes.data(), bytes.size(), &handle); + ModResult rt; + rt = svc_mng.stage->add_actor(mod_ctx, stage, room, layer, bytes.data(), bytes.size(), &handle); + if (rt == MOD_OK) { + s_stage_edits.push_back(handle); + } } } } +void onNewSave(ModContext*, uint32_t, void*) { + const std::string hash = g_pending_seed_hash; + if (hash.empty()) + return; + + if (!activateSeed(hash.c_str())) + return; + + svc_mng.save->set_blob(svc_mng.mod_ctx, kSeedHashBlobName, hash.data(), hash.size()); + setupRandomizerFile(); +} + +void onSaveLoaded(ModContext*, uint32_t, void*) { + size_t size = 0; + if (svc_mng.save->get_blob(mod_ctx, kSeedHashBlobName, nullptr, &size) != MOD_OK || size == 0) { + mods::log::error("seed_hash not found!"); + deactivateSeed(); + return; + } + + std::string hash(size, '\0'); + if (svc_mng.save->get_blob(mod_ctx, kSeedHashBlobName, hash.data(), &size) != MOD_OK) { + mods::log::error("failed to get seed_hash!"); + deactivateSeed(); + return; + } + + if (randomizer_GetContext().mHash != hash) { + deactivateSeed(); + activateSeed(hash.c_str()); + } + + loadAncientDocumentNum(); +} + +ModResult initialize(const ServiceManager& services) { + svc_mng = services; + + ModResult rt = svc_mng.save->observe_saves( + svc_mng.mod_ctx, + onNewSave, + onSaveLoaded, + nullptr, + nullptr, + &s_save_observer); + if (rt != MOD_OK) { + return rt; + } + + return MOD_OK; +} + } \ No newline at end of file diff --git a/mods/randomizer/src/session.hpp b/mods/randomizer/src/session.hpp index 3c58f15b26..ca658df57e 100644 --- a/mods/randomizer/src/session.hpp +++ b/mods/randomizer/src/session.hpp @@ -10,6 +10,8 @@ #include "mods/svc/stage.h" #include "mods/svc/item.h" +#include + namespace randomizer::session { struct ServiceManager { ModContext* mod_ctx; @@ -25,9 +27,11 @@ struct ServiceManager { }; extern ServiceManager svc_mng; +extern std::string g_pending_seed_hash; ModResult initialize(const ServiceManager& services); +void deactivateSeed(); void setupRandomizerFile(); void registerStageEdits(); } \ No newline at end of file diff --git a/mods/randomizer/src/ui/rando_config.cpp b/mods/randomizer/src/ui/rando_config.cpp index 58253075ac..6bcc42bbe4 100644 --- a/mods/randomizer/src/ui/rando_config.cpp +++ b/mods/randomizer/src/ui/rando_config.cpp @@ -553,7 +553,7 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) { if (entry.is_directory()) { std::string hash = entry.path().filename().string(); - if (randomizer_GetContext().mHash == hash) { + if (session::g_pending_seed_hash == hash) { break; } idx++; @@ -567,10 +567,7 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) { if (entry.is_directory()) { if (idx == value->int_value) { - std::string hash = entry.path().filename().string(); - randomizer_GetContext() = RandomizerContext(); - randomizer_GetContext().LoadFromHash(hash); - session::registerStageEdits(); + session::g_pending_seed_hash = entry.path().filename().string(); break; } idx++; @@ -590,7 +587,7 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane }; desc.user_data = &g_file_select_window_ctx.window_handle; desc.is_disabled = [](ModContext*, void*) { - return randomizer_GetContext().mHash.empty(); + return session::g_pending_seed_hash.empty(); }; session::svc_mng.ui->pane_add_control(session::svc_mng.mod_ctx, leftPane, &desc, nullptr); } From abbcb5a6b9054d24f713a3ab897c7ef29ad314fa Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 13 Aug 2026 03:41:39 -0700 Subject: [PATCH 09/26] location name overrides for item service --- mods/randomizer/src/randomizer_context.cpp | 8 ++-- mods/randomizer/src/session.cpp | 2 +- mods/randomizer/src/tools.cpp | 51 +++++++++++++++++++++- mods/randomizer/src/tools.h | 2 + src/d/actor/d_a_b_ds.cpp | 2 +- 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/mods/randomizer/src/randomizer_context.cpp b/mods/randomizer/src/randomizer_context.cpp index 34f3631d78..4d2599cd4b 100644 --- a/mods/randomizer/src/randomizer_context.cpp +++ b/mods/randomizer/src/randomizer_context.cpp @@ -255,7 +255,7 @@ std::optional RandomizerContext::LoadFromHash(const std::string& ha // Items we call by location name for (const auto& locationNode : in["mItemLocations"]) { - const auto& locationName = locationNode.first.as(); + const auto& locationName = nameLookupOverride(locationNode.first.as()); retrieveItemData(this->mItemLocations[locationName], locationNode.second); } @@ -857,7 +857,7 @@ std::vector HexToBytes(std::string hex) { } int randomizer_getItemAtLocation(const std::string& locationName) { - return randomizer_GetContext().mItemLocations[locationName].itemId; + return randomizer_GetContext().mItemLocations[nameLookupOverride(locationName)].itemId; } void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, s16& pointNo, s8& mapLayer) { @@ -893,7 +893,7 @@ void randomizer_setTempFlag(RandomizerContext::itemLocationData data) { } void randomizer_setTempFlagForLocation(const std::string& locationName) { - randomizer_setTempFlag(randomizer_GetContext().mItemLocations[locationName]); + randomizer_setTempFlag(randomizer_GetContext().mItemLocations[nameLookupOverride(locationName)]); } void randomizer_setTempFlagForFLWOverride(u32 key) { @@ -1357,7 +1357,7 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { // Items that we lookup just by calling their location name if (location->HasCategories("Name Lookup")) { for (const auto& locationNameNode : metaData["Name Lookup"]) { - const auto& locationName = locationNameNode.as(); + const auto& locationName = nameLookupOverride(locationNameNode.as()); const int itemId = location->GetCurrentItem()->GetID(); randoData.mItemLocations[locationName].itemId = itemId; getNodeFlags(randoData.mItemLocations[locationName], metaData); diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index 5aedde25a9..82595dda92 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -112,7 +112,7 @@ void observe_give(ModContext*, const ItemGiveInfo* info, void*) { randomizer_setTempFlagForLocation(info->check_name); } - if (std::strcmp(info->check_name, "Arbiters Grounds Dungeon Reward") == 0) { + if (std::strcmp(info->check_name, "dungeon_reward:D_MN10") == 0) { dComIfGs_onItem(0x9E, -1); } else if (auto key = parse_derived(info->check_name, "freestanding:"); key && key->stage_id == Ook) diff --git a/mods/randomizer/src/tools.cpp b/mods/randomizer/src/tools.cpp index f710d9e419..a76c0b8934 100644 --- a/mods/randomizer/src/tools.cpp +++ b/mods/randomizer/src/tools.cpp @@ -501,6 +501,55 @@ randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world: return pool; } +// overriding location names from generator to match item service api format +std::string nameLookupOverride(const std::string& locationName) { + // [generator name], [service name] + static std::unordered_map nameLookup = { + {"Arbiters Grounds Dungeon Reward", "dungeon_reward:D_MN10"}, + {"Ashei Sketch", "ashei_sketch"}, + {"Auru Gift To Fyer", "auru_memo"}, + {"Cave of Ordeals Great Fairy Reward", "fairy_reward:D_SB01"}, + {"Charlo Donation Blessing", "prayer_reward"}, + {"Coro Bottle", "coro_bottle"}, + {"Gift From Ralis", "coral_earring"}, + {"Goron Mines Gor Amato Key Shard", "key_shard_1:D_MN04"}, + {"Goron Mines Gor Ebizo Key Shard", "key_shard_2:D_MN04"}, + {"Goron Mines Gor Liggs Key Shard", "key_shard_3:D_MN04"}, + {"Herding Goats Reward", "goats_reward"}, + {"Hyrule Castle King Bulblin Key", "bulblin_key:D_MN09"}, + {"Ilia Charm", "ilia_charm"}, + {"Iza Helping Hand", "iza_reward_1"}, + {"Iza Raging Rapids Minigame", "iza_reward_2"}, + {"Jovani 20 Poe Soul Reward", "jovani_reward_1"}, + {"Jovani 60 Poe Soul Reward", "jovani_reward_2"}, + {"Ordon Cat Rescue", "sera_reward"}, + {"Ordon Shield", "ordon_shield"}, + {"Ordon Sword", "ordon_sword"}, + {"Plumm Fruit Balloon Minigame", "plumm_minigame_reward"}, + {"Renados Letter", "renado_letter"}, + {"Rutelas Blessing", "zora_armor"}, + {"Skybook From Impaz", "skybook"}, + {"Snowboard Racing Prize", "snowboard_race_reward"}, + {"Snowpeak Ruins Ball and Chain", "ball_and_chain:D_MN11"}, + {"Snowpeak Ruins Mansion Map", "dungeon_map:D_MN11"}, + {"STAR Prize 1", "star_reward_1"}, + {"STAR Prize 2", "star_reward_2"}, + {"Talo Sharpshooting", "archery_reward:F_SP109"}, + {"Telma Invoice", "telma_invoice"}, + {"Uli Cradle Delivery", "uli_cradle_reward"}, + {"Wooden Statue", "wood_statue"}, + {"Zoras Domain Underwater Goron", "goron_reward:F_SP113"}, + }; + + for (auto& [oldName, newName] : nameLookup) { + if (locationName == oldName) { + return newName; + } + } + + return locationName; +} + bool isLocationObtained(randomizer::logic::location::Location* location) { auto& locationMeta = location->GetMetadata(); if (auto& chestNode = locationMeta["Chest"]) { @@ -604,7 +653,7 @@ int getLocationItem(randomizer::logic::location::Location* location) { return context.mFlowItemMessageOverrides[key].itemId; } if (auto& nameNode = locationMeta["Name Lookup"]) { - auto name = nameNode[0].as(); + auto name = nameLookupOverride(nameNode[0].as()); return context.mItemLocations[name].itemId; } return -1; diff --git a/mods/randomizer/src/tools.h b/mods/randomizer/src/tools.h index 475fc01793..81fe24dbf3 100644 --- a/mods/randomizer/src/tools.h +++ b/mods/randomizer/src/tools.h @@ -29,6 +29,8 @@ int numFusedShadows(); int numMirrorShards(); int getTempleKeysFound(int saveId); +std::string nameLookupOverride(const std::string& locationName); + /* * Reads the current player inventory and returns an ItemPool that can be used for logic searches * diff --git a/src/d/actor/d_a_b_ds.cpp b/src/d/actor/d_a_b_ds.cpp index e0623acaba..aaf74d8c43 100644 --- a/src/d/actor/d_a_b_ds.cpp +++ b/src/d/actor/d_a_b_ds.cpp @@ -4086,7 +4086,7 @@ void daB_DS_c::executeBattle2Dead() { 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); + dusk::mods::item_check_enqueue("dungeon_reward:D_MN10", dItemNo_NONE_e); #endif /* dSv_event_flag_c::F_0265 - Arbiter's Grounds - Arbiter's Grounds clear */ dComIfGs_onEventBit(0x2010); From c65b8a42b86a945937d087388454a9d8be37a098 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 13 Aug 2026 14:50:39 -0700 Subject: [PATCH 10/26] add handful of missing hooks --- include/d/actor/d_a_door_shutter.h | 2 +- mods/randomizer/src/hooks.cpp | 142 ++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 2 deletions(-) diff --git a/include/d/actor/d_a_door_shutter.h b/include/d/actor/d_a_door_shutter.h index a8e99f1295..34ea7f7fc9 100644 --- a/include/d/actor/d_a_door_shutter.h +++ b/include/d/actor/d_a_door_shutter.h @@ -123,7 +123,7 @@ public: bool checkFlag(u16 flag) { return field_0x68e & flag; } void setAction(u8 action) { mAction = action; } -private: +// private: /* 0x56C */ request_of_phase_process_class mPhase1; /* 0x574 */ request_of_phase_process_class mPhase2; /* 0x57C */ J3DModel* mModel1; diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index e8e0c3a2ac..32d9157d90 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -12,11 +12,17 @@ #include #include "d/actor/d_a_alink.h" -#include "d/d_file_select.h" +#include "d/actor/d_a_b_bq.h" +#include "d/actor/d_a_door_shutter.h" +#include "d/actor/d_a_tag_kmsg.h" +#include "d/d_door_param2.h" #include "d/d_file_sel_info.h" +#include "d/d_file_select.h" #include "d/d_meter2_info.h" +#include "d/d_msg_object.h" #include "d/d_save.h" #include "d/d_shop_system.h" +#include "d/actor/d_a_e_mk.h" DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect); @@ -61,6 +67,16 @@ DEFINE_HOOK(&getItemFunc, dItem_getItemFunc); extern int checkItemGet(u8 i_itemNo, int i_default); DEFINE_HOOK(&checkItemGet, dItem_checkItemGet); +DEFINE_HOOK(&daAlink_c::decideDoStatus, daAlink_c__decideDoStatus); +DEFINE_HOOK_SYMBOL("daAlink_searchBouDoor", void*(fopAc_ac_c*, void*), searchBouDoor); +DEFINE_HOOK(&daAlink_c::checkGroundSpecialMode, daAlink_c__checkGroundSpecialMode); + +DEFINE_HOOK_SYMBOL("b_bq_end", void(b_bq_class*), bq_end); + +DEFINE_HOOK(&daDoor20_c::checkOpenMsgDoor, daDoor20_c__checkOpenMsgDoor); + +DEFINE_HOOK_SYMBOL("demo_camera_end", void(e_mk_class*), e_mk_demo_camera_end); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -1204,6 +1220,110 @@ HookAction hookPreOnStageSwitch(ModContext*, void* args, void* retval, void*) { return HOOK_CONTINUE; } +HookAction hookPreDecideDoStatus(ModContext*, void* args, void* retval, void*) { + daAlink_c* i_this = mods::arg(args, 0); + bool set_status = false; + + if (i_this->mAttList != NULL) { + s16 actor_name = fopAcM_GetName(i_this->field_0x27f4); + if (actor_name == fpcNm_Tag_Lv6Gate_e || + (actor_name == fpcNm_TAG_KMSG_e && static_cast(i_this->field_0x27f4)->getType() == 3)) + { + // Separate check for striking sword into the pedestal for randomizer + if (!i_this->checkEquipAnime() && randomizer_checkTempleOfTimeRequirement()) { + i_this->setDoStatus(BUTTON_STATUS_STRIKE); + set_status = true; + } + } + } + + if (set_status) { + i_this->decideCommonDoStatus(); + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + +HookAction hookPreSearchBouDoor(ModContext*, void* args, void* retval, void*) { + // In randomizer, we don't want Bo preventing us from entering his house on Day 2 + if (daAlink_c::checkStageName("F_SP103")) + { + *static_cast(retval) = nullptr; + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + +HookAction hookPreCheckGroundSpecialMode(ModContext*, void* args, void* retval, void*) { + daAlink_c* i_this = mods::arg(args, 0); + + if (i_this->mLinkAcch.ChkGroundHit() + && !i_this->checkModeFlg(daAlink_c::MODE_PLAYER_FLY) + && !i_this->checkMagneBootsOn() + && i_this->checkEndResetFlg0(daAlink_c::ERFLG0_FORCE_WOLF_CHANGE)) + { + u8 stage = getStageID(); + // In rando, don't transform in twilight fog unless we have shadow crystal + if (!dComIfGs_isEventBit(TRANSFORMING_UNLOCKED) && + (stage == Palace_of_Twilight || stage == Phantom_Zant_1 || stage == Phantom_Zant_2)) + { + *static_cast(retval) = FALSE; + return HOOK_SKIP_ORIGINAL; + } + *static_cast(retval) = i_this->procCoMetamorphoseInit(); + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + +void hookPostBqEnd(ModContext*, void* args, void* retval, void*) { + // If the player is wolf, they will softlock after the defeat cutscene is completed. + checkTransformFromWolf(); +} + +HookAction hookPreCheckOpenMsgDoor(ModContext*, void* args, void* retval, void*) { + daDoor20_c* i_this = mods::arg(args, 0); + int* param_1 = mods::arg(args, 1); + + if (!door_param2_c::isMsgDoor(i_this)) { + *static_cast(retval) = 1; + return HOOK_SKIP_ORIGINAL; + } + + int msgNo = door_param2_c::getMsgNo(i_this); + if (msgNo == 0xffff) { + *param_1 = 0; + *static_cast(retval) = 1; + return HOOK_SKIP_ORIGINAL; + } + + i_this->field_0x624.init(NULL, msgNo, 0, NULL); + int rv = 1; + // If we are in SPR, we don't want Yeta's msg flow to prevent us from opening the door if we haven't talked to her. + if (!daAlink_c::checkStageName("D_MN11")) { + rv = i_this->field_0x624.checkOpenDoor(i_this, param_1); + } + dMsgObject_endFlowGroup(); + *static_cast(retval) = rv; + return HOOK_SKIP_ORIGINAL; +} + +void hookPostEmkDemoCameraEnd(ModContext*, void* args, void* retval, void*) { + e_mk_class* i_this = mods::arg(args, 0); + + switch (i_this->demoSubMode) { + case 6: + if (i_this->demoCamCounter == 180) { + // If the player is wolf, they will void and lose the boomerang check. + checkTransformFromWolf(); + } + break; + } +} + } ModResult initialize() { @@ -1259,6 +1379,16 @@ ModResult initialize() { ADD_HOOK_PRE(onStageSwitch, hookPreOnStageSwitch); + ADD_HOOK_PRE(daAlink_c__decideDoStatus, hookPreDecideDoStatus); + ADD_HOOK_PRE(searchBouDoor, hookPreSearchBouDoor); + ADD_HOOK_PRE(daAlink_c__checkGroundSpecialMode, hookPreCheckGroundSpecialMode); + + ADD_HOOK_POST(bq_end, hookPostBqEnd); + + ADD_HOOK_PRE(daDoor20_c__checkOpenMsgDoor, hookPreCheckOpenMsgDoor); + + ADD_HOOK_POST(e_mk_demo_camera_end, hookPostEmkDemoCameraEnd); + return MOD_OK; } @@ -1303,6 +1433,16 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + return MOD_OK; } } \ No newline at end of file From 162829a8cf796db88e04aaeb64699be0d5929c98 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 13 Aug 2026 16:28:42 -0700 Subject: [PATCH 11/26] hook up randomizerState --- mods/randomizer/src/hooks.cpp | 79 +++++++++++++++++++++++++++++++++ mods/randomizer/src/mod.cpp | 1 + mods/randomizer/src/session.cpp | 7 +++ mods/randomizer/src/session.hpp | 1 + 4 files changed, 88 insertions(+) diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 32d9157d90..314576e114 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -77,6 +77,9 @@ DEFINE_HOOK(&daDoor20_c::checkOpenMsgDoor, daDoor20_c__checkOpenMsgDoor); DEFINE_HOOK_SYMBOL("demo_camera_end", void(e_mk_class*), e_mk_demo_camera_end); +DEFINE_HOOK(&dStage_changeScene4Event, changeScene4Event); +DEFINE_HOOK_SYMBOL("dStage_playerInit", int(dStage_dt_c*, void*, int, void*), stage_playerInit); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -1324,6 +1327,76 @@ void hookPostEmkDemoCameraEnd(ModContext*, void* args, void* retval, void*) { } } +void hookPostChangeScene4Event(ModContext*, void* args, void* retval, void*) { + int i_exitId = mods::arg(args, 0); + s8 room_no = mods::arg(args, 1); + + stage_scls_info_dummy_class* scls; + if (room_no == -1) { + scls = dComIfGp_getStageSclsInfo(); + } else { + dStage_roomDt_c* room = dComIfGp_roomControl_getStatusRoomDt(room_no); + scls = room->getSclsInfo(); + } + + if (scls == NULL) { + return; + } + + stage_scls_info_class* scls_info = &scls->m_entries[i_exitId]; + + // If randomizer is active and we're loading the first spawn, set our starting time of day + if (std::strcmp(scls_info->mStage, "F_SP103") + && scls_info->mRoom == 1 + && scls_info->mStart == 1) + { + dKy_set_nexttime(15.0f * randomizer_GetContext().mStartHour); + g_randomizerState.mUpdateTracker = true; + } +} + +HookAction hookPreStagePlayerInit(ModContext*, void* args, void* retval, void*) { + void* i_data = mods::arg(args, 1); + int num = mods::arg(args, 2); + + stage_actor_class* player = (stage_actor_class*)((int*)i_data + 1); + stage_actor_data_class* player_data = player->m_entries; + + // Modify entrance types in certain situations to avoid crashes + for (size_t i = 0; i < num; ++i) { + u8& entranceType = reinterpret_cast(&player_data[i].base.parameters)[2]; + switch (entranceType) { + // Only replace the entrance type if it is a door. + case 0x80: + case 0xA0: + case 0xB0: + { + if (dComIfGs_getTransformStatus() == TF_STATUS_WOLF) { + // Change the entrance type to play the animation of walking out of the + // loading zone instead of entering through the door. + entranceType = 0x50; + } + break; + } + + // Water swimming entrance. + // If we have this, but there isn't any water to spawn in, the game hangs + case 0xD0: + { + // If there's no water, change to non-swimming entrance + if (getStageID() == Lake_Hylia && !dComIfGs_isEventBit(WARPED_METEOR_TO_ZORAS_DOMAIN)) { + entranceType = 0x50; + } + break; + } + default: + break; + } + } + + return HOOK_CONTINUE; +} + } ModResult initialize() { @@ -1389,6 +1462,9 @@ ModResult initialize() { ADD_HOOK_POST(e_mk_demo_camera_end, hookPostEmkDemoCameraEnd); + ADD_HOOK_POST(changeScene4Event, hookPostChangeScene4Event); + ADD_HOOK_PRE(stage_playerInit, hookPreStagePlayerInit); + return MOD_OK; } @@ -1443,6 +1519,9 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + return MOD_OK; } } \ No newline at end of file diff --git a/mods/randomizer/src/mod.cpp b/mods/randomizer/src/mod.cpp index 219431f41a..88edc0e9ab 100644 --- a/mods/randomizer/src/mod.cpp +++ b/mods/randomizer/src/mod.cpp @@ -52,6 +52,7 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { MOD_EXPORT ModResult mod_update(ModError*) { randomizer::ui::update(); + randomizer::session::update(); return MOD_OK; } diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index 82595dda92..f4bec82ba6 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -344,4 +344,11 @@ ModResult initialize(const ServiceManager& services) { return MOD_OK; } +void update() { + if (!g_randomizerState.mInitialized) { + g_randomizerState._create(); + } + g_randomizerState.execute(); +} + } \ No newline at end of file diff --git a/mods/randomizer/src/session.hpp b/mods/randomizer/src/session.hpp index ca658df57e..ebe9ce0516 100644 --- a/mods/randomizer/src/session.hpp +++ b/mods/randomizer/src/session.hpp @@ -30,6 +30,7 @@ extern ServiceManager svc_mng; extern std::string g_pending_seed_hash; ModResult initialize(const ServiceManager& services); +void update(); void deactivateSeed(); void setupRandomizerFile(); From 8143de6bbbad53227fe67c9be60c216025672597 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 13 Aug 2026 20:10:44 -0700 Subject: [PATCH 12/26] more hooks --- mods/randomizer/src/hooks.cpp | 107 +++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 314576e114..f6a27bf363 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -14,6 +14,10 @@ #include "d/actor/d_a_alink.h" #include "d/actor/d_a_b_bq.h" #include "d/actor/d_a_door_shutter.h" +#include "d/actor/d_a_e_mk.h" +#include "d/actor/d_a_kytag08.h" +#include "d/actor/d_a_npc4.h" +#include "d/actor/d_a_npc_bans.h" #include "d/actor/d_a_tag_kmsg.h" #include "d/d_door_param2.h" #include "d/d_file_sel_info.h" @@ -22,7 +26,6 @@ #include "d/d_msg_object.h" #include "d/d_save.h" #include "d/d_shop_system.h" -#include "d/actor/d_a_e_mk.h" DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect); @@ -80,6 +83,14 @@ DEFINE_HOOK_SYMBOL("demo_camera_end", void(e_mk_class*), e_mk_demo_camera_end); DEFINE_HOOK(&dStage_changeScene4Event, changeScene4Event); DEFINE_HOOK_SYMBOL("dStage_playerInit", int(dStage_dt_c*, void*, int, void*), stage_playerInit); +DEFINE_HOOK_SYMBOL("daKytag08_Execute", int(kytag08_class*), Kytag08_Execute); + +DEFINE_HOOK(&daNpcT_chkEvtBit, NpcT_chkEvtBit); +DEFINE_HOOK(&daNpcF_chkEvtBit, NpcF_chkEvtBit); +DEFINE_HOOK(&daNpcF_c::orderEvent, daNpcF_c__orderEvent); + +DEFINE_HOOK(&daNpc_Bans_c::isDelete, daNpc_Bans_c__isDelete); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -1397,6 +1408,84 @@ HookAction hookPreStagePlayerInit(ModContext*, void* args, void* retval, void*) return HOOK_CONTINUE; } +void hookPostKytag08Execute(ModContext*, void* args, void* retval, void*) { + kytag08_class* i_this = mods::arg(args, 0); + + if (i_this->mSizeTimer < 100 || dComIfGs_BossLife_public_Get() == 1) { + dComIfGs_BossLife_public_Set(0); + i_this->mTargetAvoidPos = i_this->current.pos; + i_this->mSizeTimer = 180; + mDoAud_startFogWipeTrigger(&i_this->current.pos); + } +} + +HookAction hookPreNpcTChkEvtBit(ModContext*, void* args, void* retval, void*) { + u32 i_no = mods::arg(args, 0); + + switch (i_no) { + case 0x153: // Checking if the player has Ending Blow + if (getStageID() == Hidden_Skill) { + *static_cast(retval) = TRUE; + return HOOK_SKIP_ORIGINAL; + } + break; + case 0x40: // Checking if the player has completed Goron Mines + if (getStageID() == Kakariko_Village_Interiors) { + // Return true so Barnes will sell bombs no matter what + *static_cast(retval) = TRUE; + return HOOK_SKIP_ORIGINAL; + } + break; + } + + return HOOK_CONTINUE; +} + +HookAction hookPreNpcFChkEvtBit(ModContext*, void* args, void* retval, void*) { + u32 i_no = mods::arg(args, 0); + + switch (i_no) { + case 0x169: // Checking if Raised Mirror in Mirror Chamber + // Only let Auru despawn in randomizer if we already collected his item + if (getStageID() == Lake_Hylia) { + *static_cast(retval) = dComIfGs_isEventBit(GOT_AURUS_MEMO); + return HOOK_SKIP_ORIGINAL; + } + break; + } + + return HOOK_CONTINUE; +} + +HookAction hookPreNpcBansIsDelete(ModContext*, void* args, void* retval, void*) { + daNpc_Bans_c* i_this = mods::arg(args, 0); + + switch (i_this->mType) { + case 3: // MAKING_BOMBS + *static_cast(retval) = TRUE; + return HOOK_SKIP_ORIGINAL; + case 4: // SHOP + *static_cast(retval) = FALSE; + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + +HookAction hookPreNpcFOrderEvent(ModContext*, void* args, void* retval, void*) { + daNpcF_c* i_this = mods::arg(args, 0); + int& i_forceSpeak = mods::arg_ref(args, 1); + u16 i_priority = mods::arg(args, 4); + + // kinda hacky way to check for the state where Bo is trying to talk after getting Iron Boots + const std::string arcName = i_this->eventInfo.getArchiveName(); + if (arcName == "Bou4" && i_priority == 40) { + i_forceSpeak = FALSE; + } + + return HOOK_CONTINUE; +} + } ModResult initialize() { @@ -1465,6 +1554,14 @@ ModResult initialize() { ADD_HOOK_POST(changeScene4Event, hookPostChangeScene4Event); ADD_HOOK_PRE(stage_playerInit, hookPreStagePlayerInit); + ADD_HOOK_POST(Kytag08_Execute, hookPostKytag08Execute); + + ADD_HOOK_PRE(NpcT_chkEvtBit, hookPreNpcTChkEvtBit); + ADD_HOOK_PRE(NpcF_chkEvtBit, hookPreNpcFChkEvtBit); + ADD_HOOK_PRE(daNpcF_c__orderEvent, hookPreNpcFOrderEvent); + + ADD_HOOK_PRE(daNpc_Bans_c__isDelete, hookPreNpcBansIsDelete); + return MOD_OK; } @@ -1522,6 +1619,14 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + return MOD_OK; } } \ No newline at end of file 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 13/26] 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 14/26] 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 15/26] 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 a2cd701203198bfb79d3f8a532f1c8d8be82ca64 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 13 Aug 2026 23:32:21 -0700 Subject: [PATCH 16/26] ykm/ykw/fairy hooks --- include/d/actor/d_a_npc_fairy.h | 2 +- include/d/actor/d_a_npc_ykm.h | 2 +- mods/randomizer/src/hooks.cpp | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/include/d/actor/d_a_npc_fairy.h b/include/d/actor/d_a_npc_fairy.h index efa9d760c1..da9977ed37 100644 --- a/include/d/actor/d_a_npc_fairy.h +++ b/include/d/actor/d_a_npc_fairy.h @@ -296,7 +296,7 @@ public: static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mCutNameList[18]; static DUSK_GAME_DATA cutFunc DUSK_CONST mCutList[18]; -private: +// private: /* 0x0E40 */ NPC_FAIRY_HIO_CLASS* mHIO; /* 0x0E44 */ dCcD_Cyl mCyl; /* 0x0F80 */ u8 mType; diff --git a/include/d/actor/d_a_npc_ykm.h b/include/d/actor/d_a_npc_ykm.h index d486d6fe6a..5ad9e7210b 100644 --- a/include/d/actor/d_a_npc_ykm.h +++ b/include/d/actor/d_a_npc_ykm.h @@ -287,7 +287,7 @@ public: static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mCutNameList[10]; static DUSK_GAME_DATA cutFunc DUSK_CONST mCutList[10]; -private: +// private: /* 0x0E40 */ mDoExt_McaMorfSO* mFishModelMorf; /* 0x0E44 */ mDoExt_McaMorfSO* mLeafModelMorf; /* 0x0E48 */ NPC_YKM_HIO_CLASS* mpHIO; diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index f6a27bf363..5437aaa9ef 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -19,6 +19,9 @@ #include "d/actor/d_a_npc4.h" #include "d/actor/d_a_npc_bans.h" #include "d/actor/d_a_tag_kmsg.h" +#include "d/actor/d_a_npc_fairy.h" +#include "d/actor/d_a_npc_ykm.h" +#include "d/actor/d_a_npc_ykw.h" #include "d/d_door_param2.h" #include "d/d_file_sel_info.h" #include "d/d_file_select.h" @@ -91,6 +94,11 @@ DEFINE_HOOK(&daNpcF_c::orderEvent, daNpcF_c__orderEvent); DEFINE_HOOK(&daNpc_Bans_c::isDelete, daNpc_Bans_c__isDelete); +DEFINE_HOOK(&daNpc_Fairy_c::AppearDemoCall, daNpc_Fairy_c__AppearDemoCall); + +DEFINE_HOOK(&daNpc_ykM_c::isDelete, daNpc_ykM_c__isDelete); +DEFINE_HOOK(&daNpc_ykW_c::isDelete, daNpc_ykW_c__isDelete); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -1486,6 +1494,33 @@ HookAction hookPreNpcFOrderEvent(ModContext*, void* args, void* retval, void*) { return HOOK_CONTINUE; } +void hookPostFairyAppearDemoCall(ModContext*, void* args, void* retval, void*) { + daNpc_Fairy_c* i_this = mods::arg(args, 0); + + // randomizer overrides EVT_APPEAR_50F_04 set to always be EVT_APPEAR_50F_01 + if (i_this->field_0xff4 == 12) { + i_this->field_0xff4 = 9; + } +} + +void hookPostYkMIsDelete(ModContext*, void* args, void* retval, void*) { + daNpc_ykM_c* i_this = mods::arg(args, 0); + + if (i_this->mType == daNpc_ykM_c::TYPE_COOK) { + // We don't want cooking Yeto to leave the dungeon, even if the BK is obtained. + *static_cast(retval) = FALSE; + } +} + +void hookPostYkWIsDelete(ModContext*, void* args, void* retval, void*) { + daNpc_ykW_c* i_this = mods::arg(args, 0); + + if (i_this->field_0xf80 == 1) { + // We don't want Yeta to leave the dungeon, even if the BK is obtained. + *static_cast(retval) = FALSE; + } +} + } ModResult initialize() { @@ -1562,6 +1597,11 @@ ModResult initialize() { ADD_HOOK_PRE(daNpc_Bans_c__isDelete, hookPreNpcBansIsDelete); + ADD_HOOK_POST(daNpc_Fairy_c__AppearDemoCall, hookPostFairyAppearDemoCall); + + ADD_HOOK_POST(daNpc_ykM_c__isDelete, hookPostYkMIsDelete); + ADD_HOOK_POST(daNpc_ykW_c__isDelete, hookPostYkWIsDelete); + return MOD_OK; } @@ -1627,6 +1667,11 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + return MOD_OK; } } \ No newline at end of file From b0d4c62903add980790034a82dc53e4cc59ac103 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Fri, 14 Aug 2026 03:26:49 -0700 Subject: [PATCH 17/26] more hooks wip --- include/d/actor/d_a_obj_master_sword.h | 2 +- mods/randomizer/src/hooks.cpp | 205 ++++++++++++++++++++++++- 2 files changed, 205 insertions(+), 2 deletions(-) diff --git a/include/d/actor/d_a_obj_master_sword.h b/include/d/actor/d_a_obj_master_sword.h index a971aa335c..fd41a8d079 100644 --- a/include/d/actor/d_a_obj_master_sword.h +++ b/include/d/actor/d_a_obj_master_sword.h @@ -51,7 +51,7 @@ public: static DUSK_GAME_DATA daObjMasterSword_Attr_c const mAttr; static DUSK_GAME_DATA actionFunc ActionTable[]; -private: +// private: /* 0x568 */ J3DModel* mpModel; /* 0x56C */ request_of_phase_process_class mPhase; /* 0x574 */ mDoExt_btkAnm mBtk; diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 5437aaa9ef..98788820cd 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -14,12 +14,16 @@ #include "d/actor/d_a_alink.h" #include "d/actor/d_a_b_bq.h" #include "d/actor/d_a_door_shutter.h" +#include "d/actor/d_a_e_md.h" #include "d/actor/d_a_e_mk.h" #include "d/actor/d_a_kytag08.h" #include "d/actor/d_a_npc4.h" #include "d/actor/d_a_npc_bans.h" #include "d/actor/d_a_tag_kmsg.h" #include "d/actor/d_a_npc_fairy.h" +#include "d/actor/d_a_obj_master_sword.h" +#include "d/actor/d_a_npc_shad.h" +#include "d/actor/d_a_npc_yelia.h" #include "d/actor/d_a_npc_ykm.h" #include "d/actor/d_a_npc_ykw.h" #include "d/d_door_param2.h" @@ -29,6 +33,7 @@ #include "d/d_msg_object.h" #include "d/d_save.h" #include "d/d_shop_system.h" +#include "c/c_damagereaction.h" DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect); @@ -96,9 +101,20 @@ DEFINE_HOOK(&daNpc_Bans_c::isDelete, daNpc_Bans_c__isDelete); DEFINE_HOOK(&daNpc_Fairy_c::AppearDemoCall, daNpc_Fairy_c__AppearDemoCall); +DEFINE_HOOK(&daNpcShad_c::Create, daNpcShad_c__Create); +DEFINE_HOOK(&daNpcShad_c::wait_type1, daNpcShad_c__wait_type1); + +DEFINE_HOOK(&daNpc_Yelia_c::cutTakeWoodStatue, daNpc_Yelia_c__cutTakeWoodStatue); +DEFINE_HOOK(&dSv_player_item_c::setWarashibeItem, dSv_player_item_c__setWarashibeItem); + DEFINE_HOOK(&daNpc_ykM_c::isDelete, daNpc_ykM_c__isDelete); DEFINE_HOOK(&daNpc_ykW_c::isDelete, daNpc_ykW_c__isDelete); +DEFINE_HOOK_SYMBOL("daE_MD_Create", int(fopAc_ac_c*), daE_MD_c__create); + +DEFINE_HOOK(&daObjMasterSword_c::executeWait, daObjMasterSword_c__executeWait); +DEFINE_HOOK_SYMBOL("daObjMasterSword_Execute", int(daObjMasterSword_c*), daObjMasterSword_c__execute); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -1452,6 +1468,25 @@ HookAction hookPreNpcTChkEvtBit(ModContext*, void* args, void* retval, void*) { HookAction hookPreNpcFChkEvtBit(ModContext*, void* args, void* retval, void*) { u32 i_no = mods::arg(args, 0); + // shad handling + if (std::strcmp(dComIfGp_getStartStageName(), "R_SP209") == 0) { + switch (i_no) { + case 0x10B: + // spawn even if vanilla city requirements aren't met + *static_cast(retval) = TRUE; + return HOOK_SKIP_ORIGINAL; + case 0x12E: + case 0x12F: + // skip vanilla cannon spawn failure and move checks + *static_cast(retval) = FALSE; + return HOOK_SKIP_ORIGINAL; + case 0x311: + // despawn after custom flag is set + *static_cast(retval) = dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[0x333]); + return HOOK_SKIP_ORIGINAL; + } + } + switch (i_no) { case 0x169: // Checking if Raised Mirror in Mirror Chamber // Only let Auru despawn in randomizer if we already collected his item @@ -1521,6 +1556,144 @@ void hookPostYkWIsDelete(ModContext*, void* args, void* retval, void*) { } } +u8 hookNpcShadWaitType1_patchItemNo = dItemNo_NONE_e; +bool hookNpcShadWaitType1_isPatchItemNo = false; +HookAction hookPreNpcShadWaitType1(ModContext*, void* args, void* retval, void*) { + dEvt_control_c* event = dComIfGp_getEvent(); + hookNpcShadWaitType1_isPatchItemNo = false; + + if (event->mPreItemNo >= dItemNo_Randomizer_ANCIENT_DOCUMENT_e) { + // backup original item no so we can restore it in post hook + hookNpcShadWaitType1_patchItemNo = event->mPreItemNo; + + // force item no so that the dominion rod check is forced regardless + // of which skybook you have + event->mPreItemNo = dItemNo_Randomizer_ANCIENT_DOCUMENT_e; + + hookNpcShadWaitType1_isPatchItemNo = true; + } + + return HOOK_CONTINUE; +} + +void hookPostNpcShadWaitType1(ModContext*, void* args, void* retval, void*) { + if (hookNpcShadWaitType1_isPatchItemNo) { + dComIfGp_getEvent()->mPreItemNo = hookNpcShadWaitType1_patchItemNo; + hookNpcShadWaitType1_isPatchItemNo = false; + } +} + +// kinda ugly way to handle the wood statue. using a flag here to tell when its the right time +// to be overriding the setWarashibeItem call with offWarashibeItem instead. maybe look into +// a better solution later +bool hookYeliaTakeWoodStatue_isOffWarashibeItem = false; +HookAction hookPreNpcYeliaCutTakeWoodStatue(ModContext*, void* args, void*, void*) { + hookYeliaTakeWoodStatue_isOffWarashibeItem = false; + + auto* i_this = mods::arg(args, 0); + const int i_staffId = mods::arg(args, 1); + const int yeliaStaffId = dComIfGp_evmng_getMyStaffId("Yelia", i_this, -1); + if (i_staffId != yeliaStaffId || !dComIfGp_getEventManager().getIsAddvance(i_staffId)) { + return HOOK_CONTINUE; + } + + int* prm = dComIfGp_evmng_getMyIntegerP(i_staffId, "prm"); + if (prm != nullptr && *prm == 99) { + hookYeliaTakeWoodStatue_isOffWarashibeItem = true; + } + + return HOOK_CONTINUE; +} + +void hookPostNpcYeliaCutTakeWoodStatue(ModContext*, void*, void*, void*) { + hookYeliaTakeWoodStatue_isOffWarashibeItem = false; +} + +HookAction hookPreSetWarashibeItem(ModContext*, void* args, void*, void*) { + if (!hookYeliaTakeWoodStatue_isOffWarashibeItem) { + return HOOK_CONTINUE; + } + + const u8 itemNo = mods::arg(args, 1); + if (itemNo == dItemNo_NONE_e) { + offWarashibeItem(dItemNo_Randomizer_WOOD_STATUE_e); + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + +// kinda hacky, force the SkipInfo check to always fail so that the armor always gets created, +// then restore the original SkipInfo afterward. needed to make the ball and chain check respawn +// if the player leaves the room without grabbing it +daE_MD_c* hookEMdCreate_skipActor = nullptr; +u8 hookEMdCreate_prevSkipInfo = 0; +HookAction hookPreEMdCreate(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + if (cDmr_SkipInfo == 0 || i_this->current.pos.z <= -1500.0f) { + return HOOK_CONTINUE; + } + + hookEMdCreate_skipActor = i_this; + hookEMdCreate_prevSkipInfo = cDmr_SkipInfo; + cDmr_SkipInfo = 0; + return HOOK_CONTINUE; +} + +void hookPostEMdCreate(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + if (hookEMdCreate_skipActor != i_this) { + return; + } + + cDmr_SkipInfo = hookEMdCreate_prevSkipInfo; + hookEMdCreate_skipActor = nullptr; + hookEMdCreate_prevSkipInfo = 0; +} + +// functions are simple enough that replacing isn't too bad +// todo: item service needs to be properly hooked up to this check i think instead of +// using "randomizer_getItemAtLocation" +void hookReplaceMasterSwordExecuteWait(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + + if (daPy_getPlayerActorClass()->checkPriActorOwn(i_this)) { + for (int i = 0; i < dComIfGp_getAttention()->GetActionCount(); i++) { + if (dComIfGp_getAttention()->ActionTarget(i) == i_this) { + if (dComIfGp_getAttention()->getActionBtnB() != NULL && + dComIfGp_getAttention()->getActionBtnB()->mType == fopAc_attn_CARRY_e) + { + dComIfGp_setDoStatusForce(8, 0); + } + } + } + } + + if (fopAcM_checkCarryNow(i_this)) { + u8 itemToGive = randomizer_getItemAtLocation("Sacred Grove Pedestal Master Sword"); + g_randomizerState.addItemToEventQueue(itemToGive); + + itemToGive = randomizer_getItemAtLocation("Sacred Grove Pedestal Shadow Crystal"); + g_randomizerState.addItemToEventQueue(itemToGive); + + dComIfGs_onTmpBit(0x820); + dComIfGs_onEventBit(0x2120); + } +} + +HookAction hookPreMasterSwordExecute(ModContext*, void* args, void* retval, void*) { + auto* i_this = mods::arg(args, 0); + + if (dComIfGs_isTmpBit(dSv_event_tmp_flag_c::tempBitLabels[73])) { + // Don't automatically give the master sword in randomizer + fopAcM_delete(i_this); + *static_cast(retval) = 1; + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + } ModResult initialize() { @@ -1536,6 +1709,12 @@ ModResult initialize() { return MOD_ERROR; \ } +#define ADD_HOOK_REPLACE(originalFn, hookFn) \ + if (mods::hook::replace(hookFn) != MOD_OK) { \ + mods::log::error("Failed to add replace-hook for " #originalFn); \ + return MOD_ERROR; \ + } + ADD_HOOK_PRE(dFile_select_c__selectDataNameMove, hookPreSelectDataNameMove); ADD_HOOK_PRE(dFile_select_c__dataSelect, hookPreDataSelect); @@ -1599,9 +1778,22 @@ ModResult initialize() { ADD_HOOK_POST(daNpc_Fairy_c__AppearDemoCall, hookPostFairyAppearDemoCall); + ADD_HOOK_PRE(daNpcShad_c__wait_type1, hookPreNpcShadWaitType1); + ADD_HOOK_POST(daNpcShad_c__wait_type1, hookPostNpcShadWaitType1); + + ADD_HOOK_PRE(daNpc_Yelia_c__cutTakeWoodStatue, hookPreNpcYeliaCutTakeWoodStatue); + ADD_HOOK_POST(daNpc_Yelia_c__cutTakeWoodStatue, hookPostNpcYeliaCutTakeWoodStatue); + ADD_HOOK_PRE(dSv_player_item_c__setWarashibeItem, hookPreSetWarashibeItem); + ADD_HOOK_POST(daNpc_ykM_c__isDelete, hookPostYkMIsDelete); ADD_HOOK_POST(daNpc_ykW_c__isDelete, hookPostYkWIsDelete); + ADD_HOOK_PRE(daE_MD_c__create, hookPreEMdCreate); + ADD_HOOK_POST(daE_MD_c__create, hookPostEMdCreate); + + ADD_HOOK_REPLACE(daObjMasterSword_c__executeWait, hookReplaceMasterSwordExecuteWait); + ADD_HOOK_PRE(daObjMasterSword_c__execute, hookPreMasterSwordExecute); + return MOD_OK; } @@ -1669,9 +1861,20 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + return MOD_OK; } -} \ No newline at end of file +} From 08cda983373d59989fd7dbaf643465846d05de34 Mon Sep 17 00:00:00 2001 From: jdflyer Date: Fri, 14 Aug 2026 12:56:40 -0700 Subject: [PATCH 18/26] Basic Entrance Rando --- mods/randomizer/CMakeLists.txt | 1 + mods/randomizer/generator/logic/entrance.cpp | 27 +++++++ mods/randomizer/generator/logic/entrance.hpp | 17 ++++ .../generator/logic/entrance_shuffle.cpp | 6 +- .../generator/logic/flatten/bits.cpp | 1 + .../randomizer/generator/seedgen/settings.hpp | 1 + mods/randomizer/src/hooks.cpp | 13 ++- mods/randomizer/src/randomizer_context.cpp | 80 ++++++++++++------- mods/randomizer/src/randomizer_context.hpp | 37 ++++++--- mods/randomizer/src/ui/rando_config.cpp | 11 +++ 10 files changed, 145 insertions(+), 49 deletions(-) diff --git a/mods/randomizer/CMakeLists.txt b/mods/randomizer/CMakeLists.txt index 300539ce2e..7c25b203bb 100644 --- a/mods/randomizer/CMakeLists.txt +++ b/mods/randomizer/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.25) project(randomizer CXX) +set(CMAKE_CXX_STANDARD 23) if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(DUSK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE PATH "Path to dusk source root") diff --git a/mods/randomizer/generator/logic/entrance.cpp b/mods/randomizer/generator/logic/entrance.cpp index b27d8d2c47..a9095d914b 100644 --- a/mods/randomizer/generator/logic/entrance.cpp +++ b/mods/randomizer/generator/logic/entrance.cpp @@ -356,4 +356,31 @@ namespace randomizer::logic::entrance return {parentAreaName, connectedAreaName}; } + + bool Entrance::SetGameInfo(const YAML::Node& node) { + bool ret = true; + std::string stageIdString = node["Stage"].as(); + if (stageIdString == "null") { + stageIdString = "-1"; + ret = false; + } + int stageId = std::stoi(stageIdString); + if (stageId == -1) { + stageId = 0xFF; + } + SetStageId(static_cast(stageId)); + std::string roomNoString = node["Room"].as(); + if (roomNoString == "null") { + roomNoString = "-1"; + ret = false; + } + SetRoomNo(static_cast(std::stoi(roomNoString))); + SetPointNo(static_cast(std::stoi("0x0"+node["Spawn"].as(),nullptr,16))); + int layer = std::stoi("0x0"+node["State"].as(),nullptr,16); + if (layer == 0xFF) { + layer = -1; + } + SetLayerNo(static_cast(layer)); + return ret; + } } // namespace randomizer::logic::entrance diff --git a/mods/randomizer/generator/logic/entrance.hpp b/mods/randomizer/generator/logic/entrance.hpp index 8159d4353c..d9d27695f1 100644 --- a/mods/randomizer/generator/logic/entrance.hpp +++ b/mods/randomizer/generator/logic/entrance.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include "../utility/yaml.hpp" // Forward Declarations namespace randomizer::logic::area @@ -148,6 +150,16 @@ namespace randomizer::logic::entrance */ Entrance* AssumeReachable(); + void SetStageId(uint8_t stageId) { _stageId = stageId; } + void SetRoomNo(int8_t roomNo) { _roomNo = roomNo; } + void SetLayerNo(int8_t layerNo) { _layerNo = layerNo; } + void SetPointNo(int16_t pointNo) { _pointNo = pointNo; } + uint8_t GetStageId() const { return _stageId; } + int8_t GetRoomNo() const { return _roomNo; } + int8_t GetLayerNo() const { return _layerNo; } + int16_t GetPointNo() const { return _pointNo; } + bool SetGameInfo(const YAML::Node& node); + private: int _id = -1; area::Area* _parentArea = nullptr; @@ -159,6 +171,11 @@ namespace randomizer::logic::entrance std::string _alias = ""; world::World* _world = nullptr; + uint8_t _stageId = 0xFF; + int8_t _roomNo = -1; + int8_t _layerNo = -1; + int16_t _pointNo = -1; + /** * @brief The local requirement for this entrance assuming we have access to its parent area. */ diff --git a/mods/randomizer/generator/logic/entrance_shuffle.cpp b/mods/randomizer/generator/logic/entrance_shuffle.cpp index cc63431318..7dfc5dee54 100644 --- a/mods/randomizer/generator/logic/entrance_shuffle.cpp +++ b/mods/randomizer/generator/logic/entrance_shuffle.cpp @@ -65,7 +65,7 @@ namespace randomizer::logic::entrance_shuffle auto forwardEntrance = world->GetEntrance(forwardEntry["Connection"].as()); forwardEntrance->SetType(type); - // TODO: Set actual entrance data + forwardEntrance->SetGameInfo(forwardEntry); forwardEntrance->SetID(world->GetNewEntranceID()); forwardEntrance->SetPrimary(true); forwardEntrance->SetAlias( @@ -78,7 +78,7 @@ namespace randomizer::logic::entrance_shuffle auto returnEntrance = world->GetEntrance(returnEntry["Connection"].as()); returnEntrance->SetType(type); - // TODO: Set actual entrance data + returnEntrance->SetGameInfo(returnEntry); returnEntrance->SetID(world->GetNewEntranceID()); returnEntrance->SetAlias( returnEntry["Alias"] ? returnEntry["Alias"].as() : ""); @@ -471,7 +471,7 @@ namespace randomizer::logic::entrance_shuffle // plandomizer stuff going on. Currently, the only non-assumed entrance we're shuffling // is the randomized spawn, but if we ever shuffle warp portals, they'll go here too. - int retries = 20; + int retries = 10000; while (retries > 0) { std::unordered_map rollbacks = {}; diff --git a/mods/randomizer/generator/logic/flatten/bits.cpp b/mods/randomizer/generator/logic/flatten/bits.cpp index b95a699e75..6fef22a9a1 100644 --- a/mods/randomizer/generator/logic/flatten/bits.cpp +++ b/mods/randomizer/generator/logic/flatten/bits.cpp @@ -4,6 +4,7 @@ #include "../item.hpp" #include +#include BitVector::BitVector(const std::list& bits) { diff --git a/mods/randomizer/generator/seedgen/settings.hpp b/mods/randomizer/generator/seedgen/settings.hpp index 66c3b68fdb..3ae2a204bb 100644 --- a/mods/randomizer/generator/seedgen/settings.hpp +++ b/mods/randomizer/generator/seedgen/settings.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace randomizer::seedgen::settings { diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 314576e114..17fd01ab75 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -45,8 +45,12 @@ DEFINE_HOOK(&dSv_player_item_c::setLineUpItem, dSv_player_item_c__setLineUpItem) DEFINE_HOOK(&dSv_info_c::onSwitch, dSv_info_c__onSwitch); -/*DEFINE_HOOK_SYMBOL("__Z21dComIfGp_setNextStagePKcsaafjiasii", - void(char const*, s16, s8, s8, f32, u32, int, s8, s16, int, int), setNextStage);*/ +#ifdef _MSVC_LANG +#define setNextStage_sig "?dComIfGp_setNextStage@@YAXPEBDFCCMIHCFHH@Z" +#else +#define setNextStage_sig "_Z21dComIfGp_setNextStagePKcsaafjiasii" +#endif +DEFINE_HOOK_SYMBOL(setNextStage_sig, void(char const *, s16, s8, s8, f32, u32, int, s8, s16, int, int), setNextStage); DEFINE_HOOK_SYMBOL("daObj_Gb_Create", int(fopAc_ac_c*), ObjGb_Create); @@ -1433,7 +1437,7 @@ ModResult initialize() { ADD_HOOK_PRE(dSv_info_c__onSwitch, hookPreSaveInfoOnSwitch); - //ADD_HOOK_PRE(setNextStage, hookPreSetNextStage); + ADD_HOOK_PRE(setNextStage, hookPreSetNextStage); ADD_HOOK_PRE(ObjGb_Create, hookPreObjGbCreate); @@ -1491,6 +1495,7 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(); mods::hook::uninstall(svc_hook); @@ -1524,4 +1529,4 @@ ModResult uninstall() { return MOD_OK; } -} \ No newline at end of file +} diff --git a/mods/randomizer/src/randomizer_context.cpp b/mods/randomizer/src/randomizer_context.cpp index 4d2599cd4b..84c775e623 100644 --- a/mods/randomizer/src/randomizer_context.cpp +++ b/mods/randomizer/src/randomizer_context.cpp @@ -13,6 +13,7 @@ #include "../generator/randomizer.hpp" #include "../generator/utility/text.hpp" #include "../generator/utility/string.hpp" +#include "../generator/logic/entrance_shuffle.hpp" #include #include @@ -136,11 +137,11 @@ std::optional RandomizerContext::WriteToFile() { } for (const auto& [key, override] : mEntranceOverrides) { - out["mEntranceOverrides"][key] = std::bit_cast(override); + out["mEntranceOverrides"][std::bit_cast(key)] = std::bit_cast(override); } for (const auto& [key, override] : mReturnToPlaceOverrides) { - out["mReturnToPlaceOverrides"][key] = std::bit_cast(override); + out["mReturnToPlaceOverrides"][key] = std::bit_cast(override); } seedData << YAML::Dump(out); @@ -320,15 +321,15 @@ std::optional RandomizerContext::LoadFromHash(const std::string& ha // Entrance Overrides for (const auto& entranceNode : in["mEntranceOverrides"]) { - auto key = entranceNode.first.as(); - auto override = std::bit_cast(entranceNode.second.as()); + const auto key = std::bit_cast(entranceNode.first.as());; + const auto override = std::bit_cast(entranceNode.second.as()); this->mEntranceOverrides[key] = override; } // Return to Place Overrides for (const auto& entranceNode : in["mReturnToPlaceOverrides"]) { auto key = entranceNode.first.as(); - auto override = std::bit_cast(entranceNode.second.as()); + const auto override = std::bit_cast(entranceNode.second.as()); this->mReturnToPlaceOverrides[key] = override; } @@ -862,16 +863,21 @@ int randomizer_getItemAtLocation(const std::string& locationName) { void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, s16& pointNo, s8& mapLayer) { RandomizerContext::EntranceOverride override = { - static_cast(getStageID(stageName)), roomNo, static_cast(pointNo), mapLayer - }; + .stageId = static_cast(getStageID(stageName)), .roomNo = roomNo, .mapLayer = mapLayer, .pointNo = static_cast(pointNo)}; - int key = std::bit_cast(override); - if (randomizer_GetContext().mEntranceOverrides.contains(key)) { - auto& newOverride = randomizer_GetContext().mEntranceOverrides[key]; + // Debugging: Dump mEntranceOverrides + // for (const auto& [key,val] : randomizer_GetContext().mEntranceOverrides) { + // mods::log::info("({},{},{},{}) -> ({},{},{},{})",key.stageId,key.roomNo,key.mapLayer,key.pointNo,val.stageId,val.roomNo,val.mapLayer,val.pointNo); + // } + + if (randomizer_GetContext().mEntranceOverrides.contains(override)) { + auto& newOverride = randomizer_GetContext().mEntranceOverrides[override]; + mods::log::debug("Original Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); stageName = allStages[newOverride.stageId]; pointNo = newOverride.pointNo; roomNo = newOverride.roomNo; mapLayer = newOverride.mapLayer; + mods::log::debug("New Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); } } @@ -1728,34 +1734,34 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { // Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance // if mirror chamber access is closed RandomizerContext::EntranceOverride original = { - StageIDs::Mirror_Chamber, - 4, - 0, - -1 + .stageId = StageIDs::Mirror_Chamber, + .roomNo = 4, + .mapLayer = -1, + .pointNo = 0, }; RandomizerContext::EntranceOverride override = { - StageIDs::Bulblin_Camp, - 3, - 3, - -1 + .stageId = StageIDs::Bulblin_Camp, + .roomNo = 3, + .mapLayer = -1, + .pointNo = 3, }; - randoData.mEntranceOverrides[std::bit_cast(original)] = override; + randoData.mEntranceOverrides[original] = override; } // Vanilla Return to Place Overrides. Will need to change when boss/miniboss ER is implemented - static const std::list, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{ - {{Forest_Temple, Ook, Diababa}, {Forest_Temple, 22, 0, -1}}, - {{Goron_Mines, Dangoro, Fyrus}, {Goron_Mines, 1, 0, -1}}, - {{Lakebed_Temple, Deku_Toad, Morpheel}, {Lakebed_Temple, 0, 0, -1}}, - {{Arbiters_Grounds, Death_Sword, Stallord}, {Arbiters_Grounds, 0, 0, -1}}, - {{Snowpeak_Ruins, Darkhammer, Blizzeta}, {Snowpeak_Ruins, 0, 0, -1}}, - {{Temple_of_Time, Darknut, Armogohma}, {Temple_of_Time, 0, 0, -1}}, - {{City_in_the_Sky, Aeralfos, Argorok}, {City_in_the_Sky, 0, 3, -1}}, + static const std::vector, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{ + {{Forest_Temple, Ook, Diababa}, {.stageId = Forest_Temple, .roomNo = 22, .mapLayer = -1, .pointNo = 0}}, + {{Goron_Mines, Dangoro, Fyrus}, {.stageId = Goron_Mines, .roomNo = 1, .mapLayer = -1, .pointNo = 0}}, + {{Lakebed_Temple, Deku_Toad, Morpheel}, {.stageId = Lakebed_Temple, .roomNo = 0, .mapLayer = -1, .pointNo = 0}}, + {{Arbiters_Grounds, Death_Sword, Stallord}, {.stageId = Arbiters_Grounds, .roomNo = 0, .mapLayer = -1, .pointNo = 0}}, + {{Snowpeak_Ruins, Darkhammer, Blizzeta}, {.stageId = Snowpeak_Ruins, .roomNo = 0, .mapLayer = -1, .pointNo = 0}}, + {{Temple_of_Time, Darknut, Armogohma}, {.stageId = Temple_of_Time, .roomNo = 0, .mapLayer = -1, .pointNo = 0}}, + {{City_in_the_Sky, Aeralfos, Argorok}, {.stageId = City_in_the_Sky, .roomNo = 0, .mapLayer = -1, .pointNo = 3}}, {{Palace_of_Twilight, Phantom_Zant_1, - Phantom_Zant_2, Zant_Main_Room, Zant_Fight}, {Palace_of_Twilight, 0, 0, -1}}, - {{Hyrule_Castle, Ganondorf_Castle, Ganondorf_Field}, {Hyrule_Castle, 11, 0, -1}}, + Phantom_Zant_2, Zant_Main_Room, Zant_Fight}, {.stageId = Palace_of_Twilight, .roomNo = 0, .mapLayer = -1, .pointNo = 0}}, + {{Hyrule_Castle, Ganondorf_Castle, Ganondorf_Field}, {.stageId = Hyrule_Castle, .roomNo = 11, .mapLayer = -1, .pointNo = 0}}, }; // Return to Place Overrides @@ -1765,6 +1771,20 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { } } + // Apply entrance randomization + auto shuffleData = LOAD_EMBED_YAML(RANDO_DATA_PATH "entrance_shuffle_data.yaml"); + if (world->AnyEntranceRandomizerEnabled()) { + for (const auto& entrance : world->GetShuffledEntrances()) { + randomizer::logic::entrance::Entrance* replacesEntrance = entrance->GetReplaces(); + RandomizerContext::EntranceOverride forward = {.stageId = entrance->GetStageId(), .roomNo = entrance->GetRoomNo(), .mapLayer = entrance->GetLayerNo(), .pointNo = entrance->GetPointNo()}; + RandomizerContext::EntranceOverride replaces = {.stageId = replacesEntrance->GetStageId(), .roomNo = replacesEntrance->GetRoomNo(), .mapLayer = replacesEntrance->GetLayerNo(), .pointNo = replacesEntrance->GetPointNo()}; + if (forward.stageId == 0xFF || replaces.stageId == 0xFF || forward.roomNo == -1 || replaces.roomNo == -1) { + continue; + } + randoData.mEntranceOverrides[replaces] = forward; + } + } + return std::move(randoData); } @@ -1807,4 +1827,4 @@ bool GenerateAndWriteSeed(std::string& generationStatusMsg) { generationStatusMsg = fmt::format("Seed generated! Hash: {}", randoData.mHash); return true; -} \ No newline at end of file +} diff --git a/mods/randomizer/src/randomizer_context.hpp b/mods/randomizer/src/randomizer_context.hpp index 23f41b3378..75bc918ec3 100644 --- a/mods/randomizer/src/randomizer_context.hpp +++ b/mods/randomizer/src/randomizer_context.hpp @@ -20,7 +20,7 @@ class RandomizerContext { public: static constexpr size_t ACTR_CRC_SIZE = 32; - static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z + static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z static constexpr size_t OBJ_DELETE_SIZE = 1; static constexpr u8 ROOM_STAGE = 0xFF; @@ -37,7 +37,7 @@ public: std::unordered_map> mStartRegionFlags{}; std::list mStartingInventory{}; - struct itemLocationData{ + struct itemLocationData { int itemId{0xFF}; int stage{0xFF}; u16 flag{0xFFFF}; @@ -50,7 +50,7 @@ public: std::unordered_map mSkyCharacterOverrides{}; std::unordered_map mGoldenWolfOverrides{}; std::unordered_map mShopOverrides{}; - std::unordered_map mTwilitInsectOverrides{}; // Just used in tracker for now + std::unordered_map mTwilitInsectOverrides{}; // Just used in tracker for now std::unordered_map mFlowItemMessageOverrides{}; std::unordered_map mItemLocations{}; @@ -71,15 +71,27 @@ public: // Map of language -> map of key -> string std::unordered_map> mTextOverrides{}; + // Padded to 8 bytes so bit_cast can convert it to a uint64_t struct EntranceOverride { u8 stageId = 0xFF; s8 roomNo = -1; - s8 pointNo = -1; s8 mapLayer = -1; + u8 _pad0; + s16 pointNo = -1; + u16 _pad1; + bool operator==(const EntranceOverride& rhs) const = default; }; - // keyed by stageId << 24 | pointNo << 16 | roomNo << 8 | mapLayer - std::unordered_map mEntranceOverrides{}; + // Packs an EntranceOverride into a uint64_t + struct EntranceOverrideHash { + size_t operator()(const EntranceOverride& x) const noexcept { + return std::hash{}( + ((uint64_t)x.stageId << 32) | (x.roomNo << 24) | (x.mapLayer << 16) | x.pointNo); + } + }; + + std::unordered_map + mEntranceOverrides{}; // Overrides for returning to spawn. Keyed by stageId std::unordered_map mReturnToPlaceOverrides{}; @@ -153,7 +165,7 @@ public: static constexpr u8 EVENT_ITEM_QUEUE_SIZE = 10; - RandomizerState() {mInitialized = false;} + RandomizerState() { mInitialized = false; } int _create(); int _delete(); @@ -161,19 +173,19 @@ public: int draw(); void addItemToEventQueue(u8 item); void initGiveItemToPlayer(); - //void handleBonkDamage(); + // void handleBonkDamage(); void handleTimeOfDayChange(); void handleTimeSpeed(); void offLoad(); void handlePoeItem(u8 bitSw); - u8 getGiveItemToPlayerStatus() const { return mEventItemStatus;} + u8 getGiveItemToPlayerStatus() const { return mEventItemStatus; } u8 getTimeChange() const { return mTimeChange; } bool getRoomReloadingState() const { return mRoomReloadingState; } bool getHasPendingToDChange() const { return mHasPendingToDChange; } - void setGiveItemToPlayerStatus(u8 status) { mEventItemStatus = status;} + void setGiveItemToPlayerStatus(u8 status) { mEventItemStatus = status; } void setHasPendingToDChange(bool hasPending) { mHasPendingToDChange = hasPending; } void setTimeChange(u8 newTimeChange) { mTimeChange = newTimeChange; } void setRoomReloadingState(bool newState) { mRoomReloadingState = newState; } @@ -217,7 +229,8 @@ int randomizer_getItemAtLocation(const std::string& locationName); /* * @brief Overrides the given entrance paramaters if an override exists for them */ -void randomizer_checkAndOverrideEntranceData(const char*& i_Name, s8& i_RoomNo, s16& i_Point, s8& i_Layer); +void randomizer_checkAndOverrideEntranceData( + const char*& i_Name, s8& i_RoomNo, s16& i_Point, s8& i_Layer); /* * @brief Puts the associated flag into the randomizer state's temporary flag * variable. This allows the tracker/Archipelago to know a location has been checked @@ -285,4 +298,4 @@ u32 getStageObjCRC32(u8* data, size_t size); */ bool GenerateAndWriteSeed(std::string& generationStatusMsg); -#endif //DUSK_RANDOMIZER_CONTEXT_HPP +#endif // DUSK_RANDOMIZER_CONTEXT_HPP diff --git a/mods/randomizer/src/ui/rando_config.cpp b/mods/randomizer/src/ui/rando_config.cpp index 6bcc42bbe4..d4b7d85a95 100644 --- a/mods/randomizer/src/ui/rando_config.cpp +++ b/mods/randomizer/src/ui/rando_config.cpp @@ -420,6 +420,17 @@ ModResult buildSeedOptionsTab(ModContext* ctx, UiWindowHandle, UiElementHandle l add_select_setting(leftPane, "Lower Hyrule Castle Chandelier"); add_select_setting(leftPane, "Skip Bridge Donation"); + add_section(leftPane, "Entrance Randomizer Settings"); + add_select_setting(leftPane, "Randomize Starting Spawn"); + add_select_setting(leftPane, "Randomize Dungeon Entrances"); + add_select_setting(leftPane, "Randomize Boss Entrances"); + add_select_setting(leftPane, "Randomize Grotto Entrances"); + add_select_setting(leftPane, "Randomize Cave Entrances"); + add_select_setting(leftPane, "Randomize Interior Entrances"); + add_select_setting(leftPane, "Randomize Overworld Entrances"); + add_select_setting(leftPane, "Decouple Double Door Entrances"); + add_select_setting(leftPane, "Decouple Entrances"); + add_section(leftPane, "Additional Settings"); add_select_setting(leftPane, "Starting Time of Day"); add_select_setting(leftPane, "Logic Transform Anywhere"); From 33a9db2e491e1042d1ae828113c9f434cba18c84 Mon Sep 17 00:00:00 2001 From: jdflyer Date: Fri, 14 Aug 2026 18:26:37 -0700 Subject: [PATCH 19/26] Finish Implementing Entrance Rando --- .../generator/data/entrance_shuffle_data.yaml | 2 +- mods/randomizer/generator/logic/entrance.hpp | 5 ++ .../generator/logic/entrance_shuffle.cpp | 2 +- mods/randomizer/src/hooks.cpp | 18 ++++++ mods/randomizer/src/randomizer_context.cpp | 57 ++++++++++-------- mods/randomizer/src/randomizer_context.hpp | 3 +- mods/randomizer/src/session.cpp | 60 ++++++++++++++++++- mods/randomizer/src/session.hpp | 3 +- 8 files changed, 118 insertions(+), 32 deletions(-) diff --git a/mods/randomizer/generator/data/entrance_shuffle_data.yaml b/mods/randomizer/generator/data/entrance_shuffle_data.yaml index 4634003d31..d967cc0e48 100644 --- a/mods/randomizer/generator/data/entrance_shuffle_data.yaml +++ b/mods/randomizer/generator/data/entrance_shuffle_data.yaml @@ -1222,7 +1222,7 @@ Forward: Connection: Kakariko Renados Sanctuary -> Kakariko Renados Sanctuary Basement Stage: 75 - Room: 5 + Room: 7 Spawn: "00" Spawn Type: "FF" Parameters: "FFFF" diff --git a/mods/randomizer/generator/logic/entrance.hpp b/mods/randomizer/generator/logic/entrance.hpp index d9d27695f1..78c1df9bf2 100644 --- a/mods/randomizer/generator/logic/entrance.hpp +++ b/mods/randomizer/generator/logic/entrance.hpp @@ -159,6 +159,8 @@ namespace randomizer::logic::entrance int8_t GetLayerNo() const { return _layerNo; } int16_t GetPointNo() const { return _pointNo; } bool SetGameInfo(const YAML::Node& node); + void SetCoupledDoor(int16_t entrance) { _coupledEntrance = entrance; } + int16_t getCoupledDoor() { return _coupledEntrance; } private: int _id = -1; @@ -219,6 +221,9 @@ namespace randomizer::logic::entrance // to this one. So if the entrance is North Faron Woods -> Forest Temple Entrance, // then _assumed is the target entrance Root -> Forest Temple Entrance Entrance* _assumed = nullptr; + + // If the entrance is a coupled door, this is the other door's point to override + int16_t _coupledEntrance = -1; }; using EntrancePool = std::vector; diff --git a/mods/randomizer/generator/logic/entrance_shuffle.cpp b/mods/randomizer/generator/logic/entrance_shuffle.cpp index 7dfc5dee54..d042f2d264 100644 --- a/mods/randomizer/generator/logic/entrance_shuffle.cpp +++ b/mods/randomizer/generator/logic/entrance_shuffle.cpp @@ -114,7 +114,7 @@ namespace randomizer::logic::entrance_shuffle [&](const auto& door) { return door->IsPrimary() == mainDoor->IsPrimary(); }); auto coupledDoor = *coupledDoorItr; - // TODO: Add the coupled door's info to the main door + mainDoor->SetCoupledDoor(coupledDoor->GetPointNo()); // Completely remove the coupled door from the world graph doors.erase(coupledDoorItr); diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 6bc2a2ebe8..ff15469e6e 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -33,6 +33,9 @@ #include "d/d_msg_object.h" #include "d/d_save.h" #include "d/d_shop_system.h" +#include "d/d_s_name.h" +#include "f_op/f_op_overlap_mng.h" +#include "m_Do/m_Do_Reset.h" #include "c/c_damagereaction.h" DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); @@ -119,6 +122,8 @@ DEFINE_HOOK_SYMBOL("daE_MD_Create", int(fopAc_ac_c*), daE_MD_c__create); DEFINE_HOOK(&daObjMasterSword_c::executeWait, daObjMasterSword_c__executeWait); DEFINE_HOOK_SYMBOL("daObjMasterSword_Execute", int(daObjMasterSword_c*), daObjMasterSword_c__execute); +DEFINE_HOOK(&dScnName_c::changeGameScene, dScnName_c__changeGameScene); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -1525,6 +1530,9 @@ HookAction hookPreNpcFOrderEvent(ModContext*, void* args, void* retval, void*) { u16 i_priority = mods::arg(args, 4); // kinda hacky way to check for the state where Bo is trying to talk after getting Iron Boots + if (i_this->eventInfo.getArchiveName() == nullptr) { + return HOOK_CONTINUE; + } const std::string arcName = i_this->eventInfo.getArchiveName(); if (arcName == "Bou4" && i_priority == 40) { i_forceSpeak = FALSE; @@ -1698,6 +1706,12 @@ HookAction hookPreMasterSwordExecute(ModContext*, void* args, void* retval, void return HOOK_CONTINUE; } +void hookPost_dScnName_c__changeGameScene(ModContext* ctx, void* args, void* retval, void* userdata) { + if (!mDoRst::isReset() && !fopOvlpM_IsPeek()) { + randomizer::session::registerStartingLocation(); + } +} + } ModResult initialize() { @@ -1798,6 +1812,8 @@ ModResult initialize() { ADD_HOOK_REPLACE(daObjMasterSword_c__executeWait, hookReplaceMasterSwordExecuteWait); ADD_HOOK_PRE(daObjMasterSword_c__execute, hookPreMasterSwordExecute); + ADD_HOOK_POST(dScnName_c__changeGameScene, hookPost_dScnName_c__changeGameScene); + return MOD_OK; } @@ -1880,6 +1896,8 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(); + return MOD_OK; } } diff --git a/mods/randomizer/src/randomizer_context.cpp b/mods/randomizer/src/randomizer_context.cpp index 84c775e623..55edcd5ab9 100644 --- a/mods/randomizer/src/randomizer_context.cpp +++ b/mods/randomizer/src/randomizer_context.cpp @@ -867,17 +867,17 @@ void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, // Debugging: Dump mEntranceOverrides // for (const auto& [key,val] : randomizer_GetContext().mEntranceOverrides) { - // mods::log::info("({},{},{},{}) -> ({},{},{},{})",key.stageId,key.roomNo,key.mapLayer,key.pointNo,val.stageId,val.roomNo,val.mapLayer,val.pointNo); + // mods::log::debug("({},{},{},{}) -> ({},{},{},{})",key.stageId,key.roomNo,key.mapLayer,key.pointNo,val.stageId,val.roomNo,val.mapLayer,val.pointNo); // } + // mods::log::debug("Original Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); if (randomizer_GetContext().mEntranceOverrides.contains(override)) { auto& newOverride = randomizer_GetContext().mEntranceOverrides[override]; - mods::log::debug("Original Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); stageName = allStages[newOverride.stageId]; pointNo = newOverride.pointNo; roomNo = newOverride.roomNo; mapLayer = newOverride.mapLayer; - mods::log::debug("New Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); + // mods::log::debug("New Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); } } @@ -1729,27 +1729,6 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { } } - // Entrance Overrides - if (world->Setting("Mirror Chamber Access") == "Closed") { - // Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance - // if mirror chamber access is closed - RandomizerContext::EntranceOverride original = { - .stageId = StageIDs::Mirror_Chamber, - .roomNo = 4, - .mapLayer = -1, - .pointNo = 0, - }; - - RandomizerContext::EntranceOverride override = { - .stageId = StageIDs::Bulblin_Camp, - .roomNo = 3, - .mapLayer = -1, - .pointNo = 3, - }; - - randoData.mEntranceOverrides[original] = override; - } - // Vanilla Return to Place Overrides. Will need to change when boss/miniboss ER is implemented static const std::vector, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{ {{Forest_Temple, Ook, Diababa}, {.stageId = Forest_Temple, .roomNo = 22, .mapLayer = -1, .pointNo = 0}}, @@ -1781,10 +1760,38 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { if (forward.stageId == 0xFF || replaces.stageId == 0xFF || forward.roomNo == -1 || replaces.roomNo == -1) { continue; } - randoData.mEntranceOverrides[replaces] = forward; + randoData.mEntranceOverrides[forward] = replaces; + + // Set an override for the second door, if the entrance is coupled + if (entrance->getCoupledDoor() != -1) { + RandomizerContext::EntranceOverride coupled = {.stageId = entrance->GetStageId(), .roomNo = entrance->GetRoomNo(), .mapLayer = entrance->GetLayerNo(), .pointNo = entrance->getCoupledDoor()}; + randoData.mEntranceOverrides[coupled] = replaces; + } } } + if (world->Setting("Mirror Chamber Access") == "Closed") { + // Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance + // if mirror chamber access is closed + RandomizerContext::EntranceOverride original = { + .stageId = StageIDs::Mirror_Chamber, + .roomNo = 4, + .mapLayer = -1, + .pointNo = 0, + }; + + RandomizerContext::EntranceOverride override = { + .stageId = StageIDs::Bulblin_Camp, + .roomNo = 3, + .mapLayer = -1, + .pointNo = 3, + }; + + // Check if we are already overriding the bulblin camp entrance, and correctly override the entrance + const auto& it = randoData.mEntranceOverrides.find(override); + randoData.mEntranceOverrides[original] = (it != randoData.mEntranceOverrides.end()) ? it->second : override; + } + return std::move(randoData); } diff --git a/mods/randomizer/src/randomizer_context.hpp b/mods/randomizer/src/randomizer_context.hpp index 75bc918ec3..7e789a500c 100644 --- a/mods/randomizer/src/randomizer_context.hpp +++ b/mods/randomizer/src/randomizer_context.hpp @@ -85,8 +85,7 @@ public: // Packs an EntranceOverride into a uint64_t struct EntranceOverrideHash { size_t operator()(const EntranceOverride& x) const noexcept { - return std::hash{}( - ((uint64_t)x.stageId << 32) | (x.roomNo << 24) | (x.mapLayer << 16) | x.pointNo); + return std::hash{}(std::bit_cast(x)); } }; diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index f4bec82ba6..dc5c74218f 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -135,7 +135,7 @@ bool activateSeed(const char* hash) { svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver); svc_mng.item->observe_gives(mod_ctx, observe_give, nullptr, &s_check_observer); - registerStageEdits(); + registerStageEdits(); mods::log::info("activated seed {}", ctx.mHash); return true; } @@ -292,6 +292,62 @@ void registerStageEdits() { } } +// Set link to spawn outside of his house if we are playing with entrance rando and aren't already spawning in a dungeon +void registerStartingLocation() { + auto& ctx = randomizer_GetContext(); + + if (ctx.mEntranceOverrides.empty()) { + return; + } + + // Check that we aren't playing only with the setting "Mirror Chamber Access") == "Closed" + bool isExclusivelyMirrorChamber = false; + const auto& mirrorChamberIt = ctx.mEntranceOverrides.find(RandomizerContext::EntranceOverride{ + .stageId = StageIDs::Mirror_Chamber, + .roomNo = 4, + .mapLayer = -1, + .pointNo = 0 + }); + if (ctx.mEntranceOverrides.size() == 1 && mirrorChamberIt != ctx.mEntranceOverrides.end()) { + if (mirrorChamberIt->second == RandomizerContext::EntranceOverride{ + .stageId = StageIDs::Bulblin_Camp, + .roomNo = 3, + .mapLayer = -1, + .pointNo = 3, + }) { + isExclusivelyMirrorChamber = true; + } + } + + if (isExclusivelyMirrorChamber) { + return; + } + + constexpr std::array kDungeonNames = { + "D_MN01","D_MN04","D_MN05","D_MN06","D_MN07","D_MN08","D_MN09","D_MN10","D_MN11" + }; + + dSv_player_return_place_c& returnPlace = dComIfGs_getSaveData()->mPlayer.getPlayerReturnPlace(); + bool isDungeon = false; + for (const auto& name : kDungeonNames) { + if (returnPlace.getName() == name) { + isDungeon = true; + break; + } + } + + if (isDungeon) { + // Force-set the entrance without the override + g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set(returnPlace.getName(), returnPlace.getRoomNo(), returnPlace.mPlayerStatus, -1); + return; + } + + // Set the spawn to outside of link's house, which will get overrided later if we are replacing it + returnPlace.set("F_SP103",1,1); + // g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set("F_SP103", 1, 1, -1); + dComIfGp_setNextStage("F_SP103", 1, 1, -1); +} + void onNewSave(ModContext*, uint32_t, void*) { const std::string hash = g_pending_seed_hash; if (hash.empty()) @@ -351,4 +407,4 @@ void update() { g_randomizerState.execute(); } -} \ No newline at end of file +} diff --git a/mods/randomizer/src/session.hpp b/mods/randomizer/src/session.hpp index ebe9ce0516..002bba97d3 100644 --- a/mods/randomizer/src/session.hpp +++ b/mods/randomizer/src/session.hpp @@ -35,4 +35,5 @@ void update(); void deactivateSeed(); void setupRandomizerFile(); void registerStageEdits(); -} \ No newline at end of file +void registerStartingLocation(); +} From 9c52a059bd0b4450ade3f457fa605519f9ac3a4b Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Fri, 14 Aug 2026 18:56:55 -0700 Subject: [PATCH 20/26] alink demo and uzr portal hooks --- mods/randomizer/src/hooks.cpp | 425 +++++++++++++++++++++++++++++++++- 1 file changed, 423 insertions(+), 2 deletions(-) diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 98788820cd..f40bd26073 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -81,6 +81,10 @@ DEFINE_HOOK(&checkItemGet, dItem_checkItemGet); DEFINE_HOOK(&daAlink_c::decideDoStatus, daAlink_c__decideDoStatus); DEFINE_HOOK_SYMBOL("daAlink_searchBouDoor", void*(fopAc_ac_c*, void*), searchBouDoor); DEFINE_HOOK(&daAlink_c::checkGroundSpecialMode, daAlink_c__checkGroundSpecialMode); +DEFINE_HOOK(&daAlink_c::setGetItemFace, daAlink_c__setGetItemFace); +DEFINE_HOOK(&daAlink_c::setGetSubBgm, daAlink_c__setGetSubBgm); +DEFINE_HOOK(&daAlink_c::procCoGetItem, daAlink_c__procCoGetItem); +DEFINE_HOOK(&daAlink_c::procCoWarpInit, daAlink_c__procCoWarpInit); DEFINE_HOOK_SYMBOL("b_bq_end", void(b_bq_class*), bq_end); @@ -567,9 +571,27 @@ void hookPostSetLineUpItem(ModContext*, void* args, void*, void*) { } HookAction hookPreSaveInfoOnSwitch(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + const int i_no = mods::arg(args, 1); + const int room_no = mods::arg(args, 2); + // Set custom flag for the Temple of Time pedestal strike - if (getStageID() == Sacred_Grove && mods::arg(args, 1) == 0xEE) { - mods::arg(args, 0)->onSwitch(0x63, mods::arg(args, 2)); + if (getStageID() == Sacred_Grove && i_no == 0xEE) { + i_this->onSwitch(0x63, room_no); + } + + // We check to see if the flag being set is for the UZR portal as a safety precaution. + if (daAlink_c::checkStageName("F_SP126") && i_no == 0x15 && + dComIfGs_getTransformStatus() == TF_STATUS_WOLF) + { + // Set the flag to make Iza 1 available and set the memory bit for having talked to her + // after opening the portal as human. + dComIfGs_onEventBit(0xB02); + i_this->onSwitch(0x37, room_no); + + // Note for the above stuff. This works for now. Eventually would like to adjust this to + // a FLW patch since I think we could accomplish similar results by having the conversation + // continue as normal regardless of form, but I haven't looked into it that much. } return HOOK_CONTINUE; } @@ -1317,6 +1339,396 @@ HookAction hookPreCheckGroundSpecialMode(ModContext*, void* args, void* retval, return HOOK_CONTINUE; } +void hookPostSetGetItemFace(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + const u16 i_itemNo = mods::arg(args, 1); + + switch (i_itemNo) { + case dItemNo_Randomizer_WOOD_STICK_e: + case dItemNo_Randomizer_SWORD_e: + case dItemNo_Randomizer_SHIELD_e: + case dItemNo_Randomizer_MASTER_SWORD_e: + case dItemNo_Randomizer_LIGHT_SWORD_e: + case dItemNo_Randomizer_MAGIC_LV1_e: + i_this->setFaceBasicBck(dRes_ID_ALANM_BCK_FI_e); + break; + case dItemNo_Randomizer_FOOLISH_ITEM_e: + i_this->setFaceBasicBck(dRes_ID_ALANM_BCK_FJ_e); + break; + } +} + +HookAction hookPreSetGetSubBgm(ModContext*, void* args, void*, void*) { + enum { + SETYPE_HEART, + SETYPE_ITEM_GET, + SETYPE_ITEM_GET_MINI, + SETYPE_ITEM_GET_ME, + SETYPE_ITEM_GET_INSECT, + SETYPE_ITEM_GET_SMELL, + SETYPE_ITEM_GET_POU, + SETYPE_ITEM_GET_ME_S, + SETYPE_NONE, + }; + + static constexpr u8 getSeTypeRandomizer[255] = { + /* dItemNo_Randomizer_HEART_e */ SETYPE_NONE, + /* dItemNo_Randomizer_GREEN_RUPEE_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_BLUE_RUPEE_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_YELLOW_RUPEE_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_RED_RUPEE_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_PURPLE_RUPEE_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_ORANGE_RUPEE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_SILVER_RUPEE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_S_MAGIC_e */ SETYPE_NONE, + /* dItemNo_Randomizer_L_MAGIC_e */ SETYPE_NONE, + /* dItemNo_Randomizer_BOMB_5_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_BOMB_10_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_BOMB_20_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_BOMB_30_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_ARROW_10_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_ARROW_20_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_ARROW_30_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_ARROW_1_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_PACHINKO_SHOT_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_FOOLISH_ITEM_e */ SETYPE_NONE, + /* dItemNo_Randomizer_NOENTRY_20_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_21_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WATER_BOMB_5_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_WATER_BOMB_10_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_WATER_BOMB_20_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_WATER_BOMB_30_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_BOMB_INSECT_5_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_BOMB_INSECT_10_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_BOMB_INSECT_20_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_BOMB_INSECT_30_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_RECOVERY_FAILY_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_TRIPLE_HEART_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_SMALL_KEY_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_KAKERA_HEART_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_UTAWA_HEART_e */ SETYPE_HEART, + /* dItemNo_Randomizer_MAP_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_COMPUS_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_DUNGEON_EXIT_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_BOSS_KEY_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_DUNGEON_BACK_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_SWORD_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_MASTER_SWORD_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_WOOD_SHIELD_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_SHIELD_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_HYLIA_SHIELD_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_TKS_LETTER_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WEAR_CASUAL_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WEAR_KOKIRI_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_ARMOR_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_WEAR_ZORA_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_MAGIC_LV1_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_DUNGEON_EXIT_2_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WALLET_LV1_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WALLET_LV2_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_WALLET_LV3_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_55_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_56_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_57_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_58_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_59_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_60_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_ZORAS_JEWEL_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_HAWK_EYE_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_WOOD_STICK_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_BOOMERANG_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_SPINNER_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_IRONBALL_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_BOW_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_HOOKSHOT_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_HVY_BOOTS_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_COPY_ROD_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_W_HOOKSHOT_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_KANTERA_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_LIGHT_SWORD_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_FISHING_ROD_1_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_PACHINKO_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_COPY_ROD_2_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_77_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_78_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_BOMB_BAG_LV2_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_BOMB_BAG_LV1_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_BOMB_IN_BAG_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_82_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LIGHT_ARROW_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_ARROW_LV1_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_ARROW_LV2_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_ARROW_LV3_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_87_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LURE_ROD_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_BOMB_ARROW_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_HAWK_ARROW_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_BEE_ROD_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_JEWEL_ROD_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WORM_ROD_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_JEWEL_BEE_ROD_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_JEWEL_WORM_ROD_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_EMPTY_BOTTLE_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_RED_BOTTLE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_GREEN_BOTTLE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_BLUE_BOTTLE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_MILK_BOTTLE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_HALF_MILK_BOTTLE_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_OIL_BOTTLE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WATER_BOTTLE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_OIL_BOTTLE_2_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_RED_BOTTLE_2_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_UGLY_SOUP_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_HOT_SPRING_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_FAIRY_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_HOT_SPRING_2_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_OIL2_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_OIL_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NORMAL_BOMB_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WATER_BOMB_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_POKE_BOMB_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_FAIRY_DROP_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_WORM_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_DROP_BOTTLE_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_BEE_CHILD_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_CHUCHU_RARE_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_CHUCHU_RED_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_CHUCHU_BLUE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_CHUCHU_GREEN_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_CHUCHU_YELLOW_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_CHUCHU_PURPLE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LV1_SOUP_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LV2_SOUP_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LV3_SOUP_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LETTER_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_BILL_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_WOOD_STATUE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_IRIAS_PENDANT_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_HORSE_FLUTE_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_133_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_134_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_135_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_136_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_137_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_138_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_139_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_140_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_141_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_142_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_143_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_RAFRELS_MEMO_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_ASHS_SCRIBBLING_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_146_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_147_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_148_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_149_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_150_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_151_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_152_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_153_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_154_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_155_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_CHUCHU_YELLOW2_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_OIL_BOTTLE3_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_SHOP_BEE_CHILD_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_CHUCHU_BLACK_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LIGHT_DROP_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_DROP_CONTAINER_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_DROP_CONTAINER02_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_DROP_CONTAINER03_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_FILLED_CONTAINER_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_MIRROR_PIECE_2_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_MIRROR_PIECE_3_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_MIRROR_PIECE_4_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_168_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_169_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_170_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_171_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_172_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_173_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_174_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_175_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_SMELL_YELIA_POUCH_e */ SETYPE_ITEM_GET_SMELL, + /* dItemNo_Randomizer_SMELL_PUMPKIN_e */ SETYPE_ITEM_GET_SMELL, + /* dItemNo_Randomizer_SMELL_POH_e */ SETYPE_ITEM_GET_SMELL, + /* dItemNo_Randomizer_SMELL_FISH_e */ SETYPE_ITEM_GET_SMELL, + /* dItemNo_Randomizer_SMELL_CHILDREN_e */ SETYPE_ITEM_GET_SMELL, + /* dItemNo_Randomizer_SMELL_MEDICINE_e */ SETYPE_ITEM_GET_SMELL, + /* dItemNo_Randomizer_NOENTRY_182_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_183_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_184_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_185_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_186_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_187_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_188_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_189_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_190_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_NOENTRY_191_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_M_BEETLE_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_BEETLE_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_BUTTERFLY_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_BUTTERFLY_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_STAG_BEETLE_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_STAG_BEETLE_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_GRASSHOPPER_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_GRASSHOPPER_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_NANAFUSHI_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_NANAFUSHI_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_DANGOMUSHI_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_DANGOMUSHI_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_MANTIS_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_MANTIS_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_LADYBUG_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_LADYBUG_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_SNAIL_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_SNAIL_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_DRAGONFLY_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_DRAGONFLY_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_ANT_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_ANT_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_M_MAYFLY_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_F_MAYFLY_e */ SETYPE_ITEM_GET_INSECT, + /* dItemNo_Randomizer_NOENTRY_216_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_217_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_218_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_219_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_220_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_221_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_222_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_223_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_POU_SPIRIT_e */ SETYPE_ITEM_GET_POU, + /* dItemNo_Randomizer_NOENTRY_225_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_226_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_227_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_228_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_229_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_230_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_231_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_NOENTRY_232_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_ANCIENT_DOCUMENT_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_AIR_LETTER_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_ANCIENT_DOCUMENT2_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_LV7_DUNGEON_EXIT_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LINKS_SAVINGS_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_SMALL_KEY2_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_POU_FIRE1_e */ SETYPE_NONE, + /* dItemNo_Randomizer_POU_FIRE2_e */ SETYPE_NONE, + /* dItemNo_Randomizer_POU_FIRE3_e */ SETYPE_NONE, + /* dItemNo_Randomizer_POU_FIRE4_e */ SETYPE_NONE, + /* dItemNo_Randomizer_BOSSRIDER_KEY_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_TOMATO_PUREE_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_TASTE_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_LV5_BOSS_KEY_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_SURFBOARD_e */ SETYPE_NONE, + /* dItemNo_Randomizer_KANTERA2_e */ SETYPE_ITEM_GET_ME, + /* dItemNo_Randomizer_L2_KEY_PIECES1_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_L2_KEY_PIECES2_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_L2_KEY_PIECES3_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_KEY_OF_CARAVAN_e */ SETYPE_ITEM_GET_MINI, + /* dItemNo_Randomizer_LV2_BOSS_KEY_e */ SETYPE_ITEM_GET, + /* dItemNo_Randomizer_KEY_OF_FILONE_e */ SETYPE_ITEM_GET_MINI, + }; + + static constexpr u32 bgmLabel[8] = { + Z2BGM_HEART_GET, Z2BGM_ITEM_GET, Z2BGM_ITEM_GET_MINI, Z2BGM_ITEM_GET_ME, + Z2BGM_ITEM_GET_INSECT, Z2BGM_ITEM_GET_SMELL, Z2BGM_ITEM_GET_POU, Z2BGM_ITEM_GET_ME_S, + }; + + auto* i_this = mods::arg(args, 0); + const int i_itemNo = mods::arg(args, 1); + u32 se_type = getSeTypeRandomizer[i_itemNo]; + + if (se_type == SETYPE_ITEM_GET_ME && i_this->mProcVar4.field_0x3010 == 0) { + se_type = SETYPE_ITEM_GET_ME_S; + } + + if (se_type != SETYPE_NONE || i_itemNo == dItemNo_Randomizer_FOOLISH_ITEM_e) { + mDoAud_subBgmStart(bgmLabel[se_type]); + dComIfGp_setMesgBgmOn(); + } + + return HOOK_SKIP_ORIGINAL; +} + +HookAction hookPreProcCoGetItem(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + if (i_this->field_0x32cc != 0 || i_this->mProcVar2.field_0x300c != dItemNo_Randomizer_POU_SPIRIT_e) { + return HOOK_CONTINUE; + } + + auto* item_partner_p = static_cast(fopAcM_getItemEventPartner(i_this)); + if (item_partner_p == nullptr || fpcM_IsCreating(fpcM_GetID(item_partner_p))) { + return HOOK_CONTINUE; + } + + // Don't show special text in rando + if (dComIfGs_getPohSpiritNum() == 20) { + i_this->field_0x32cc = i_this->mProcVar2.field_0x300c + 0x65; + } + + return HOOK_CONTINUE; +} + +u8 hookProcCoWarpInit_prevSlot18 = dItemNo_NONE_e; +bool hookProcCoWarpInit_restoreSlot18 = false; +HookAction hookPreProcCoWarpInit(ModContext*, void* args, void*, void*) { + hookProcCoWarpInit_restoreSlot18 = false; + + auto* i_this = mods::arg(args, 0); + const int param_0 = mods::arg(args, 1); + const int param_1 = mods::arg(args, 2); + + // copy necessary functionality from og function to set up patch state + if (param_0 != 0 || i_this->checkWolf()) { + return HOOK_CONTINUE; + } + + const BOOL isSideWarp = param_1 == 0 && + ((daAlink_c::checkStageName("F_SP125") && fopAcM_GetRoomNo(i_this) == 4) || + (daAlink_c::checkStageName("D_MN08") && fopAcM_GetRoomNo(i_this) == 0)); + if (isSideWarp || !i_this->checkBossRoom() || fopAcM_GetRoomNo(i_this) != 50) { + return HOOK_CONTINUE; + } + + char stageName[32]; + SAFE_STRCPY(stageName, dComIfGp_getStartStageName()); + + for (int i = 0; i < 32; i++) { + if ((s64)stageName[i] == 0) { + stageName[i - 1] = 0; + break; + } + } + + if (!checkItemGet(dItemNo_DUNGEON_EXIT_e, 1) && + !(checkItemGet(dItemNo_DUNGEON_BACK_e, 1) && + strcmp(stageName, dComIfGs_getWarpStageName()) == 0)) + { + return HOOK_CONTINUE; + } + + // In rando, we only want to clear the Ooccoo slot if Ooccoo is in it. + // so continue with normal function if Ooccoo is in slot. + u8 ooccooSlot = dComIfGs_getItem(SLOT_18, false); + if (ooccooSlot == dItemNo_Randomizer_DUNGEON_EXIT_e || + ooccooSlot == dItemNo_Randomizer_DUNGEON_EXIT_2_e || + ooccooSlot == dItemNo_Randomizer_LV7_DUNGEON_EXIT_e) + { + return HOOK_CONTINUE; + } + + hookProcCoWarpInit_prevSlot18 = ooccooSlot; + hookProcCoWarpInit_restoreSlot18 = true; + return HOOK_CONTINUE; +} + +void hookPostProcCoWarpInit(ModContext*, void*, void*, void*) { + if (hookProcCoWarpInit_restoreSlot18) { + dComIfGs_setItem(SLOT_18, hookProcCoWarpInit_prevSlot18); + hookProcCoWarpInit_restoreSlot18 = false; + } +} + void hookPostBqEnd(ModContext*, void* args, void* retval, void*) { // If the player is wolf, they will softlock after the defeat cutscene is completed. checkTransformFromWolf(); @@ -1758,6 +2170,11 @@ ModResult initialize() { ADD_HOOK_PRE(daAlink_c__decideDoStatus, hookPreDecideDoStatus); ADD_HOOK_PRE(searchBouDoor, hookPreSearchBouDoor); ADD_HOOK_PRE(daAlink_c__checkGroundSpecialMode, hookPreCheckGroundSpecialMode); + ADD_HOOK_POST(daAlink_c__setGetItemFace, hookPostSetGetItemFace); + ADD_HOOK_PRE(daAlink_c__setGetSubBgm, hookPreSetGetSubBgm); + ADD_HOOK_PRE(daAlink_c__procCoGetItem, hookPreProcCoGetItem); + ADD_HOOK_PRE(daAlink_c__procCoWarpInit, hookPreProcCoWarpInit); + ADD_HOOK_POST(daAlink_c__procCoWarpInit, hookPostProcCoWarpInit); ADD_HOOK_POST(bq_end, hookPostBqEnd); @@ -1841,6 +2258,10 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); From c722cb1be01eb534656919d580bbe647bf7e0a95 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Fri, 14 Aug 2026 19:12:14 -0700 Subject: [PATCH 21/26] 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 4cbb8762f691825d87b80a74e4d20c706dcb6929 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Fri, 14 Aug 2026 19:16:49 -0700 Subject: [PATCH 22/26] update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index 1d10fa1bc5..1f84646de4 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 1d10fa1bc502910a6336fdac32f31cd0ac39710d +Subproject commit 1f84646de4c35483e580b07ab5c537c681a55e5e From 1f43e16e2d7440b8f2811391a32bf01363bccaf3 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Fri, 14 Aug 2026 23:41:12 -0700 Subject: [PATCH 23/26] more hooks wip --- include/d/actor/d_a_npc_zrz.h | 2 +- include/d/actor/d_a_tbox2.h | 2 +- mods/randomizer/src/hooks.cpp | 167 ++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 2 deletions(-) diff --git a/include/d/actor/d_a_npc_zrz.h b/include/d/actor/d_a_npc_zrz.h index 76c782b7d3..9e03215aa4 100644 --- a/include/d/actor/d_a_npc_zrz.h +++ b/include/d/actor/d_a_npc_zrz.h @@ -114,7 +114,7 @@ public: void himoCalc(); void adjustShapeAngle() {} -private: +// private: /* 0x0B48 */ Z2Creature mCreatureSound; /* 0x0BD8 */ daNpcF_MatAnm_c* mpMatAnm; /* 0x0BDC */ mDoExt_invisibleModel mInvisibleModel; diff --git a/include/d/actor/d_a_tbox2.h b/include/d/actor/d_a_tbox2.h index 9b06e685a3..5e3989db57 100644 --- a/include/d/actor/d_a_tbox2.h +++ b/include/d/actor/d_a_tbox2.h @@ -67,7 +67,7 @@ public: u8 getModelType() { return fopAcM_GetParamBit(this, 8, 4); } u8 getItemNo() { return fopAcM_GetParamBit(this, 0, 8); } -private: +// private: /* 0x718 */ u8 mReturnRupee; /* 0x71C */ request_of_phase_process_class mPhase; /* 0x724 */ J3DModel* mpModel; diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 3e7dd8bc81..f24f4ce932 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -22,10 +22,13 @@ #include "d/actor/d_a_tag_kmsg.h" #include "d/actor/d_a_npc_fairy.h" #include "d/actor/d_a_obj_master_sword.h" +#include "d/actor/d_a_obj_swBallC.h" #include "d/actor/d_a_npc_shad.h" +#include "d/actor/d_a_tbox2.h" #include "d/actor/d_a_npc_yelia.h" #include "d/actor/d_a_npc_ykm.h" #include "d/actor/d_a_npc_ykw.h" +#include "d/actor/d_a_npc_zrz.h" #include "d/d_door_param2.h" #include "d/d_file_sel_info.h" #include "d/d_file_select.h" @@ -34,6 +37,7 @@ #include "d/d_save.h" #include "d/d_shop_system.h" #include "d/d_s_name.h" +#include "d/d_gameover.h" #include "f_op/f_op_overlap_mng.h" #include "m_Do/m_Do_Reset.h" #include "c/c_damagereaction.h" @@ -128,6 +132,16 @@ DEFINE_HOOK_SYMBOL("daObjMasterSword_Execute", int(daObjMasterSword_c*), daObjMa DEFINE_HOOK(&dScnName_c::changeGameScene, dScnName_c__changeGameScene); +DEFINE_HOOK(&daNpc_zrZ_c::isDelete, daNpc_zrZ_c__isDelete); + +DEFINE_HOOK(&daTbox2_c::Create, daTbox2_c__Create); +DEFINE_HOOK(&daTbox2_c::setGetDemoItem, daTbox2_c__setGetDemoItem); + +DEFINE_HOOK(&dGameover_c::_create, dGameover_c___create); + +DEFINE_HOOK(&daObjSwBallC_c::Create, daObjSwBallC_c__Create); +DEFINE_HOOK(&daObjSwBallC_c::actionWait, daObjSwBallC_c__actionWait); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -1980,6 +1994,22 @@ void hookPostYkWIsDelete(ModContext*, void* args, void* retval, void*) { } } +void hookPostNpcZrzIsDelete(ModContext*, void* args, void* retval, void*) { + if (dComIfGs_isEventBit(GOT_ZORA_ARMOR_FROM_RUTELA)) { + return; + } + + auto* i_this = mods::arg(args, 0); + if (i_this->mDemoMode != 3) { + return; + } + + const int roomNo = fopAcM_GetRoomNo(i_this); + if (dComIfGs_isSwitch(i_this->mSwitch1, roomNo) && dComIfGs_isSwitch(i_this->mSwitch2, roomNo)) { + *static_cast(retval) = FALSE; + } +} + u8 hookNpcShadWaitType1_patchItemNo = dItemNo_NONE_e; bool hookNpcShadWaitType1_isPatchItemNo = false; HookAction hookPreNpcShadWaitType1(ModContext*, void* args, void* retval, void*) { @@ -2124,6 +2154,122 @@ void hookPost_dScnName_c__changeGameScene(ModContext* ctx, void* args, void* ret } } +void hookPostTbox2Create(ModContext*, void* args, void* retval, void*) { + if (*static_cast(retval) != 1) { + return; + } + + auto* i_this = mods::arg(args, 0); + u8 tboxId = fopAcM_GetParamBit(i_this, 16, 8); + if (tboxId == 0xFF || !dComIfGs_isTbox(tboxId)) { + return; + } + // If the flag for this box is set, open it + + // Set the action for not allowing the player to open it + i_this->init_actionWait(); + + // Set the animation frame to open + i_this->mpBck->setFrame(i_this->mpBck->getEndFrame()); + + // Set collision to open + if (i_this->mpBgW != NULL) { + dComIfG_Bgsp().Release(i_this->mpBgW); + } + if (i_this->mBoxBgW != NULL) { + dComIfG_Bgsp().Regist(i_this->mBoxBgW, i_this); + } +} + +HookAction hookPreTbox2SetGetDemoItem(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + if (i_this->mReturnRupee) { + return HOOK_CONTINUE; + } + + u8 tboxId = fopAcM_GetParamBit(i_this, 16, 8); + if (tboxId != 0xFF) { + dComIfGs_onTbox(tboxId); + } + + return HOOK_CONTINUE; +} + +u8 hookGameoverCreate_prevSlot18 = dItemNo_NONE_e; +bool hookGameoverCreate_restoreSlot18 = false; +HookAction hookPreGameoverCreate(ModContext*, void*, void*, void*) { + if (dMeter2Info_getGameOverType() == 1 && strcmp(dComIfGp_getLastPlayStageName(), "D_MN10A") == 0) { + // In rando, we only want to clear the Ooccoo slot if Ooccoo is in it. + // so continue with normal function if Ooccoo is in slot. + u8 ooccooSlot = dComIfGs_getItem(SLOT_18, false); + if (ooccooSlot == dItemNo_Randomizer_DUNGEON_EXIT_e || + ooccooSlot == dItemNo_Randomizer_DUNGEON_EXIT_2_e) + { + return HOOK_CONTINUE; + } + + hookGameoverCreate_prevSlot18 = ooccooSlot; + hookGameoverCreate_restoreSlot18 = true; + } + + return HOOK_CONTINUE; +} + +void hookPostGameoverCreate(ModContext*, void*, void*, void*) { + if (hookGameoverCreate_restoreSlot18) { + dComIfGs_setItem(SLOT_18, hookGameoverCreate_prevSlot18); + hookGameoverCreate_restoreSlot18 = false; + } +} + +inline void createPalaceSolsRewardItem(daObjSwBallC_c* i_this) { + cXyz scale{1.0f, 1.0f, 1.0f}; + cXyz position{250.0f, -200.0f, 11000.0f}; + initCreatePlayerItem( + dItemNo_Randomizer_WOOD_STICK_e, + 0x81, + &position, + fopAcM_GetRoomNo(i_this), + &i_this->shape_angle, + &scale); +} + +void hookPostSwBallCreate(ModContext*, void* args, void* retval, void*) { + auto* i_this = mods::arg(args, 0); + if (*static_cast(retval) != 1) { + return; + } + + if (fopAcM_isSwitch(i_this, 0x3d) && fopAcM_isSwitch(i_this, 0x3e)) { + createPalaceSolsRewardItem(i_this); + } +} + +HookAction hookPreSwBallActionWait(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + if (!fopAcM_isSwitch(i_this, 0x3d) || + !fopAcM_isSwitch(i_this, 0x3e)) + { + return HOOK_CONTINUE; + } + + // Don't play the cutscene in rando, just spawn in the item for + // Palace of Twilight Collect Both Sols + + dComIfGs_onTbox(10); + dComIfGs_onTbox(11); + + i_this->calcLightBallScale(); + i_this->field_0x574->setPlaySpeed(1.0f); + if (i_this->field_0x574->play() != 0 && !fopAcM_isSwitch(i_this, 0x3f)) { + fopAcM_onSwitch(i_this, 0x3f); + fopAcM_onSwitch(i_this, 0x27); + createPalaceSolsRewardItem(i_this); + } + + return HOOK_SKIP_ORIGINAL; +} + } ModResult initialize() { @@ -2231,6 +2377,17 @@ ModResult initialize() { ADD_HOOK_POST(dScnName_c__changeGameScene, hookPost_dScnName_c__changeGameScene); + ADD_HOOK_POST(daNpc_zrZ_c__isDelete, hookPostNpcZrzIsDelete); + + ADD_HOOK_POST(daTbox2_c__Create, hookPostTbox2Create); + ADD_HOOK_PRE(daTbox2_c__setGetDemoItem, hookPreTbox2SetGetDemoItem); + + ADD_HOOK_PRE(dGameover_c___create, hookPreGameoverCreate); + ADD_HOOK_POST(dGameover_c___create, hookPostGameoverCreate); + + ADD_HOOK_POST(daObjSwBallC_c__Create, hookPostSwBallCreate); + ADD_HOOK_PRE(daObjSwBallC_c__actionWait, hookPreSwBallActionWait); + return MOD_OK; } @@ -2319,6 +2476,16 @@ ModResult uninstall() { mods::hook::uninstall(); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + return MOD_OK; } } From d319bb10491336c4b7689cab4a2f93609cac63bf Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Sat, 15 Aug 2026 02:00:10 -0700 Subject: [PATCH 24/26] demo item / bosswarp hooks --- include/d/actor/d_a_obj_bosswarp.h | 2 +- mods/randomizer/src/hooks.cpp | 134 +++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/include/d/actor/d_a_obj_bosswarp.h b/include/d/actor/d_a_obj_bosswarp.h index 9b62f1a0f6..71b5b2b58f 100644 --- a/include/d/actor/d_a_obj_bosswarp.h +++ b/include/d/actor/d_a_obj_bosswarp.h @@ -60,7 +60,7 @@ public: bool isFirst() { return fopAcM_GetParamBit(this, 0x1b, 1); } void setAction(u8 action) { mAction = action; } -private: +// private: /* 0x568 */ request_of_phase_process_class mPhaseReq; /* 0x570 */ J3DModel* mpModel; /* 0x574 */ mDoExt_btkAnm* mpBtkAnm[2]; diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index f24f4ce932..ccfe4d98c0 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -22,6 +22,7 @@ #include "d/actor/d_a_tag_kmsg.h" #include "d/actor/d_a_npc_fairy.h" #include "d/actor/d_a_obj_master_sword.h" +#include "d/actor/d_a_obj_bosswarp.h" #include "d/actor/d_a_obj_swBallC.h" #include "d/actor/d_a_npc_shad.h" #include "d/actor/d_a_tbox2.h" @@ -29,6 +30,7 @@ #include "d/actor/d_a_npc_ykm.h" #include "d/actor/d_a_npc_ykw.h" #include "d/actor/d_a_npc_zrz.h" +#include "d/actor/d_a_demo_item.h" #include "d/d_door_param2.h" #include "d/d_file_sel_info.h" #include "d/d_file_select.h" @@ -142,6 +144,10 @@ DEFINE_HOOK(&dGameover_c::_create, dGameover_c___create); DEFINE_HOOK(&daObjSwBallC_c::Create, daObjSwBallC_c__Create); DEFINE_HOOK(&daObjSwBallC_c::actionWait, daObjSwBallC_c__actionWait); +DEFINE_HOOK_SYMBOL("daDitem_Execute", int(daDitem_c*), daDitem_c__execute); + +DEFINE_HOOK(&daObjBossWarp_c::demoProc, daObjBossWarp_c__demoProc); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -2270,6 +2276,125 @@ HookAction hookPreSwBallActionWait(ModContext*, void* args, void*, void*) { return HOOK_SKIP_ORIGINAL; } +void hookPostDitemExecute(ModContext*, void* args, void* retval, void*) { + auto* i_this = mods::arg(args, 0); + + // Certain items use field models that are too big to fit in link's hands so we want to scale them down to fit. + switch (i_this->m_itemNo) { + case dItemNo_Randomizer_MIRROR_PIECE_1_e: + case dItemNo_Randomizer_MIRROR_PIECE_2_e: + case dItemNo_Randomizer_MIRROR_PIECE_3_e: + case dItemNo_Randomizer_MIRROR_PIECE_4_e: + { + i_this->scale.x = 0.05f; + break; + } + case dItemNo_Randomizer_MASTER_SWORD_e: + case dItemNo_Randomizer_LIGHT_SWORD_e: + { + i_this->scale.x = 0.001f; + break; + } + } +} + +// pretty ugly way of handling this, but oh well. basically, we reconstruct the actionTable +// and use it to check that we're in the correct action before proceeding. then store info +// about what level we're on, and use it in the post-hook to undo the flag that's normally set. +daObjBossWarp_c* hookBossWarpDemoProc_patchActor = nullptr; +int hookBossWarpDemoProc_nowLevel = -1; +HookAction hookPreBossWarpDemoProc(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + + hookBossWarpDemoProc_patchActor = nullptr; + hookBossWarpDemoProc_nowLevel = -1; + + static const char* const actionTable[15] = { + "WAIT", + "APPEAR", + "DISAPPEAR", + "SCENE_CHG", + "STONE_FALL", + "STONE_MIDNA", + "WALK_TARGET1", + "APPEAR_END", + "STONE_DELETE", + "STONE_PUTAWAY", + "WCHECK", + "SETPOS", + "SCALING", + "STONE_SCALE", + "HEART_MOVE", + }; + + bool isRewardAction = + dComIfGp_evmng_getIsAddvance(i_this->mStaffId) + && dComIfGp_evmng_getMyActIdx(i_this->mStaffId, actionTable, 15, 0, 0) == 4; + if (!isRewardAction) { + return HOOK_CONTINUE; + } + + // this was a static function in the original TU, so reconstructing it for use here + auto getNowLevel = []() { + static const char* const stages[9] = { + "D_MN05A", + "D_MN04A", + "D_MN01A", + "D_MN10A", + "D_MN11A", + "D_MN06A", + "D_MN07A", + "D_MN08A", + "D_MN01A", + }; + + for (int i = 0; i < 9; i++) { + if (std::strcmp(dComIfGp_getStartStageName(), stages[i]) == 0) { + return i; + } + } + + return -1; + }; + + hookBossWarpDemoProc_patchActor = i_this; + hookBossWarpDemoProc_nowLevel = getNowLevel(); + return HOOK_CONTINUE; +} + +void hookPostBossWarpDemoProc(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + if (hookBossWarpDemoProc_patchActor != i_this) { + return; + } + + int level = hookBossWarpDemoProc_nowLevel; + hookBossWarpDemoProc_patchActor = nullptr; + hookBossWarpDemoProc_nowLevel = -1; + + // undo the flag that was set in the original function + switch (level) { + case 0: + dComIfGs_offCollectCrystal(0); + break; + case 1: + dComIfGs_offCollectCrystal(1); + break; + case 2: + dComIfGs_offCollectCrystal(2); + break; + case 4: + dComIfGs_offCollectMirror(1); + break; + case 5: + dComIfGs_offCollectMirror(2); + break; + case 6: + dComIfGs_offCollectMirror(3); + break; + } +} + } ModResult initialize() { @@ -2388,6 +2513,11 @@ ModResult initialize() { ADD_HOOK_POST(daObjSwBallC_c__Create, hookPostSwBallCreate); ADD_HOOK_PRE(daObjSwBallC_c__actionWait, hookPreSwBallActionWait); + ADD_HOOK_POST(daDitem_c__execute, hookPostDitemExecute); + + ADD_HOOK_PRE(daObjBossWarp_c__demoProc, hookPreBossWarpDemoProc); + ADD_HOOK_POST(daObjBossWarp_c__demoProc, hookPostBossWarpDemoProc); + return MOD_OK; } @@ -2486,6 +2616,10 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + return MOD_OK; } } From 9f7360b3590b0726fc2e09e95d4aad15dcd3b312 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Sat, 15 Aug 2026 03:20:06 -0700 Subject: [PATCH 25/26] mg_rod hooks --- mods/randomizer/src/hooks.cpp | 88 +++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index ccfe4d98c0..32bc237e0b 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -12,6 +12,7 @@ #include #include "d/actor/d_a_alink.h" +#include "d/actor/d_a_mg_rod.h" #include "d/actor/d_a_b_bq.h" #include "d/actor/d_a_door_shutter.h" #include "d/actor/d_a_e_md.h" @@ -148,6 +149,16 @@ DEFINE_HOOK_SYMBOL("daDitem_Execute", int(daDitem_c*), daDitem_c__execute); DEFINE_HOOK(&daObjBossWarp_c::demoProc, daObjBossWarp_c__demoProc); +DEFINE_HOOK_SYMBOL("lure_heart", void(dmg_rod_class*), mgRod_lure_heart); +DEFINE_HOOK_SYMBOL("uki_catch", void(dmg_rod_class*), mgRod_uki_catch); + +#ifdef _MSVC_LANG +#define setEmptyBottle_noarg_sig "?setEmptyBottle@dSv_player_item_c@@QEAAXXZ" +#else +#define setEmptyBottle_noarg_sig "_ZN17dSv_player_item_c14setEmptyBottleEv" +#endif +DEFINE_HOOK_SYMBOL(setEmptyBottle_noarg_sig, void(dSv_player_item_c*), dSv_player_item_c__setEmptyBottle); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -625,8 +636,24 @@ HookAction hookPreSaveInfoOnSwitch(ModContext*, void* args, void*, void*) { return HOOK_CONTINUE; } +bool hookLureHeart_isSkipGetItem = false; +HookAction hookPreLureHeart(ModContext*, void* args, void*, void*) { + hookLureHeart_isSkipGetItem = true; + return HOOK_CONTINUE; +} + +void hookPostLureHeart(ModContext*, void*, void*, void*) { + hookLureHeart_isSkipGetItem = false; +} + HookAction hookPreGetItemFunc(ModContext*, void* args, void*, void*) { const u8 item = mods::arg(args, 0); + + // coming from lure_heart, don't give item here. let FLW message handle it + if (hookLureHeart_isSkipGetItem && item == dItemNo_KAKERA_HEART_e) { + return HOOK_SKIP_ORIGINAL; + } + item::exec_item_get(item); return HOOK_SKIP_ORIGINAL; } @@ -2395,6 +2422,55 @@ void hookPostBossWarpDemoProc(ModContext*, void* args, void*, void*) { } } +bool hookUkiCatch_isSkipSetBottle = false; +HookAction hookPreUkiCatch(ModContext*, void* args, void*, void*) { + auto* i_this = mods::arg(args, 0); + hookUkiCatch_isSkipSetBottle = false; + + fopAc_ac_c* mgfish_a = fopAcM_SearchByID(i_this->mg_fish_id); + mg_fish_class* mgfish = (mg_fish_class*)mgfish_a; + if (mgfish == nullptr) { + return HOOK_CONTINUE; + } + + // replicating the bottle catch check here to remove rng check by overriding whatever catch + // you have if you meet the bottle catch conditions + if (!dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[468]) && + strcmp(dComIfGp_getStartStageName(), "F_SP127") == 0) + { + fopAc_ac_c* player = dComIfGp_getPlayer(0); + cXyz bin_pos(6800.0f, 30.0f, -270.0f); + bin_pos -= player->current.pos; + if (bin_pos.abs() < 2500.0f) { + s16 angle = player->shape_angle.y - cM_atan2s(bin_pos.x, bin_pos.z); + if (angle < 0x4000 && angle > -0x4000) { + mgfish->mCaughtType = MG_CATCH_BIN; + } + } + } + + // if catching a bottle, set a flag to skip giving an empty bottle so that the FLW patch + // can handle the check + if (mgfish->mCaughtType == MG_CATCH_BIN) { + hookUkiCatch_isSkipSetBottle = true; + } + + return HOOK_CONTINUE; +} + +void hookPostUkiCatch(ModContext*, void*, void*, void*) { + hookUkiCatch_isSkipSetBottle = false; +} + +HookAction hookPreSetEmptyBottle(ModContext*, void*, void*, void*) { + // coming from uki_catch, skip giving bottle. let FLW patch handle it + if (hookUkiCatch_isSkipSetBottle) { + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + } ModResult initialize() { @@ -2518,6 +2594,14 @@ ModResult initialize() { ADD_HOOK_PRE(daObjBossWarp_c__demoProc, hookPreBossWarpDemoProc); ADD_HOOK_POST(daObjBossWarp_c__demoProc, hookPostBossWarpDemoProc); + ADD_HOOK_PRE(mgRod_lure_heart, hookPreLureHeart); + ADD_HOOK_POST(mgRod_lure_heart, hookPostLureHeart); + + ADD_HOOK_PRE(mgRod_uki_catch, hookPreUkiCatch); + ADD_HOOK_POST(mgRod_uki_catch, hookPostUkiCatch); + + ADD_HOOK_PRE(dSv_player_item_c__setEmptyBottle, hookPreSetEmptyBottle); + return MOD_OK; } @@ -2620,6 +2704,10 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + return MOD_OK; } } From aa3db8f9491ed202315921ea6066810a50047b94 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Sat, 15 Aug 2026 04:57:14 -0700 Subject: [PATCH 26/26] more hooks --- include/d/actor/d_a_npc_zrc.h | 2 +- mods/randomizer/src/hooks.cpp | 87 +++++++++++++++++++++++++++++++---- 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/include/d/actor/d_a_npc_zrc.h b/include/d/actor/d_a_npc_zrc.h index f4aeee6ac9..6f6a00061b 100644 --- a/include/d/actor/d_a_npc_zrc.h +++ b/include/d/actor/d_a_npc_zrc.h @@ -96,7 +96,7 @@ public: static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mEvtCutNameList[2]; static DUSK_GAME_DATA EventFn DUSK_CONST mEvtCutList[2]; -private: +// private: /* 0xB48 */ Z2Creature mCreatureSound; /* 0xBD8 */ daNpcF_MatAnm_c* mpMatAnm; /* 0xBDC */ daNpcF_Lookat_c mLookat; diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 32bc237e0b..0c76793ee1 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -11,39 +11,42 @@ #include #include +#include "c/c_damagereaction.h" #include "d/actor/d_a_alink.h" -#include "d/actor/d_a_mg_rod.h" #include "d/actor/d_a_b_bq.h" +#include "d/actor/d_a_demo_item.h" #include "d/actor/d_a_door_shutter.h" #include "d/actor/d_a_e_md.h" #include "d/actor/d_a_e_mk.h" #include "d/actor/d_a_kytag08.h" +#include "d/actor/d_a_mg_rod.h" #include "d/actor/d_a_npc4.h" #include "d/actor/d_a_npc_bans.h" -#include "d/actor/d_a_tag_kmsg.h" #include "d/actor/d_a_npc_fairy.h" -#include "d/actor/d_a_obj_master_sword.h" -#include "d/actor/d_a_obj_bosswarp.h" -#include "d/actor/d_a_obj_swBallC.h" #include "d/actor/d_a_npc_shad.h" -#include "d/actor/d_a_tbox2.h" #include "d/actor/d_a_npc_yelia.h" #include "d/actor/d_a_npc_ykm.h" #include "d/actor/d_a_npc_ykw.h" +#include "d/actor/d_a_npc_zrc.h" #include "d/actor/d_a_npc_zrz.h" -#include "d/actor/d_a_demo_item.h" +#include "d/actor/d_a_obj_bosswarp.h" +#include "d/actor/d_a_obj_master_sword.h" +#include "d/actor/d_a_obj_swBallC.h" +#include "d/actor/d_a_obj_zra_rock.h" +#include "d/actor/d_a_tag_kmsg.h" +#include "d/actor/d_a_tbox2.h" #include "d/d_door_param2.h" #include "d/d_file_sel_info.h" #include "d/d_file_select.h" +#include "d/d_gameover.h" #include "d/d_meter2_info.h" #include "d/d_msg_object.h" +#include "d/d_s_name.h" +#include "d/d_s_play.h" #include "d/d_save.h" #include "d/d_shop_system.h" -#include "d/d_s_name.h" -#include "d/d_gameover.h" #include "f_op/f_op_overlap_mng.h" #include "m_Do/m_Do_Reset.h" -#include "c/c_damagereaction.h" DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect); @@ -159,6 +162,17 @@ DEFINE_HOOK_SYMBOL("uki_catch", void(dmg_rod_class*), mgRod_uki_catch); #endif DEFINE_HOOK_SYMBOL(setEmptyBottle_noarg_sig, void(dSv_player_item_c*), dSv_player_item_c__setEmptyBottle); +DEFINE_HOOK(&daNpc_zrC_c::isDelete, daNpc_zrC_c__isDelete); + +DEFINE_HOOK(&daObjZraRock_c::create, daObjZraRock_c__create); + +#ifdef _MSVC_LANG +#define phase_1_dScnPly_sig "?phase_1@@YAHPAUdScnPly_c@@@Z" // sig is wrong? +#else +#define phase_1_dScnPly_sig "_Z7phase_1P9dScnPly_c" +#endif +// DEFINE_HOOK_SYMBOL(phase_1_dScnPly_sig, int(dScnPly_c*), phase_1__dScnPly_c); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -2471,6 +2485,47 @@ HookAction hookPreSetEmptyBottle(ModContext*, void*, void*, void*) { return HOOK_CONTINUE; } +HookAction hookPreNpcZrCIsDelete(ModContext*, void* args, void* retval, void*) { + auto* i_this = mods::arg(args, 0); + if (i_this->mType == 4 + || i_this->mType == 0 + || i_this->mType == 1 + || (i_this->mType == 2 && daNpcF_chkEvtBit(0x108)) + || i_this->mType == 3 + ) + { + *static_cast(retval) = FALSE; + return HOOK_SKIP_ORIGINAL; + } + + return HOOK_CONTINUE; +} + +void hookPostObjZraRockCreate(ModContext*, void* args, void* retval, void*) { + auto* i_this = mods::arg(args, 0); + if (*static_cast(retval) != cPhs_ERROR_e) { + return; + } + + // if returned cPhs_ERROR_e, check if it's because the switch was set. + if (dComIfGs_isSwitch((fopAcM_GetParam(i_this) >> 8) & 0xff, fopAcM_GetRoomNo(i_this))) { + // Don't delete the rock when we're following rutela + if (!dComIfGs_isEventBit(GOT_ZORA_ARMOR_FROM_RUTELA) && dComIfGs_isEventBit(ZORA_ESCORT_CLEARED)) { + *static_cast(retval) = cPhs_COMPLEATE_e; + } + } +} + +void hookPostScnPlyPhase1(ModContext*, void*, void*, void*) { + if (!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 && + dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12) + { + // Undo item set for rando + dComIfGs_offItemFirstBit(dItemNo_HORSE_FLUTE_e); + dComIfGs_setItem(SLOT_21, dItemNo_NONE_e); + } +} + } ModResult initialize() { @@ -2602,6 +2657,12 @@ ModResult initialize() { ADD_HOOK_PRE(dSv_player_item_c__setEmptyBottle, hookPreSetEmptyBottle); + ADD_HOOK_PRE(daNpc_zrC_c__isDelete, hookPreNpcZrCIsDelete); + + ADD_HOOK_POST(daObjZraRock_c__create, hookPostObjZraRockCreate); + + //ADD_HOOK_POST(phase_1__dScnPly_c, hookPostScnPlyPhase1); + return MOD_OK; } @@ -2708,6 +2769,12 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + + mods::hook::uninstall(svc_hook); + + //mods::hook::uninstall(); + return MOD_OK; } }