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
#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
#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);
s32 func_8002DDE4(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 Actor_MountHorse(struct PlayState* play, struct Player* player, Actor* horse);
int func_8002DEEC(struct Player* player);

View File

@ -1119,15 +1119,24 @@ s32 func_8002DDF4(PlayState* play) {
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);
hookshot->grabbed = actorB;
hookshot->grabbedDistDiff.x = 0.0f;
hookshot->grabbedDistDiff.y = 0.0f;
hookshot->grabbedDistDiff.z = 0.0f;
actorB->flags |= ACTOR_FLAG_13;
actorA->flags &= ~ACTOR_FLAG_13;
hookshot->attachedActor = destActor;
// The hookshot will attach at exactly the actors world position with 0 offset
hookshot->attachPointOffset.x = 0.0f;
hookshot->attachPointOffset.y = 0.0f;
hookshot->attachPointOffset.z = 0.0f;
destActor->flags |= ACTOR_FLAG_HOOKSHOT_ATTACHED;
srcActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
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) {
ArmsHook* this = (ArmsHook*)thisx;
if (this->grabbed != NULL) {
this->grabbed->flags &= ~ACTOR_FLAG_13;
if (this->attachedActor != NULL) {
this->attachedActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
Collider_DestroyQuad(play, &this->collider);
}
@ -109,10 +110,10 @@ s32 ArmsHook_AttachToPlayer(ArmsHook* this, Player* player) {
return false;
}
void ArmsHook_DetachHookFromActor(ArmsHook* this) {
if (this->grabbed != NULL) {
this->grabbed->flags &= ~ACTOR_FLAG_13;
this->grabbed = NULL;
void ArmsHook_DetachFromActor(ArmsHook* this) {
if (this->attachedActor != NULL) {
this->attachedActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
this->attachedActor = NULL;
}
}
@ -123,7 +124,7 @@ s32 ArmsHook_CheckForCancel(ArmsHook* this) {
if ((player->itemAction != player->heldItemAction) || (player->actor.flags & ACTOR_FLAG_TALK) ||
((player->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_26)))) {
this->timer = 0;
ArmsHook_DetachHookFromActor(this);
ArmsHook_DetachFromActor(this);
Math_Vec3f_Copy(&this->actor.world.pos, &player->unk_3C8);
return 1;
}
@ -131,17 +132,17 @@ s32 ArmsHook_CheckForCancel(ArmsHook* this) {
return 0;
}
void ArmsHook_AttachHookToActor(ArmsHook* this, Actor* actor) {
actor->flags |= ACTOR_FLAG_13;
this->grabbed = actor;
Math_Vec3f_Diff(&actor->world.pos, &this->actor.world.pos, &this->grabbedDistDiff);
void ArmsHook_AttachToActor(ArmsHook* this, Actor* actor) {
actor->flags |= ACTOR_FLAG_HOOKSHOT_ATTACHED;
this->attachedActor = actor;
Math_Vec3f_Diff(&actor->world.pos, &this->actor.world.pos, &this->attachPointOffset);
}
void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
Player* player = GET_PLAYER(play);
if ((this->actor.parent == NULL) || (!Player_HoldsHookshot(player))) {
ArmsHook_DetachHookFromActor(this);
ArmsHook_DetachFromActor(this);
Actor_Kill(&this->actor);
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 (this->collider.elem.atHitElem->acElemFlags & ACELEM_HOOKABLE) {
ArmsHook_AttachHookToActor(this, touchedActor);
ArmsHook_AttachToActor(this, touchedActor);
if (CHECK_FLAG_ALL(touchedActor->flags, ACTOR_FLAG_10)) {
func_80865044(this);
}
@ -168,41 +170,50 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
}
if (DECR(this->timer) == 0) {
Actor* grabbed;
Actor* attachedActor;
Vec3f bodyDistDiffVec;
Vec3f newPos;
f32 bodyDistDiff;
f32 phi_f16;
s32 pad1;
f32 curGrabbedDist;
f32 grabbedDist;
f32 curActorOffsetXYZ;
f32 attachPointOffsetXYZ;
f32 velocity;
grabbed = this->grabbed;
if (grabbed != NULL) {
if ((grabbed->update == NULL) || !CHECK_FLAG_ALL(grabbed->flags, ACTOR_FLAG_13)) {
grabbed = NULL;
this->grabbed = NULL;
attachedActor = this->attachedActor;
if (attachedActor != NULL) {
if ((attachedActor->update == NULL) ||
!CHECK_FLAG_ALL(attachedActor->flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
attachedActor = NULL;
this->attachedActor = NULL;
} else if (this->actor.child != NULL) {
curGrabbedDist = Actor_WorldDistXYZToActor(&this->actor, grabbed);
grabbedDist =
sqrtf(SQ(this->grabbedDistDiff.x) + SQ(this->grabbedDistDiff.y) + SQ(this->grabbedDistDiff.z));
Math_Vec3f_Diff(&grabbed->world.pos, &this->grabbedDistDiff, &this->actor.world.pos);
if ((curGrabbedDist - grabbedDist) > 50.0f) {
ArmsHook_DetachHookFromActor(this);
grabbed = NULL;
curActorOffsetXYZ = Actor_WorldDistXYZToActor(&this->actor, attachedActor);
attachPointOffsetXYZ = sqrtf(SQ(this->attachPointOffset.x) + SQ(this->attachPointOffset.y) +
SQ(this->attachPointOffset.z));
// Keep the hookshot actor at the same relative offset as the initial attachment even if the actor moves
Math_Vec3f_Diff(&attachedActor->world.pos, &this->attachPointOffset, &this->actor.world.pos);
// 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);
if (bodyDistDiff < 30.0f) {
velocity = 0.0f;
phi_f16 = 0.0f;
} else {
if (this->actor.child != NULL) {
velocity = 30.0f;
} else if (grabbed != NULL) {
} else if (attachedActor != NULL) {
velocity = 50.0f;
} else {
velocity = 200.0f;
@ -219,13 +230,13 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
newPos.z = bodyDistDiffVec.z * velocity;
if (this->actor.child == NULL) {
if ((grabbed != NULL) && (grabbed->id == ACTOR_BG_SPOT06_OBJECTS)) {
Math_Vec3f_Diff(&grabbed->world.pos, &this->grabbedDistDiff, &this->actor.world.pos);
if ((attachedActor != NULL) && (attachedActor->id == ACTOR_BG_SPOT06_OBJECTS)) {
Math_Vec3f_Diff(&attachedActor->world.pos, &this->attachPointOffset, &this->actor.world.pos);
phi_f16 = 1.0f;
} else {
Math_Vec3f_Sum(&player->unk_3C8, &newPos, &this->actor.world.pos);
if (grabbed != NULL) {
Math_Vec3f_Sum(&this->actor.world.pos, &this->grabbedDistDiff, &grabbed->world.pos);
if (attachedActor != NULL) {
Math_Vec3f_Sum(&this->actor.world.pos, &this->attachPointOffset, &attachedActor->world.pos);
}
}
} else {
@ -235,7 +246,7 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
}
if (phi_f16 < 50.0f) {
ArmsHook_DetachHookFromActor(this);
ArmsHook_DetachFromActor(this);
if (phi_f16 == 0.0f) {
ArmsHook_SetupAction(this, ArmsHook_Wait);
if (ArmsHook_AttachToPlayer(this, player)) {
@ -274,8 +285,9 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
if (bgId != BGCHECK_SCENE) {
dynaPolyActor = DynaPoly_GetActor(&play->colCtx, bgId);
if (dynaPolyActor != NULL) {
ArmsHook_AttachHookToActor(this, &dynaPolyActor->actor);
ArmsHook_AttachToActor(this, &dynaPolyActor->actor);
}
}
func_80865044(this);

View File

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

View File

@ -335,7 +335,7 @@ void BgSpot06Objects_LockPullOutward(BgSpot06Objects* this, PlayState* play) {
if (this->timer == 0) {
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;
}

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[LEFT].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 = CLAMP_MAX(this->timer, 50);
} 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))) {
this->grabbed = this->collider.base.at;
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->bgCheckFlags &= ~(BGCHECKFLAG_GROUND | BGCHECKFLAG_GROUND_TOUCH);
} else {
target->flags &= ~ACTOR_FLAG_13;
target->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
}
// 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;
}
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) {
func_8002DE04(play, &this->actor, this->actor.child);
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
Actor_SwapHookshotAttachment(play, &this->actor, this->actor.child);
}
this->coreActive = true;
@ -668,15 +668,14 @@ void EnFd_Update(Actor* thisx, PlayState* play) {
EnFd_SpawnDot(this, play);
}
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) {
// has been hookshoted
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
if (EnFd_SpawnCore(this, play)) {
this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED;
this->invincibilityTimer = 30;
Actor_PlaySfx(&this->actor, NA_SE_EN_FLAME_DAMAGE);
Enemy_StartFinishingBlow(play, &this->actor);
} else {
this->actor.flags &= ~ACTOR_FLAG_13;
this->actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
} else if (this->actionFunc != EnFd_WaitForCore) {
EnFd_ColliderCheck(this, play);

View File

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

View File

@ -80,7 +80,7 @@ s32 func_80AFB748(EnSi* this, PlayState* play) {
void func_80AFB768(EnSi* this, PlayState* 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;
} else {
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);
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);
player->actor.freezeTimer = 10;
Message_StartTextbox(play, 0xB4, NULL);