Document Hookshot Attachment (#1745)

* hookshot attachment

* period
This commit is contained in:
engineer124 2024-11-26 11:26:55 +11:00 committed by GitHub
parent a448168d37
commit ba693efb08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 79 additions and 65 deletions

View File

@ -465,20 +465,20 @@ typedef enum DoorLockType {
// Actor is discoverable by the Attention System. This enables Tatl to hover over the actor when it is in range.
// The actor can also be locked onto (as long as `ACTOR_FLAG_LOCK_ON_DISABLED` is not set).
#define ACTOR_FLAG_ATTENTION_ENABLED (1 << 0)
#define ACTOR_FLAG_ATTENTION_ENABLED (1 << 0)
// Unused
#define ACTOR_FLAG_2 (1 << 1)
#define ACTOR_FLAG_2 (1 << 1)
// Actor is hostile toward the Player. Player has specific "battle" behavior when locked onto hostile actors.
// Enemy background music will also be played when the player is close enough to a hostile actor.
// Note: This must be paired with `ACTOR_FLAG_ATTENTION_ENABLED` to have any effect.
#define ACTOR_FLAG_HOSTILE (1 << 2)
#define ACTOR_FLAG_HOSTILE (1 << 2)
// Actor is considered "friendly"; Opposite flag of `ACTOR_FLAG_HOSTILE`.
// Note that this flag doesn't have any effect on either the actor, or Player's behavior.
// What actually matters is the presence or lack of `ACTOR_FLAG_HOSTILE`.
#define ACTOR_FLAG_FRIENDLY (1 << 3)
#define ACTOR_FLAG_FRIENDLY (1 << 3)
//
#define ACTOR_FLAG_10 (1 << 4)
//
@ -501,9 +501,15 @@ typedef enum DoorLockType {
#define ACTOR_FLAG_800 (1 << 11)
// 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_200` and `ACTOR_FLAG_400`.
// If neither of those flags are set attachment cannot occur, and the hookshot will simply act as a damage source.
//
#define ACTOR_FLAG_2000 (1 << 13)
// This flag is also reused to indicate that an actor is attached to the Zora 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)
@ -538,15 +544,15 @@ typedef enum DoorLockType {
// When Player is carrying this actor, it can only be thrown, not dropped/placed.
// Typically an actor can only be thrown when moving, but this allows an actor to be thrown when standing still.
#define ACTOR_FLAG_THROW_ONLY (1 << 23)
#define ACTOR_FLAG_THROW_ONLY (1 << 23)
// When colliding with Player's body AC collider, a "thump" sound will play indicating his body has been hit
#define ACTOR_FLAG_SFX_FOR_PLAYER_BODY_HIT (1 << 24)
#define ACTOR_FLAG_SFX_FOR_PLAYER_BODY_HIT (1 << 24)
// Actor can update even if Player is currently using the ocarina.
// Typically an actor will halt while the ocarina is active (depending on category).
// This flag allows a given actor to be an exception.
#define ACTOR_FLAG_UPDATE_DURING_OCARINA (1 << 25)
#define ACTOR_FLAG_UPDATE_DURING_OCARINA (1 << 25)
// Actor can press and hold down switches.
// See usages of `DynaPolyActor_SetSwitchPressed` and `DynaPolyActor_IsSwitchPressed` for more context on how switches work.
@ -554,7 +560,7 @@ typedef enum DoorLockType {
// Player is not able to lock onto the actor.
// Tatl will still be able to hover over the actor, assuming `ACTOR_FLAG_ATTENTION_ENABLED` is set.
#define ACTOR_FLAG_LOCK_ON_DISABLED (1 << 27)
#define ACTOR_FLAG_LOCK_ON_DISABLED (1 << 27)
//
#define ACTOR_FLAG_10000000 (1 << 28)
//

View File

@ -68,8 +68,8 @@ void ArmsHook_Init(Actor* thisx, PlayState* play) {
void ArmsHook_Destroy(Actor* thisx, PlayState* play) {
ArmsHook* this = THIS;
if (this->grabbed != NULL) {
this->grabbed->flags &= ~ACTOR_FLAG_2000;
if (this->attachedActor != NULL) {
this->attachedActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
Collider_DestroyQuad(play, &this->collider);
}
@ -98,10 +98,10 @@ s32 ArmsHook_AttachToPlayer(ArmsHook* this, Player* player) {
return 0;
}
void ArmsHook_DetachHookFromActor(ArmsHook* this) {
if (this->grabbed != NULL) {
this->grabbed->flags &= ~ACTOR_FLAG_2000;
this->grabbed = NULL;
void ArmsHook_DetachFromActor(ArmsHook* this) {
if (this->attachedActor != NULL) {
this->attachedActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
this->attachedActor = NULL;
}
}
@ -112,7 +112,7 @@ s32 ArmsHook_CheckForCancel(ArmsHook* this) {
if ((player->itemAction != player->heldItemAction) || (player->actor.flags & ACTOR_FLAG_TALK) ||
(player->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_4000000))) {
this->timer = 0;
ArmsHook_DetachHookFromActor(this);
ArmsHook_DetachFromActor(this);
Math_Vec3f_Copy(&this->actor.world.pos, &player->rightHandWorld.pos);
return 1;
}
@ -120,17 +120,17 @@ s32 ArmsHook_CheckForCancel(ArmsHook* this) {
return 0;
}
void ArmsHook_AttachHookToActor(ArmsHook* this, Actor* actor) {
actor->flags |= ACTOR_FLAG_2000;
this->grabbed = actor;
Math_Vec3f_Diff(&actor->world.pos, &this->actor.world.pos, &this->unk1FC);
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_IsHoldingHookshot(player)) {
ArmsHook_DetachHookFromActor(this);
ArmsHook_DetachFromActor(this);
Actor_Kill(&this->actor);
return;
}
@ -144,7 +144,7 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
if ((touchedActor->update != NULL) && (touchedActor->flags & (ACTOR_FLAG_200 | ACTOR_FLAG_400))) {
if (this->collider.elem.atHitElem->acElemFlags & ACELEM_HOOKABLE) {
ArmsHook_AttachHookToActor(this, touchedActor);
ArmsHook_AttachToActor(this, touchedActor);
if (CHECK_FLAG_ALL(touchedActor->flags, ACTOR_FLAG_400)) {
func_808C1154(this);
}
@ -157,25 +157,31 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
}
if (DECR(this->timer) == 0) {
Actor* grabbed = this->grabbed;
Actor* attachedActor = this->attachedActor;
Vec3f bodyDistDiffVec;
Vec3f newPos;
f32 bodyDistDiff;
f32 phi_f16;
s32 pad;
if (grabbed != NULL) {
if ((grabbed->update == NULL) || !CHECK_FLAG_ALL(grabbed->flags, ACTOR_FLAG_2000)) {
grabbed = NULL;
this->grabbed = NULL;
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) {
f32 sp94 = Actor_WorldDistXYZToActor(&this->actor, grabbed);
f32 sp90 = sqrtf(SQXYZ(this->unk1FC));
f32 curActorOffsetXYZ = Actor_WorldDistXYZToActor(&this->actor, attachedActor);
f32 attachPointOffsetXYZ = sqrtf(SQXYZ(this->attachPointOffset));
Math_Vec3f_Diff(&grabbed->world.pos, &this->unk1FC, &this->actor.world.pos);
if (50.0f < (sp94 - sp90)) {
ArmsHook_DetachHookFromActor(this);
grabbed = NULL;
// 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;
}
}
}
@ -185,13 +191,14 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
bodyDistDiff =
Math_Vec3f_DistXYZAndStoreDiff(&player->rightHandWorld.pos, &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;
@ -210,15 +217,15 @@ void ArmsHook_Shoot(ArmsHook* this, PlayState* play) {
if (this->actor.child == NULL) {
Math_Vec3f_Sum(&player->rightHandWorld.pos, &newPos, &this->actor.world.pos);
if (grabbed != NULL) {
Math_Vec3f_Sum(&this->actor.world.pos, &this->unk1FC, &grabbed->world.pos);
if (attachedActor != NULL) {
Math_Vec3f_Sum(&this->actor.world.pos, &this->attachPointOffset, &attachedActor->world.pos);
}
} else {
Math_Vec3f_Diff(&bodyDistDiffVec, &newPos, &player->actor.velocity);
player->actor.world.rot.x = Math_Atan2S_XY(sqrtf(SQXZ(bodyDistDiffVec)), -bodyDistDiffVec.y);
}
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)) {
@ -259,7 +266,7 @@ 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_808C1154(this);

View File

@ -13,8 +13,8 @@ typedef struct ArmsHook {
/* 0x1C4 */ WeaponInfo weaponInfo;
/* 0x1E0 */ Vec3f unk1E0;
/* 0x1EC */ Vec3f unk1EC;
/* 0x1F8 */ Actor* grabbed;
/* 0x1FC */ Vec3f unk1FC;
/* 0x1F8 */ Actor* attachedActor;
/* 0x1FC */ Vec3f attachPointOffset; // Distance from the hookshot attach point to world pos of `attachedActor`
/* 0x208 */ s8 unk_208;
/* 0x20A */ s16 timer;
/* 0x20C */ ArmsHookActionFunc actionFunc;

View File

@ -1159,7 +1159,7 @@ void EnBigpo_Update(Actor* thisx, PlayState* play) {
s32 pad;
ColliderCylinder* thisCollider;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->hoverHeightCycleTimer = 0;
this->savedHeight = this->actor.world.pos.y;
}

View File

@ -2179,8 +2179,8 @@ void EnBigslime_SetupDamageGekko(EnBigslime* this, s32 isNotFrozen) {
}
EnBigslime_GekkoSfxOutsideBigslime(this, NA_SE_EN_FROG_DAMAGE);
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
this->actor.flags &= ~ACTOR_FLAG_2000;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
this->actionFunc = EnBigslime_DamageGekko;

View File

@ -221,7 +221,7 @@ void func_808A2918(EnBoom* this, PlayState* play) {
(this->collider.base.at->id == ACTOR_EN_SI))) {
this->unk_1C8 = this->collider.base.at;
if (this->collider.base.at->id == ACTOR_EN_SI) {
this->collider.base.at->flags |= ACTOR_FLAG_2000;
this->collider.base.at->flags |= ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
}
@ -266,7 +266,7 @@ void func_808A2918(EnBoom* this, PlayState* play) {
targetActor->gravity = -0.9f;
targetActor->bgCheckFlags &= ~(BGCHECKFLAG_GROUND | BGCHECKFLAG_GROUND_TOUCH);
} else {
targetActor->flags &= ~ACTOR_FLAG_2000;
targetActor->flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
}
Actor_Kill(&this->actor);

View File

@ -363,7 +363,7 @@ void EnFg_Destroy(Actor* thisx, PlayState* play) {
void EnFg_Update(Actor* thisx, PlayState* play) {
EnFg* this = THIS;
if ((CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000) == 0) &&
if ((CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED) == 0) &&
(CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_ATTACHED_TO_ARROW) == 0)) {
this->actionFunc(this, play);
Actor_UpdateBgCheckInfo(play, &this->actor, sREG(0), sREG(1), 0.0f,

View File

@ -1604,7 +1604,8 @@ void func_80B89280(EnKaizoku* this, PlayState* play) {
}
}
if ((this->unk_2B6 == 0) && (this->unk_2B8 == 0) && !CHECK_FLAG_ALL(this->picto.actor.flags, ACTOR_FLAG_2000) &&
if ((this->unk_2B6 == 0) && (this->unk_2B8 == 0) &&
!CHECK_FLAG_ALL(this->picto.actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED) &&
(this->picto.actor.bgCheckFlags & BGCHECKFLAG_GROUND)) {
this->unk_2D8 = 0;
func_80B85A00(this, play, true);

View File

@ -500,8 +500,8 @@ void EnMinislime_SetupMoveToBigslime(EnMinislime* this) {
}
this->frozenAlpha = 0;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
this->actor.flags &= ~ACTOR_FLAG_2000;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
this->actionFunc = EnMinislime_MoveToBigslime;
}
@ -556,8 +556,8 @@ void EnMinislime_SetupDefeatIdle(EnMinislime* this) {
}
this->frozenAlpha = 0;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
this->actor.flags &= ~ACTOR_FLAG_2000;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
this->actor.shape.rot.x = 0;
@ -625,8 +625,8 @@ void EnMinislime_SetupMoveToGekko(EnMinislime* this) {
this->actor.velocity.y = 0.0f;
this->collider.base.acFlags &= ~AC_ON;
this->collider.base.ocFlags1 &= ~OC1_ON;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
this->actor.flags &= ~ACTOR_FLAG_2000;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
this->actionFunc = EnMinislime_MoveToGekko;
@ -715,7 +715,7 @@ void EnMinislime_Update(Actor* thisx, PlayState* play) {
} else if ((this->actor.params == MINISLIME_FORM_BIGSLIME) && (this->actionFunc != EnMinislime_MoveToBigslime)) {
EnMinislime_SetupMoveToBigslime(this);
} else {
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->collider.base.acFlags &= ~AC_HIT;
return;
}

View File

@ -1121,7 +1121,7 @@ void EnPp_Mask_SetupDetach(EnPp* this, PlayState* play) {
* Moves the mask through the air and eventually makes it burst into flames.
*/
void EnPp_Mask_Detach(EnPp* this, PlayState* play) {
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000) || (this->action == EN_PP_ACTION_MASK_DEAD)) {
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED) || (this->action == EN_PP_ACTION_MASK_DEAD)) {
switch (this->actionVar.maskDetachState) {
case EN_PP_MASK_DETACH_STATE_START:
this->action = EN_PP_ACTION_MASK_DEAD;

View File

@ -835,7 +835,7 @@ void EnRat_Update(Actor* thisx, PlayState* play) {
if (this->damageReaction.hookedState == EN_RAT_HOOK_STARTED) {
// The player just hit the Real Bombchu with the Hookshot.
this->damageReaction.hookedState = EN_RAT_HOOKED;
} else if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
} else if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
// The player has hooked the Real Bombchu for more than one frame, but
// the actor flag indicating that the Hookshot is attached is *not* set.
EnRat_Explode(this, play);

View File

@ -114,7 +114,7 @@ void EnSi_GiveToken(EnSi* this, PlayState* play) {
}
void EnSi_Wait(EnSi* this, PlayState* play) {
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
this->actionFunc = EnSi_DraggedByHookshot;
} else if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) {
EnSi_GiveToken(this, play);
@ -125,7 +125,7 @@ void EnSi_Wait(EnSi* this, PlayState* play) {
}
void EnSi_DraggedByHookshot(EnSi* this, PlayState* play) {
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000)) {
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
EnSi_GiveToken(this, play);
Actor_Kill(&this->actor);
}

View File

@ -559,8 +559,8 @@ void EnTanron2_Update(Actor* thisx, PlayState* play) {
}
}
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_2000) && (this->actor.xzDistToPlayer < 80.0f)) {
this->actor.flags &= ~ACTOR_FLAG_2000;
if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED) && (this->actor.xzDistToPlayer < 80.0f)) {
this->actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
this->unk_15A = 25;
this->unk_159 = 1;
}

View File

@ -368,7 +368,7 @@ void EnWallmas_ReturnToCeiling(EnWallmas* this, PlayState* play) {
if (this->skelAnime.curFrame > 20.0f) {
this->actor.world.pos.y += 30.0f;
this->timer += 9;
this->actor.flags &= ~ACTOR_FLAG_2000;
this->actor.flags &= ~ACTOR_FLAG_HOOKSHOT_ATTACHED;
}
if (Animation_OnFrame(&this->skelAnime, 20.0f)) {

View File

@ -6105,9 +6105,9 @@
0x808C10F8:("ArmsHook_Wait",),
0x808C1154:("func_808C1154",),
0x808C1168:("ArmsHook_AttachToPlayer",),
0x808C1198:("ArmsHook_DetachHookFromActor",),
0x808C1198:("ArmsHook_DetachFromActor",),
0x808C11C0:("ArmsHook_CheckForCancel",),
0x808C125C:("ArmsHook_AttachHookToActor",),
0x808C125C:("ArmsHook_AttachToActor",),
0x808C12A4:("ArmsHook_Shoot",),
0x808C18D8:("ArmsHook_Update",),
0x808C1918:("ArmsHook_Draw",),