diff --git a/include/d/actor/d_a_itembase.h b/include/d/actor/d_a_itembase.h index da1f541cd7..374e4b7df2 100644 --- a/include/d/actor/d_a_itembase.h +++ b/include/d/actor/d_a_itembase.h @@ -32,6 +32,12 @@ struct daItemBase_data { class daItemBase_c : public fopAc_ac_c { public: u8 getItemNo(); +#if TARGET_PC + u8 getDisplayItemNo() const { + return mDisplayItemNo != 0xFF ? mDisplayItemNo : m_itemNo; + } + void setDisplayItemNo(u8 itemNo) { mDisplayItemNo = itemNo; } +#endif void hide(); void show(); void changeDraw(); @@ -79,6 +85,9 @@ public: /* 0x928 */ s16 m_get_timer; /* 0x92A */ u8 m_itemNo; /* 0x92B */ u8 field_0x92b; +#if TARGET_PC + u8 mDisplayItemNo = 0xFF; +#endif }; #endif /* D_A_D_A_ITEMBASE_H */ diff --git a/include/f_op/f_op_actor_mng.h b/include/f_op/f_op_actor_mng.h index b23fc40383..7aa3bf2156 100644 --- a/include/f_op/f_op_actor_mng.h +++ b/include/f_op/f_op_actor_mng.h @@ -532,10 +532,12 @@ 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 IF_DUSK_ARG(u32 i_itemGiveTag = 0)); + createFunc i_createFunc IF_DUSK_ARG(u32 i_itemGiveTag = 0) + IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF)); 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 IF_DUSK_ARG(u32 i_itemGiveTag = 0)); + const csXyz* i_angle, const cXyz* i_scale, s8 i_argument IF_DUSK_ARG(u32 i_itemGiveTag = 0) + IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF)); inline fpc_ProcID fopAcM_Create(s16 i_procName, createFunc i_createFunc, void* params) { return fpcM_Create(i_procName, i_createFunc,params); diff --git a/sdk/include/mods/items.h b/sdk/include/mods/items.h index e0b1cae2d9..b79563026c 100644 --- a/sdk/include/mods/items.h +++ b/sdk/include/mods/items.h @@ -27,6 +27,7 @@ #define ITEM_CHECK_FISHING_HEART_PIECE "fishing_heart_piece" #define ITEM_CHECK_GOATS_REWARD "goats_reward" #define ITEM_CHECK_GORON_REWARD "goron_reward:F_SP113" +#define ITEM_CHECK_GORON_SPRINGWATER_RUSH "goron_springwater_rush" #define ITEM_CHECK_ILIA_CHARM "ilia_charm" #define ITEM_CHECK_ILIA_MEMORY "ilia_memory" #define ITEM_CHECK_IZA_REWARD_1 "iza_reward_1" @@ -42,6 +43,7 @@ #define ITEM_CHECK_PRAYER_REWARD "prayer_reward" #define ITEM_CHECK_RENADO_LETTER "renado_letter" #define ITEM_CHECK_SERA_REWARD "sera_reward" +#define ITEM_CHECK_SHAD_DOMINION_ROD "shad_dominion_rod" #define ITEM_CHECK_SKYBOOK "skybook" #define ITEM_CHECK_SNOWBOARD_REWARD "snowboard_race_reward" #define ITEM_CHECK_STAR_REWARD_1 "star_reward_1" diff --git a/sdk/include/mods/svc/item.h b/sdk/include/mods/svc/item.h index 1f2c3d3b43..94fec6fb6c 100644 --- a/sdk/include/mods/svc/item.h +++ b/sdk/include/mods/svc/item.h @@ -8,7 +8,7 @@ #define ITEM_SERVICE_ID "dev.twilitrealm.dusklight.item" #define ITEM_SERVICE_MAJOR 2u -#define ITEM_SERVICE_MINOR 0u +#define ITEM_SERVICE_MINOR 1u /* 0 is never a valid handle. */ typedef uint64_t ItemCheckHandle; @@ -31,11 +31,17 @@ typedef struct ItemCheckInfo { const void* giver_actor; /* fopAc_ac_c*, or NULL when unavailable */ uint8_t vanilla_item; uint8_t current_item; + uint8_t current_display_item; } ItemCheckInfo; -/* Return true and write out_item to replace current_item, or false to leave it unchanged. */ +typedef struct ItemCheckResolution { + uint8_t item; + uint8_t display_item; /* leave unset (0xFF/NONE) to use the item */ +} ItemCheckResolution; + +/* Return true and write out_result to replace the result, or false to leave it unchanged. */ typedef bool (*ItemCheckResolveFn)( - ModContext* ctx, const ItemCheckInfo* info, uint8_t* out_item, void* user_data); + ModContext* ctx, const ItemCheckInfo* info, ItemCheckResolution* out_result, void* user_data); typedef enum ItemGiveOrigin { ITEM_GIVE_ORIGIN_GAME = 0, diff --git a/src/d/actor/d_a_demo_item.cpp b/src/d/actor/d_a_demo_item.cpp index 5876c26718..465532a5f0 100644 --- a/src/d/actor/d_a_demo_item.cpp +++ b/src/d/actor/d_a_demo_item.cpp @@ -449,7 +449,7 @@ int daDitem_c::Delete() { endInsectEffect(); mSound.deleteObject(); - return DeleteBase(dItem_data::getArcName(m_itemNo)); + return DeleteBase(dItem_data::getArcName(DUSK_IF_ELSE(getDisplayItemNo(), m_itemNo))); } static int daDitem_Delete(daDitem_c* i_this) { @@ -461,8 +461,16 @@ int daDitem_c::create() { m_itemNo = daDitem_prm::getNo(this); +#if TARGET_PC + const auto [item, displayItem] = dusk::mods::item_check_resolve(mItemGiveTag, m_itemNo, this); + m_itemNo = item; + setDisplayItemNo(displayItem); + const char* arc_name = dItem_data::getArcName(displayItem); + s16 bmd_name = dItem_data::getBmdName(displayItem); +#else const char* arc_name = dItem_data::getArcName(m_itemNo); s16 bmd_name = dItem_data::getBmdName(m_itemNo); +#endif if (bmd_name < 0 || arc_name == NULL) { if (bmd_name == 0) { @@ -476,9 +484,13 @@ int daDitem_c::create() { } m_itemNo = dItemNo_GREEN_RUPEE_e; +#if TARGET_PC + setDisplayItemNo(dItemNo_NONE_e); +#endif } - int phase_state = dComIfG_resLoad(&mPhase, dItem_data::getArcName(m_itemNo)); + int phase_state = dComIfG_resLoad( + &mPhase, dItem_data::getArcName(DUSK_IF_ELSE(getDisplayItemNo(), m_itemNo))); if (phase_state == cPhs_COMPLEATE_e) { if (!fopAcM_entrySolidHeap(this, CheckItemCreateHeap, 0x80003390)) { return cPhs_ERROR_e; diff --git a/src/d/actor/d_a_e_po.cpp b/src/d/actor/d_a_e_po.cpp index a415cfaa65..a7a7be6dd1 100644 --- a/src/d/actor/d_a_e_po.cpp +++ b/src/d/actor/d_a_e_po.cpp @@ -7,9 +7,6 @@ #include "d/actor/d_a_e_po.h" #include "d/actor/d_a_obj_poFire.h" -#if TARGET_PC -#include "d/actor/d_a_alink.h" -#endif #include "d/d_cc_d.h" #include "d/d_cc_uty.h" #include "f_op/f_op_actor_enemy.h" @@ -1135,17 +1132,7 @@ static void e_po_dead(e_po_class* i_this) { camera_player->mCamera.Start(); camera_player->mCamera.SetTrimSize(0); dComIfGp_event_reset(); -#if TARGET_PC - const auto itemCheck = dusk::mods::item_check_commit( - dusk::mods::item_give_tag_poe(i_this->BitSW), dItemNo_POU_SPIRIT_e, a_this); - daPy_getPlayerActorClass()->cancelOriginalDemo(); - if (itemCheck.itemNo == dItemNo_NONE_e) { - dusk::mods::item_check_complete(itemCheck, a_this); - } else { - daAlink_getAlinkActorClass()->procWolfAtnActorMoveInit(); - dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::ForcedDemo); - } -#else +#if !TARGET_PC dComIfGs_addPohSpiritNum(); #if !PLATFORM_SHIELD if (dComIfGs_getPohSpiritNum() == 0x14) { @@ -1153,8 +1140,8 @@ static void e_po_dead(e_po_class* i_this) { dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[457]); } #endif - daPy_getPlayerActorClass()->cancelOriginalDemo(); #endif + daPy_getPlayerActorClass()->cancelOriginalDemo(); } else if (mArg0Check(i_this, 0) != 0) { if (!fopAcM_isSwitch(a_this, 0x22)) { if (fopAcM_SearchByID(i_this->field_0x5B8, &local_1b0_actor) != 0 && diff --git a/src/d/actor/d_a_itembase.cpp b/src/d/actor/d_a_itembase.cpp index 084fb5221f..474b03c784 100644 --- a/src/d/actor/d_a_itembase.cpp +++ b/src/d/actor/d_a_itembase.cpp @@ -242,21 +242,21 @@ BOOL daItemBase_c::chkFlag(int i_flag) { } s8 daItemBase_c::getTevFrm() { - return dItem_data::getTevFrm(m_itemNo); + return dItem_data::getTevFrm(DUSK_IF_ELSE(getDisplayItemNo(), m_itemNo)); } s8 daItemBase_c::getBtpFrm() { - return dItem_data::getBtpFrm(m_itemNo); + return dItem_data::getBtpFrm(DUSK_IF_ELSE(getDisplayItemNo(), m_itemNo)); } u8 daItemBase_c::getShadowSize() { - return dItem_data::getShadowSize(m_itemNo); + return dItem_data::getShadowSize(DUSK_IF_ELSE(getDisplayItemNo(), m_itemNo)); } u8 daItemBase_c::getCollisionH() { - return dItem_data::getH(m_itemNo); + return dItem_data::getH(DUSK_IF_ELSE(getDisplayItemNo(), m_itemNo)); } u8 daItemBase_c::getCollisionR() { - return dItem_data::getR(m_itemNo); + return dItem_data::getR(DUSK_IF_ELSE(getDisplayItemNo(), m_itemNo)); } diff --git a/src/d/actor/d_a_npc_gra.cpp b/src/d/actor/d_a_npc_gra.cpp index 13f3215fae..e5e0d6eed0 100644 --- a/src/d/actor/d_a_npc_gra.cpp +++ b/src/d/actor/d_a_npc_gra.cpp @@ -12,6 +12,9 @@ #include "d/actor/d_a_npc_gra.h" #include "d/actor/d_a_tag_gra.h" #include "Z2AudioLib/Z2Instances.h" +#if TARGET_PC +#include "mods/items.h" +#endif #include DUSK_GAME_DATA const daNpc_grA_HIOParam daNpc_grA_Param_c::m = { @@ -1323,7 +1326,7 @@ BOOL daNpc_grA_c::isDelete() { return FALSE; } fopAcM_createItemForBoss(¤t.pos, 0x21, fopAcM_GetRoomNo(this), NULL, NULL, 0.0f, 0.0f, - 0x80); + 0x80 IF_DUSK_ARG(ITEM_CHECK_GORON_SPRINGWATER_RUSH)); return TRUE; } return TRUE; @@ -2812,7 +2815,7 @@ BOOL daNpc_grA_c::ECut_carrySpaWater(int i_staffID) { c.y += 200.0f; csXyz c2(0, fopAcM_searchPlayerAngleY(this), 0); fopAcM_createItemForBoss(&c, 0x21, fopAcM_GetRoomNo(this), &c2, NULL, 0.0f, 20.0f, - 0x80); + 0x80 IF_DUSK_ARG(ITEM_CHECK_GORON_SPRINGWATER_RUSH)); } break; } } diff --git a/src/d/actor/d_a_obj_item.cpp b/src/d/actor/d_a_obj_item.cpp index a67e780286..fba0ad7d91 100644 --- a/src/d/actor/d_a_obj_item.cpp +++ b/src/d/actor/d_a_obj_item.cpp @@ -158,8 +158,8 @@ void daItem_c::CreateInit() { mCcCyl.SetCoHitCallback(itemGetCoCallBack); mCcCyl.SetTgHitCallback(itemGetTgCallBack); - f32 cylHeight = dItem_data::getH(m_itemNo); - f32 cylRadius = dItem_data::getR(m_itemNo); + f32 cylHeight = DUSK_IF_ELSE(getCollisionH(), dItem_data::getH(m_itemNo)); + f32 cylRadius = DUSK_IF_ELSE(getCollisionR(), dItem_data::getR(m_itemNo)); if (scale.x > 1.0f) { cylHeight *= scale.x; @@ -267,12 +267,13 @@ int daItem_c::_daItem_create() { #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)); + const auto [item, displayItem] = + dusk::mods::item_check_resolve(mItemGiveTag, mOriginalItemNo, this); + mItemOverridden = item != mOriginalItemNo; + setDisplayItemNo(displayItem); if (mItemOverridden) { - fopAcM_SetParam(this, (params & 0xFFFFFF00) | resolvedItem); + fopAcM_SetParam(this, (params & 0xFFFFFF00) | item); } #endif field_0x95d = true; @@ -288,7 +289,7 @@ int daItem_c::_daItem_create() { #if DEBUG #if TARGET_PC - if (dItem_fieldModelArc(m_itemNo) == NULL) { + if (dItem_fieldModelArc(getDisplayItemNo()) == NULL) { #else if (dItem_data::getFieldArc(m_itemNo) == NULL) { #endif @@ -324,23 +325,21 @@ int daItem_c::_daItem_create() { CreateInit(); } else { #if TARGET_PC - // Resolved items may have no field model; display their get-demo model instead. - if (dItem_data::getFieldArc(m_itemNo) == NULL) { - phase_state = dComIfG_resLoad(&mPhase, dItem_data::getArcName(m_itemNo)); - if (phase_state == cPhs_COMPLEATE_e) { - if (!fopAcM_entrySolidHeap(this, CheckItemCreateHeap, 0x4000)) { - return cPhs_ERROR_e; - } - - CreateInit(); - } - return phase_state; - } -#endif + const u8 displayItemNo = getDisplayItemNo(); + phase_state = dComIfG_resLoad(&mPhase, dItem_fieldModelArc(displayItemNo)); +#else phase_state = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo)); +#endif if (phase_state == cPhs_COMPLEATE_e) { +#if TARGET_PC + const bool useModelFallback = dItem_data::getFieldArc(displayItemNo) == NULL; + if (!fopAcM_entrySolidHeap(this, + useModelFallback ? CheckItemCreateHeap : CheckFieldItemCreateHeap, + useModelFallback ? 0x4000 : dItem_data::getFieldHeapSize(displayItemNo))) +#else if (!fopAcM_entrySolidHeap(this, CheckFieldItemCreateHeap, dItem_data::getFieldHeapSize(m_itemNo))) +#endif { return cPhs_ERROR_e; } @@ -357,7 +356,7 @@ int daItem_c::_daItem_execute() { CountTimer(); eyePos = current.pos; - eyePos.y += (f32)dItem_data::getH(m_itemNo) / 2; + eyePos.y += (f32)DUSK_IF_ELSE(getCollisionH(), dItem_data::getH(m_itemNo)) / 2; attention_info.position = current.pos; @@ -431,7 +430,7 @@ int daItem_c::_daItem_delete() { } #if TARGET_PC - DeleteBase(dItem_fieldModelArc(m_itemNo)); + DeleteBase(dItem_fieldModelArc(getDisplayItemNo())); #else DeleteBase(dItem_data::getFieldArc(m_itemNo)); #endif @@ -604,8 +603,8 @@ void daItem_c::procInitBoomerangCarry() { scale = mItemScale; mBoomerangMove.initOffset(¤t.pos); - u8 height = dItem_data::getH(m_itemNo); - u8 radius = dItem_data::getR(m_itemNo); + u8 height = DUSK_IF_ELSE(getCollisionH(), dItem_data::getH(m_itemNo)); + u8 radius = DUSK_IF_ELSE(getCollisionR(), dItem_data::getR(m_itemNo)); mCcCyl.SetR((f32)radius * 2.0f); mCcCyl.SetH((f32)height * 2.0f); mCcCyl.OnCoSPrmBit(1); diff --git a/src/d/actor/d_a_obj_life_container.cpp b/src/d/actor/d_a_obj_life_container.cpp index 815d03f390..b2eb30167a 100644 --- a/src/d/actor/d_a_obj_life_container.cpp +++ b/src/d/actor/d_a_obj_life_container.cpp @@ -88,8 +88,8 @@ int daObjLife_c::Create() { mCcCyl.SetStts(&mCcStts); mCcCyl.SetCoHitCallback(lifeGetCoCallBack); mCcCyl.SetTgHitCallback(lifeGetTgCallBack); - mCcCyl.SetR(dItem_data::getR(m_itemNo)); - mCcCyl.SetH(dItem_data::getH(m_itemNo)); + mCcCyl.SetR(DUSK_IF_ELSE(getCollisionR(), dItem_data::getR(m_itemNo))); + mCcCyl.SetH(DUSK_IF_ELSE(getCollisionH(), dItem_data::getH(m_itemNo))); fopAcM_SetCullSize(this, fopAc_CULLSPHERE_0_e); fopAcM_SetGravity(this, -3.2f); @@ -152,25 +152,29 @@ int daObjLife_c::create() { 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; + const auto [item, displayItem] = + dusk::mods::item_check_resolve(mItemGiveTag, mOriginalItemNo, this); + setDisplayItemNo(displayItem); + mItemOverridden = item != mOriginalItemNo; if (mItemOverridden) { - fopAcM_SetParam(this, (params & 0xFFFFFF00) | resolvedItem); + fopAcM_SetParam(this, (params & 0xFFFFFF00) | item); } } else if (mGoldenWolfItem) { mOriginalItemNo = mItemGiveOriginalNo; - const u16 obtainedItemFlag = static_cast(field_0x938); - const u8 resolvedItem = - dusk::mods::item_check_golden_wolf(obtainedItemFlag, mOriginalItemNo, this); - mItemGiveTag = dusk::mods::item_give_tag_golden_wolf(obtainedItemFlag); - mItemOverridden = resolvedItem != mOriginalItemNo; - if (resolvedItem != parameterItemNo) { - fopAcM_SetParam(this, (params & 0xFFFFFF00) | resolvedItem); + mItemGiveTag = dusk::mods::item_give_tag_golden_wolf(static_cast(field_0x938)); + const auto [item, displayItem] = + dusk::mods::item_check_resolve(mItemGiveTag, mOriginalItemNo, this); + setDisplayItemNo(displayItem); + mItemOverridden = item != mOriginalItemNo; + if (item != parameterItemNo) { + fopAcM_SetParam(this, (params & 0xFFFFFF00) | item); } } else { mOriginalItemNo = mItemGiveOriginalNo; + setDisplayItemNo( + dusk::mods::item_check_resolve(mItemGiveTag, parameterItemNo, this) + .display_item); mItemOverridden = parameterItemNo != mOriginalItemNo; } mOverrideHover = @@ -197,8 +201,9 @@ int daObjLife_c::create() { #if TARGET_PC // Resolved items may have no field model; display their get-demo model instead. - const bool useGetModel = dItem_data::getFieldArc(m_itemNo) == NULL; - int phase_state = dComIfG_resLoad(&mPhase, dItem_fieldModelArc(m_itemNo)); + const u8 displayItemNo = getDisplayItemNo(); + const bool useGetModel = dItem_data::getFieldArc(displayItemNo) == NULL; + int phase_state = dComIfG_resLoad(&mPhase, dItem_fieldModelArc(displayItemNo)); if (phase_state == cPhs_COMPLEATE_e) { if (!fopAcM_entrySolidHeap( this, useGetModel ? CheckItemCreateHeap : CheckFieldItemCreateHeap, 0x4000)) @@ -421,8 +426,8 @@ int daObjLife_c::actionInitBoomerangCarry() { mCcCyl.OnTgSPrmBit(1); mCcCyl.OnCoSPrmBit(1); - f32 height = dItem_data::getH(m_itemNo) * 4.0f; - f32 radius = dItem_data::getR(m_itemNo) * 4.0f; + f32 height = DUSK_IF_ELSE(getCollisionH(), dItem_data::getH(m_itemNo)) * 4.0f; + f32 radius = DUSK_IF_ELSE(getCollisionR(), dItem_data::getR(m_itemNo)) * 4.0f; mCcCyl.SetR(radius); mCcCyl.SetH(height); @@ -544,7 +549,7 @@ int daObjLife_c::_delete() { endEffect02(); #if TARGET_PC - DeleteBase(dItem_fieldModelArc(m_itemNo)); + DeleteBase(dItem_fieldModelArc(getDisplayItemNo())); #else DeleteBase(dItem_data::getFieldArc(m_itemNo)); #endif diff --git a/src/d/actor/d_a_obj_smallkey.cpp b/src/d/actor/d_a_obj_smallkey.cpp index 9bf8face57..6914cee5f0 100644 --- a/src/d/actor/d_a_obj_smallkey.cpp +++ b/src/d/actor/d_a_obj_smallkey.cpp @@ -1,6 +1,6 @@ /** * @file d_a_obj_smallkey.cpp - * + * */ #include "d/dolzel_rel.h" // IWYU pragma: keep @@ -119,9 +119,9 @@ int daKey_c::Create() { mCcCyl.SetStts(&mCcStts); mCcCyl.SetCoHitCallback(keyGetCoCallBack); mCcCyl.SetTgHitCallback(keyGetTgCallBack); - mCcCyl.SetR(dItem_data::getR(m_itemNo)); - mCcCyl.SetH(dItem_data::getH(m_itemNo)); - + mCcCyl.SetR(DUSK_IF_ELSE(getCollisionR(), dItem_data::getR(m_itemNo))); + mCcCyl.SetH(DUSK_IF_ELSE(getCollisionH(), dItem_data::getH(m_itemNo))); + fopAcM_SetCullSize(this, fopAc_CULLSPHERE_0_e); if (getSwNo() == 0xFF) { @@ -155,7 +155,21 @@ int daKey_c::create() { mIsPrmInit = TRUE; } +#if TARGET_PC + mItemGiveTag = dusk::mods::item_give_tag_freestanding(getSaveBitNo()); + const auto [item, displayItem] = + dusk::mods::item_check_resolve(mItemGiveTag, dItemNo_SMALL_KEY_e, this); + m_itemNo = item; + if (m_itemNo == dItemNo_NONE_e) { + // Keep the pickup visible until the empty check is collected. + m_itemNo = dItemNo_SMALL_KEY_e; + setDisplayItemNo(dItemNo_NONE_e); + } else { + setDisplayItemNo(displayItem); + } +#else m_itemNo = dItemNo_SMALL_KEY_e; +#endif if (strcmp(dComIfGp_getStartStageName(), "F_SP118") == 0) { OS_REPORT(" SMKEY 0\n"); @@ -170,9 +184,21 @@ int daKey_c::create() { return cPhs_ERROR_e; } +#if TARGET_PC + const u8 displayItemNo = getDisplayItemNo(); + int phase_state = dComIfG_resLoad(&mPhase, dItem_fieldModelArc(displayItemNo)); +#else int phase_state = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo)); +#endif if (phase_state == cPhs_COMPLEATE_e) { +#if TARGET_PC + const bool useModelFallback = dItem_data::getFieldArc(displayItemNo) == NULL; + if (!fopAcM_entrySolidHeap( + this, useModelFallback ? CheckItemCreateHeap : CheckFieldItemCreateHeap, 0x4000)) + { +#else if (!fopAcM_entrySolidHeap(this, CheckFieldItemCreateHeap, 0x840)) { +#endif return cPhs_ERROR_e; } @@ -317,10 +343,26 @@ int daKey_c::initActionOrderGetDemo() { hide(); effectStop(); +#if TARGET_PC + const auto itemCheck = + dusk::mods::item_check_commit(mItemGiveTag, dItemNo_SMALL_KEY_e, this); + m_itemNo = itemCheck.itemNo; + if (m_itemNo == dItemNo_NONE_e) { + dusk::mods::item_check_complete(itemCheck, this); + dComIfGs_onTbox(getSaveBitNo()); + dTres_c::offStatus(getSaveBitNo(), 1); + dComIfGp_event_reset(); + fopAcM_delete(this); + return 1; + } +#endif + fopAcM_orderItemEvent(this, 0, 0); eventInfo.onCondition(8); - mItemId = fopAcM_createItemForTrBoxDemo(¤t.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL); + mItemId = fopAcM_createItemForTrBoxDemo( + ¤t.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, + NULL IF_DUSK_ARG(itemCheck.tag)); JUT_ASSERT(699, mItemId != fpcM_ERROR_PROCESS_ID_e); setStatus(STATUS_ORDER_GET_DEMO_e); @@ -387,8 +429,8 @@ int daKey_c::actionInitBoomerangCarry() { mCcCyl.OnTgSPrmBit(1); mCcCyl.OnCoSPrmBit(1); - u8 height = dItem_data::getH(m_itemNo); - u8 radius = dItem_data::getR(m_itemNo); + u8 height = DUSK_IF_ELSE(getCollisionH(), dItem_data::getH(m_itemNo)); + u8 radius = DUSK_IF_ELSE(getCollisionR(), dItem_data::getR(m_itemNo)); mCcCyl.SetR(4.0f * radius); mCcCyl.SetH(4.0f * height); @@ -527,7 +569,11 @@ int daKey_c::draw() { int daKey_c::_delete() { effectStop(); +#if TARGET_PC + DeleteBase(dItem_fieldModelArc(getDisplayItemNo())); +#else DeleteBase(dItem_data::getFieldArc(m_itemNo)); +#endif return 1; } diff --git a/src/d/actor/d_a_shop_item.cpp b/src/d/actor/d_a_shop_item.cpp index 3e768a1684..a7a159ab1c 100644 --- a/src/d/actor/d_a_shop_item.cpp +++ b/src/d/actor/d_a_shop_item.cpp @@ -105,17 +105,19 @@ const char* daShopItem_c::getShopArcname() { #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; + mItemGiveTag = dusk::mods::item_give_tag_shop(mItemGiveOriginalNo); + const auto [_, displayItem] = + dusk::mods::item_check_resolve(mItemGiveTag, mItemGiveOriginalNo, this); + mItemOverridden = displayItem != 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.mArcName = dItem_data::getArcName(displayItem); + mOverrideData.mBmdName = dItem_data::getBmdName(displayItem); + mOverrideData.mBtkName = dItem_data::getBtkName(displayItem); + mOverrideData.mBckName = dItem_data::getBckName(displayItem); + mOverrideData.mBrkName = dItem_data::getBrkName(displayItem); + mOverrideData.mBtpName = dItem_data::getBtpName(displayItem); + mOverrideData.mTevFrm = dItem_data::getTevFrm(displayItem); mOverrideData.mBtpFrm = -1; mOverrideData.mFlag = static_cast(-1); mOverrideData.mOffsetY = mShopItemID == SHOP_ITEMNO_ARMOR ? 60.0f : 15.0f; diff --git a/src/d/d_a_itembase_static.cpp b/src/d/d_a_itembase_static.cpp index 9bdd933f5f..ec3610a787 100644 --- a/src/d/d_a_itembase_static.cpp +++ b/src/d/d_a_itembase_static.cpp @@ -50,7 +50,7 @@ bool daItemBase_c::chkDead() { int CheckItemCreateHeap(fopAc_ac_c* i_this) { daItemBase_c* a_this = static_cast(i_this); - u8 item_no = a_this->getItemNo(); + u8 item_no = DUSK_IF_ELSE(a_this->getDisplayItemNo(), a_this->getItemNo()); return a_this->CreateItemHeap(dItem_data::getArcName(item_no), dItem_data::getBmdName(item_no), dItem_data::getBtkName(item_no), dItem_data::getBpkName(item_no), dItem_data::getBckName(item_no), dItem_data::getBxaName(item_no), @@ -67,7 +67,7 @@ const char* dItem_fieldModelArc(u8 itemNo) { int CheckFieldItemCreateHeap(fopAc_ac_c* i_this) { daItemBase_c* a_this = static_cast(i_this); - u8 item_no = a_this->getItemNo(); + u8 item_no = DUSK_IF_ELSE(a_this->getDisplayItemNo(), a_this->getItemNo()); return a_this->CreateItemHeap( dItem_data::getFieldArc(item_no), dItem_data::getItemBmdName(item_no), dItem_data::getItemBtkName(item_no), dItem_data::getItemBpkName(item_no), diff --git a/src/dusk/mods/item.hpp b/src/dusk/mods/item.hpp index f27079427e..bda0d4f7ee 100644 --- a/src/dusk/mods/item.hpp +++ b/src/dusk/mods/item.hpp @@ -1,7 +1,10 @@ #pragma once +#include "mods/svc/item.h" + #include #include +#include class fopAc_ac_c; @@ -16,8 +19,11 @@ enum class ItemGiveMode : uint8_t { struct ItemCheckResult { uint32_t tag = 0; uint8_t itemNo = 0; + uint8_t displayItemNo = 0; }; +ItemCheckResolution item_check_resolve(const char* name, uint8_t itemNo, fopAc_ac_c* giver); +ItemCheckResolution item_check_resolve(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver); uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver); ItemCheckResult item_check_commit(const char* name, uint8_t itemNo, fopAc_ac_c* giver); ItemCheckResult item_check_commit(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver); @@ -54,7 +60,7 @@ uint32_t item_give_queue_take_tag(); namespace detail { struct CommittedCheck { uint8_t vanillaItem = 0; - uint8_t resolvedItem = 0; + ItemCheckResolution resolution{}; bool queued = false; }; @@ -77,10 +83,13 @@ public: return existing->second; } - const uint8_t resolvedItem = std::forward(resolve)(); + const ItemCheckResolution resolution = std::forward(resolve)(); return mChecks - .emplace( - giveTag, CommittedCheck{.vanillaItem = vanillaItem, .resolvedItem = resolvedItem}) + .emplace(giveTag, + CommittedCheck{ + .vanillaItem = vanillaItem, + .resolution = resolution, + }) .first->second; } diff --git a/src/dusk/mods/item_checks.cpp b/src/dusk/mods/item_checks.cpp index 6327a07e0a..fb145a48df 100644 --- a/src/dusk/mods/item_checks.cpp +++ b/src/dusk/mods/item_checks.cpp @@ -186,9 +186,9 @@ void invalidate_committed_check(const std::string& name) { } // namespace -uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { +ItemCheckResolution item_check_resolve(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { if (name == nullptr || *name == '\0' || s_modChecks.empty()) { - return itemNo; + return {.item = itemNo, .display_item = itemNo}; } // Callbacks may change registrations, so copy the applicable chain before invoking one. @@ -228,6 +228,7 @@ uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { .giver_actor = giver, .vanilla_item = itemNo, .current_item = itemNo, + .current_display_item = itemNo, }; for (const auto& resolve : resolves) { if (!resolve.mod->active) { @@ -235,13 +236,21 @@ uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { } if (resolve.fixedValue) { info.current_item = resolve.itemNo; + info.current_display_item = resolve.itemNo; continue; } - uint8_t resolvedItem = info.current_item; + ItemCheckResolution resolution{ + .item = info.current_item, + .display_item = dItemNo_NONE_e, + }; try { - if (resolve.fn(resolve.mod->context.get(), &info, &resolvedItem, resolve.userData)) { - info.current_item = resolvedItem; + if (resolve.fn(resolve.mod->context.get(), &info, &resolution, resolve.userData)) { + if (resolution.display_item == UINT8_MAX) { + resolution.display_item = resolution.item; + } + info.current_item = resolution.item; + info.current_display_item = resolution.display_item; } } catch (const std::exception& e) { fail_mod(*resolve.mod, MOD_ERROR, @@ -251,7 +260,14 @@ uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { fmt::format("Unknown exception in item check resolver for '{}'", name)); } } - return info.current_item; + return { + .item = info.current_item, + .display_item = info.current_display_item, + }; +} + +uint8_t item_check(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { + return item_check_resolve(name, itemNo, giver).item; } uint8_t item_check_chest(uint8_t boxNo, uint8_t itemNo, fopAc_ac_c* chest) { diff --git a/src/dusk/mods/item_gives.cpp b/src/dusk/mods/item_gives.cpp index 739a5ac5ee..5d75dcfc15 100644 --- a/src/dusk/mods/item_gives.cpp +++ b/src/dusk/mods/item_gives.cpp @@ -97,11 +97,11 @@ void notify_gives(const char* checkName, uint8_t itemNo, fopAc_ac_c* giver, Item void complete_check(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver, ItemGiveOrigin origin) { if (const auto* committed = s_committedChecks.find(giveTag); - committed != nullptr && committed->resolvedItem != itemNo) + committed != nullptr && committed->resolution.item != itemNo) { Log.error("committed check '{}' completed with item {:#x} instead of {:#x}", item_give_name(giveTag) != nullptr ? item_give_name(giveTag) : "", itemNo, - committed->resolvedItem); + committed->resolution.item); } s_committedChecks.erase(giveTag); notify_gives(item_give_name(giveTag), itemNo, giver, origin); @@ -241,10 +241,19 @@ uint32_t item_give_tag(const char* name) { return tag; } +ItemCheckResolution item_check_resolve(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver) { + if (const auto* committed = s_committedChecks.find(giveTag); committed != nullptr) { + return committed->resolution; + } + const char* name = item_give_name(giveTag); + return name != nullptr ? item_check_resolve(name, itemNo, giver) : + ItemCheckResolution{.item = itemNo, .display_item = itemNo}; +} + ItemCheckResult item_check_commit(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver) { const char* name = item_give_name(giveTag); if (name == nullptr) { - return {.tag = giveTag, .itemNo = itemNo}; + return {.tag = giveTag, .itemNo = itemNo, .displayItemNo = itemNo}; } if (const auto* committed = s_committedChecks.find(giveTag); committed != nullptr) { @@ -252,12 +261,20 @@ ItemCheckResult item_check_commit(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* Log.error("committed check '{}' changed vanilla item from {:#x} to {:#x}", name, committed->vanillaItem, itemNo); } - return {.tag = giveTag, .itemNo = committed->resolvedItem}; + return { + .tag = giveTag, + .itemNo = committed->resolution.item, + .displayItemNo = committed->resolution.display_item, + }; } - const auto& committed = - s_committedChecks.commit(giveTag, itemNo, [&] { return item_check(name, itemNo, giver); }); - return {.tag = giveTag, .itemNo = committed.resolvedItem}; + const auto& committed = s_committedChecks.commit( + giveTag, itemNo, [&] { return item_check_resolve(name, itemNo, giver); }); + return { + .tag = giveTag, + .itemNo = committed.resolution.item, + .displayItemNo = committed.resolution.display_item, + }; } ItemCheckResult item_check_commit(const char* name, uint8_t itemNo, fopAc_ac_c* giver) { @@ -279,10 +296,10 @@ bool item_check_enqueue(ItemCheckResult check, ItemGiveMode mode) { item_give_name(check.tag) != nullptr ? item_give_name(check.tag) : ""); return false; } - if (committed->resolvedItem != check.itemNo) { + if (committed->resolution.item != check.itemNo) { Log.error("committed check '{}' changed item from {:#x} to {:#x}", item_give_name(check.tag) != nullptr ? item_give_name(check.tag) : "", check.itemNo, - committed->resolvedItem); + committed->resolution.item); return false; } if (committed->queued) { @@ -292,7 +309,7 @@ bool item_check_enqueue(ItemCheckResult check, ItemGiveMode mode) { committed->queued = true; s_giveQueue.push_back({ .tag = check.tag, - .itemNo = committed->resolvedItem, + .itemNo = committed->resolution.item, .silent = mode == ItemGiveMode::Silent, .forced = mode == ItemGiveMode::ForcedDemo, }); diff --git a/src/f_op/f_op_actor_mng.cpp b/src/f_op/f_op_actor_mng.cpp index 5ac24d534e..3097b9294f 100644 --- a/src/f_op/f_op_actor_mng.cpp +++ b/src/f_op/f_op_actor_mng.cpp @@ -257,9 +257,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 IF_DUSK_ARG(u32 i_itemGiveTag)) { + createFunc i_createFunc IF_DUSK_ARG(u32 i_itemGiveTag) IF_DUSK_ARG(u8 i_itemOriginalNo)) { fopAcM_prm_class* append = createAppend(i_setId, i_parameters, i_pos, i_roomNo, i_angle, - i_scale, i_argument, fpcM_ERROR_PROCESS_ID_e IF_DUSK_ARG(i_itemGiveTag)); + i_scale, i_argument, fpcM_ERROR_PROCESS_ID_e IF_DUSK_ARG(i_itemGiveTag) + IF_DUSK_ARG(i_itemOriginalNo)); if (append == NULL) { return fpcM_ERROR_PROCESS_ID_e; } @@ -268,9 +269,10 @@ 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 IF_DUSK_ARG(u32 i_itemGiveTag)) { + const csXyz* i_angle, const cXyz* i_scale, s8 i_argument IF_DUSK_ARG(u32 i_itemGiveTag) + IF_DUSK_ARG(u8 i_itemOriginalNo)) { return fopAcM_create(i_procName, 0xFFFF, i_parameters, i_pos, i_roomNo, i_angle, i_scale, - i_argument, NULL IF_DUSK_ARG(i_itemGiveTag)); + i_argument, NULL IF_DUSK_ARG(i_itemGiveTag) IF_DUSK_ARG(i_itemOriginalNo)); } fopAc_ac_c* fopAcM_fastCreate(s16 i_procName, u32 i_parameters, const cXyz* i_pos, int i_roomNo, @@ -1606,6 +1608,22 @@ fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomN i_itemNo = dusk::mods::item_check_boss(originalItemNo, NULL); giveTag = dusk::mods::item_give_tag_boss(); } + + if (i_itemNo == dItemNo_NONE_e) { + const auto itemCheck = dusk::mods::item_check_commit(giveTag, originalItemNo, NULL); + i_itemNo = itemCheck.itemNo; + if (i_itemNo == dItemNo_NONE_e) { + dusk::mods::item_check_complete(itemCheck, NULL); + dComIfGs_onItem(param_8, i_roomNo); + return fpcM_ERROR_PROCESS_ID_e; + } + } + + if (i_itemNo != originalItemNo) { + const u32 params = 0xFFFF0000 | param_8 << 8 | (i_itemNo & 0xFF); + return fopAcM_create(fpcNm_Obj_LifeContainer_e, params, i_pos, i_roomNo, i_angle, + i_scale, -1, giveTag, originalItemNo); + } #endif u32 params = 0xFFFF0000 | param_8 << 8 | (i_itemNo & 0xFF);