From 8f5ecdba1cac2d89d80366a0d21a3af3ffbafa9a Mon Sep 17 00:00:00 2001 From: Luke Street Date: Fri, 21 Aug 2026 20:45:43 -0600 Subject: [PATCH] Fix Golden Wolf item persistence --- include/d/actor/d_a_obj_life_container.h | 1 + mods/randomizer/src/hooks.cpp | 48 +++++++++++++++++------- mods/randomizer/src/session.cpp | 16 ++++++++ sdk/include/mods/items.h | 1 + src/d/actor/d_a_obj_life_container.cpp | 12 +++++- src/dusk/mods/item.hpp | 2 + src/dusk/mods/item_checks.cpp | 16 ++++++++ 7 files changed, 82 insertions(+), 14 deletions(-) diff --git a/include/d/actor/d_a_obj_life_container.h b/include/d/actor/d_a_obj_life_container.h index b9e8b40e1f..d4dc5bb334 100644 --- a/include/d/actor/d_a_obj_life_container.h +++ b/include/d/actor/d_a_obj_life_container.h @@ -92,6 +92,7 @@ public: u8 mOriginalItemNo; bool mItemOverridden; bool mOverrideHover; + bool mGoldenWolfItem; #endif }; diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index cb19e87a34..1896a33370 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "c/c_damagereaction.h" #include "d/actor/d_a_alink.h" @@ -2812,17 +2813,33 @@ HookAction hookPreObjLifeCreate(ModContext*, void* args, void* retval, void*) { auto goldenWolfFlags = getCurrentGoldenWolfFlags(roomNo); // Don't spawn this item if we haven't howled at the howling stone, or if we've already // obtained the item - if ((goldenWolfFlags.howledAtStoneFlag != 0xFFFF && !dComIfGs_isEventBit(goldenWolfFlags.howledAtStoneFlag)) || - dComIfGs_isEventBit(goldenWolfFlags.obtainedItemFlag)) + if (goldenWolfFlags.obtainedItemFlag == 0xFFFF || + (goldenWolfFlags.howledAtStoneFlag != 0xFFFF && + !dComIfGs_isEventBit(goldenWolfFlags.howledAtStoneFlag)) || + dComIfGs_isEventBit(goldenWolfFlags.obtainedItemFlag) || + !randomizer_GetContext().mGoldenWolfOverrides.contains( + goldenWolfFlags.obtainedItemFlag)) { *static_cast(retval) = cPhs_ERROR_e; return HOOK_SKIP_ORIGINAL; } - // Store the map marker flag and obtained item flags to turn off/on later if - // the player collects the item + // The actor moves these placement angles into field_0x938/field_0x93a during create. i_this->home.angle.z = goldenWolfFlags.mapMarkerFlag; i_this->home.angle.x = static_cast(goldenWolfFlags.obtainedItemFlag); + + i_this->mItemGiveOriginalNo = itemId; + i_this->mGoldenWolfItem = true; + const std::string checkName = + std::string{ITEM_CHECK_GOLDEN_WOLF_PREFIX} + + std::to_string(goldenWolfFlags.obtainedItemFlag); + if (session::svc_mng.item->resolve_check(session::svc_mng.mod_ctx, checkName.c_str(), + itemId, &itemId) != MOD_OK) + { + *static_cast(retval) = cPhs_ERROR_e; + return HOOK_SKIP_ORIGINAL; + } + fopAcM_SetParam(i_this, (params & 0xFFFFFF00) | itemId); } // Also adjust the height of the object depending on the item @@ -2913,18 +2930,23 @@ HookAction hookPreObjLifeCreate(ModContext*, void* args, void* retval, void*) { return HOOK_CONTINUE; } -void hookPostObjLifeActionGetDemo(ModContext*, void* args, void*, void*) { +HookAction hookPreObjLifeActionGetDemo(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); - // In randomizer, turn off the map marker flag for this golden wolf replacement item - // if we're collecting it. We store the map marker flag in unused home.angle.z - // Also set the flag for having collected this golden wolf item, stored in home.angle.x - if (static_cast(i_this->home.angle.z) != 0xFFFF) { - dComIfGs_offSwitch(static_cast(i_this->home.angle.z), fopAcM_GetRoomNo(i_this)); + if (!i_this->mGoldenWolfItem || !dComIfGp_evmng_endCheck("DEFAULT_GETITEM")) + { + return HOOK_CONTINUE; } - if (static_cast(i_this->home.angle.x) != 0xFFFF) { - dComIfGs_onEventBit(static_cast(i_this->home.angle.x)); + + const u16 mapMarkerFlag = static_cast(i_this->field_0x93a); + const u16 obtainedItemFlag = static_cast(i_this->field_0x938); + if (mapMarkerFlag != 0xFFFF) { + dComIfGs_offSwitch(mapMarkerFlag, fopAcM_GetRoomNo(i_this)); } + if (obtainedItemFlag != 0xFFFF) { + dComIfGs_onEventBit(obtainedItemFlag); + } + return HOOK_CONTINUE; } HookAction hookPreObjLifeCalcScale(ModContext*, void* args, void* retval, void*) { @@ -3231,7 +3253,7 @@ ModResult initialize() { ADD_HOOK_PRE(daObjLife_c__setEffect, hookPreObjLifeSetEffect); ADD_HOOK_PRE(daObjLife_c__create, hookPreObjLifeCreate); - ADD_HOOK_POST(daObjLife_c__actionGetDemo, hookPostObjLifeActionGetDemo); + ADD_HOOK_PRE(daObjLife_c__actionGetDemo, hookPreObjLifeActionGetDemo); ADD_HOOK_PRE(daObjLife_c__calcScale, hookPreObjLifeCalcScale); ADD_HOOK_POST(getCollectSmell, hookPostGetCollectSmell); diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index 6db90ecaa6..5f88b34cc1 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -70,6 +70,19 @@ std::optional parse_derived(const char* name, std::string_view prefi return DerivedKey{stage_id, static_cast((stage_id << 8) | (n & 0xFF))}; } +std::optional parse_flag_check(const char* name, std::string_view prefix) { + if (std::strncmp(name, prefix.data(), prefix.size()) != 0) { + return std::nullopt; + } + const char* value = name + prefix.size(); + char* end = nullptr; + const unsigned long flag = std::strtoul(value, &end, 10); + if (value == end || *end != '\0' || flag > 0xFFFF) { + return std::nullopt; + } + return static_cast(flag); +} + template bool lookup_override(const Map& map, u16 key, uint8_t* out_item, bool progressive) { const auto it = map.find(key); @@ -102,6 +115,9 @@ bool resolve_check(ModContext*, const ItemCheckInfo* info, uint8_t* out_item, vo } return lookup_override(ctx.mFreestandingItemOverrides, key->key, out_item, true); } + if (auto flag = parse_flag_check(info->name, ITEM_CHECK_GOLDEN_WOLF_PREFIX)) { + return lookup_override(ctx.mGoldenWolfOverrides, *flag, out_item, true); + } if (auto key = parse_derived(info->name, ITEM_CHECK_POE_PREFIX)) { return lookup_override(ctx.mPoeOverrides, key->key, out_item, true); } diff --git a/sdk/include/mods/items.h b/sdk/include/mods/items.h index e10b6df639..e0b1cae2d9 100644 --- a/sdk/include/mods/items.h +++ b/sdk/include/mods/items.h @@ -8,6 +8,7 @@ #define ITEM_CHECK_BUG_PREFIX "bug:" /* */ #define ITEM_CHECK_CHEST_PREFIX "chest:" /* : */ #define ITEM_CHECK_FREESTANDING_PREFIX "freestanding:" /* : */ +#define ITEM_CHECK_GOLDEN_WOLF_PREFIX "golden_wolf:" /* */ #define ITEM_CHECK_POE_PREFIX "poe:" /* : */ #define ITEM_CHECK_SHOP_PREFIX "shop:" /* : */ #define ITEM_CHECK_SKY_PREFIX "sky:" /* : */ diff --git a/src/d/actor/d_a_obj_life_container.cpp b/src/d/actor/d_a_obj_life_container.cpp index cd58c828dc..815d03f390 100644 --- a/src/d/actor/d_a_obj_life_container.cpp +++ b/src/d/actor/d_a_obj_life_container.cpp @@ -159,9 +159,19 @@ int daObjLife_c::create() { if (mItemOverridden) { fopAcM_SetParam(this, (params & 0xFFFFFF00) | resolvedItem); } + } 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); + } } else { mOriginalItemNo = mItemGiveOriginalNo; - mItemOverridden = parameterItemNo != mItemGiveOriginalNo; + mItemOverridden = parameterItemNo != mOriginalItemNo; } mOverrideHover = mItemOverridden && diff --git a/src/dusk/mods/item.hpp b/src/dusk/mods/item.hpp index 5577358f38..f27079427e 100644 --- a/src/dusk/mods/item.hpp +++ b/src/dusk/mods/item.hpp @@ -25,12 +25,14 @@ ItemCheckResult item_check_commit(uint32_t giveTag, uint8_t itemNo, fopAc_ac_c* 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_golden_wolf(uint16_t eventFlag, uint8_t itemNo, fopAc_ac_c* item); uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver); uint32_t item_give_tag(const char* name); uint32_t item_give_tag_chest(uint8_t boxNo); uint32_t item_give_tag_boss(); uint32_t item_give_tag_freestanding(uint8_t bitNo); +uint32_t item_give_tag_golden_wolf(uint16_t eventFlag); uint32_t item_give_tag_poe(uint8_t bitNo); uint32_t item_give_tag_shop(uint8_t itemNo); uint32_t item_give_tag_bug(uint8_t insectId); diff --git a/src/dusk/mods/item_checks.cpp b/src/dusk/mods/item_checks.cpp index 1684310c42..6327a07e0a 100644 --- a/src/dusk/mods/item_checks.cpp +++ b/src/dusk/mods/item_checks.cpp @@ -143,6 +143,10 @@ std::string freestanding_check_name(uint8_t bitNo) { return fmt::format("freestanding:{}:{}", current_stage_name(), bitNo); } +std::string golden_wolf_check_name(uint16_t eventFlag) { + return fmt::format("golden_wolf:{}", eventFlag); +} + std::string poe_check_name(uint8_t bitNo) { return fmt::format("poe:{}:{}", current_stage_name(), bitNo); } @@ -274,6 +278,14 @@ 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_golden_wolf(uint16_t eventFlag, uint8_t itemNo, fopAc_ac_c* item) { + if (s_modChecks.empty()) { + return itemNo; + } + const auto name = golden_wolf_check_name(eventFlag); + return item_check(name.c_str(), itemNo, item); +} + uint8_t item_check_shop(uint8_t itemNo, fopAc_ac_c* giver) { if (s_modChecks.empty()) { return itemNo; @@ -314,6 +326,10 @@ uint32_t item_give_tag_freestanding(uint8_t bitNo) { return item_give_tag(freestanding_check_name(bitNo).c_str()); } +uint32_t item_give_tag_golden_wolf(uint16_t eventFlag) { + return item_give_tag(golden_wolf_check_name(eventFlag).c_str()); +} + uint32_t item_give_tag_poe(uint8_t bitNo) { return item_give_tag(poe_check_name(bitNo).c_str()); }