From 44057c087a4e57bff4ece44b9aa9da06e2db3a57 Mon Sep 17 00:00:00 2001 From: Tharo <17233964+Thar0@users.noreply.github.com> Date: Sun, 19 May 2024 18:18:34 +0100 Subject: [PATCH] ovl_En_Wdhand (#1629) * 2 non-matching * Matching Co-authored-by: Derek Hensley * Cleanup + docs * Suggested changes * Format * Update EnWdhand_GetInitVelocity --------- Co-authored-by: Derek Hensley --- assets/xml/objects/object_wdhand.xml | 26 +- spec | 3 +- .../actors/ovl_En_Wdhand/z_en_wdhand.c | 789 ++++++++++++++++-- .../actors/ovl_En_Wdhand/z_en_wdhand.h | 26 +- tools/disasm/variables.txt | 3 +- 5 files changed, 776 insertions(+), 71 deletions(-) diff --git a/assets/xml/objects/object_wdhand.xml b/assets/xml/objects/object_wdhand.xml index 311d9299f5..0662718ec1 100644 --- a/assets/xml/objects/object_wdhand.xml +++ b/assets/xml/objects/object_wdhand.xml @@ -1,14 +1,14 @@  - + - - + + - - + + @@ -16,13 +16,13 @@ - - - - - - - - + + + + + + + + diff --git a/spec b/spec index 08433ecd4d..1672e4092b 100644 --- a/spec +++ b/spec @@ -3432,8 +3432,7 @@ beginseg name "ovl_En_Wdhand" compress include "$(BUILD_DIR)/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.o" - include "$(BUILD_DIR)/data/ovl_En_Wdhand/ovl_En_Wdhand.data.o" - include "$(BUILD_DIR)/data/ovl_En_Wdhand/ovl_En_Wdhand.reloc.o" + include "$(BUILD_DIR)/src/overlays/actors/ovl_En_Wdhand/ovl_En_Wdhand_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.c b/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.c index ae491fbf00..fe39b96c56 100644 --- a/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.c +++ b/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.c @@ -15,7 +15,23 @@ void EnWdhand_Destroy(Actor* thisx, PlayState* play); void EnWdhand_Update(Actor* thisx, PlayState* play); void EnWdhand_Draw(Actor* thisx, PlayState* play); -#if 0 +void EnWdhand_ReturnToIdle(EnWdhand* this, PlayState* play); +void EnWdhand_Idle(EnWdhand* this, PlayState* play); +void EnWdhand_LungeForPlayer(EnWdhand* this, PlayState* play); +void EnWdhand_FailedToGrabPlayer(EnWdhand* this, PlayState* play); +void EnWdhand_GrabbedPlayer(EnWdhand* this, PlayState* play); +void EnWdhand_Die(EnWdhand* this, PlayState* play); + +void EnWdhand_SetupReturnToIdle(EnWdhand* this); +void EnWdhand_SetupIdle(EnWdhand* this); +void EnWdhand_SetupLungeForPlayer(EnWdhand* this); +void EnWdhand_SetupFailedToGrabPlayer(EnWdhand* this); +void EnWdhand_SetupGrabbedPlayer(EnWdhand* this, PlayState* play); +void EnWdhand_SetupDie(EnWdhand* this); + +void EnWdhand_GetInitVelocity(EnWdhand* this, Vec3f* dst); +void EnWdhand_Vec3fToVec3s(Vec3s* dst, Vec3f* src); + ActorInit En_Wdhand_InitVars = { /**/ ACTOR_EN_WDHAND, /**/ ACTORCAT_ENEMY, @@ -28,49 +44,102 @@ ActorInit En_Wdhand_InitVars = { /**/ EnWdhand_Draw, }; -// static ColliderJntSphElementInit sJntSphElementsInit[7] = { -static ColliderJntSphElementInit D_80AF63E0[7] = { +static ColliderJntSphElementInit sJntSphElementsInit[EN_WDHAND_NUM_COLLIDER_ELEMENTS] = { { - { ELEMTYPE_UNK1, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NONE, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK1, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, { 50, { { 0, 575, 0 }, 10 }, 100 }, }, { - { ELEMTYPE_UNK1, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NONE, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK1, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, { 50, { { 0, 1725, 0 }, 10 }, 100 }, }, { - { ELEMTYPE_UNK1, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NONE, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK1, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, { 50, { { 0, 575, 0 }, 10 }, 100 }, }, { - { ELEMTYPE_UNK1, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NONE, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK1, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, { 50, { { 0, 1725, 0 }, 10 }, 100 }, }, { - { ELEMTYPE_UNK1, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NONE, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK1, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, { 50, { { 0, 575, 0 }, 10 }, 100 }, }, { - { ELEMTYPE_UNK1, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NONE, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK1, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, { 50, { { 0, 1725, 0 }, 10 }, 100 }, }, { - { ELEMTYPE_UNK1, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NONE, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK1, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, { 50, { { 0, 1000, 0 }, 15 }, 100 }, }, }; -// static ColliderJntSphInit sJntSphInit = { -static ColliderJntSphInit D_80AF64DC = { - { COLTYPE_HIT0, AT_NONE | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_JNTSPH, }, - ARRAY_COUNT(sJntSphElementsInit), D_80AF63E0, // sJntSphElementsInit, +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT0, + AT_NONE | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementsInit), + sJntSphElementsInit, }; -// sColChkInfoInit -static CollisionCheckInfoInit D_80AF64EC = { 1, 25, 25, MASS_HEAVY }; +static CollisionCheckInfoInit sColChkInfoInit = { 1, 25, 25, MASS_HEAVY }; -// static DamageTable sDamageTable = { -static DamageTable D_80AF64F4 = { +static DamageTable sDamageTable = { /* Deku Nut */ DMG_ENTRY(0, 0x0), /* Deku Stick */ DMG_ENTRY(0, 0x0), /* Horse trample */ DMG_ENTRY(0, 0x0), @@ -105,63 +174,679 @@ static DamageTable D_80AF64F4 = { /* Powder Keg */ DMG_ENTRY(1, 0x0), }; -#endif +void EnWdhand_Init(Actor* thisx, PlayState* play) { + EnWdhand* this = THIS; + s32 i; + Vec3f initVel; -extern ColliderJntSphElementInit D_80AF63E0[7]; -extern ColliderJntSphInit D_80AF64DC; -extern CollisionCheckInfoInit D_80AF64EC; -extern DamageTable D_80AF64F4; + SkelAnime_InitFlex(play, &this->skelAnime, &gDexihandSkel, &gDexihandIdleAnim, this->jointTable, this->morphTable, + DEXIHAND_LIMB_MAX); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); -extern UNK_TYPE D_060000F4; -extern UNK_TYPE D_06000364; -extern UNK_TYPE D_06000534; -extern UNK_TYPE D_06000854; -extern UNK_TYPE D_060014C0; + // Constructs an affine transformation that maps world origin to actor origin and rotation + // (model -> world but without scaling) + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateYS(this->actor.shape.rot.y, MTXMODE_APPLY); + Matrix_RotateXS(this->actor.shape.rot.x, MTXMODE_APPLY); + Matrix_RotateZS(this->actor.shape.rot.z, MTXMODE_APPLY); + // Get a world position offset by 3 units + Matrix_MultVecY(3.0f, &this->endPoints[EN_WDHAND_HAND_POINT]); + // Save this matrix for later + Matrix_MtxFCopy(&this->relativeToWorldTransform, Matrix_GetCurrent()); + // Write the rotation back to the actor + Matrix_MtxFToYXZRot(&this->relativeToWorldTransform, &this->actor.shape.rot, false); + // Transpose the matrix, since it's a rotation matrix this creates the inverse rotation matrix + Matrix_Transpose(&this->relativeToWorldTransform); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/EnWdhand_Init.s") + this->actor.world.rot.y = this->actor.shape.rot.y; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/EnWdhand_Destroy.s") + EnWdhand_GetInitVelocity(this, &initVel); + this->actor.speed = initVel.z; + this->actor.velocity.y = initVel.y; + this->actor.velocity.x = Math_SinS(this->actor.world.rot.y) * this->actor.speed; + this->actor.velocity.z = Math_CosS(this->actor.world.rot.y) * this->actor.speed; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4608.s") + Collider_InitAndSetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->colliderElements); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4670.s") + for (i = 0; i < ARRAY_COUNT(this->colliderElements); i++) { + ColliderJntSphElement* elem = &this->collider.elements[i]; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF46F0.s") + elem->dim.worldSphere.radius = elem->dim.modelSphere.radius; + EnWdhand_Vec3fToVec3s(&elem->dim.worldSphere.center, &this->actor.world.pos); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF488C.s") + for (i = 0; i < EN_WDHAND_NUM_SEGMENTS; i++) { + this->limbScaleFactors[i] = 1.0f; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF48D0.s") + this->limbIndexBeforeCut = -1; + this->limbIndexAfterCut = 0; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4964.s") + this->actor.world.rot.x = 0; + this->actor.world.rot.z = + Math_Atan2S_XY(this->relativeToWorldTransform.mf[2][1], this->relativeToWorldTransform.mf[0][1]); + this->actor.hintId = TATL_HINT_ID_DEXIHAND; + EnWdhand_SetupIdle(this); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4A88.s") +void EnWdhand_Destroy(Actor* thisx, PlayState* play) { + EnWdhand* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4C18.s") + Collider_DestroyJntSph(play, &this->collider); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4C64.s") +void EnWdhand_GetInitVelocity(EnWdhand* this, Vec3f* dst) { + s32 param; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4ED0.s") + param = EN_WDHAND_GET_Y_INIT_VELOCITY(&this->actor); + if (param == EN_WDHAND_INIT_VELOCITY_MAX) { + param = 40; + } + dst->y = param * 0.2f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4F30.s") + param = EN_WDHAND_GET_Z_INIT_VELOCITY(&this->actor); + if (param == EN_WDHAND_INIT_VELOCITY_MAX) { + param = 40; + } + dst->z = param * 0.2f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4F6C.s") + dst->x = 0.0f; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF4FF8.s") +void EnWdhand_GetRelativeRotationsToPlayerRightFoot(EnWdhand* this, Player* player, s16* pitchOut, s16* yawOut) { + Vec3f diffToPlayerRightFoot; + Vec3f diffToPlayerRightFootRelative; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF5130.s") + // Get the difference in world positions + Math_Vec3f_Diff(&player->bodyPartsPos[PLAYER_BODYPART_RIGHT_FOOT], &this->actor.world.pos, &diffToPlayerRightFoot); + // Re-express the difference in actor-relative coordinates + Matrix_Put(&this->relativeToWorldTransform); + Matrix_MultVec3f(&diffToPlayerRightFoot, &diffToPlayerRightFootRelative); + // Return actor-relative yaw and pitch + *yawOut = Actor_WorldYawTowardPoint(&this->actor, &diffToPlayerRightFootRelative); + *pitchOut = Actor_WorldPitchTowardPoint(&this->actor, &diffToPlayerRightFootRelative) + 0x4000; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF520C.s") +void EnWdhand_SetTransform(EnWdhand* this, s32 limbIndex, s32 arg2, Vec3f* translation) { + MtxF* currentMatrix = Matrix_GetCurrent(); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF5650.s") + Matrix_RotateZYX(this->limbRotations[limbIndex].x, this->limbRotations[limbIndex].y, 0, MTXMODE_APPLY); + if (arg2) { + Matrix_RotateYS(-this->limbRotations[limbIndex].y, MTXMODE_APPLY); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF56A0.s") + if (limbIndex == DEXIHAND_LIMB_03) { + if (this->globalLimbScaleFactor > 1.0f) { + f32 scale = CLAMP_MAX(this->globalLimbScaleFactor, 1.5f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF5820.s") + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + } + } else if ((this->limbScaleFactors[limbIndex] < 1.0f) || (this->globalLimbScaleFactor > 1.0f)) { + if (this->limbScaleFactors[limbIndex] < 0.1f) { + Matrix_Scale(0.0f, this->globalLimbScaleFactor * this->limbScaleFactors[limbIndex], 0.0f, MTXMODE_APPLY); + } else { + Matrix_Scale(1.0f, this->globalLimbScaleFactor * this->limbScaleFactors[limbIndex], 1.0f, MTXMODE_APPLY); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF5E3C.s") + currentMatrix->mf[3][0] = translation->x; + currentMatrix->mf[3][1] = translation->y; + currentMatrix->mf[3][2] = translation->z; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/EnWdhand_Update.s") +void EnWdhand_Vec3fToVec3s(Vec3s* dst, Vec3f* src) { + dst->x = src->x; + dst->y = src->y; + dst->z = src->z; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/func_80AF5FE4.s") +s16 EnWdhand_GetLimbXRotation(EnWdhand* this, s32 limbIndex) { + return this->limbRotations[limbIndex].z * Math_SinF((this->timer - limbIndex * 20) * (M_PI / 40)); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Wdhand/EnWdhand_Draw.s") +void EnWdhand_SetupIdle(EnWdhand* this) { + s32 i; + + Animation_MorphToLoop(&this->skelAnime, &gDexihandIdleAnim, -5.0f); + + if (this->actionFunc != EnWdhand_ReturnToIdle) { + this->timer = 80; + this->limbRotations[0].y = Rand_Next() >> 16; + + for (i = 1; i < ARRAY_COUNT(this->limbRotations); i++) { + this->limbRotations[i].y = this->limbRotations[i - 1].y + (s32)Rand_ZeroFloat(0x2000) + 0x8000; + } + + for (i = 0; i < ARRAY_COUNT(this->limbRotations); i++) { + this->limbRotations[i].z = Rand_S16Offset(0x1800, 0x1000); + } + } + + for (i = 0; i < ARRAY_COUNT(this->limbRotations); i++) { + this->limbRotations[i].x = EnWdhand_GetLimbXRotation(this, i); + } + + this->actionFunc = EnWdhand_Idle; + this->globalLimbScaleFactor = 1.0f; +} + +void EnWdhand_Idle(EnWdhand* this, PlayState* play) { + Player* player = GET_PLAYER(play); + s32 i; + s16 pitchToPlayerRightFoot; + s16 yawToPlayerRightFoot; + + SkelAnime_Update(&this->skelAnime); + + this->timer--; + + for (i = 0; i < ARRAY_COUNT(this->limbRotations); i++) { + this->limbRotations[i].x = EnWdhand_GetLimbXRotation(this, i); + + if (this->timer == 20 * i) { + if (i != 0) { + this->limbRotations[i].y = this->limbRotations[i - 1].y + (s32)Rand_CenteredFloat(16384.0f); + this->limbRotations[i].y -= 0x8000; + } else { + this->limbRotations[0].y += (s16)(s32)Rand_CenteredFloat(16384.0f); + } + this->limbRotations[i].z = Rand_S16Offset(0x1800, 0x1000); + } + } + + if (this->timer == 0) { + this->timer = 80; + } + + if (!(player->stateFlags2 & PLAYER_STATE2_80) && (this->actor.xyzDistToPlayerSq < SQ(120.75f))) { + EnWdhand_GetRelativeRotationsToPlayerRightFoot(this, player, &pitchToPlayerRightFoot, &yawToPlayerRightFoot); + if (ABS_ALT(pitchToPlayerRightFoot) <= 0x4000) { + EnWdhand_SetupLungeForPlayer(this); + } + } +} + +void EnWdhand_SetupLungeForPlayer(EnWdhand* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDexihandOpenAnim, -5.0f); + this->timer = 5; + this->actionFunc = EnWdhand_LungeForPlayer; +} + +void EnWdhand_LungeForPlayer(EnWdhand* this, PlayState* play) { + Player* player = GET_PLAYER(play); + s32 allStepsDone = true; + s32 i; + s16 pitchToPlayerRightFoot; + s16 yawToPlayerRightFoot; + + SkelAnime_Update(&this->skelAnime); + + // Only activate the collider and grab check after a short delay + if (this->timer > 0) { + this->timer--; + if (this->timer == 0) { + this->collider.base.atFlags |= AT_ON; + } + return; + } + + Math_StepToF(&this->globalLimbScaleFactor, 1.5f, 0.05f); + EnWdhand_GetRelativeRotationsToPlayerRightFoot(this, player, &pitchToPlayerRightFoot, &yawToPlayerRightFoot); + + for (i = 0; i < ARRAY_COUNT(this->limbRotations); i++) { + s16 yawDiff = yawToPlayerRightFoot - this->limbRotations[i].y; + + if (ABS_ALT(yawDiff) < 0x4000) { + allStepsDone &= + Math_ScaledStepToS(&this->limbRotations[i].x, pitchToPlayerRightFoot, (i * 0.1f + 1.0f) * 1920.0f); + allStepsDone &= Math_ScaledStepToS(&this->limbRotations[i].y, yawToPlayerRightFoot, 0x800); + } else { + allStepsDone &= + Math_ScaledStepToS(&this->limbRotations[i].x, -pitchToPlayerRightFoot, (i * 0.1f + 1.0f) * 1920.0f); + allStepsDone &= Math_ScaledStepToS(&this->limbRotations[i].y, yawToPlayerRightFoot + 0x8000, 0x800); + } + } + + if ((this->collider.base.atFlags & AT_HIT) && play->grabPlayer(play, player)) { + // Touched the player, grab + EnWdhand_SetupGrabbedPlayer(this, play); + } else if (allStepsDone) { + // Failed to grab player in time + EnWdhand_SetupFailedToGrabPlayer(this); + } + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + } +} + +void EnWdhand_SetupFailedToGrabPlayer(EnWdhand* this) { + Animation_PlayOnce(&this->skelAnime, &gDexihandCloseAnim); + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.atFlags &= ~AT_HIT; + this->actionFunc = EnWdhand_FailedToGrabPlayer; + this->globalLimbScaleFactor = 1.5f; +} + +void EnWdhand_FailedToGrabPlayer(EnWdhand* this, PlayState* play) { + if (SkelAnime_Update(&this->skelAnime)) { + EnWdhand_SetupReturnToIdle(this); + } +} + +void EnWdhand_SetupReturnToIdle(EnWdhand* this) { + s32 i; + + Animation_MorphToLoop(&this->skelAnime, &gDexihandIdleAnim, -10.0f); + + for (i = 0; i < ARRAY_COUNT(this->limbRotations); i++) { + this->limbRotations[i].z = Rand_S16Offset(0x1800, 0x1000); + } + this->timer = 80; + this->actionFunc = EnWdhand_ReturnToIdle; +} + +void EnWdhand_ReturnToIdle(EnWdhand* this, PlayState* play) { + s32 i; + s32 allStepsDone; + + SkelAnime_Update(&this->skelAnime); + + this->timer--; + + allStepsDone = Math_ScaledStepToS(&this->limbRotations[0].x, EnWdhand_GetLimbXRotation(this, 0), 0x200); + + for (i = 1; i < ARRAY_COUNT(this->limbRotations); i++) { + allStepsDone &= Math_ScaledStepToS(&this->limbRotations[i].x, EnWdhand_GetLimbXRotation(this, i), 0x300); + if (this->timer == 20 * i) { + this->limbRotations[i].z = Rand_S16Offset(0x1800, 0x1000); + } + } + + if (this->timer == 0) { + this->timer = 80; + this->limbRotations[0].z = Rand_S16Offset(0x1800, 0x1000); + } + + Math_StepToF(&this->globalLimbScaleFactor, 1.0f, 0.05f); + + if (allStepsDone) { + EnWdhand_SetupIdle(this); + } +} + +void EnWdhand_SetupGrabbedPlayer(EnWdhand* this, PlayState* play) { + Player* player = GET_PLAYER(play); + s32 i; + + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.atFlags &= ~AT_HIT; + this->collider.base.ocFlags1 &= ~OC1_ON; + Animation_PlayOnce(&this->skelAnime, &gDexihandCloseAnim); + + for (i = 0; i < ARRAY_COUNT(this->limbRotations); i++) { + this->limbRotations[i].z = Rand_S16Offset(0x2000, 0x1000); + } + this->timer = 80; + player->actor.parent = &this->actor; + // Return to original state as set in init, that is relative -> world + Matrix_Transpose(&this->relativeToWorldTransform); + this->globalLimbScaleFactor = 1.5f; + + Actor_PlaySfx(&this->actor, NA_SE_EN_HANDW_GET); + this->actionFunc = EnWdhand_GrabbedPlayer; +} + +void EnWdhand_GrabbedPlayer(EnWdhand* this, PlayState* play) { + Player* player = GET_PLAYER(play); + s32 i; + s32 t; + + SkelAnime_Update(&this->skelAnime); + + this->timer--; + player->av2.actionVar2 = 0; + t = this->timer; + + for (i = 0; i < ARRAY_COUNT(this->limbRotations); i++) { + if (this->timer < 76) { + this->limbRotations[i].x = this->limbRotations[i].z * Math_SinF(t * (M_PI / 8)); + } else { + Math_ScaledStepToS(&this->limbRotations[i].x, this->limbRotations[i].z * Math_SinF(t * (M_PI / 8)), 0x400); + } + if (t % 16 == 0) { + if (t == 16) { + this->limbRotations[i].y = 0; + } else if (i != 0) { + this->limbRotations[i].y = this->limbRotations[i - 1].y + (s32)Rand_CenteredFloat(12288.0f); + } else { + this->limbRotations[0].y += (s16)(s32)Rand_CenteredFloat(12288.0f); + } + this->limbRotations[i].z = Rand_S16Offset(0x2000, 0x1000); + } + t += 2; + } + + if (this->timer < 4) { + Math_StepToF(&this->globalLimbScaleFactor, 1.5f, 1.0f / 8); + } else if (this->timer < 16) { + Math_StepToF(&this->globalLimbScaleFactor, 2.0f, 1.0f / 24); + } + + if (this->timer == 0) { + this->collider.base.ocFlags1 |= OC1_ON; + EnWdhand_SetupReturnToIdle(this); + } else if (this->timer >= 4) { + Vec3f limbPos; + + // Create a model -> world matrix + Matrix_Put(&this->relativeToWorldTransform); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + + Matrix_MultVecY(300.0f, &limbPos); + + for (i = 0; i < EN_WDHAND_NUM_SEGMENTS; i++) { + Matrix_Push(); + // Further rotate and translate + EnWdhand_SetTransform(this, i, false, &limbPos); + Matrix_MultVecY(2300.0f, &limbPos); + Matrix_Pop(); + } + + // Set transform for the hand + EnWdhand_SetTransform(this, EN_WDHAND_NUM_SEGMENTS, true, &limbPos); + // Copy the hand rotation to player shape rotation + Matrix_MtxFToYXZRot(Matrix_GetCurrent(), &player->actor.shape.rot, false); + + if (this->globalLimbScaleFactor > 1.0f) { + f32 zRotFactor = CLAMP_MAX(this->globalLimbScaleFactor, 1.5f); + + Matrix_RotateZS((zRotFactor - 1.0f) * -32768.0f, MTXMODE_APPLY); + } + Matrix_MultVecY(1000.0f, &player->actor.world.pos); + + if ((this->timer == 4) && (player->actor.parent == &this->actor)) { + // Throws the player, releasing them + player->av2.actionVar2 = 100; + player->actor.parent = NULL; + // Transpose again + Matrix_Transpose(&this->relativeToWorldTransform); + player->actor.shape.rot.x = 0; + player->actor.shape.rot.z = 0; + player->actor.world.pos.x += 2.0f * this->actor.velocity.x; + player->actor.world.pos.y += 2.0f * this->actor.velocity.y; + player->actor.world.pos.z += 2.0f * this->actor.velocity.z; + func_800B8D50(play, &this->actor, this->actor.speed, this->actor.world.rot.y, this->actor.velocity.y, 0); + Actor_PlaySfx(&this->actor, NA_SE_EN_HANDW_RELEASE); + } else if (this->timer == 2) { + Animation_PlayOnce(&this->skelAnime, &object_wdhand_Anim_000364); + } + } +} + +s32 EnWdhand_ShrinkLimb(EnWdhand* this, s32 limbIndex) { + this->limbScaleFactors[limbIndex] -= 0.1f; + if (this->limbScaleFactors[limbIndex] <= 0.05f) { + this->limbScaleFactors[limbIndex] = 0.05f; + return true; + } + return false; +} + +void EnWdhand_SetupDie(EnWdhand* this) { + s32 i; + + this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; + this->actor.flags |= ACTOR_FLAG_10; + + // Finds the particular collider that was hit + for (i = 0; i < ARRAY_COUNT(this->colliderElements); i++) { + if (this->collider.elements[i].info.bumperFlags & BUMP_HIT) { + break; + } + } + + // Record the limb indices immediately to either side of the cut point + this->limbIndexAfterCut = ((i + 1) / 2) % 4; + this->limbIndexBeforeCut = this->limbIndexAfterCut - 1; + + // Create model -> world transform + Matrix_Put(&this->relativeToWorldTransform); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + + Matrix_MultVecY(300.0f, &this->endPoints[EN_WDHAND_HAND_POINT]); + + for (i = 0; i < this->limbIndexAfterCut; i++) { + Matrix_Push(); + EnWdhand_SetTransform(this, i, false, &this->endPoints[EN_WDHAND_HAND_POINT]); + Matrix_MultVecY(2300.0f, &this->endPoints[EN_WDHAND_HAND_POINT]); + Matrix_Pop(); + } + + this->actor.velocity.y = 2.45f; + this->actor.velocity.x = 2.0f * Math_SinS(this->actor.world.rot.z); + this->actor.velocity.z = 2.0f * Math_CosS(this->actor.world.rot.z); + this->timer = 5; + this->actionFunc = EnWdhand_Die; +} + +void EnWdhand_Die(EnWdhand* this, PlayState* play) { + static Vec3f sEffectVelocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f sEffectAccel = { 0.0f, 0.05f, 0.0f }; + Vec3s* handCollider = &this->collider.elements[6].dim.worldSphere.center; + Vec3f spA0; + Vec3f sp94; + s16 effectScale; + s32 pointIndex; + s32 i; + s32 t; + Vec3f* endPoint = &this->endPoints[EN_WDHAND_HAND_POINT]; + s32 limbIndex; + Vec3f effPos; + + spA0.x = (endPoint->x - handCollider->x) * 0.5f; + spA0.y = (endPoint->y - handCollider->y) * 0.5f; + spA0.z = (endPoint->z - handCollider->z) * 0.5f; + Matrix_RotateXS(0x100, MTXMODE_NEW); + Matrix_MultVec3f(&spA0, &sp94); + endPoint->x = spA0.x + sp94.x + handCollider->x; + endPoint->y = spA0.y + sp94.y + handCollider->y; + endPoint->z = spA0.z + sp94.z + handCollider->z; + if (this->actor.velocity.y > -2.0f) { + this->actor.velocity.y += -0.15f; + } + Math_Vec3f_Sum(endPoint, &this->actor.velocity, endPoint); + + this->actor.world.rot.x += 0x100; + + if (this->limbIndexBeforeCut >= 0 && EnWdhand_ShrinkLimb(this, this->limbIndexBeforeCut)) { + this->limbIndexBeforeCut--; + } + + limbIndex = this->limbIndexAfterCut; + + if (this->limbIndexAfterCut < EN_WDHAND_NUM_SEGMENTS) { + if (EnWdhand_ShrinkLimb(this, this->limbIndexAfterCut)) { + this->limbIndexAfterCut++; + } + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.z, 0, MTXMODE_NEW); + Matrix_RotateYS(-this->actor.world.rot.z, MTXMODE_APPLY); + Matrix_Mult(&this->relativeToWorldTransform, MTXMODE_APPLY); + EnWdhand_SetTransform(this, limbIndex, false, endPoint); + Matrix_MultVecY(2.3f / this->limbScaleFactors[limbIndex], endPoint); + } + + this->timer--; + + limbIndex = this->limbIndexBeforeCut + 1; + t = this->timer; + for (i = 0; i < limbIndex; i++) { + if (this->timer > 0) { + Math_ScaledStepToS(&this->limbRotations[i].x, this->limbRotations[i].z * Math_SinF(t * (2 * M_PI / 7)), + 0x200); + } else if (Rand_ZeroOne() < 0.65f) { + this->limbRotations[i].x = this->limbRotations[i].z * Math_SinF(t * (2 * M_PI / 7)); + } + if (t % 7 == 0) { + if (i != 0) { + this->limbRotations[i].y = this->limbRotations[i - 1].y + (s32)Rand_CenteredFloat(12288.0f); + } else { + this->limbRotations[0].y += (s16)(s32)Rand_CenteredFloat(12288.0f); + } + this->limbRotations[i].z = Rand_S16Offset(i * 0x100 + 0xC00, 0x800); + } + t += 2; + } + + if ((this->limbIndexBeforeCut < 0) && (this->limbIndexAfterCut >= EN_WDHAND_NUM_SEGMENTS)) { + if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.001f)) { + for (pointIndex = 0; pointIndex < ARRAY_COUNT(this->endPoints); pointIndex++) { + for (i = 0; i < 5; i++) { + endPoint = &this->endPoints[pointIndex]; + + sEffectVelocity.y = Rand_ZeroOne() + 1.0f; + effectScale = Rand_S16Offset(0x28, 0x28); + effPos.x = endPoint->x + Rand_CenteredFloat(12.0f); + effPos.y = endPoint->y + Rand_CenteredFloat(12.0f); + effPos.z = endPoint->z + Rand_CenteredFloat(12.0f); + EffectSsDtBubble_SpawnColorProfile(play, &effPos, &sEffectVelocity, &sEffectAccel, effectScale, 25, + 2, true); + } + } + + Actor_Kill(&this->actor); + } else { + this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; + } + } + + for (i = 0; i < ARRAY_COUNT(this->endPoints); i++) { + sEffectVelocity.y = Rand_ZeroOne() + 1.0f; + EffectSsDtBubble_SpawnColorProfile(play, &this->endPoints[i], &sEffectVelocity, &sEffectAccel, + Rand_S16Offset(40, 40), 25, 2, true); + } +} + +void EnWdhand_UpdateDamage(EnWdhand* this, PlayState* play) { + if (this->collider.base.acFlags & AC_HIT) { + Player* player = GET_PLAYER(play); + + this->collider.base.acFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_HIT; + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.ocFlags1 &= ~AT_ON; + + Actor_SetDropFlagJntSph(&this->actor, &this->collider); + Enemy_StartFinishingBlow(play, &this->actor); + + if ((player->stateFlags2 & PLAYER_STATE2_80) && (&this->actor == player->actor.parent)) { + // Drop the player + player->av2.actionVar2 = 100; + player->actor.parent = NULL; + player->actor.shape.rot.x = 0; + player->actor.shape.rot.z = 0; + func_800B8D50(play, &this->actor, this->actor.speed, this->actor.world.rot.y, this->actor.velocity.y, 0); + } else { + // Only transpose if player is not grabbed, since it is already in the correct state if so. + Matrix_Transpose(&this->relativeToWorldTransform); + } + + EnWdhand_SetupDie(this); + } +} + +void EnWdhand_Update(Actor* thisx, PlayState* play) { + EnWdhand* this = THIS; + + EnWdhand_UpdateDamage(this, play); + + this->actionFunc(this, play); + + if (this->collider.base.atFlags & AT_ON) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); + } + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); + } + if (this->collider.base.ocFlags1 & OC1_ON) { + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } +} + +void EnWdhand_UpdateColliderLocationsForLimb(EnWdhand* this, s32 limbIndex, Vec3f* limbPos) { + EnWdhand_SetTransform(this, limbIndex, true, limbPos); + Matrix_MultVecY(575.0f, limbPos); + EnWdhand_Vec3fToVec3s(&this->collider.elements[2 * limbIndex + 0].dim.worldSphere.center, limbPos); + Matrix_MultVecY(1725.0f, limbPos); + EnWdhand_Vec3fToVec3s(&this->collider.elements[2 * limbIndex + 1].dim.worldSphere.center, limbPos); + Matrix_MultVecY(2300.0f, limbPos); +} + +void EnWdhand_Draw(Actor* thisx, PlayState* play) { + EnWdhand* this = THIS; + Vec3f limbPos; + Gfx* gfx; + s32 limbIndex; + s32 i; + + OPEN_DISPS(play->state.gfxCtx); + + gfx = POLY_OPA_DISP; + + gSPDisplayList(&gfx[0], gSetupDLs[25]); + gSPMatrix(&gfx[1], Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(&gfx[2], gDexihandBaseDL); + + Matrix_MultVecY(300.0f, &limbPos); + + gfx = &gfx[3]; + + limbIndex = this->limbIndexBeforeCut + 1; + for (i = 0; i < limbIndex; i++) { + Matrix_Push(); + EnWdhand_UpdateColliderLocationsForLimb(this, i, &limbPos); + gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, gDexihandArmSegmentDL); + Matrix_Pop(); + } + + Math_Vec3f_Copy(&this->endPoints[EN_WDHAND_CUT_POINT], &limbPos); + Math_Vec3f_Copy(&limbPos, &this->endPoints[EN_WDHAND_HAND_POINT]); + + if (this->actor.world.rot.x != 0) { + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.z, 0, MTXMODE_NEW); + Matrix_RotateYS(-this->actor.world.rot.z, MTXMODE_APPLY); + Matrix_Mult(&this->relativeToWorldTransform, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + } + + for (i = this->limbIndexAfterCut; i < EN_WDHAND_NUM_SEGMENTS; i++) { + Matrix_Push(); + EnWdhand_UpdateColliderLocationsForLimb(this, i, &limbPos); + gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, gDexihandArmSegmentDL); + Matrix_Pop(); + } + + EnWdhand_SetTransform(this, EN_WDHAND_NUM_SEGMENTS, true, &limbPos); + + if (this->globalLimbScaleFactor > 1.0f) { + s32 pad; + f32 scale = CLAMP_MAX(this->globalLimbScaleFactor, 1.5f); + + Matrix_RotateZS((scale - 1.0f) * -32768.0f, MTXMODE_APPLY); + } + if (this->actor.scale.x < 0.01f) { + Matrix_Translate(0.0f, (10.0f / this->actor.scale.x) - 1000.0f, 0.0f, MTXMODE_APPLY); + } + + Math_Vec3f_Copy(&this->actor.focus.pos, &limbPos); + Matrix_MultVecY(1000.0f, &limbPos); + EnWdhand_Vec3fToVec3s(&this->collider.elements[6].dim.worldSphere.center, &limbPos); + + POLY_OPA_DISP = gfx; + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, NULL, + NULL, &this->actor); + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.h b/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.h index 5eb02fa1a2..1d479fc776 100644 --- a/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.h +++ b/src/overlays/actors/ovl_En_Wdhand/z_en_wdhand.h @@ -2,16 +2,38 @@ #define Z_EN_WDHAND_H #include "global.h" +#include "objects/object_wdhand/object_wdhand.h" struct EnWdhand; typedef void (*EnWdhandActionFunc)(struct EnWdhand*, PlayState*); +#define EN_WDHAND_NUM_SEGMENTS 3 +#define EN_WDHAND_NUM_COLLIDER_ELEMENTS (2 * EN_WDHAND_NUM_SEGMENTS + 1) + +#define EN_WDHAND_CUT_POINT 0 +#define EN_WDHAND_HAND_POINT 1 + +#define EN_WDHAND_INIT_VELOCITY_MAX 0x7F +#define EN_WDHAND_GET_Y_INIT_VELOCITY(thisx) (((thisx)->params >> 0) & 0x7F) +#define EN_WDHAND_GET_Z_INIT_VELOCITY(thisx) (((thisx)->params >> 7) & 0x7F) + typedef struct EnWdhand { /* 0x000 */ Actor actor; - /* 0x144 */ char unk_144[0xA4]; + /* 0x144 */ SkelAnime skelAnime; + /* 0x188 */ Vec3s jointTable[DEXIHAND_LIMB_MAX]; + /* 0x1B8 */ Vec3s morphTable[DEXIHAND_LIMB_MAX]; /* 0x1E8 */ EnWdhandActionFunc actionFunc; - /* 0x1EC */ char unk_1EC[0x268]; + /* 0x1EC */ s16 limbIndexBeforeCut; + /* 0x1EE */ s16 limbIndexAfterCut; + /* 0x1F0 */ s16 timer; + /* 0x1F2 */ Vec3s limbRotations[EN_WDHAND_NUM_SEGMENTS + 1]; + /* 0x20C */ f32 globalLimbScaleFactor; + /* 0x20C */ f32 limbScaleFactors[EN_WDHAND_NUM_SEGMENTS]; + /* 0x21C */ MtxF relativeToWorldTransform; + /* 0x25C */ Vec3f endPoints[2]; + /* 0x274 */ ColliderJntSph collider; + /* 0x294 */ ColliderJntSphElement colliderElements[EN_WDHAND_NUM_COLLIDER_ELEMENTS]; } EnWdhand; // size = 0x454 #endif // Z_EN_WDHAND_H diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt index 61d7a39cbf..576f0cd53e 100644 --- a/tools/disasm/variables.txt +++ b/tools/disasm/variables.txt @@ -13198,8 +13198,7 @@ 0x80AF64DC:("D_80AF64DC","UNK_TYPE1","",0x1), 0x80AF64EC:("D_80AF64EC","UNK_TYPE1","",0x1), 0x80AF64F4:("D_80AF64F4","UNK_TYPE1","",0x1), - 0x80AF6514:("D_80AF6514","UNK_TYPE1","",0x1), - 0x80AF6518:("D_80AF6518","f32","",0x4), + 0x80AF6514:("D_80AF6514","Vec3f","",0xC), 0x80AF6520:("D_80AF6520","UNK_TYPE1","",0x1), 0x80AF6530:("D_80AF6530","f32","",0x4), 0x80AF6534:("D_80AF6534","f32","",0x4),