ItemService improvements (#2322)

* ItemService improvements

* Move Poe increment to item
This commit is contained in:
Luke Street
2026-08-21 00:50:51 -06:00
committed by GitHub
parent 172d06f38d
commit 5f0f3d4ebe
43 changed files with 851 additions and 181 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ public:
/* 0xDEC */ u8 field_0xdec[0xdf9 - 0xdec];
/* 0xDF9 */ u8 field_0xdf9;
#if TARGET_PC
bool mItemCheckOverridden;
bool mItemCheckHandled;
#endif
};
+5
View File
@@ -1,10 +1,15 @@
#ifndef D_A_D_A_ITEMBASE_STATIC_H
#define D_A_D_A_ITEMBASE_STATIC_H
#include "dolphin/types.h"
class fopAc_ac_c;
int CheckFieldItemCreateHeap(fopAc_ac_c* actor);
int CheckItemCreateHeap(fopAc_ac_c* i_this);
#if TARGET_PC
const char* dItem_fieldModelArc(u8 itemNo);
#endif
#endif /* D_A_D_A_ITEMBASE_STATIC_H */
+5
View File
@@ -142,7 +142,12 @@ enum {
/* 0x2F */ dItemNo_WEAR_KOKIRI_e,
/* 0x30 */ dItemNo_ARMOR_e,
/* 0x31 */ dItemNo_WEAR_ZORA_e,
#if TARGET_PC
/* 0x32 */ dItemNo_MAGIC_LV1_e,
dItemNo_SHADOW_CRYSTAL_e = dItemNo_MAGIC_LV1_e,
#else
/* 0x32 */ dItemNo_MAGIC_LV1_e,
#endif
/* 0x33 */ dItemNo_DUNGEON_EXIT_2_e,
/* 0x34 */ dItemNo_WALLET_LV1_e,
/* 0x35 */ dItemNo_WALLET_LV2_e,
+7 -1
View File
@@ -259,13 +259,19 @@ using std::isnan;
#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)
(item_no) = ::dusk::mods::item_check_commit(name, (item_no), giver).itemNo
#define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) \
(::dusk::mods::item_check_commit(name, (item_no), giver).itemNo)
#define DUSK_ITEM_CHECK_PREVIEW(name, item_no, giver) \
(item_no) = ::dusk::mods::item_check(name, (item_no), giver)
#define DUSK_ITEM_CHECK_PREVIEW_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_ITEM_CHECK_PREVIEW(name, item_no, giver)
#define DUSK_ITEM_CHECK_PREVIEW_EXPR(name, item_no, giver) (item_no)
#define DUSK_GIVE_TAG(name)
#endif
-2
View File
@@ -188,8 +188,6 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) {
g_stageHook = 0;
return mods::set_error(error, MOD_ERROR, "failed to register mod panel");
}
mods::log::info("auxiliary WebGPU window ready");
return MOD_OK;
}
+62
View File
@@ -0,0 +1,62 @@
#pragma once
/*
* ItemService check names.
*/
#define ITEM_CHECK_BOSS_PREFIX "boss:" /* <stage> */
#define ITEM_CHECK_BUG_PREFIX "bug:" /* <insect_item> */
#define ITEM_CHECK_CHEST_PREFIX "chest:" /* <stage>:<box> */
#define ITEM_CHECK_FREESTANDING_PREFIX "freestanding:" /* <stage>:<bit> */
#define ITEM_CHECK_POE_PREFIX "poe:" /* <stage>:<switch> */
#define ITEM_CHECK_SHOP_PREFIX "shop:" /* <stage>:<item> */
#define ITEM_CHECK_SKY_PREFIX "sky:" /* <stage>:<room> */
#define ITEM_CHECK_DUNGEON_REWARD_PREFIX "dungeon_reward:" /* <stage> */
#define ITEM_CHECK_ARCHERY_REWARD "archery_reward:F_SP109"
#define ITEM_CHECK_ASHEI_SKETCH "ashei_sketch"
#define ITEM_CHECK_AURU_MEMO "auru_memo"
#define ITEM_CHECK_BALL_AND_CHAIN "ball_and_chain:D_MN11"
#define ITEM_CHECK_BULBLIN_KEY "bulblin_key:D_MN09"
#define ITEM_CHECK_CORAL_EARRING "coral_earring"
#define ITEM_CHECK_CORO_BOTTLE "coro_bottle"
#define ITEM_CHECK_DUNGEON_MAP_SNOWPEAK "dungeon_map:D_MN11"
#define ITEM_CHECK_FAIRY_REWARD "fairy_reward:D_SB01"
#define ITEM_CHECK_FISHING_BOTTLE "fishing_bottle"
#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_ILIA_CHARM "ilia_charm"
#define ITEM_CHECK_ILIA_MEMORY "ilia_memory"
#define ITEM_CHECK_IZA_REWARD_1 "iza_reward_1"
#define ITEM_CHECK_IZA_REWARD_2 "iza_reward_2"
#define ITEM_CHECK_JOVANI_REWARD_1 "jovani_reward_1"
#define ITEM_CHECK_JOVANI_REWARD_2 "jovani_reward_2"
#define ITEM_CHECK_KEY_SHARD_1 "key_shard_1:D_MN04"
#define ITEM_CHECK_KEY_SHARD_2 "key_shard_2:D_MN04"
#define ITEM_CHECK_KEY_SHARD_3 "key_shard_3:D_MN04"
#define ITEM_CHECK_ORDON_SHIELD "ordon_shield"
#define ITEM_CHECK_ORDON_SWORD "ordon_sword"
#define ITEM_CHECK_PLUMM_REWARD "plumm_minigame_reward"
#define ITEM_CHECK_PRAYER_REWARD "prayer_reward"
#define ITEM_CHECK_RENADO_LETTER "renado_letter"
#define ITEM_CHECK_SERA_REWARD "sera_reward"
#define ITEM_CHECK_SKYBOOK "skybook"
#define ITEM_CHECK_SNOWBOARD_REWARD "snowboard_race_reward"
#define ITEM_CHECK_STAR_REWARD_1 "star_reward_1"
#define ITEM_CHECK_STAR_REWARD_2 "star_reward_2"
#define ITEM_CHECK_TELMA_INVOICE "telma_invoice"
#define ITEM_CHECK_ULI_CRADLE_REWARD "uli_cradle_reward"
#define ITEM_CHECK_WOOD_STATUE "wood_statue"
#define ITEM_CHECK_ZORA_ARMOR "zora_armor"
#define ITEM_CHECK_DUNGEON_REWARD_FOREST "dungeon_reward:D_MN05"
#define ITEM_CHECK_DUNGEON_REWARD_GORON "dungeon_reward:D_MN04"
#define ITEM_CHECK_DUNGEON_REWARD_LAKEBED "dungeon_reward:D_MN01"
#define ITEM_CHECK_DUNGEON_REWARD_ARBITERS "dungeon_reward:D_MN10"
#define ITEM_CHECK_DUNGEON_REWARD_SNOWPEAK "dungeon_reward:D_MN11"
#define ITEM_CHECK_DUNGEON_REWARD_TIME "dungeon_reward:D_MN06"
#define ITEM_CHECK_DUNGEON_REWARD_CITY "dungeon_reward:D_MN07"
#define ITEM_CHECK_MASTER_SWORD "master_sword"
#define ITEM_CHECK_SHADOW_CRYSTAL "shadow_crystal"
+9 -7
View File
@@ -17,10 +17,12 @@ 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.
* Check names are case-sensitive. Resolvers must be free of side effects because previews may be
* resolved repeatedly. For checks representing a non-item side effect, vanilla_item is 0xFF:
* resolving to 0xFF preserves that side effect, while resolving to another item replaces it.
*
* Registrations and pending grants are removed when the calling mod is detached. Callbacks run on
* the game thread. Check names are listed in <mods/items.h>.
*/
/* Host-owned callback data, valid only for the duration of the callback. */
@@ -72,14 +74,14 @@ typedef struct ItemService {
ModResult (*clear_check_resolver)(ModContext* ctx, ItemCheckHandle handle);
/* Resolve without granting an item or notifying give observers. */
/* Resolve a live preview 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.
* 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);
+24 -1
View File
@@ -1027,6 +1027,9 @@ void daAlink_c::setGetItemFace(u16 i_itemNo) {
|| i_itemNo == dItemNo_ANCIENT_DOCUMENT2_e
|| i_itemNo == dItemNo_DROP_CONTAINER_e
|| i_itemNo == dItemNo_DROP_CONTAINER02_e
#if TARGET_PC
|| i_itemNo == dItemNo_SHADOW_CRYSTAL_e
#endif
|| i_itemNo == dItemNo_DROP_CONTAINER03_e)
{
setFaceBasicBck(dRes_ID_ALANM_BCK_FI_e);
@@ -2009,7 +2012,11 @@ void daAlink_c::setGetSubBgm(int i_itemNo) {
/* dItemNo_WEAR_KOKIRI_e */ SETYPE_NONE,
/* dItemNo_ARMOR_e */ SETYPE_ITEM_GET,
/* dItemNo_WEAR_ZORA_e */ SETYPE_ITEM_GET,
#if TARGET_PC
/* dItemNo_SHADOW_CRYSTAL_e */ SETYPE_ITEM_GET,
#else
/* dItemNo_MAGIC_LV1_e */ SETYPE_NONE,
#endif
/* dItemNo_DUNGEON_EXIT_2_e */ SETYPE_ITEM_GET_MINI,
/* dItemNo_WALLET_LV1_e */ SETYPE_NONE,
/* dItemNo_WALLET_LV2_e */ SETYPE_ITEM_GET,
@@ -2307,8 +2314,13 @@ int daAlink_c::procCoGetItemInit() {
item_no = dComIfGp_event_getGtItm();
}
#if TARGET_PC
const u32 giveTag = mDemo.getParam0() != 0x100 && mDemo.getParam1() != 0 ?
static_cast<u32>(mDemo.getParam1()) :
dusk::mods::item_give_queue_take_tag();
#endif
fpc_ProcID item_partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1,
fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(dusk::mods::item_give_queue_take_tag()));
fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(giveTag));
if (item_partner_id != fpcM_ERROR_PROCESS_ID_e) {
dComIfGp_event_setItemPartnerId(item_partner_id);
}
@@ -2533,6 +2545,16 @@ int daAlink_c::procCoGetItem() {
} else if (mProcVar2.field_0x300c == 0x23 && checkStageName("D_MN11")) {
field_0x32cc = 0x5C0;
} else if (mProcVar2.field_0x300c == 0xE0) {
#if TARGET_PC
// Dusklight increments the Poe count when the displayed item is granted.
if (dComIfGs_getPohSpiritNum() + 1 == 20) {
field_0x32cc = 0x4CF;
} else if (dComIfGs_getPohSpiritNum() + 1 == 60) {
field_0x32cc = 0x4D0;
} else {
field_0x32cc = mProcVar2.field_0x300c + 0x65;
}
#else
if (dComIfGs_getPohSpiritNum() == 20) {
field_0x32cc = 0x4CF;
} else if (dComIfGs_getPohSpiritNum() == 60) {
@@ -2540,6 +2562,7 @@ int daAlink_c::procCoGetItem() {
} else {
field_0x32cc = mProcVar2.field_0x300c + 0x65;
}
#endif
} else if (mProcVar2.field_0x300c == 0x21) {
static u32 const heartPieceMessage[5] = {0x86, 0x9C, 0x9D, 0x9E, 0x9F};
field_0x32cc = heartPieceMessage[dComIfGs_getMaxLife() % 5];
+6 -1
View File
@@ -19,6 +19,10 @@
#include "f_op/f_op_actor_enemy.h"
#include "Z2AudioLib/Z2Instances.h"
#if TARGET_PC
#include "mods/items.h"
#endif
enum daB_DS_Joint {
DS_JNT_BACKBONE1,
DS_JNT_BACKBONE2,
@@ -4086,7 +4090,8 @@ void daB_DS_c::executeBattle2Dead() {
dComIfGs_onStageBossEnemy(0x13);
#if TARGET_PC
// This reward has no original grant at this point in the cutscene.
dusk::mods::item_check_enqueue("Arbiters Grounds Dungeon Reward", dItemNo_NONE_e);
dusk::mods::item_check_enqueue_deferred(
ITEM_CHECK_DUNGEON_REWARD_ARBITERS, dItemNo_NONE_e);
#endif
/* dSv_event_flag_c::F_0265 - Arbiter's Grounds - Arbiter's Grounds clear */
dComIfGs_onEventBit(0x2010);
+7
View File
@@ -1090,6 +1090,13 @@ inline int daDemo00_c::execute() {
break;
}
case 1:
#if TARGET_PC
// Suppress demo22_01's M_077 write. On Dusklight, the transform flag
// is set by the Shadow Crystal item.
if (dSv_event_flag_c::saveBitLabels[sp0E] == dSv_event_flag_c::M_077) {
break;
}
#endif
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[sp0E]);
break;
+14 -8
View File
@@ -727,19 +727,25 @@ void daE_HP_c::executeDead() {
}
#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
const auto itemCheck = dusk::mods::item_check_commit(
dusk::mods::item_give_tag_poe(bitSw), dItemNo_POU_SPIRIT_e, this);
mItemCheckHandled = true;
if (itemCheck.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(itemCheck, this);
} else {
dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::ForcedDemo);
}
#else
dComIfGs_addPohSpiritNum();
#endif
dComIfGs_addPohSpiritNum();
field_0x784 = -1;
if (dComIfGs_getPohSpiritNum() == 20 IF_DUSK(&&!mItemCheckOverridden)) {
#if !TARGET_PC
if (dComIfGs_getPohSpiritNum() == 20) {
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[0x1c9]);
}
#endif
movemode++;
}
@@ -759,7 +765,7 @@ void daE_HP_c::executeDead() {
field_0x788 = 1;
}
}
} else if (field_0x788 != 0 IF_DUSK(|| mItemCheckOverridden)) {
} else if (field_0x788 != 0 IF_DUSK(|| mItemCheckHandled)) {
fopAcM_createDisappear(this, &current.pos, 8, 3, 0xff);
fopAcM_delete(this);
} else {
+21 -12
View File
@@ -1133,21 +1133,24 @@ static void e_po_dead(e_po_class* i_this) {
camera_player->mCamera.SetTrimSize(0);
dComIfGp_event_reset();
#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();
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 {
dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::ForcedDemo);
}
#else
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();
#endif
} else if (mArg0Check(i_this, 0) != 0) {
if (!fopAcM_isSwitch(a_this, 0x22)) {
if (fopAcM_SearchByID(i_this->field_0x5B8, &local_1b0_actor) != 0 &&
@@ -1274,10 +1277,16 @@ 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));
const auto itemCheck = dusk::mods::item_check_commit(
dusk::mods::item_give_tag_poe(i_this->BitSW), dItemNo_POU_SPIRIT_e, a_this);
if (itemCheck.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(itemCheck, a_this);
i_this->field_0x75B = 1;
} else {
i_this->field_0x75C = fopAcM_createItemForPresentDemo(
&a_this->current.pos, itemCheck.itemNo, 0, -1, -1, NULL, NULL,
itemCheck.tag);
}
#else
i_this->field_0x75C = fopAcM_createItemForPresentDemo(&a_this->current.pos, 0xE0, 0,
-1, -1, NULL, NULL);
+16 -3
View File
@@ -16,6 +16,10 @@
#include "f_op/f_op_camera_mng.h"
#include <cstring>
#if TARGET_PC
#include "mods/items.h"
#endif
class daE_RDB_HIO_c : public JORReflexible {
public:
daE_RDB_HIO_c();
@@ -1250,9 +1254,18 @@ static void demo_camera(e_rdb_class* i_this) {
}
if (iVar1 != 0) {
daPy_getPlayerActorClass()->changeDemoMode(11,
DUSK_ITEM_CHECK_EXPR("bulblin_key:D_MN09", dItemNo_SMALL_KEY_e, &i_this->enemy), 0,
0);
#if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit(
ITEM_CHECK_BULBLIN_KEY, dItemNo_SMALL_KEY_e, &i_this->enemy);
if (itemCheck.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(itemCheck, &i_this->enemy);
}
daPy_getPlayerActorClass()->changeDemoMode(
11, itemCheck.itemNo == dItemNo_NONE_e ? 0 : itemCheck.itemNo,
itemCheck.itemNo == dItemNo_NONE_e ? 0 : static_cast<int>(itemCheck.tag), 0);
#else
daPy_getPlayerActorClass()->changeDemoMode(11, dItemNo_SMALL_KEY_e, 0, 0);
#endif
i_this->mDemoMode = 12;
i_this->field_0x10aa = 0;
i_this->field_0xfe5 = 1;
+32
View File
@@ -27,8 +27,10 @@
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#include "dusk/mods/item.hpp"
#include "dusk/settings.h"
#include "dusk/version.hpp"
#include "mods/items.h"
#endif
class dmg_rod_HIO_c : public JORReflexible {
@@ -2955,7 +2957,17 @@ static void lure_heart(dmg_rod_class* i_this) {
if (obj_life != NULL) {
fopAcM_delete(obj_life);
fopAcM_onItem(obj_life, 0x80);
#if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit(
ITEM_CHECK_FISHING_HEART_PIECE, dItemNo_KAKERA_HEART_e, actor);
if (itemCheck.itemNo == dItemNo_KAKERA_HEART_e) {
execItemGet(dItemNo_KAKERA_HEART_e, itemCheck.tag, actor);
} else if (itemCheck.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(itemCheck, actor);
}
#else
execItemGet(dItemNo_KAKERA_HEART_e);
#endif
u8 eventReg = dComIfGs_getEventReg(0xECFF);
eventReg |= (u8)0x40;
dComIfGs_setEventReg(0xECFF, eventReg);
@@ -4090,7 +4102,15 @@ static void uki_catch(dmg_rod_class* i_this) {
} else if (mgfish->mCaughtType == MG_CATCH_BIN) {
i_this->msgflow.init(actor, 0x139A, 0, NULL);
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[468]);
#if TARGET_PC
const auto itemCheck = dusk::mods::item_check_commit(
ITEM_CHECK_FISHING_BOTTLE, dItemNo_EMPTY_BOTTLE_e, actor);
if (itemCheck.itemNo == dItemNo_EMPTY_BOTTLE_e) {
dComIfGs_setEmptyBottle();
}
#else
dComIfGs_setEmptyBottle();
#endif
} else if (mgfish->mCaughtType == MG_CATCH_KN) {
i_this->msgflow.init(actor, 0x139C, 0, NULL);
} else if (mgfish->mCaughtType == MG_CATCH_ED) {
@@ -4167,6 +4187,18 @@ static void uki_catch(dmg_rod_class* i_this) {
if (mgfish->mCaughtType == MG_CATCH_LH) {
dComIfGp_setItemRupeeCount(10.0f + cM_rndF(40.9f));
}
#if TARGET_PC
else if (mgfish->mCaughtType == MG_CATCH_BIN)
{
const auto itemCheck = dusk::mods::item_check_commit(
ITEM_CHECK_FISHING_BOTTLE, dItemNo_EMPTY_BOTTLE_e, actor);
if (itemCheck.itemNo == dItemNo_EMPTY_BOTTLE_e ||
itemCheck.itemNo == dItemNo_NONE_e)
{
dusk::mods::item_check_complete(itemCheck, actor);
}
}
#endif
} else {
dComIfGs_addFishNum(fish_kind);
if (i_this->field_0x14c0 != 0) {
+6 -2
View File
@@ -1806,17 +1806,21 @@ int daNpcChin_c::_Evt_GameSucceed_CutMain(const int& param_0) {
#if TARGET_PC
const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (itemId1 == dItemNo_ARROW_LV2_e) {
itemCheckName = "star_reward_1";
} else if (itemId1 == dItemNo_ARROW_LV3_e) {
itemCheckName = "star_reward_2";
}
if (itemCheckName != nullptr) {
itemId1 = dusk::mods::item_check(itemCheckName, itemId1, this);
const auto itemCheck =
dusk::mods::item_check_commit(itemCheckName, itemId1, this);
itemId1 = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
}
#endif
fpc_ProcID itemId2 = fopAcM_createItemForPresentDemo(&current.pos, itemId1, 0, -1, -1,
0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
0, 0 IF_DUSK_ARG(itemGiveTag));
if (itemId2 != -1) {
dComIfGp_event_setItemPartnerId(itemId2);
}
+6 -4
View File
@@ -4019,14 +4019,16 @@ BOOL daNpc_grA_c::talk(void*) {
if (r26 && talkProc(NULL, TRUE, NULL)) {
if (mFlow.getEventId(&sp8) == 1) {
#if TARGET_PC
const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (sp8 == dItemNo_BOMB_IN_BAG_e) {
itemCheckName = "goron_reward:F_SP113";
sp8 = dusk::mods::item_check(itemCheckName, sp8, this);
const auto itemCheck =
dusk::mods::item_check_commit("goron_reward:F_SP113", sp8, this);
sp8 = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
}
#endif
field_0x1480 = fopAcM_createItemForPresentDemo(&current.pos, sp8, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
NULL IF_DUSK_ARG(itemGiveTag));
if (field_0x1480 != fpcM_ERROR_PROCESS_ID_e) {
s16 r25 = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff);
dComIfGp_getEvent()->reset(this);
+5 -3
View File
@@ -1495,9 +1495,11 @@ int daNpcIns_c::talk(void* param_1) {
#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);
const auto itemCheck = dusk::mods::item_check_commit(
dusk::mods::item_give_tag_bug(s_givenInsectId), itemNo & 0xFF,
this);
itemNo = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
s_givenInsectId = dItemNo_NONE_e;
}
#endif
+6 -4
View File
@@ -1182,15 +1182,17 @@ int daNpc_Kkri_c::talk(void*) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC
const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (item_no == dItemNo_OIL_BOTTLE3_e) {
itemCheckName = "coro_bottle";
item_no = dusk::mods::item_check(itemCheckName, item_no, this);
const auto itemCheck =
dusk::mods::item_check_commit("coro_bottle", item_no, this);
item_no = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
}
#endif
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, item_no,
0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
NULL IF_DUSK_ARG(itemGiveTag));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+6 -4
View File
@@ -1192,14 +1192,16 @@ int daNpc_myna2_c::ECut_gameGoalSuccess(int i_staffId) {
int itemNo = 0;
if (mFlow.getEventId(&itemNo) == 1) {
#if TARGET_PC
const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (itemNo == dItemNo_KAKERA_HEART_e) {
itemCheckName = "plumm_minigame_reward";
itemNo = dusk::mods::item_check(itemCheckName, itemNo, this);
const auto itemCheck =
dusk::mods::item_check_commit("plumm_minigame_reward", itemNo, this);
itemNo = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
}
#endif
mItemPid = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
NULL IF_DUSK_ARG(itemGiveTag));
}
break;
}
+6 -2
View File
@@ -960,17 +960,21 @@ int daNpc_Pouya_c::cutHaveFavorToAsk(int param_0) {
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC
const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (local_64 == dItemNo_DROP_BOTTLE_e) {
itemCheckName = "jovani_reward_1";
} else if (local_64 == dItemNo_SILVER_RUPEE_e) {
itemCheckName = "jovani_reward_2";
}
if (itemCheckName != nullptr) {
local_64 = dusk::mods::item_check(itemCheckName, local_64, this);
const auto itemCheck =
dusk::mods::item_check_commit(itemCheckName, local_64, this);
local_64 = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
}
#endif
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_64, 0,
-1, -1, 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
-1, -1, 0, 0 IF_DUSK_ARG(itemGiveTag));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
field_0xfce = 1;
+6 -2
View File
@@ -2611,17 +2611,21 @@ BOOL daNpc_zrA_c::ECut_thanksBlast(int i_staffID) {
if (mFlow.getEventId(&item_id) == 1) {
#if TARGET_PC
const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (item_id == dItemNo_BOMB_IN_BAG_e) {
itemCheckName = "iza_reward_1";
} else if (item_id == dItemNo_BOMB_BAG_LV2_e) {
itemCheckName = "iza_reward_2";
}
if (itemCheckName != nullptr) {
item_id = dusk::mods::item_check(itemCheckName, item_id, this);
const auto itemCheck =
dusk::mods::item_check_commit(itemCheckName, item_id, this);
item_id = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
}
#endif
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_id, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
NULL IF_DUSK_ARG(itemGiveTag));
}
field_0x9eb = true;
break;
+72 -19
View File
@@ -14,6 +14,11 @@
#include "d/actor/d_a_obj_ystone.h"
#include <cstring>
#if TARGET_PC
#include "dusk/mods/item.hpp"
#include "mods/items.h"
#endif
static DUSK_CONST char* l_arcName = "ef_Portal";
static char const* l_clearEvName[9] = {
@@ -77,6 +82,27 @@ static int getNowLevel() {
return -1;
}
#if TARGET_PC
static const char* dungeon_reward_check_name(int level) {
switch (level) {
case 0:
return ITEM_CHECK_DUNGEON_REWARD_FOREST;
case 1:
return ITEM_CHECK_DUNGEON_REWARD_GORON;
case 2:
return ITEM_CHECK_DUNGEON_REWARD_LAKEBED;
case 4:
return ITEM_CHECK_DUNGEON_REWARD_SNOWPEAK;
case 5:
return ITEM_CHECK_DUNGEON_REWARD_TIME;
case 6:
return ITEM_CHECK_DUNGEON_REWARD_CITY;
default:
return nullptr;
}
}
#endif
void daObjBossWarp_c::initBaseMtx() {
scale.y = 0.15f;
mpModel->setBaseScale(scale);
@@ -485,6 +511,9 @@ int daObjBossWarp_c::demoProc() {
player->onSceneChangeArea(scene, 0xff, 0);
break;
case 4: // STONE_FALL
#if TARGET_PC
{
#endif
if (ystone != NULL) {
mYstonePos = ystone->current.pos;
if (getNowLevel() < 3) {
@@ -506,28 +535,52 @@ int daObjBossWarp_c::demoProc() {
mDoMtx_stack_c::multVec(&mYstoneTargetPos, &mYstoneTargetPos);
mYstonePos.x = mYstoneTargetPos.x;
mYstonePos.z = mYstoneTargetPos.z;
switch (getNowLevel()) {
case 0:
dComIfGs_onCollectCrystal(0);
break;
case 1:
dComIfGs_onCollectCrystal(1);
break;
case 2:
dComIfGs_onCollectCrystal(2);
break;
case 4:
dComIfGs_onCollectMirror(1);
break;
case 5:
dComIfGs_onCollectMirror(2);
break;
case 6:
dComIfGs_onCollectMirror(3);
break;
#if TARGET_PC
int level = getNowLevel();
const char* checkName = dungeon_reward_check_name(level);
dusk::mods::ItemCheckResult itemCheck{};
bool applyVanillaReward = true;
if (checkName != nullptr) {
itemCheck = dusk::mods::item_check_commit(checkName, dItemNo_NONE_e, this);
applyVanillaReward = itemCheck.itemNo == dItemNo_NONE_e;
}
if (applyVanillaReward) {
switch (level) {
#else
switch (getNowLevel()) {
#endif
case 0:
dComIfGs_onCollectCrystal(0);
break;
case 1:
dComIfGs_onCollectCrystal(1);
break;
case 2:
dComIfGs_onCollectCrystal(2);
break;
case 4:
dComIfGs_onCollectMirror(1);
break;
case 5:
dComIfGs_onCollectMirror(2);
break;
case 6:
dComIfGs_onCollectMirror(3);
break;
}
#if TARGET_PC
if (checkName != nullptr) {
dusk::mods::item_check_complete(itemCheck, this);
}
} else {
dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::Silent);
}
#endif
mCounter = 0;
break;
#if TARGET_PC
}
#endif
case 5: // STONE_MIDNA
mCounter = 0;
break;
+30 -6
View File
@@ -287,7 +287,11 @@ int daItem_c::_daItem_create() {
BOOL flag = dItem_data::chkFlag(m_itemNo, 2);
#if DEBUG
#if TARGET_PC
if (dItem_fieldModelArc(m_itemNo) == NULL) {
#else
if (dItem_data::getFieldArc(m_itemNo) == NULL) {
#endif
// "Item Num<%d>: No Resource Archive specified!!\n"
OS_REPORT_ERROR("アイテム番号<%d>:リソースのアーカイブ指定がありません!!\n", m_itemNo);
return cPhs_ERROR_e;
@@ -319,6 +323,20 @@ int daItem_c::_daItem_create() {
if (flag) {
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
phase_state = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo));
if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, CheckFieldItemCreateHeap,
@@ -412,7 +430,11 @@ int daItem_c::_daItem_delete() {
mSparkleEmtr.remove();
}
#if TARGET_PC
DeleteBase(dItem_fieldModelArc(m_itemNo));
#else
DeleteBase(dItem_data::getFieldArc(m_itemNo));
#endif
return 1;
}
@@ -520,9 +542,10 @@ void daItem_c::procInitGetDemoEvent() {
#if TARGET_PC
const u8 displayItemNo = m_itemNo;
if (mItemOverridden) {
m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
}
const auto itemCheck = dusk::mods::item_check_commit(mItemGiveTag, mOriginalItemNo, this);
m_itemNo = itemCheck.itemNo;
mItemGiveTag = itemCheck.tag;
mItemOverridden = m_itemNo != mOriginalItemNo;
#endif
m_item_id = fopAcM_createItemForTrBoxDemo(
&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag));
@@ -907,9 +930,10 @@ 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);
}
const auto itemCheck = dusk::mods::item_check_commit(mItemGiveTag, mOriginalItemNo, this);
m_itemNo = itemCheck.itemNo;
mItemGiveTag = itemCheck.tag;
mItemOverridden = m_itemNo != mOriginalItemNo;
#endif
switch (m_itemNo) {
#if TARGET_PC
+21 -3
View File
@@ -6,6 +6,7 @@
#include "d/dolzel_rel.h" // IWYU pragma: keep
#include "d/actor/d_a_obj_life_container.h"
#include "d/d_a_itembase_static.h"
#include "d/d_com_inf_game.h"
#include "d/actor/d_a_player.h"
#include "d/d_item_data.h"
@@ -184,11 +185,23 @@ int daObjLife_c::create() {
return cPhs_ERROR_e;
}
#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));
if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(
this, useGetModel ? CheckItemCreateHeap : CheckFieldItemCreateHeap, 0x4000))
{
return cPhs_ERROR_e;
}
#else
int phase_state = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo));
if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, CheckFieldItemCreateHeap, 0x4000)) {
return cPhs_ERROR_e;
}
#endif
if (!Create()) {
return cPhs_ERROR_e;
@@ -325,9 +338,10 @@ int daObjLife_c::initActionOrderGetDemo() {
#if TARGET_PC
const u8 displayItemNo = m_itemNo;
if (mItemOverridden) {
m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
}
const auto itemCheck = dusk::mods::item_check_commit(mItemGiveTag, mOriginalItemNo, this);
m_itemNo = itemCheck.itemNo;
mItemGiveTag = itemCheck.tag;
mItemOverridden = m_itemNo != mOriginalItemNo;
#endif
mItemId = fopAcM_createItemForTrBoxDemo(
&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag));
@@ -519,7 +533,11 @@ int daObjLife_c::_delete() {
endEffect00();
endEffect02();
#if TARGET_PC
DeleteBase(dItem_fieldModelArc(m_itemNo));
#else
DeleteBase(dItem_data::getFieldArc(m_itemNo));
#endif
return 1;
}
+40 -1
View File
@@ -1,6 +1,6 @@
/**
* @file d_a_obj_master_sword.cpp
*
*
*/
#include "d/dolzel_rel.h" // IWYU pragma: keep
@@ -10,6 +10,12 @@
#include "d/d_com_inf_game.h"
#include "d/d_meter2_info.h"
#if TARGET_PC
#include "d/d_item.h"
#include "dusk/mods/item.hpp"
#include "mods/items.h"
#endif
DUSK_GAME_DATA daObjMasterSword_Attr_c const daObjMasterSword_c::mAttr = {1.0f};
void daObjMasterSword_c::initBaseMtx() {
@@ -145,7 +151,11 @@ void daObjMasterSword_c::create_init() {
int daObjMasterSword_c::create() {
fopAcM_ct(this, daObjMasterSword_c);
#if TARGET_PC
if (dComIfGs_isEventBit(dSv_event_flag_c::F_0264)) {
#else
if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[getFlagNo()])) {
#endif
return cPhs_ERROR_e;
}
@@ -186,12 +196,41 @@ int daObjMasterSword_c::execute() {
mBrk.play();
if (dComIfGs_isTmpBit(dSv_event_tmp_flag_c::tempBitLabels[73])) {
#if TARGET_PC
const auto masterSword = dusk::mods::item_check_commit(
ITEM_CHECK_MASTER_SWORD, dItemNo_MASTER_SWORD_e, this);
if (masterSword.itemNo == dItemNo_MASTER_SWORD_e) {
dComIfGs_onItemFirstBit(dItemNo_MASTER_SWORD_e);
dMeter2Info_setSword(dItemNo_MASTER_SWORD_e, false);
dComIfGs_setSelectEquipSword(dItemNo_MASTER_SWORD_e);
dusk::mods::item_check_complete(masterSword, this);
} else if (masterSword.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(masterSword, this);
} else {
dusk::mods::item_check_enqueue(masterSword, dusk::mods::ItemGiveMode::Demo);
}
dComIfGp_setItemLifeCount(dComIfGs_getMaxLife(), 0);
const auto shadowCrystal = dusk::mods::item_check_commit(
ITEM_CHECK_SHADOW_CRYSTAL, dItemNo_SHADOW_CRYSTAL_e, this);
if (shadowCrystal.itemNo == dItemNo_SHADOW_CRYSTAL_e) {
execItemGet(shadowCrystal.itemNo, shadowCrystal.tag, this);
} else if (shadowCrystal.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(shadowCrystal, this);
} else {
dusk::mods::item_check_enqueue(shadowCrystal, dusk::mods::ItemGiveMode::Demo);
}
dComIfGs_onEventBit(dSv_event_flag_c::F_0264);
#else
dComIfGs_onItemFirstBit(dItemNo_MASTER_SWORD_e);
dMeter2Info_setSword(dItemNo_MASTER_SWORD_e, false);
dComIfGs_setSelectEquipSword(dItemNo_MASTER_SWORD_e);
dComIfGp_setItemLifeCount(dComIfGs_getMaxLife(), 0);
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[getFlagNo()]);
#endif
fopAcM_delete(this);
}
+1 -1
View File
@@ -109,7 +109,7 @@ int daItemShield_c::__CreateHeap() {
int daItemShield_c::create() {
fopAcM_ct(this, daItemShield_c);
m_itemNo = dItemNo_WOOD_SHIELD_e;
DUSK_ITEM_CHECK("ordon_shield", m_itemNo, this);
DUSK_ITEM_CHECK_PREVIEW("ordon_shield", m_itemNo, this);
if (fopAcM_isSwitch(this, getSwBit2())) {
OS_REPORT("木の盾:もう取ったので出ません\n");
return cPhs_ERROR_e;
+1 -1
View File
@@ -37,7 +37,7 @@ int daObjSword_c::Create() {
cPhs_Step daObjSword_c::create() {
fopAcM_ct(this, daObjSword_c);
m_itemNo = 0x28;
DUSK_ITEM_CHECK("ordon_sword", m_itemNo, this);
DUSK_ITEM_CHECK_PREVIEW("ordon_sword", m_itemNo, this);
if (fopAcM_isItem(this, getItemBit())) {
return cPhs_ERROR_e;
}
+1 -1
View File
@@ -32,7 +32,7 @@ int daObjZCloth_c::Create() {
int daObjZCloth_c::create() {
fopAcM_ct(this, daObjZCloth_c);
m_itemNo = 0x31;
DUSK_ITEM_CHECK("zora_armor", m_itemNo, this);
DUSK_ITEM_CHECK_PREVIEW("zora_armor", m_itemNo, this);
int phase = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo));
if (phase == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, (heapCallbackFunc)CheckFieldItemCreateHeap, 0x2fb0)) {
+6 -3
View File
@@ -363,10 +363,13 @@ int daTagStatue_c::demoProc() {
}
#if TARGET_PC
item = dusk::mods::item_check_sky_character(item & 0xFF, this);
const auto itemCheck = dusk::mods::item_check_commit(
dusk::mods::item_give_tag_sky_character(), item & 0xFF, this);
item = itemCheck.itemNo;
#endif
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos, item, -1,
fopAcM_GetRoomNo(this), 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag_sky_character()));
mItemId =
fopAcM_createItemForTrBoxDemo(&current.pos, item, -1, fopAcM_GetRoomNo(this), 0,
0 IF_DUSK_ARG(itemCheck.tag));
JUT_ASSERT(580, mItemId != fpcM_ERROR_PROCESS_ID_e);
+5 -4
View File
@@ -1391,8 +1391,9 @@ 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);
const auto itemCheck = dusk::mods::item_check_commit(
dusk::mods::item_give_tag_chest(getTboxNo()), mOriginalItemNo, this);
item_no = itemCheck.itemNo;
#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 ||
@@ -1404,10 +1405,10 @@ int daTbox_c::setGetDemoItem() {
fpc_ProcID item_id;
if (field_0x718) {
item_id = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(itemCheck.tag));
} else {
item_id = fopAcM_createItemForTrBoxDemo(
&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(itemCheck.tag));
}
if (item_id != fpcM_ERROR_PROCESS_ID_e) {
+7 -4
View File
@@ -383,15 +383,18 @@ int daTbox2_c::setGetDemoItem() {
u8 item_no = getItemNo();
#if TARGET_PC
int tboxNo = fopAcM_GetParamBit(this, 16, 8);
const u32 giveTag = dusk::mods::item_give_tag_chest(tboxNo);
item_no = dusk::mods::item_check_tagged(giveTag, mOriginalItemNo, this);
const auto itemCheck = dusk::mods::item_check_commit(
dusk::mods::item_give_tag_chest(tboxNo), mOriginalItemNo, this);
item_no = itemCheck.itemNo;
#endif
u32 partner_id;
if (mReturnRupee) {
partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
partner_id = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(itemCheck.tag));
} else {
partner_id = fopAcM_createItemForTrBoxDemo(&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
partner_id = fopAcM_createItemForTrBoxDemo(
&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(itemCheck.tag));
}
if (partner_id != -1) {
+7
View File
@@ -57,6 +57,13 @@ int CheckItemCreateHeap(fopAc_ac_c* i_this) {
dItem_data::getBrkName(item_no), dItem_data::getBtpName(item_no));
}
#if TARGET_PC
const char* dItem_fieldModelArc(u8 itemNo) {
const char* arcName = dItem_data::getFieldArc(itemNo);
return arcName != NULL ? arcName : dItem_data::getArcName(itemNo);
}
#endif
int CheckFieldItemCreateHeap(fopAc_ac_c* i_this) {
daItemBase_c* a_this = static_cast<daItemBase_c*>(i_this);
+17 -1
View File
@@ -741,8 +741,12 @@ void item_func_ARMOR() {}
void item_func_WEAR_ZORA() {}
void item_func_MAGIC_LV1() {
#if TARGET_PC
dComIfGs_onEventBit(0xD04);
#else
dComIfGp_setItemMagicCount(16);
dComIfGp_setItemMaxMagicCount(16);
#endif
}
void item_func_DUNGEON_EXIT_2() {
@@ -1152,7 +1156,15 @@ void item_func_M_MAYFLY() {}
void item_func_F_MAYFLY() {}
void item_func_POU_SPIRIT() {}
void item_func_POU_SPIRIT() {
#if TARGET_PC
dComIfGs_addPohSpiritNum();
if (dComIfGs_getPohSpiritNum() == 20) {
/* dSv_event_flag_c::F_0457 - Castle Town - Revived cat */
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[457]);
}
#endif
}
void item_func_ANCIENT_DOCUMENT() {
dComIfGs_setItem(SLOT_22, dItemNo_ANCIENT_DOCUMENT_e);
@@ -1411,7 +1423,11 @@ int item_getcheck_func_WEAR_ZORA() {
}
int item_getcheck_func_MAGIC_LV1() {
#if TARGET_PC
return dComIfGs_isEventBit(0xD04);
#else
return -1;
#endif
}
int item_getcheck_func_DUNGEON_EXIT_2() {
+4
View File
@@ -53,7 +53,11 @@ DUSK_GAME_DATA dItem_itemResource dItem_data::item_resource[] = {
{"F_gD_rupy", 0x0004,-0x0001,-0x0001, 0x0007,-0x0001, 0x0, -0x1, 0x006B, 0x64, 0x0000},
{"O_gD_marm", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0041, 0x64, 0x01A9},
{"O_gD_zora", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x004D, 0x64, 0x01AA},
#if TARGET_PC
{"O_gD_Injy", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002E, 0x64, 0x0000},
#else
{"O_gD_Injy", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x002D, 0x64, 0x0000},
#endif
{"O_gD_TKS", 0x0008,-0x0001, 0x0005,-0x0001,-0x0001, -0x1, -0x1, 0x0028, 0x64, 0x0000},
{"O_gD_puL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0047, 0x64, 0x0000},
{"O_gD_puL2", 0x0003,-0x0001,-0x0001,-0x0001,-0x0001, -0x1, -0x1, 0x0048, 0x64, 0x0000},
+4
View File
@@ -36,6 +36,7 @@
#include "dusk/language.hpp"
#include "dusk/logging.h"
#include "dusk/menu_pointer.h"
#include "dusk/mods/item.hpp"
#include "dusk/mods/svc/flow.hpp"
#include "dusk/settings.h"
#include "dusk/version.hpp"
@@ -832,6 +833,9 @@ u32 dMsgObject_c::getMessageIndex(u32 param_0) {
u32 dMsgObject_c::getRevoMessageIndex(u32 param_1) {
#if TARGET_PC
const u16 sourceGroup = param_1 > 5000 ? static_cast<u16>(s_groupID) : 0;
param_1 = dusk::mods::item_check_message(sourceGroup, param_1);
u16 customGroup = 0;
if (param_1 <= 0xffff &&
dusk::flow::custom_message_group(static_cast<u16>(param_1), customGroup))
+28 -2
View File
@@ -42,7 +42,9 @@
#if TARGET_PC
#include "dusk/autosave.h"
#include "dusk/memory.h"
#include "dusk/mods/item.hpp"
#include "dusk/ui/ui.hpp"
#include "mods/items.h"
#endif
#if DEBUG
@@ -1192,11 +1194,35 @@ static int phase_1(dScnPly_c* i_this) {
}
// Stage: Ordon Spring, Room: Ordon Spring
#if TARGET_PC
const bool iliaMemoryArrival =
!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 &&
dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12;
const u32 iliaMemoryTag = dusk::mods::item_give_tag(ITEM_CHECK_ILIA_MEMORY);
if (!iliaMemoryArrival) {
dusk::mods::item_check_cancel(iliaMemoryTag);
}
if (iliaMemoryArrival)
#else
if (!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 &&
dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12)
#endif
{
dComIfGs_onItemFirstBit(dItemNo_HORSE_FLUTE_e);
dComIfGs_setItem(SLOT_21, dItemNo_HORSE_FLUTE_e);
#if TARGET_PC
const auto itemCheck =
dusk::mods::item_check_commit(iliaMemoryTag, dItemNo_HORSE_FLUTE_e, nullptr);
if (itemCheck.itemNo == dItemNo_HORSE_FLUTE_e) {
#endif
dComIfGs_onItemFirstBit(dItemNo_HORSE_FLUTE_e);
dComIfGs_setItem(SLOT_21, dItemNo_HORSE_FLUTE_e);
#if TARGET_PC
dusk::mods::item_check_complete(itemCheck, nullptr);
} else if (itemCheck.itemNo == dItemNo_NONE_e) {
dusk::mods::item_check_complete(itemCheck, nullptr);
} else {
dusk::mods::item_check_enqueue(itemCheck, dusk::mods::ItemGiveMode::Silent);
}
#endif
}
if ((u8)dKy_darkworld_stage_check(dComIfGp_getStartStageName(),
+9 -5
View File
@@ -906,15 +906,17 @@ int dShopSystem_c::seq_start(fopAc_ac_c* actor, dMsgFlow_c* i_flow) {
if (mFlow.getEventId(&itemNo) == 1) {
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC
const char* itemCheckName = nullptr;
u32 itemGiveTag = 0;
if (itemNo == dItemNo_HALF_MILK_BOTTLE_e) {
itemCheckName = "sera_reward";
itemNo = dusk::mods::item_check(itemCheckName, itemNo, actor);
const auto itemCheck =
dusk::mods::item_check_commit("sera_reward", itemNo, actor);
itemNo = itemCheck.itemNo;
itemGiveTag = itemCheck.tag;
}
#endif
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
NULL IF_DUSK_ARG(itemGiveTag));
}
if (fpcEx_IsExist(mItemPartnerId)) {
@@ -1205,7 +1207,9 @@ int dShopSystem_c::seq_decide_yes(fopAc_ac_c* actor, dMsgFlow_c* i_flow) {
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC
const u32 itemGiveTag = dusk::mods::item_give_tag_shop(itemNo & 0xFF);
itemNo = dusk::mods::item_check_shop(itemNo & 0xFF, actor);
const auto itemCheck =
dusk::mods::item_check_commit(itemGiveTag, itemNo & 0xFF, actor);
itemNo = itemCheck.itemNo;
#endif
mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, itemNo, 0, -1, -1, NULL, NULL IF_DUSK_ARG(itemGiveTag));
+1 -1
View File
@@ -76,7 +76,7 @@ namespace dusk {
{ dItemNo_WEAR_KOKIRI_e, {"Hero's Clothes"} },
{ dItemNo_ARMOR_e, {"Magic Armor"} },
{ dItemNo_WEAR_ZORA_e, {"Zora Armor"} },
{ dItemNo_MAGIC_LV1_e, {"Magic Level 1"} },
{ dItemNo_SHADOW_CRYSTAL_e, {"Shadow Crystal"} },
{ dItemNo_DUNGEON_EXIT_2_e, {"Ooccoo Sr.", ITEMTYPE_EQUIP_e} },
{ dItemNo_WALLET_LV1_e, {"Wallet"} },
{ dItemNo_WALLET_LV2_e, {"Big Wallet"} },
+67 -6
View File
@@ -1,21 +1,31 @@
#pragma once
#include <cstdint>
#include <unordered_map>
class fopAc_ac_c;
namespace dusk::mods {
enum class ItemGiveMode : uint8_t {
Demo,
Silent,
ForcedDemo,
};
struct ItemCheckResult {
uint32_t tag = 0;
uint8_t itemNo = 0;
};
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);
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);
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);
@@ -26,12 +36,63 @@ 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_check_enqueue_deferred(const char* name, uint8_t itemNo);
bool item_check_enqueue(ItemCheckResult check, ItemGiveMode mode);
void item_check_complete(ItemCheckResult check, fopAc_ac_c* giver);
void item_check_cancel(uint32_t giveTag);
void item_check_clear_committed();
void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver);
uint32_t item_check_message(uint16_t group, uint32_t messageId);
bool item_give_queue_dispatching();
uint32_t item_give_queue_take_tag();
namespace detail {
struct CommittedCheck {
uint8_t vanillaItem = 0;
uint8_t resolvedItem = 0;
bool queued = false;
};
class ItemCommitStore {
public:
CommittedCheck* find(uint32_t giveTag) {
const auto it = mChecks.find(giveTag);
return it != mChecks.end() ? &it->second : nullptr;
}
const CommittedCheck* find(uint32_t giveTag) const {
const auto it = mChecks.find(giveTag);
return it != mChecks.end() ? &it->second : nullptr;
}
template <typename ResolveFn>
CommittedCheck& commit(uint32_t giveTag, uint8_t vanillaItem, ResolveFn&& resolve) {
const auto existing = mChecks.find(giveTag);
if (existing != mChecks.end()) {
return existing->second;
}
const uint8_t resolvedItem = std::forward<ResolveFn>(resolve)();
return mChecks
.emplace(
giveTag, CommittedCheck{.vanillaItem = vanillaItem, .resolvedItem = resolvedItem})
.first->second;
}
bool contains(uint32_t giveTag) const { return mChecks.contains(giveTag); }
void erase(uint32_t giveTag) { mChecks.erase(giveTag); }
void clear() { mChecks.clear(); }
bool empty() const { return mChecks.empty(); }
private:
std::unordered_map<uint32_t, CommittedCheck> mChecks;
};
} // namespace detail
} // namespace dusk::mods
+143 -28
View File
@@ -6,9 +6,14 @@
#include "aurora/lib/logging.hpp"
#include "d/d_com_inf_game.h"
#include "d/d_item_data.h"
#include "mods/items.h"
#include <fmt/format.h>
#include <algorithm>
#include <array>
#include <cstring>
#include <exception>
#include <string>
#include <unordered_map>
@@ -45,6 +50,78 @@ struct PendingResolve {
void* userData = nullptr;
};
struct MessageCheck {
uint16_t group = 0;
uint16_t messageId = 0;
const char* name = nullptr;
uint8_t vanillaItem = 0;
bool enqueueAtDisplay = true;
};
constexpr std::array kMessageChecks{
MessageCheck{
.group = 0,
.messageId = 1822,
.name = ITEM_CHECK_FISHING_BOTTLE,
.vanillaItem = dItemNo_EMPTY_BOTTLE_e,
},
MessageCheck{
.group = 7,
.messageId = 7564,
.name = ITEM_CHECK_FISHING_HEART_PIECE,
.vanillaItem = dItemNo_KAKERA_HEART_e,
},
MessageCheck{
.group = 7,
.messageId = 7578,
.name = ITEM_CHECK_FISHING_HEART_PIECE,
.vanillaItem = dItemNo_KAKERA_HEART_e,
},
MessageCheck{
.group = 5,
.messageId = 5001,
.name = ITEM_CHECK_DUNGEON_REWARD_FOREST,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 6011,
.name = ITEM_CHECK_DUNGEON_REWARD_GORON,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 7001,
.name = ITEM_CHECK_DUNGEON_REWARD_LAKEBED,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 9301,
.name = ITEM_CHECK_DUNGEON_REWARD_SNOWPEAK,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 9401,
.name = ITEM_CHECK_DUNGEON_REWARD_TIME,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 5,
.messageId = 11001,
.name = ITEM_CHECK_DUNGEON_REWARD_CITY,
.vanillaItem = dItemNo_NONE_e,
},
MessageCheck{
.group = 0,
.messageId = 233,
.name = ITEM_CHECK_ILIA_MEMORY,
.vanillaItem = dItemNo_HORSE_FLUTE_e,
.enqueueAtDisplay = false,
},
};
std::unordered_map<LoadedMod*, ModItemChecks> s_modChecks;
std::unordered_set<std::string> s_warnedCollisions;
ItemCheckHandle s_nextCheckHandle = 1;
@@ -79,7 +156,28 @@ std::string bug_check_name(uint8_t insectId) {
}
std::string sky_character_check_name() {
return fmt::format("skychar:{}:{}", current_stage_name(), dStage_roomControl_c::getStayNo());
return fmt::format("sky:{}:{}", current_stage_name(), dStage_roomControl_c::getStayNo());
}
bool ilia_memory_context() {
return std::strcmp(current_stage_name(), "R_SP109") == 0 &&
dComIfGp_roomControl_getStayNo() == 0 && dComIfG_play_c::getLayerNo(0) == 9;
}
uint16_t item_get_message(uint8_t itemNo) {
if (itemNo == dItemNo_KAKERA_HEART_e) {
constexpr std::array<uint16_t, 5> heartPieceMessages{0x86, 0x9C, 0x9D, 0x9E, 0x9F};
return heartPieceMessages[dComIfGs_getMaxLife() % heartPieceMessages.size()];
}
return static_cast<uint16_t>(itemNo + 0x65);
}
void invalidate_committed_check(const std::string& name) {
if (name.empty()) {
item_check_clear_committed();
} else {
item_check_cancel(item_give_tag(name.c_str()));
}
}
} // namespace
@@ -176,14 +274,6 @@ uint8_t item_check_freestanding(uint8_t bitNo, uint8_t itemNo, fopAc_ac_c* item)
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;
@@ -192,20 +282,24 @@ uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver) {
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);
}
uint32_t item_check_message(uint16_t group, uint32_t messageId) {
for (const auto& check : kMessageChecks) {
if (check.group != group || check.messageId != messageId ||
(check.group == 0 && check.messageId == 233 && !ilia_memory_context()))
{
continue;
}
uint8_t item_check_sky_character(uint8_t itemNo, fopAc_ac_c* statue) {
if (s_modChecks.empty()) {
return itemNo;
const ItemCheckResult result = item_check_commit(check.name, check.vanillaItem, nullptr);
if (result.itemNo == check.vanillaItem || result.itemNo == dItemNo_NONE_e) {
return messageId;
}
if (check.enqueueAtDisplay) {
item_check_enqueue(result, ItemGiveMode::Silent);
}
return item_get_message(result.itemNo);
}
const auto name = sky_character_check_name();
return item_check(name.c_str(), itemNo, statue);
return messageId;
}
uint32_t item_give_tag_chest(uint8_t boxNo) {
@@ -236,10 +330,6 @@ 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) {
@@ -247,10 +337,12 @@ ModResult item_check_set_override(LoadedMod& mod, const char* name, uint8_t item
for (auto& checkOverride : checks.overrides) {
if (checkOverride.name == name) {
checkOverride.itemNo = itemNo;
invalidate_committed_check(checkOverride.name);
return MOD_OK;
}
}
checks.overrides.push_back({.name = name, .itemNo = itemNo});
invalidate_committed_check(checks.overrides.back().name);
return MOD_OK;
}
@@ -261,6 +353,9 @@ ModResult item_check_clear_override(LoadedMod& mod, const char* name) {
}
const auto removed = std::erase_if(modIt->second.overrides,
[&](const auto& checkOverride) { return checkOverride.name == name; });
if (removed != 0) {
invalidate_committed_check(name);
}
return removed != 0 ? MOD_OK : MOD_INVALID_ARGUMENT;
}
@@ -272,6 +367,7 @@ ModResult item_check_add_resolver(LoadedMod& mod, const char* name, ItemCheckRes
resolver.fn = fn;
resolver.userData = userData;
outHandle = resolver.handle;
invalidate_committed_check(resolver.name);
return MOD_OK;
}
@@ -280,12 +376,31 @@ ModResult item_check_remove_resolver(LoadedMod& mod, ItemCheckHandle handle) {
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;
const auto resolverIt =
std::find_if(modIt->second.resolvers.begin(), modIt->second.resolvers.end(),
[&](const auto& resolver) { return resolver.handle == handle; });
if (resolverIt == modIt->second.resolvers.end()) {
return MOD_INVALID_ARGUMENT;
}
const std::string name = resolverIt->name;
modIt->second.resolvers.erase(resolverIt);
invalidate_committed_check(name);
return MOD_OK;
}
void item_checks_remove_mod(LoadedMod& mod) {
if (const auto modIt = s_modChecks.find(&mod); modIt != s_modChecks.end()) {
for (const auto& resolver : modIt->second.resolvers) {
if (resolver.name.empty()) {
item_check_clear_committed();
break;
}
invalidate_committed_check(resolver.name);
}
for (const auto& checkOverride : modIt->second.overrides) {
invalidate_committed_check(checkOverride.name);
}
}
s_modChecks.erase(&mod);
s_warnedCollisions.clear();
}
+130 -37
View File
@@ -17,6 +17,7 @@
#include <exception>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace dusk::mods {
@@ -28,6 +29,8 @@ aurora::Module Log{"dusk::mods::item_gives"};
std::deque<std::string> s_giveNames;
std::unordered_map<std::string, uint32_t> s_giveNameIds;
detail::ItemCommitStore s_committedChecks;
const char* item_give_name(uint32_t tag) {
if (tag == 0 || tag > s_giveNames.size()) {
return nullptr;
@@ -92,7 +95,18 @@ void notify_gives(const char* checkName, uint8_t itemNo, fopAc_ac_c* giver, Item
}
}
constexpr size_t kGiveQueueLimit = 64;
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)
{
Log.error("committed check '{}' completed with item {:#x} instead of {:#x}",
item_give_name(giveTag) != nullptr ? item_give_name(giveTag) : "", itemNo,
committed->resolvedItem);
}
s_committedChecks.erase(giveTag);
notify_gives(item_give_name(giveTag), itemNo, giver, origin);
}
constexpr int kGiveMaxRetries = 5;
struct QueuedGive {
@@ -101,6 +115,7 @@ struct QueuedGive {
uint8_t itemNo = 0;
bool silent = false;
bool resolveAtDispatch = false;
bool forced = false;
};
std::deque<QueuedGive> s_giveQueue;
@@ -111,6 +126,11 @@ bool s_inFlight = false;
bool s_inFlightSpawned = false;
bool s_dispatchingSilent = false;
bool has_forced_give() {
return std::any_of(
s_giveQueue.begin(), s_giveQueue.end(), [](const QueuedGive& give) { return give.forced; });
}
bool safe_to_dispatch() {
daAlink_c* link = daAlink_getAlinkActorClass();
if (link == nullptr) {
@@ -142,13 +162,13 @@ bool safe_to_dispatch() {
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);
outItem = item_check_commit(give.tag, give.itemNo, nullptr).itemNo;
}
if (outItem != dItemNo_NONE_e) {
return true;
}
notify_gives(item_give_name(give.tag), dItemNo_NONE_e, nullptr, origin);
complete_check(dItemNo_NONE_e, give.tag, nullptr, origin);
return false;
}
@@ -171,11 +191,40 @@ void dispatch_demo_give() {
daAlink_c* link = daAlink_getAlinkActorClass();
dComIfGp_getEvent()->setGtItm(s_inFlightItem);
link->procCoGetItemInit();
link->mProcID = daAlink_c::PROC_GET_ITEM;
const s16 eventIndex = dComIfGp_getEventManager().getEventIdx(link, "DEFAULT_GETITEM", 0xFF);
fopAcM_orderChangeEventId(link, eventIndex, 1, 0xFFFF);
}
void dispatch_next_give() {
while (!s_giveQueue.empty()) {
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();
s_giveQueue.pop_front();
uint8_t itemNo = 0;
if (!resolve_queued_give(give, ITEM_GIVE_ORIGIN_QUEUE, itemNo)) {
continue;
}
s_inFlightGive = give;
s_inFlightItem = itemNo;
s_inFlightRetries = 0;
s_inFlight = true;
s_inFlightSpawned = false;
dispatch_demo_give();
return;
}
}
} // namespace
uint32_t item_give_tag(const char* name) {
@@ -192,16 +241,30 @@ uint32_t item_give_tag(const char* name) {
return tag;
}
uint8_t item_check_tagged(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* giver) {
ItemCheckResult item_check_commit(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;
if (name == nullptr) {
return {.tag = giveTag, .itemNo = itemNo};
}
if (const auto* committed = s_committedChecks.find(giveTag); committed != nullptr) {
if (committed->vanillaItem != itemNo) {
Log.error("committed check '{}' changed vanilla item from {:#x} to {:#x}", name,
committed->vanillaItem, itemNo);
}
return {.tag = giveTag, .itemNo = committed->resolvedItem};
}
const auto& committed =
s_committedChecks.commit(giveTag, itemNo, [&] { return item_check(name, itemNo, giver); });
return {.tag = giveTag, .itemNo = committed.resolvedItem};
}
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;
}
ItemCheckResult item_check_commit(const char* name, uint8_t itemNo, fopAc_ac_c* giver) {
return item_check_commit(item_give_tag(name), itemNo, giver);
}
void item_check_enqueue_deferred(const char* name, uint8_t itemNo) {
s_giveQueue.push_back({
.tag = item_give_tag(name),
.itemNo = itemNo,
@@ -209,6 +272,53 @@ void item_check_enqueue(const char* name, uint8_t itemNo) {
});
}
bool item_check_enqueue(ItemCheckResult check, ItemGiveMode mode) {
auto* committed = s_committedChecks.find(check.tag);
if (committed == nullptr) {
Log.error("cannot enqueue uncommitted check '{}'",
item_give_name(check.tag) != nullptr ? item_give_name(check.tag) : "");
return false;
}
if (committed->resolvedItem != 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);
return false;
}
if (committed->queued) {
return false;
}
committed->queued = true;
s_giveQueue.push_back({
.tag = check.tag,
.itemNo = committed->resolvedItem,
.silent = mode == ItemGiveMode::Silent,
.forced = mode == ItemGiveMode::ForcedDemo,
});
if (mode == ItemGiveMode::ForcedDemo && !s_inFlight) {
dispatch_next_give();
}
return true;
}
void item_check_complete(ItemCheckResult check, fopAc_ac_c* giver) {
complete_check(check.itemNo, check.tag, giver, ITEM_GIVE_ORIGIN_GAME);
}
void item_check_cancel(uint32_t giveTag) {
s_committedChecks.erase(giveTag);
std::erase_if(s_giveQueue,
[&](const QueuedGive& give) { return give.owner == nullptr && give.tag == giveTag; });
}
void item_check_clear_committed() {
std::erase_if(s_giveQueue, [&](const QueuedGive& give) {
return give.owner == nullptr && s_committedChecks.contains(give.tag);
});
s_committedChecks.clear();
}
void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver) {
ItemGiveOrigin origin = ITEM_GIVE_ORIGIN_GAME;
if (s_dispatchingSilent) {
@@ -218,7 +328,7 @@ void item_granted(uint8_t itemNo, uint32_t giveTag, fopAc_ac_c* giver) {
s_inFlight = false;
s_inFlightSpawned = false;
}
notify_gives(item_give_name(giveTag), itemNo, giver, origin);
complete_check(itemNo, giveTag, giver, origin);
}
bool item_give_queue_dispatching() {
@@ -236,17 +346,21 @@ uint32_t item_give_queue_take_tag() {
namespace svc {
void item_gives_tick() {
if ((!s_inFlight && s_giveQueue.empty()) || !safe_to_dispatch()) {
if (!s_inFlight && s_giveQueue.empty()) {
return;
}
if (s_inFlight) {
if (!safe_to_dispatch()) {
return;
}
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);
item_check_cancel(s_inFlightGive.tag);
s_inFlight = false;
s_inFlightSpawned = false;
return;
@@ -256,29 +370,10 @@ void item_gives_tick() {
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()) {
if (!has_forced_give() && !safe_to_dispatch()) {
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();
dispatch_next_give();
}
void item_gives_clear() {
@@ -289,12 +384,10 @@ void item_gives_clear() {
s_giveQueue.clear();
s_inFlight = false;
s_inFlightSpawned = false;
s_committedChecks.clear();
}
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),
+1 -1
View File
@@ -275,7 +275,7 @@ std::map<int, itemInfo> itemMap = {
{dItemNo_WEAR_KOKIRI_e, {"Hero's Clothes"}},
{dItemNo_ARMOR_e, {"Magic Armor"}},
{dItemNo_WEAR_ZORA_e, {"Zora Armor"}},
{dItemNo_MAGIC_LV1_e, {"Magic Level 1"}},
{dItemNo_SHADOW_CRYSTAL_e, {"Shadow Crystal"}},
{dItemNo_DUNGEON_EXIT_2_e, {"Ooccoo Sr.", ITEMTYPE_EQUIP_e}},
{dItemNo_WALLET_LV1_e, {"Wallet"}},
{dItemNo_WALLET_LV2_e, {"Big Wallet"}},
+6
View File
@@ -1592,6 +1592,12 @@ fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomN
int _ = -1;
#if TARGET_PC
const u8 originalItemNo = i_itemNo & 0xFF;
// Overridden item checks do not set StageLife, so boss containers
// also need a persistent local item bit.
constexpr u8 kBossHeartContainerFlag = 0x9F;
if (originalItemNo == dItemNo_UTAWA_HEART_e && param_8 == -1) {
param_8 = kBossHeartContainerFlag;
}
u32 giveTag;
if (i_itemCheckName != NULL) {
i_itemNo = dusk::mods::item_check(i_itemCheckName, originalItemNo, NULL);