1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2024-11-25 09:45:02 +00:00

Document Hookshot Attachment (#2300)

* document hookshot attachment

* change comment

* swap hookshot function comment

* remove comments
This commit is contained in:
fig02 2024-11-21 19:46:49 -05:00 committed by GitHub
parent e0e0e93644
commit 6239f8e0b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 83 additions and 59 deletions

View file

@ -153,8 +153,13 @@ typedef struct ActorShape {
// Actor will not shake when a quake occurs // Actor will not shake when a quake occurs
#define ACTOR_FLAG_IGNORE_QUAKE (1 << 12) #define ACTOR_FLAG_IGNORE_QUAKE (1 << 12)
// The hookshot is currently attached to this actor.
// The behavior that occurs after attachment is determined by `ACTOR_FLAG_9` and `ACTOR_FLAG_10`.
// If neither of those flags are set attachment cannot occur, and the hookshot will simply act as a damage source.
// //
#define ACTOR_FLAG_13 (1 << 13) // This flag is also reused to indicate that an actor is attached to the boomerang.
// This only has an effect for Gold Skulltula Tokens (EN_SI) which has overlapping behavior for hookshot and boomerang
#define ACTOR_FLAG_HOOKSHOT_ATTACHED (1 << 13)
// When hit by an arrow, the actor will be able to attach to the arrow and fly with it in the air // When hit by an arrow, the actor will be able to attach to the arrow and fly with it in the air
#define ACTOR_FLAG_CAN_ATTACH_TO_ARROW (1 << 14) #define ACTOR_FLAG_CAN_ATTACH_TO_ARROW (1 << 14)
@ -806,7 +811,7 @@ int func_8002DD6C(struct Player* player);
int func_8002DD78(struct Player* player); int func_8002DD78(struct Player* player);
s32 func_8002DDE4(struct PlayState* play); s32 func_8002DDE4(struct PlayState* play);
s32 func_8002DDF4(struct PlayState* play); s32 func_8002DDF4(struct PlayState* play);
void func_8002DE04(struct PlayState* play, Actor* actorA, Actor* actorB); void Actor_SwapHookshotAttachment(struct PlayState* play, Actor* srcActor, Actor* destActor);
void func_8002DE74(struct PlayState* play, struct Player* player); void func_8002DE74(struct PlayState* play, struct Player* player);
void Actor_MountHorse(struct PlayState* play, struct Player* player, Actor* horse); void Actor_MountHorse(struct PlayState* play, struct Player* player, Actor* horse);
int func_8002DEEC(struct Player* player); int func_8002DEEC(struct Player* player);

View file

@ -1119,15 +1119,24 @@ s32 func_8002DDF4(PlayState* play) {
return player->stateFlags2 & PLAYER_STATE2_12; return player->stateFlags2 & PLAYER_STATE2_12;
} }
void func_8002DE04(PlayState* play, Actor* actorA, Actor* actorB) { /**
* Swap hookshot attachment state from one actor to another.
*
* Note: There is no safety check for a NULL hookshot pointer.
* The responsibility is on the caller to make sure the hookshot exists.
*/
void Actor_SwapHookshotAttachment(PlayState* play, Actor* srcActor, Actor* destActor) {
ArmsHook* hookshot = (ArmsHook*)Actor_Find(&play->actorCtx, ACTOR_ARMS_HOOK, ACTORCAT_ITEMACTION); ArmsHook* hookshot = (ArmsHook*)Actor_Find(&play->actorCtx, ACTOR_ARMS_HOOK, ACTORCAT_ITEMACTION);
hookshot->grabbed = actorB; hookshot->attachedActor = destActor;
hookshot->grabbedDistDiff.x = 0.0f;
hookshot->grabbedDistDiff.y = 0.0f; // The hookshot will attach at exactly the actors world position with 0 offset
hookshot->grabbedDistDiff.z = 0.0f; hookshot->attachPointOffset.x = 0.0f;
actorB->flags |= ACTOR_FLAG_13; hookshot->attachPointOffset.y = 0.0f;
actorA->flags &= ~ACTOR_FLAG_13; hookshot->attachPointOffset.z = 0.0f;
destActor->flags |= ACTOR_FLAG_HOOKSHOT_ATTACHED;
srcActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
} }
void func_8002DE74(PlayState* play, Player* player) { void func_8002DE74(PlayState* play, Player* player) {

View file

@ -74,9 +74,10 @@ void ArmsHook_Init(Actor* thisx, PlayState* play) {
void ArmsHook_Destroy(Actor* thisx, PlayState* play) { void ArmsHook_Destroy(Actor* thisx, PlayState* play) {
ArmsHook* this = (ArmsHook*)thisx; ArmsHook* this = (ArmsHook*)thisx;
if (this->grabbed != NULL) { if (this->attachedActor != NULL) {
this->grabbed->flags &= ~ACTOR_FLAG_13; this->attachedActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
} }
Collider_DestroyQuad(play, &this->collider); Collider_DestroyQuad(play, &this->collider);
} }
@ -109,10 +110,10 @@ s32 ArmsHook_AttachToPlayer(ArmsHook* this, Player* player) {
return false; return false;
} }
void ArmsHook_DetachHookFromActor(ArmsHook* this) { void ArmsHook_DetachFromActor(ArmsHook* this) {
if (this->grabbed != NULL) { if (this->attachedActor != NULL) {
this->grabbed->flags &= ~ACTOR_FLAG_13; this->attachedActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
this->grabbed = NULL; this->attachedActor = NULL;
} }
} }
@ -123,7 +124,7 @@ s32 ArmsHook_CheckForCancel(ArmsHook* this) {
if ((player->itemAction != player->heldItemAction) || (player->actor.flags & ACTOR_FLAG_TALK) || if ((player->itemAction != player->heldItemAction) || (player->actor.flags & ACTOR_FLAG_TALK) ||
((player->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_26)))) { ((player->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_26)))) {
this->timer = 0; this->timer = 0;
ArmsHook_DetachHookFromActor(this); ArmsHook_DetachFromActor(this);
Math_Vec3f_Copy(&this->actor.world.pos, &player->unk_3C8); Math_Vec3f_Copy(&this->actor.world.pos, &player->unk_3C8);
return 1; return 1;
} }
@ -131,17 +132,17 @@ s32 ArmsHook_CheckForCancel(ArmsHook* this) {
return 0; return 0;
} }
void ArmsHook_AttachHookToActor(ArmsHook* this, Actor* actor) { void ArmsHook_AttachToActor(ArmsHook* this, Actor* actor) {
actor->flags |= ACTOR_FLAG_13; actor->flags |= ACTOR_FLAG_HOOKSHOT_ATTACHED;
this->grabbed = actor; this->attachedActor = actor;
Math_Vec3f_Diff(&actor->world.pos, &this->actor.world.pos, &this->grabbedDistDiff); Math_Vec3f_Diff(&actor->world.pos, &this->actor.world.pos, &this->attachPointOffset);
} }
void ArmsHook_Shoot(ArmsHook* this, PlayState* play) { void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
if ((this->actor.parent == NULL) || (!Player_HoldsHookshot(player))) { if ((this->actor.parent == NULL) || (!Player_HoldsHookshot(player))) {
ArmsHook_DetachHookFromActor(this); ArmsHook_DetachFromActor(this);
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
return; return;
} }
@ -155,7 +156,8 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
if ((touchedActor->update != NULL) && (touchedActor->flags & (ACTOR_FLAG_9 | ACTOR_FLAG_10))) { if ((touchedActor->update != NULL) && (touchedActor->flags & (ACTOR_FLAG_9 | ACTOR_FLAG_10))) {
if (this->collider.elem.atHitElem->acElemFlags & ACELEM_HOOKABLE) { if (this->collider.elem.atHitElem->acElemFlags & ACELEM_HOOKABLE) {
ArmsHook_AttachHookToActor(this, touchedActor); ArmsHook_AttachToActor(this, touchedActor);
if (CHECK_FLAG_ALL(touchedActor->flags, ACTOR_FLAG_10)) { if (CHECK_FLAG_ALL(touchedActor->flags, ACTOR_FLAG_10)) {
func_80865044(this); func_80865044(this);
} }
@ -168,41 +170,50 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
} }
if (DECR(this->timer) == 0) { if (DECR(this->timer) == 0) {
Actor* grabbed; Actor* attachedActor;
Vec3f bodyDistDiffVec; Vec3f bodyDistDiffVec;
Vec3f newPos; Vec3f newPos;
f32 bodyDistDiff; f32 bodyDistDiff;
f32 phi_f16; f32 phi_f16;
s32 pad1; s32 pad1;
f32 curGrabbedDist; f32 curActorOffsetXYZ;
f32 grabbedDist; f32 attachPointOffsetXYZ;
f32 velocity; f32 velocity;
grabbed = this->grabbed; attachedActor = this->attachedActor;
if (grabbed != NULL) {
if ((grabbed->update == NULL) || !CHECK_FLAG_ALL(grabbed->flags, ACTOR_FLAG_13)) { if (attachedActor != NULL) {
grabbed = NULL; if ((attachedActor->update == NULL) ||
this->grabbed = NULL; !CHECK_FLAG_ALL(attachedActor->flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
attachedActor = NULL;
this->attachedActor = NULL;
} else if (this->actor.child != NULL) { } else if (this->actor.child != NULL) {
curGrabbedDist = Actor_WorldDistXYZToActor(&this->actor, grabbed); curActorOffsetXYZ = Actor_WorldDistXYZToActor(&this->actor, attachedActor);
grabbedDist = attachPointOffsetXYZ = sqrtf(SQ(this->attachPointOffset.x) + SQ(this->attachPointOffset.y) +
sqrtf(SQ(this->grabbedDistDiff.x) + SQ(this->grabbedDistDiff.y) + SQ(this->grabbedDistDiff.z)); SQ(this->attachPointOffset.z));
Math_Vec3f_Diff(&grabbed->world.pos, &this->grabbedDistDiff, &this->actor.world.pos);
if ((curGrabbedDist - grabbedDist) > 50.0f) { // Keep the hookshot actor at the same relative offset as the initial attachment even if the actor moves
ArmsHook_DetachHookFromActor(this); Math_Vec3f_Diff(&attachedActor->world.pos, &this->attachPointOffset, &this->actor.world.pos);
grabbed = NULL;
// If the actor the hookshot is attached to is moving, the hookshot's current relative
// position will be different than the initial attachment position.
// If the distance between those two points is larger than 50 units, detach the hookshot.
if ((curActorOffsetXYZ - attachPointOffsetXYZ) > 50.0f) {
ArmsHook_DetachFromActor(this);
attachedActor = NULL;
} }
} }
} }
bodyDistDiff = Math_Vec3f_DistXYZAndStoreDiff(&player->unk_3C8, &this->actor.world.pos, &bodyDistDiffVec); bodyDistDiff = Math_Vec3f_DistXYZAndStoreDiff(&player->unk_3C8, &this->actor.world.pos, &bodyDistDiffVec);
if (bodyDistDiff < 30.0f) { if (bodyDistDiff < 30.0f) {
velocity = 0.0f; velocity = 0.0f;
phi_f16 = 0.0f; phi_f16 = 0.0f;
} else { } else {
if (this->actor.child != NULL) { if (this->actor.child != NULL) {
velocity = 30.0f; velocity = 30.0f;
} else if (grabbed != NULL) { } else if (attachedActor != NULL) {
velocity = 50.0f; velocity = 50.0f;
} else { } else {
velocity = 200.0f; velocity = 200.0f;
@ -219,13 +230,13 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
newPos.z = bodyDistDiffVec.z * velocity; newPos.z = bodyDistDiffVec.z * velocity;
if (this->actor.child == NULL) { if (this->actor.child == NULL) {
if ((grabbed != NULL) && (grabbed->id == ACTOR_BG_SPOT06_OBJECTS)) { if ((attachedActor != NULL) && (attachedActor->id == ACTOR_BG_SPOT06_OBJECTS)) {
Math_Vec3f_Diff(&grabbed->world.pos, &this->grabbedDistDiff, &this->actor.world.pos); Math_Vec3f_Diff(&attachedActor->world.pos, &this->attachPointOffset, &this->actor.world.pos);
phi_f16 = 1.0f; phi_f16 = 1.0f;
} else { } else {
Math_Vec3f_Sum(&player->unk_3C8, &newPos, &this->actor.world.pos); Math_Vec3f_Sum(&player->unk_3C8, &newPos, &this->actor.world.pos);
if (grabbed != NULL) { if (attachedActor != NULL) {
Math_Vec3f_Sum(&this->actor.world.pos, &this->grabbedDistDiff, &grabbed->world.pos); Math_Vec3f_Sum(&this->actor.world.pos, &this->attachPointOffset, &attachedActor->world.pos);
} }
} }
} else { } else {
@ -235,7 +246,7 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
} }
if (phi_f16 < 50.0f) { if (phi_f16 < 50.0f) {
ArmsHook_DetachHookFromActor(this); ArmsHook_DetachFromActor(this);
if (phi_f16 == 0.0f) { if (phi_f16 == 0.0f) {
ArmsHook_SetupAction(this, ArmsHook_Wait); ArmsHook_SetupAction(this, ArmsHook_Wait);
if (ArmsHook_AttachToPlayer(this, player)) { if (ArmsHook_AttachToPlayer(this, player)) {
@ -274,8 +285,9 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
if (bgId != BGCHECK_SCENE) { if (bgId != BGCHECK_SCENE) {
dynaPolyActor = DynaPoly_GetActor(&play->colCtx, bgId); dynaPolyActor = DynaPoly_GetActor(&play->colCtx, bgId);
if (dynaPolyActor != NULL) { if (dynaPolyActor != NULL) {
ArmsHook_AttachHookToActor(this, &dynaPolyActor->actor); ArmsHook_AttachToActor(this, &dynaPolyActor->actor);
} }
} }
func_80865044(this); func_80865044(this);

View file

@ -14,8 +14,8 @@ typedef struct ArmsHook {
/* 0x01CC */ WeaponInfo hookInfo; /* 0x01CC */ WeaponInfo hookInfo;
/* 0x01E8 */ Vec3f unk_1E8; /* 0x01E8 */ Vec3f unk_1E8;
/* 0x01F4 */ Vec3f unk_1F4; /* 0x01F4 */ Vec3f unk_1F4;
/* 0x0200 */ Actor* grabbed; /* 0x0200 */ Actor* attachedActor;
/* 0x0204 */ Vec3f grabbedDistDiff; /* 0x0204 */ Vec3f attachPointOffset; // Distance from the hookshot attach point to world pos of `attachedActor`
/* 0x0210 */ s16 timer; /* 0x0210 */ s16 timer;
/* 0x0214 */ ArmsHookActionFunc actionFunc; /* 0x0214 */ ArmsHookActionFunc actionFunc;
} ArmsHook; // size = 0x0218 } ArmsHook; // size = 0x0218

View file

@ -335,7 +335,7 @@ void BgSpot06Objects_LockPullOutward(BgSpot06Objects* this, PlayState* play) {
if (this->timer == 0) { if (this->timer == 0) {
this->dyna.actor.velocity.y = 0.5f; this->dyna.actor.velocity.y = 0.5f;
this->dyna.actor.flags &= ~ACTOR_FLAG_13; this->dyna.actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
this->actionFunc = BgSpot06Objects_LockSwimToSurface; this->actionFunc = BgSpot06Objects_LockSwimToSurface;
} }

View file

@ -894,7 +894,7 @@ void BossSst_HeadVulnerable(BossSst* this, PlayState* play) {
Math_StepToF(&sHandOffsets[RIGHT].z, 600.0f, 20.0f); Math_StepToF(&sHandOffsets[RIGHT].z, 600.0f, 20.0f);
Math_StepToF(&sHandOffsets[LEFT].x, 200.0f, 20.0f); Math_StepToF(&sHandOffsets[LEFT].x, 200.0f, 20.0f);
Math_StepToF(&sHandOffsets[RIGHT].x, -200.0f, 20.0f); Math_StepToF(&sHandOffsets[RIGHT].x, -200.0f, 20.0f);
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->timer += 2; this->timer += 2;
this->timer = CLAMP_MAX(this->timer, 50); this->timer = CLAMP_MAX(this->timer, 50);
} else { } else {

View file

@ -160,7 +160,7 @@ void EnBoom_Fly(EnBoom* this, PlayState* play) {
if (((this->collider.base.at->id == ACTOR_EN_ITEM00) || (this->collider.base.at->id == ACTOR_EN_SI))) { if (((this->collider.base.at->id == ACTOR_EN_ITEM00) || (this->collider.base.at->id == ACTOR_EN_SI))) {
this->grabbed = this->collider.base.at; this->grabbed = this->collider.base.at;
if (this->collider.base.at->id == ACTOR_EN_SI) { if (this->collider.base.at->id == ACTOR_EN_SI) {
this->collider.base.at->flags |= ACTOR_FLAG_13; this->collider.base.at->flags |= ACTOR_FLAG_HOOKSHOT_ATTACHED;
} }
} }
} }
@ -183,7 +183,7 @@ void EnBoom_Fly(EnBoom* this, PlayState* play) {
target->gravity = -0.9f; target->gravity = -0.9f;
target->bgCheckFlags &= ~(BGCHECKFLAG_GROUND | BGCHECKFLAG_GROUND_TOUCH); target->bgCheckFlags &= ~(BGCHECKFLAG_GROUND | BGCHECKFLAG_GROUND_TOUCH);
} else { } else {
target->flags &= ~ACTOR_FLAG_13; target->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
} }
} }
// Set player flags and kill the boomerang beacause Link caught it. // Set player flags and kill the boomerang beacause Link caught it.

View file

@ -228,8 +228,8 @@ s32 EnFd_SpawnCore(EnFd* this, PlayState* play) {
this->actor.child->colChkInfo.health = 8; this->actor.child->colChkInfo.health = 8;
} }
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
func_8002DE04(play, &this->actor, this->actor.child); Actor_SwapHookshotAttachment(play, &this->actor, this->actor.child);
} }
this->coreActive = true; this->coreActive = true;
@ -668,15 +668,14 @@ void EnFd_Update(Actor* thisx, PlayState* play) {
EnFd_SpawnDot(this, play); EnFd_SpawnDot(this, play);
} }
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
// has been hookshoted
if (EnFd_SpawnCore(this, play)) { if (EnFd_SpawnCore(this, play)) {
this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED;
this->invincibilityTimer = 30; this->invincibilityTimer = 30;
Actor_PlaySfx(&this->actor, NA_SE_EN_FLAME_DAMAGE); Actor_PlaySfx(&this->actor, NA_SE_EN_FLAME_DAMAGE);
Enemy_StartFinishingBlow(play, &this->actor); Enemy_StartFinishingBlow(play, &this->actor);
} else { } else {
this->actor.flags &= ~ACTOR_FLAG_13; this->actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
} }
} else if (this->actionFunc != EnFd_WaitForCore) { } else if (this->actionFunc != EnFd_WaitForCore) {
EnFd_ColliderCheck(this, play); EnFd_ColliderCheck(this, play);

View file

@ -361,8 +361,7 @@ void EnFw_Update(Actor* thisx, PlayState* play) {
EnFw* this = (EnFw*)thisx; EnFw* this = (EnFw*)thisx;
SkelAnime_Update(&this->skelAnime); SkelAnime_Update(&this->skelAnime);
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
// not attached to hookshot.
Actor_MoveXZGravity(&this->actor); Actor_MoveXZGravity(&this->actor);
Actor_UpdateBgCheckInfo(play, &this->actor, 10.0f, 20.0f, 0.0f, UPDBGCHECKINFO_FLAG_0 | UPDBGCHECKINFO_FLAG_2); Actor_UpdateBgCheckInfo(play, &this->actor, 10.0f, 20.0f, 0.0f, UPDBGCHECKINFO_FLAG_0 | UPDBGCHECKINFO_FLAG_2);
this->actionFunc(this, play); this->actionFunc(this, play);

View file

@ -80,7 +80,7 @@ s32 func_80AFB748(EnSi* this, PlayState* play) {
void func_80AFB768(EnSi* this, PlayState* play) { void func_80AFB768(EnSi* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->actionFunc = func_80AFB89C; this->actionFunc = func_80AFB89C;
} else { } else {
Math_SmoothStepToF(&this->actor.scale.x, 0.25f, 0.4f, 1.0f, 0.0f); Math_SmoothStepToF(&this->actor.scale.x, 0.25f, 0.4f, 1.0f, 0.0f);
@ -113,7 +113,7 @@ void func_80AFB89C(EnSi* this, PlayState* play) {
Actor_SetScale(&this->actor, this->actor.scale.x); Actor_SetScale(&this->actor, this->actor.scale.x);
this->actor.shape.rot.y += 0x400; this->actor.shape.rot.y += 0x400;
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
Item_Give(play, ITEM_SKULL_TOKEN); Item_Give(play, ITEM_SKULL_TOKEN);
player->actor.freezeTimer = 10; player->actor.freezeTimer = 10;
Message_StartTextbox(play, 0xB4, NULL); Message_StartTextbox(play, 0xB4, NULL);