mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-09-29 03:17:52 -04:00
Add restoration for hookshot as child to softlock (#7219)
Requested for speedrun mode Also hookify more enemy rando stuff while I'm in the neighbourhood
This commit is contained in:
@@ -54,7 +54,8 @@ void RegisterAlwaysOnFixes() {
|
||||
// spawn (Player_InitItemAction removes the ranged weapon state elsewhere).
|
||||
COND_VB_SHOULD(VB_INIT_HOOKSHOT_IA, true, {
|
||||
Player* player = va_arg(args, Player*);
|
||||
if (player->heldActor == NULL) {
|
||||
if (player->heldActor == NULL && !(CVarGetInteger(CVAR_ENHANCEMENT("ChildHookshotSoftlock"), 0) &&
|
||||
Object_GetIndex(&gPlayState->objectCtx, OBJECT_LINK_BOY) < 0)) {
|
||||
Player_UseItem(gPlayState, player, 0xFF);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ extern "C" {
|
||||
#include "src/overlays/actors/ovl_En_Tp/z_en_tp.h"
|
||||
|
||||
extern PlayState* gPlayState;
|
||||
extern int gMapLoading;
|
||||
}
|
||||
|
||||
namespace SohGui {
|
||||
@@ -745,6 +746,26 @@ void RegisterEnemyRandomizer() {
|
||||
// If Random Gerudo Fighters are defeated, drop some items
|
||||
COND_ID_HOOK(OnEnemyDefeat, ACTOR_EN_GELDB, ENEMY_RANDOMIZER_ENABLED, OnGerudoFighterDefeat);
|
||||
|
||||
// Spawn random enemies even if their object isn't loaded
|
||||
COND_VB_SHOULD(VB_SPAWN_ACTOR_WITHOUT_OBJECT, ENEMY_RANDOMIZER_ENABLED, {
|
||||
if (gMapLoading) {
|
||||
*should = true;
|
||||
}
|
||||
});
|
||||
|
||||
// The following enemies break when the parent actor isn't the same as what would happen in authentic gameplay.
|
||||
// As such, don't assign a parent to them at all when spawned with Enemy Randomizer.
|
||||
// Gohma (z_boss_goma.c) and the falling platform spawning Stalfos in
|
||||
// Forest Temple (z_bg_mori_bigst.c) that normally rely on this behaviour are changed when
|
||||
// Enemy Rando is on so they still work properly even without assigning a parent.
|
||||
COND_VB_SHOULD(VB_SET_CHILD_ACTOR_PARENT, ENEMY_RANDOMIZER_ENABLED, {
|
||||
Actor* spawnedActor = va_arg(args, Actor*);
|
||||
|
||||
if (spawnedActor->id == ACTOR_EN_FLOORMAS || spawnedActor->id == ACTOR_EN_PEEHAT) {
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
COND_VB_SHOULD(VB_SPAWN_ACTOR_ENTRY, ENEMY_RANDOMIZER_ENABLED, {
|
||||
ActorContext* actorCtx = va_arg(args, ActorContext*);
|
||||
ActorEntry* actorEntry = va_arg(args, ActorEntry*);
|
||||
@@ -1209,5 +1230,19 @@ void RegisterEnemyRandomizerWidgets() {
|
||||
}
|
||||
}
|
||||
|
||||
// Remove object dependency for Enemy Randomizer and Crowd Control to allow Like-likes to
|
||||
// drop equipment correctly in rooms where Like-likes normally don't spawn.
|
||||
static void RegisterItem00WithoutObject() {
|
||||
bool required = ENEMY_RANDOMIZER_ENABLED || CVarGetInteger(CVAR_REMOTE_CROWD_CONTROL("Enabled"), 0);
|
||||
COND_VB_SHOULD(VB_ITEM00_REQUIRE_OBJECT, required, {
|
||||
EnItem00* item = va_arg(args, EnItem00*);
|
||||
|
||||
*should = false;
|
||||
item->actor.objBankIndex = 0;
|
||||
});
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME });
|
||||
static RegisterShipInitFunc initFuncItem00(RegisterItem00WithoutObject,
|
||||
{ CVAR_ENEMY_RANDOMIZER_NAME, CVAR_REMOTE_CROWD_CONTROL("Enabled") });
|
||||
static RegisterMenuInitFunc menuInitFunc(RegisterEnemyRandomizerWidgets);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <libultraship/bridge/consolevariablebridge.h>
|
||||
|
||||
#include "soh/cvar_prefixes.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "z64actor_enum.h"
|
||||
}
|
||||
|
||||
static constexpr const char* CVAR_CHILD_HOOKSHOT_SOFTLOCK = CVAR_ENHANCEMENT("ChildHookshotSoftlock");
|
||||
|
||||
// As child object_link_boy isn't loaded, so vanilla fails to spawn the hookshot and Link is stuck holding it
|
||||
static void RegisterChildHookshotSoftlock() {
|
||||
COND_VB_SHOULD(VB_SPAWN_ACTOR_WITHOUT_OBJECT, CVarGetInteger(CVAR_CHILD_HOOKSHOT_SOFTLOCK, 0), {
|
||||
s16 actorId = va_arg(args, int);
|
||||
if (actorId == ACTOR_ARMS_HOOK) {
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initFunc(RegisterChildHookshotSoftlock, { CVAR_CHILD_HOOKSHOT_SOFTLOCK });
|
||||
@@ -1643,6 +1643,14 @@ typedef enum {
|
||||
// - `*EnItem00`
|
||||
VB_ITEM00_DESPAWN,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*EnItem00`
|
||||
VB_ITEM00_REQUIRE_OBJECT,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// this->unk_15A > 0
|
||||
@@ -2741,6 +2749,14 @@ typedef enum {
|
||||
// - `int32_t` (button - promoted from `s16`)
|
||||
VB_SET_BUTTON_ITEM_FROM_C_BUTTON_SLOT,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*Actor` (spawnedActor)
|
||||
VB_SET_CHILD_ACTOR_PARENT,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
@@ -2968,6 +2984,14 @@ typedef enum {
|
||||
// - None
|
||||
VB_SKIP_SCARECROWS_SONG,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// !gMapLoading
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `s16` (actorId)
|
||||
VB_SPAWN_ACTOR_WITHOUT_OBJECT,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
|
||||
@@ -5,15 +5,18 @@
|
||||
#include "soh_assets.h"
|
||||
#include <libultraship/bridge/consolevariablebridge.h>
|
||||
#include "spdlog/common.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
extern "C" {
|
||||
#include "z64.h"
|
||||
#include "macros.h"
|
||||
#include "functions.h"
|
||||
|
||||
extern float OTRGetAspectRatio();
|
||||
|
||||
extern f32 sFontWidths[144];
|
||||
extern const char* fontTbl[140];
|
||||
extern int gMapLoading;
|
||||
}
|
||||
|
||||
extern std::string Ship_FormatTimeDisplay(uint32_t value) {
|
||||
@@ -59,6 +62,16 @@ extern "C" void Ship_ExtendedCullingActorAdjustProjectedX(Actor* actor) {
|
||||
// Actor_GetProjectedPos(play, &actor->world.pos, &actor->projectedPos, &invW);
|
||||
//}
|
||||
|
||||
// Vanilla fails to spawn an actor whose object isn't loaded
|
||||
// SoH instead gives it gameplay_keep's slot, except while the room's actors are loading
|
||||
extern "C" s32 Ship_GetActorSpawnObjectIndex(PlayState* play, s16 objectId, s16 actorId) {
|
||||
s32 objBankIndex = Object_GetIndex(&play->objectCtx, objectId);
|
||||
if (objBankIndex < 0 && GameInteractor_Should(VB_SPAWN_ACTOR_WITHOUT_OBJECT, !gMapLoading, actorId)) {
|
||||
return 0;
|
||||
}
|
||||
return objBankIndex;
|
||||
}
|
||||
|
||||
extern "C" bool Ship_IsCStringEmpty(const char* str) {
|
||||
return str == NULL || str[0] == '\0';
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ void Ship_ExtendedCullingActorAdjustProjectedZ(Actor* actor);
|
||||
void Ship_ExtendedCullingActorAdjustProjectedX(Actor* actor);
|
||||
void Ship_ExtendedCullingActorRestoreProjectedPos(PlayState* play, Actor* actor);
|
||||
|
||||
s32 Ship_GetActorSpawnObjectIndex(PlayState* play, s16 objectId, s16 actorId);
|
||||
|
||||
bool Ship_IsCStringEmpty(const char* str);
|
||||
void Ship_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH);
|
||||
f32 Ship_GetCharFontWidth(u8 character);
|
||||
|
||||
@@ -1381,6 +1381,9 @@ void SohMenu::AddMenuEnhancements() {
|
||||
.Options(CheckboxOptions().Tooltip(
|
||||
"Restores a bug from NTSC 1.0/1.1 that allows you to obtain the eyeball frog from King Zora "
|
||||
"instead of the Zora Tunic by Holding Shield."));
|
||||
AddWidget(path, "Child Hookshot Softlock", WIDGET_CVAR_CHECKBOX)
|
||||
.CVar(CVAR_ENHANCEMENT("ChildHookshotSoftlock"))
|
||||
.Options(CheckboxOptions().Tooltip("Using the Hookshot as child softlocks."));
|
||||
AddWidget(path, "Get Item Manipulation", WIDGET_CVAR_COMBOBOX)
|
||||
.CVar(CVAR_ENHANCEMENT("GetItemManipulation"))
|
||||
.Options(ComboboxOptions()
|
||||
|
||||
+3
-12
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "soh/ActorDB.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/ShipUtils.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -3346,11 +3347,7 @@ Actor* Actor_Spawn(ActorContext* actorCtx, PlayState* play, s16 actorId, f32 pos
|
||||
return NULL;
|
||||
}
|
||||
|
||||
objBankIndex = Object_GetIndex(&gPlayState->objectCtx, dbEntry->objectId);
|
||||
|
||||
if (objBankIndex < 0 && (!gMapLoading || CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0))) {
|
||||
objBankIndex = 0;
|
||||
}
|
||||
objBankIndex = Ship_GetActorSpawnObjectIndex(play, dbEntry->objectId, actorId);
|
||||
|
||||
if ((objBankIndex < 0) ||
|
||||
((dbEntry->category == ACTORCAT_ENEMY) && Flags_GetClear(play, play->roomCtx.curRoom.num))) {
|
||||
@@ -3428,13 +3425,7 @@ Actor* Actor_SpawnAsChild(ActorContext* actorCtx, Actor* parent, PlayState* play
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The following enemies break when the parent actor isn't the same as what would happen in authentic gameplay.
|
||||
// As such, don't assign a parent to them at all when spawned with Enemy Randomizer.
|
||||
// Gohma (z_boss_goma.c) and the falling platform spawning Stalfos in
|
||||
// Forest Temple (z_bg_mori_bigst.c) that normally rely on this behaviour are changed when
|
||||
// Enemy Rando is on so they still work properly even without assigning a parent.
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0) &&
|
||||
(spawnedActor->id == ACTOR_EN_FLOORMAS || spawnedActor->id == ACTOR_EN_PEEHAT)) {
|
||||
if (!GameInteractor_Should(VB_SET_CHILD_ACTOR_PARENT, true, spawnedActor)) {
|
||||
return spawnedActor;
|
||||
}
|
||||
|
||||
|
||||
@@ -334,12 +334,7 @@ void EnItem00_SetupAction(EnItem00* this, EnItem00ActionFunc actionFunc) {
|
||||
}
|
||||
|
||||
void EnItem00_SetObjectDependency(EnItem00* this, PlayState* play, s16 objectIndex) {
|
||||
// Remove object dependency for Enemy Randomizer and Crowd Control to allow Like-likes to
|
||||
// drop equipment correctly in rooms where Like-likes normally don't spawn.
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0) ||
|
||||
(CVarGetInteger(CVAR_REMOTE_CROWD_CONTROL("Enabled"), 0))) {
|
||||
this->actor.objBankIndex = 0;
|
||||
} else {
|
||||
if (GameInteractor_Should(VB_ITEM00_REQUIRE_OBJECT, true, this)) {
|
||||
this->actor.objBankIndex = Object_GetIndex(&play->objectCtx, objectIndex);
|
||||
Actor_SetObjectDependency(play, &this->actor);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user