Hookify Sunlight Arrows (#6366)
This commit is contained in:
97
soh/soh/Enhancements/SunlightArrows.cpp
Normal file
97
soh/soh/Enhancements/SunlightArrows.cpp
Normal 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") });
|
||||
@@ -1323,6 +1323,14 @@ typedef enum {
|
||||
// - `*EnKz`
|
||||
VB_KING_ZORA_TUNIC_CHECK,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// varies
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `ObjLightswitch*`
|
||||
VB_LIGHTSWITCH_OFF,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user