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;