diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 6b10cf944..c35326f6a 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -336,6 +336,31 @@ void RegisterRupeeDash() { }); } +void RegisterShadowTag() { + static bool shouldSpawn = false; + static uint16_t delayTimer = 60; + + GameInteractor::Instance->RegisterGameHook([]() { + if (!CVarGetInteger("gShadowTag", 0)) { + return; + } + if (shouldSpawn && (delayTimer <= 0)) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_WALLMAS, 0, 0, 0, 0, 0, 0, 3, false); + shouldSpawn = false; + } else { + delayTimer--; + } + }); + GameInteractor::Instance->RegisterGameHook([]() { + shouldSpawn = true; + delayTimer = 60; + }); + GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { + shouldSpawn = true; + delayTimer = 60; + }); +} + struct DayTimeGoldSkulltulas { uint16_t scene; uint16_t room; @@ -533,6 +558,7 @@ void InitMods() { RegisterAutoSave(); RegisterDaytimeGoldSkultullas(); RegisterRupeeDash(); + RegisterShadowTag(); RegisterHyperBosses(); RegisterHyperEnemies(); RegisterBonkDamage(); diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index 185258390..be5193eb7 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -981,6 +981,14 @@ namespace GameMenuBar { UIWidgets::Tooltip("Interval between Rupee reduction in Rupee Dash Mode"); } + UIWidgets::Spacer(0); + + UIWidgets::PaddedEnhancementCheckbox("Shadow Tag Mode", "gShadowTag", true, false); + + if (CVarGetInteger("gShadowTag", 0)) { + UIWidgets::Tooltip("A wallmaster follows Link everywhere, don't get caught!"); + } + ImGui::EndMenu(); } diff --git a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c index c015413db..7c454ea2c 100644 --- a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c +++ b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c @@ -130,12 +130,16 @@ void EnWallmas_Init(Actor* thisx, PlayState* play) { this->switchFlag = (u8)(thisx->params >> 0x8); thisx->params = thisx->params & 0xFF; + if (this->actor.params == WMT_SHADOWTAG) { + Actor_ChangeCategory(play, &play->actorCtx, this, ACTORCAT_NPC); + } + if (thisx->params == WMT_FLAG) { if (Flags_GetSwitch(play, this->switchFlag) != 0) { Actor_Kill(thisx); return; } - + EnWallmas_ProximityOrSwitchInit(this); } else if (thisx->params == WMT_PROXIMITY) { EnWallmas_ProximityOrSwitchInit(this); @@ -273,7 +277,7 @@ void EnWallmas_ProximityOrSwitchInit(EnWallmas* this) { this->timer = 0; this->actor.draw = NULL; this->actor.flags &= ~ACTOR_FLAG_0; - if (this->actor.params == WMT_PROXIMITY) { + if (this->actor.params == WMT_PROXIMITY || this->actor.params == WMT_SHADOWTAG) { this->actionFunc = EnWallmas_WaitForProximity; } else { this->actionFunc = EnWallmas_WaitForSwitchFlag; @@ -307,11 +311,16 @@ void EnWallmas_WaitToDrop(EnWallmas* this, PlayState* play) { this->timer--; } - if ((player->stateFlags1 & 0x100000) || (player->stateFlags1 & 0x8000000) || !(player->actor.bgCheckFlags & 1) || + if (this->actor.params == WMT_SHADOWTAG) { + if ((player->stateFlags1 & 0x100000) || (player->stateFlags1 & 0x8000000) || !(player->actor.bgCheckFlags & 1)) { + Audio_StopSfxById(NA_SE_EN_FALL_AIM); + this->timer = 0x82; + } + } else if ((player->stateFlags1 & 0x100000) || (player->stateFlags1 & 0x8000000) || !(player->actor.bgCheckFlags & 1) || ((this->actor.params == 1) && (320.0f < Math_Vec3f_DistXZ(&this->actor.home.pos, playerPos)))) { Audio_StopSfxById(NA_SE_EN_FALL_AIM); this->timer = 0x82; - } + } if (this->timer == 0x50) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_AIM); @@ -390,6 +399,11 @@ void EnWallmas_ReturnToCeiling(EnWallmas* this, PlayState* play) { EnWallmas_ProximityOrSwitchInit(this); } } + if (this->actor.params == WMT_SHADOWTAG) { + if (!CVarGetInteger("gShadowTag", 0)) { + Actor_Kill(&this->actor); + } + } } void EnWallmas_TakeDamage(EnWallmas* this, PlayState* play) { @@ -419,6 +433,13 @@ void EnWallmas_Die(EnWallmas* this, PlayState* play) { Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xC0); Actor_Kill(&this->actor); } + if (this->actor.params == WMT_SHADOWTAG) { + if (!CVarGetInteger("gShadowTag", 0)) { + Actor_Kill(&this->actor); + } else { + EnWallmas_Init(this, play); + } + } this->actor.scale.z = this->actor.scale.x; this->actor.scale.y = this->actor.scale.x; } @@ -478,7 +499,8 @@ void EnWallmas_TakePlayer(EnWallmas* this, PlayState* play) { void EnWallmas_WaitForProximity(EnWallmas* this, PlayState* play) { Player* player = GET_PLAYER(play); - if (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < 200.0f) { + if (this->actor.params == WMT_SHADOWTAG || + Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < 200.0f) { EnWallmas_TimerInit(this, play); } } diff --git a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.h b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.h index 33e51df60..d4a5f68fb 100644 --- a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.h +++ b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.h @@ -7,7 +7,8 @@ typedef enum { /* 0x00 */ WMT_TIMER, /* 0x01 */ WMT_PROXIMITY, - /* 0x02 */ WMT_FLAG + /* 0x02 */ WMT_FLAG, + /* 0x03 */ WMT_SHADOWTAG } WallmasType; struct EnWallmas;