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`
|
// - `*EnKz`
|
||||||
VB_KING_ZORA_TUNIC_CHECK,
|
VB_KING_ZORA_TUNIC_CHECK,
|
||||||
|
|
||||||
|
// #### `result`
|
||||||
|
// ```c
|
||||||
|
// varies
|
||||||
|
// ```
|
||||||
|
// #### `args`
|
||||||
|
// - `ObjLightswitch*`
|
||||||
|
VB_LIGHTSWITCH_OFF,
|
||||||
|
|
||||||
// #### `result`
|
// #### `result`
|
||||||
// ```c
|
// ```c
|
||||||
// true
|
// true
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "vt.h"
|
#include "vt.h"
|
||||||
#include "overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h"
|
#include "overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h"
|
||||||
#include "objects/object_lightswitch/object_lightswitch.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
|
#define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED
|
||||||
|
|
||||||
@@ -74,36 +74,6 @@ static ColliderJntSphInit sColliderJntSphInit = {
|
|||||||
1,
|
1,
|
||||||
sColliderJntSphElementInit,
|
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 };
|
static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_IMMOVABLE };
|
||||||
|
|
||||||
@@ -123,17 +93,8 @@ static InitChainEntry sInitChain[] = {
|
|||||||
void ObjLightswitch_InitCollider(ObjLightswitch* this, PlayState* play) {
|
void ObjLightswitch_InitCollider(ObjLightswitch* this, PlayState* play) {
|
||||||
s32 pad;
|
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);
|
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,
|
Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x,
|
||||||
this->actor.world.pos.y + (this->actor.shape.yOffset * this->actor.scale.y),
|
this->actor.world.pos.y + (this->actor.shape.yOffset * this->actor.scale.y),
|
||||||
this->actor.world.pos.z, &this->actor.shape.rot);
|
this->actor.world.pos.z, &this->actor.shape.rot);
|
||||||
@@ -249,26 +210,6 @@ void ObjLightswitch_Destroy(Actor* thisx, PlayState* play2) {
|
|||||||
PlayState* play = play2;
|
PlayState* play = play2;
|
||||||
ObjLightswitch* this = (ObjLightswitch*)thisx;
|
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);
|
Collider_DestroyJntSph(play, &this->collider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,9 +220,6 @@ void ObjLightswitch_SetupOff(ObjLightswitch* this) {
|
|||||||
this->color[1] = 125 << 6;
|
this->color[1] = 125 << 6;
|
||||||
this->color[2] = 255 << 6;
|
this->color[2] = 255 << 6;
|
||||||
this->alpha = 255 << 6;
|
this->alpha = 255 << 6;
|
||||||
if (sunLightArrowsEnabledOnSunSwitchLoad) {
|
|
||||||
sunSwitchActivatedByLightArrow = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// A Sun Switch that is currently turned off
|
// A Sun Switch that is currently turned off
|
||||||
void ObjLightswitch_Off(ObjLightswitch* this, PlayState* play) {
|
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) {
|
if (this->collider.base.acFlags & AC_HIT) {
|
||||||
ObjLightswitch_SetupTurnOn(this);
|
ObjLightswitch_SetupTurnOn(this);
|
||||||
ObjLightswitch_SetSwitchFlag(this, play);
|
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;
|
break;
|
||||||
case OBJLIGHTSWITCH_TYPE_1:
|
case OBJLIGHTSWITCH_TYPE_1:
|
||||||
@@ -364,33 +295,20 @@ void ObjLightswitch_On(ObjLightswitch* this, PlayState* play) {
|
|||||||
if (!Flags_GetSwitch(play, this->actor.params >> 8 & 0x3F)) {
|
if (!Flags_GetSwitch(play, this->actor.params >> 8 & 0x3F)) {
|
||||||
ObjLightswitch_SetupTurnOff(this);
|
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;
|
break;
|
||||||
case OBJLIGHTSWITCH_TYPE_1:
|
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_SetupTurnOff(this);
|
||||||
ObjLightswitch_ClearSwitchFlag(this, play);
|
ObjLightswitch_ClearSwitchFlag(this, play);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OBJLIGHTSWITCH_TYPE_2:
|
case OBJLIGHTSWITCH_TYPE_2:
|
||||||
// If hit by sunlight after already being turned on, then behave as if originally activated by sunlight
|
if (GameInteractor_Should(VB_LIGHTSWITCH_OFF, !(this->collider.base.acFlags & AC_HIT), this)) {
|
||||||
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 (this->timer >= 7) {
|
if (this->timer >= 7) {
|
||||||
// If we aren't using Enhanced Light Arrows, let the switch turn off normally
|
|
||||||
if (!sunSwitchActivatedByLightArrow) {
|
|
||||||
ObjLightswitch_SetupTurnOff(this);
|
ObjLightswitch_SetupTurnOff(this);
|
||||||
ObjLightswitch_ClearSwitchFlag(this, play);
|
ObjLightswitch_ClearSwitchFlag(this, play);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this->timer++;
|
this->timer++;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user