Hookify Sunlight Arrows (#6366)

This commit is contained in:
Philip Dubé
2026-03-18 16:23:33 +00:00
committed by GitHub
parent d7b4e5a24d
commit 1f57f72acd
3 changed files with 113 additions and 90 deletions

View File

@@ -0,0 +1,97 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/Enhancements/randomizer/SeedContext.h"
#include "soh/ShipInit.hpp"
#include "soh/ObjectExtension/ObjectExtension.h"
extern "C" {
#include "overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.h"
}
static ColliderJntSphElementInit sColliderLightArrowElementInit[] = {
{
{
ELEMTYPE_UNK0,
{ 0x00000000, 0x00, 0x00 },
{ 0x00202000, 0x00, 0x00 },
TOUCH_NONE,
BUMP_ON,
OCELEM_ON,
},
{ 0, { { 0, 0, 0 }, 19 }, 100 },
},
};
static ColliderJntSphInit sColliderLightArrowInit = {
{
COLTYPE_NONE,
AT_NONE,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_2,
COLSHAPE_JNTSPH,
},
1,
sColliderLightArrowElementInit,
};
struct SunlightArrowData {
bool activatedByLightArrow = false;
};
static ObjectExtension::Register<SunlightArrowData> SunlightArrowDataRegister;
void RegisterSunlightArrowsHooks() {
bool shouldRegister =
CVarGetInteger(CVAR_ENHANCEMENT("SunlightArrows"), 0) || (IS_RANDO && RAND_GET_OPTION(RSK_SUNLIGHT_ARROWS));
COND_ID_HOOK(OnActorInit, ACTOR_OBJ_LIGHTSWITCH, shouldRegister, [](void* actor) {
auto* thisx = (ObjLightswitch*)actor;
Collider_SetJntSph(gPlayState, &thisx->collider, &thisx->actor, &sColliderLightArrowInit, thisx->colliderItems);
Collider_UpdateSpheres(0, &thisx->collider);
});
COND_ID_HOOK(OnActorDestroy, ACTOR_OBJ_LIGHTSWITCH, shouldRegister, [](void* actor) {
auto* thisx = (ObjLightswitch*)actor;
auto sunData = ObjectExtension::GetInstance().Get<SunlightArrowData>(&thisx->actor);
if (sunData != nullptr && sunData->activatedByLightArrow) {
switch (thisx->actor.params >> 4 & 3) {
case OBJLIGHTSWITCH_TYPE_STAY_ON:
case OBJLIGHTSWITCH_TYPE_2:
case OBJLIGHTSWITCH_TYPE_1:
// Unset the switch flag on room exit to prevent the rock in the wall from
// vanishing on its own after activating the sun switch by Light Arrow
// Also prevents the cobra mirror from rotating to face the sun on its own
// Makes sun switches temporary when activated by Light Arrows (will turn off on room exit)
if (thisx->actor.room != 25) {
Flags_UnsetSwitch(gPlayState, thisx->actor.params >> 8 & 0x3F);
}
break;
case OBJLIGHTSWITCH_TYPE_BURN:
break;
}
}
});
COND_VB_SHOULD(VB_LIGHTSWITCH_OFF, shouldRegister, {
ObjLightswitch* thisx = va_arg(args, ObjLightswitch*);
auto sunData = ObjectExtension::GetInstance().Get<SunlightArrowData>(&thisx->actor);
if (sunData != nullptr && sunData->activatedByLightArrow) {
*should = false;
}
});
COND_ID_HOOK(ShouldActorUpdate, ACTOR_OBJ_LIGHTSWITCH, shouldRegister, [](void* actorPtr, bool* result) {
ObjLightswitch* thisx = (ObjLightswitch*)actorPtr;
if ((thisx->collider.base.acFlags & AC_HIT) && thisx->collider.base.ac != nullptr) {
auto sunData = ObjectExtension::GetInstance().Get<SunlightArrowData>(&thisx->actor);
if (sunData == nullptr) {
ObjectExtension::GetInstance().Set(&thisx->actor, SunlightArrowData{});
sunData = ObjectExtension::GetInstance().Get<SunlightArrowData>(&thisx->actor);
}
sunData->activatedByLightArrow = thisx->collider.base.ac->id == ACTOR_EN_ARROW;
}
});
}
static RegisterShipInitFunc initFunc(RegisterSunlightArrowsHooks, { "IS_RANDO", CVAR_ENHANCEMENT("SunlightArrows") });

View File

@@ -1323,6 +1323,14 @@ typedef enum {
// - `*EnKz`
VB_KING_ZORA_TUNIC_CHECK,
// #### `result`
// ```c
// varies
// ```
// #### `args`
// - `ObjLightswitch*`
VB_LIGHTSWITCH_OFF,
// #### `result`
// ```c
// true

View File

@@ -8,7 +8,7 @@
#include "vt.h"
#include "overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h"
#include "objects/object_lightswitch/object_lightswitch.h"
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED
@@ -74,36 +74,6 @@ static ColliderJntSphInit sColliderJntSphInit = {
1,
sColliderJntSphElementInit,
};
// Collider info used for "Sunlight Arrows"
static ColliderJntSphElementInit sColliderLightArrowElementInit[] = {
{
{
ELEMTYPE_UNK0,
{ 0x00000000, 0x00, 0x00 },
{ 0x00202000, 0x00, 0x00 },
TOUCH_NONE,
BUMP_ON,
OCELEM_ON,
},
{ 0, { { 0, 0, 0 }, 19 }, 100 },
},
};
// Sphere collider used for "Sunlight Arrows"
static ColliderJntSphInit sColliderLightArrowInit = {
{
COLTYPE_NONE,
AT_NONE,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_2,
COLSHAPE_JNTSPH,
},
1,
sColliderLightArrowElementInit,
};
bool sunSwitchActivatedByLightArrow = false;
bool sunLightArrowsEnabledOnSunSwitchLoad = false;
static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_IMMOVABLE };
@@ -123,17 +93,8 @@ static InitChainEntry sInitChain[] = {
void ObjLightswitch_InitCollider(ObjLightswitch* this, PlayState* play) {
s32 pad;
// Initialize this with the sun switch, so it can't be affected by toggling while the actor is loaded
sunLightArrowsEnabledOnSunSwitchLoad = CVarGetInteger(CVAR_ENHANCEMENT("SunlightArrows"), 0) ||
(IS_RANDO && Randomizer_GetSettingValue(RSK_SUNLIGHT_ARROWS));
Collider_InitJntSph(play, &this->collider);
// If "Sunlight Arrows" is enabled, set up the collider to allow Light Arrow hits
if (sunLightArrowsEnabledOnSunSwitchLoad) {
Collider_SetJntSph(play, &this->collider, &this->actor, &sColliderLightArrowInit, this->colliderItems);
} else {
Collider_SetJntSph(play, &this->collider, &this->actor, &sColliderJntSphInit, this->colliderItems);
}
Collider_SetJntSph(play, &this->collider, &this->actor, &sColliderJntSphInit, this->colliderItems);
Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x,
this->actor.world.pos.y + (this->actor.shape.yOffset * this->actor.scale.y),
this->actor.world.pos.z, &this->actor.shape.rot);
@@ -249,26 +210,6 @@ void ObjLightswitch_Destroy(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
ObjLightswitch* this = (ObjLightswitch*)thisx;
// Unset the switch flag on room exit to prevent the rock in the wall from
// vanishing on its own after activating the sun switch by Light Arrow
// Also prevents the cobra mirror from rotating to face the sun on its own
// Makes sun switches temporary when activated by Light Arrows (will turn off on room exit)
if (sunSwitchActivatedByLightArrow) {
switch (this->actor.params >> 4 & 3) {
case OBJLIGHTSWITCH_TYPE_STAY_ON:
case OBJLIGHTSWITCH_TYPE_2:
case OBJLIGHTSWITCH_TYPE_1:
// Except for this one, because we want the chain platform to stay down for good
if (this->actor.room != 25) {
Flags_UnsetSwitch(play, this->actor.params >> 8 & 0x3F);
}
sunSwitchActivatedByLightArrow = false;
break;
case OBJLIGHTSWITCH_TYPE_BURN:
break;
}
}
Collider_DestroyJntSph(play, &this->collider);
}
@@ -279,9 +220,6 @@ void ObjLightswitch_SetupOff(ObjLightswitch* this) {
this->color[1] = 125 << 6;
this->color[2] = 255 << 6;
this->alpha = 255 << 6;
if (sunLightArrowsEnabledOnSunSwitchLoad) {
sunSwitchActivatedByLightArrow = false;
}
}
// A Sun Switch that is currently turned off
void ObjLightswitch_Off(ObjLightswitch* this, PlayState* play) {
@@ -291,13 +229,6 @@ void ObjLightswitch_Off(ObjLightswitch* this, PlayState* play) {
if (this->collider.base.acFlags & AC_HIT) {
ObjLightswitch_SetupTurnOn(this);
ObjLightswitch_SetSwitchFlag(this, play);
// Remember if we've been activated by a Light Arrow, so we can
// prevent the switch from immediately turning back off
if (sunLightArrowsEnabledOnSunSwitchLoad) {
if (this->collider.base.ac != NULL && this->collider.base.ac->id == ACTOR_EN_ARROW) {
sunSwitchActivatedByLightArrow = true;
}
}
}
break;
case OBJLIGHTSWITCH_TYPE_1:
@@ -364,33 +295,20 @@ void ObjLightswitch_On(ObjLightswitch* this, PlayState* play) {
if (!Flags_GetSwitch(play, this->actor.params >> 8 & 0x3F)) {
ObjLightswitch_SetupTurnOff(this);
}
// If hit by sunlight after already being turned on, then behave as if originally activated by sunlight
if (sunLightArrowsEnabledOnSunSwitchLoad && (this->collider.base.acFlags & AC_HIT)) {
if (this->collider.base.ac != NULL && this->collider.base.ac->id != ACTOR_EN_ARROW) {
sunSwitchActivatedByLightArrow = false;
}
}
break;
case OBJLIGHTSWITCH_TYPE_1:
if (this->collider.base.acFlags & AC_HIT && !(this->prevFrameACflags & AC_HIT)) {
if (GameInteractor_Should(VB_LIGHTSWITCH_OFF,
this->collider.base.acFlags & AC_HIT && !(this->prevFrameACflags & AC_HIT),
this)) {
ObjLightswitch_SetupTurnOff(this);
ObjLightswitch_ClearSwitchFlag(this, play);
}
break;
case OBJLIGHTSWITCH_TYPE_2:
// If hit by sunlight after already being turned on, then behave as if originally activated by sunlight
if (sunLightArrowsEnabledOnSunSwitchLoad && (this->collider.base.acFlags & AC_HIT)) {
if (this->collider.base.ac != NULL && this->collider.base.ac->id != ACTOR_EN_ARROW) {
sunSwitchActivatedByLightArrow = false;
}
}
if (!(this->collider.base.acFlags & AC_HIT)) {
if (GameInteractor_Should(VB_LIGHTSWITCH_OFF, !(this->collider.base.acFlags & AC_HIT), this)) {
if (this->timer >= 7) {
// If we aren't using Enhanced Light Arrows, let the switch turn off normally
if (!sunSwitchActivatedByLightArrow) {
ObjLightswitch_SetupTurnOff(this);
ObjLightswitch_ClearSwitchFlag(this, play);
}
ObjLightswitch_SetupTurnOff(this);
ObjLightswitch_ClearSwitchFlag(this, play);
} else {
this->timer++;
}