Fix Golden Wolf item persistence

This commit is contained in:
Luke Street
2026-08-21 20:45:43 -06:00
parent 42805210df
commit 8f5ecdba1c
7 changed files with 82 additions and 14 deletions
+1
View File
@@ -92,6 +92,7 @@ public:
u8 mOriginalItemNo;
bool mItemOverridden;
bool mOverrideHover;
bool mGoldenWolfItem;
#endif
};
+35 -13
View File
@@ -11,6 +11,7 @@
#include <mods/svc/hook.hpp>
#include <mods/svc/log.hpp>
#include <mods/items.h>
#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<int*>(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<s16>(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<int*>(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<daObjLife_c*>(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<u16>(i_this->home.angle.z) != 0xFFFF) {
dComIfGs_offSwitch(static_cast<u16>(i_this->home.angle.z), fopAcM_GetRoomNo(i_this));
if (!i_this->mGoldenWolfItem || !dComIfGp_evmng_endCheck("DEFAULT_GETITEM"))
{
return HOOK_CONTINUE;
}
if (static_cast<u16>(i_this->home.angle.x) != 0xFFFF) {
dComIfGs_onEventBit(static_cast<u16>(i_this->home.angle.x));
const u16 mapMarkerFlag = static_cast<u16>(i_this->field_0x93a);
const u16 obtainedItemFlag = static_cast<u16>(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);
+16
View File
@@ -70,6 +70,19 @@ std::optional<DerivedKey> parse_derived(const char* name, std::string_view prefi
return DerivedKey{stage_id, static_cast<u16>((stage_id << 8) | (n & 0xFF))};
}
std::optional<u16> 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<u16>(flag);
}
template <typename Map>
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);
}
+1
View File
@@ -8,6 +8,7 @@
#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_GOLDEN_WOLF_PREFIX "golden_wolf:" /* <event_flag> */
#define ITEM_CHECK_POE_PREFIX "poe:" /* <stage>:<switch> */
#define ITEM_CHECK_SHOP_PREFIX "shop:" /* <stage>:<item> */
#define ITEM_CHECK_SKY_PREFIX "sky:" /* <stage>:<room> */
+11 -1
View File
@@ -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<u16>(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 &&
+2
View File
@@ -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);
+16
View File
@@ -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());
}