diff --git a/assets/xml/objects/object_mu.xml b/assets/xml/objects/object_mu.xml index 1f246b07b5..37886e8f50 100644 --- a/assets/xml/objects/object_mu.xml +++ b/assets/xml/objects/object_mu.xml @@ -1,62 +1,63 @@  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec b/spec index 48b1695ac5..0b500363a4 100644 --- a/spec +++ b/spec @@ -2029,8 +2029,7 @@ beginseg name "ovl_En_Tg" compress include "build/src/overlays/actors/ovl_En_Tg/z_en_tg.o" - include "build/data/ovl_En_Tg/ovl_En_Tg.data.o" - include "build/data/ovl_En_Tg/ovl_En_Tg.reloc.o" + include "build/src/overlays/actors/ovl_En_Tg/ovl_En_Tg_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_En_Fu/z_en_fu.c b/src/overlays/actors/ovl_En_Fu/z_en_fu.c index 1704451ec3..8ab640253a 100644 --- a/src/overlays/actors/ovl_En_Fu/z_en_fu.c +++ b/src/overlays/actors/ovl_En_Fu/z_en_fu.c @@ -74,13 +74,13 @@ static Vec3f D_80964B18 = { 0.0f, 55.0f, 12.0f }; static Vec3f D_80964B24 = { 0.0f, 60.0f, 0.0f }; static AnimationInfo sAnimationInfo[] = { - { &object_mu_Anim_0053E0, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -4.0f }, - { &object_mu_Anim_001F74, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -4.0f }, - { &object_mu_Anim_002F64, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -4.0f }, - { &object_mu_Anim_004904, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, - { &object_mu_Anim_005304, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, - { &object_mu_Anim_005304, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, - { &object_mu_Anim_00BAC4, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, 0.0f }, + { &gHoneyAndDarlingIdleAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -4.0f }, + { &gHoneyAndDarlingCupCheeksLoopAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -4.0f }, + { &gHoneyAndDarlingHugLoopAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -4.0f }, + { &gHoneyAndDarlingGameDanceLoopAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, + { &gHoneyAndDarlingHoldHandsLoopAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, + { &gHoneyAndDarlingHoldHandsLoopAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, + { &gHoneyAndDarlingSurpiseAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, 0.0f }, }; static ColliderCylinderInit sCylinderInit = { @@ -194,8 +194,8 @@ void EnFu_Init(Actor* thisx, PlayState* play) { if (fuKaiten != NULL) { ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); - SkelAnime_InitFlex(play, &this->skelAnime, &object_mu_Skel_00B2B0, &object_mu_Anim_001F74, this->jointTable, - this->morphTable, 21); + SkelAnime_InitFlex(play, &this->skelAnime, &gHoneyAndDarlingSkel, &gHoneyAndDarlingCupCheeksLoopAnim, + this->jointTable, this->morphTable, HONEY_AND_DARLING_LIMB_MAX); Collider_InitCylinder(play, &this->collider); Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit); this->actor.colChkInfo.mass = MASS_IMMOVABLE; @@ -1454,7 +1454,7 @@ void func_80964950(PlayState* play, EnFuUnkStruct* ptr, s32 len) { for (i = 0; i < len; i++, ptr++) { if (ptr->unk_36 == 1) { if (!flag) { - gSPDisplayList(POLY_OPA_DISP++, object_mu_DL_00B0A0); + gSPDisplayList(POLY_OPA_DISP++, gHoneyAndDarlingHeartMaterialDL); flag = true; } Matrix_Translate(ptr->unk_08.x, ptr->unk_08.y, ptr->unk_08.z, MTXMODE_NEW); @@ -1463,7 +1463,7 @@ void func_80964950(PlayState* play, EnFuUnkStruct* ptr, s32 len) { gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(gDropRecoveryHeartTex)); gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); - gSPDisplayList(POLY_OPA_DISP++, object_mu_DL_00B0E0); + gSPDisplayList(POLY_OPA_DISP++, gHoneyAndDarlingHeartModelDL); } } diff --git a/src/overlays/actors/ovl_En_Tg/z_en_tg.c b/src/overlays/actors/ovl_En_Tg/z_en_tg.c index c090a050de..f4c970ea80 100644 --- a/src/overlays/actors/ovl_En_Tg/z_en_tg.c +++ b/src/overlays/actors/ovl_En_Tg/z_en_tg.c @@ -1,10 +1,11 @@ /* * File: z_en_tg.c * Overlay: ovl_En_Tg - * Description: Target Game (Honey & Darling) + * Description: Target Game (Honey & Darling) - End Credits Scene */ #include "z_en_tg.h" +#include "objects/gameplay_keep/gameplay_keep.h" #define FLAGS (ACTOR_FLAG_1 | ACTOR_FLAG_8) @@ -15,9 +16,11 @@ void EnTg_Destroy(Actor* thisx, PlayState* play); void EnTg_Update(Actor* thisx, PlayState* play); void EnTg_Draw(Actor* thisx, PlayState* play); -void func_8098FA70(EnTg* this, PlayState* play); +void EnTg_Idle(EnTg* this, PlayState* play); +void EnTg_UpdateHearts(PlayState* play, EnTgHeartEffect* effect, s32 numEffects); +void EnTg_DrawHearts(PlayState* play, EnTgHeartEffect* effect, s32 numEffects); +void EnTg_SpawnHeart(EnTg* this, EnTgHeartEffect* effect, Vec3f* heartStartPos, s32 numEffects); -#if 0 const ActorInit En_Tg_InitVars = { ACTOR_EN_TG, ACTORCAT_NPC, @@ -30,18 +33,29 @@ const ActorInit En_Tg_InitVars = { (ActorFunc)EnTg_Draw, }; -// static ColliderCylinderInit sCylinderInit = { -static ColliderCylinderInit D_809901C0 = { - { COLTYPE_HIT0, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_CYLINDER, }, - { ELEMTYPE_UNK1, { 0x00000000, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, { 18, 64, 0, { 0, 0, 0 } }, }; -// sColChkInfoInit -static CollisionCheckInfoInit2 D_809901EC = { 0, 0, 0, 0, MASS_IMMOVABLE }; +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; -// static DamageTable sDamageTable = { -static DamageTable D_809901F8 = { +static DamageTable sDamageTable = { /* Deku Nut */ DMG_ENTRY(0, 0x0), /* Deku Stick */ DMG_ENTRY(0, 0x0), /* Horse trample */ DMG_ENTRY(0, 0x0), @@ -76,37 +90,193 @@ static DamageTable D_809901F8 = { /* Powder Keg */ DMG_ENTRY(0, 0x0), }; -#endif +static AnimationInfoS sAnimationInfo[] = { + { &gHoneyAndDarlingIdleAnim, 1.0f, 0, -1, ANIMMODE_LOOP, 0 }, +}; -extern ColliderCylinderInit D_809901C0; -extern CollisionCheckInfoInit2 D_809901EC; -extern DamageTable D_809901F8; +void EnTg_ChangeAnim(SkelAnime* skelAnime, AnimationInfoS* animationInfo, s16 animIndex) { + f32 endFrame; -extern UNK_TYPE D_0600B0E0; -extern UNK_TYPE D_0600B2B0; + animationInfo += animIndex; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8098F800.s") + if (animationInfo->frameCount < 0) { + endFrame = Animation_GetLastFrame(animationInfo->animation); + } else { + endFrame = animationInfo->frameCount; + } + Animation_Change(skelAnime, animationInfo->animation, animationInfo->playSpeed, animationInfo->startFrame, endFrame, + animationInfo->mode, animationInfo->morphFrames); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8098F8A8.s") +void EnTg_UpdateCollider(EnTg* this, PlayState* play) { + this->collider.dim.pos.x = this->actor.world.pos.x; + this->collider.dim.pos.y = this->actor.world.pos.y; + this->collider.dim.pos.z = this->actor.world.pos.z; + CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8098F928.s") +void EnTg_UpdateSkelAnime(EnTg* this, PlayState* play) { + SkelAnime_Update(&this->skelAnime); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/EnTg_Init.s") +void EnTg_Init(Actor* thisx, PlayState* play) { + EnTg* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/EnTg_Destroy.s") + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + SkelAnime_InitFlex(play, &this->skelAnime, &gHoneyAndDarlingSkel, NULL, this->jointTable, this->morphTable, + HONEY_AND_DARLING_LIMB_MAX); + EnTg_ChangeAnim(&this->skelAnime, sAnimationInfo, 0); + Collider_InitCylinder(play, &this->collider); + Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + Actor_SetScale(&this->actor, 0.01f); + this->actionFunc = EnTg_Idle; + this->actor.gravity = -4.0f; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8098FA70.s") +void EnTg_Destroy(Actor* thisx, PlayState* play) { + EnTg* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/EnTg_Update.s") + Collider_DestroyCylinder(play, &this->collider); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8098FBB4.s") +/** + * The actor spins, and a heart is spawned above the actor every 12 frames. + */ +void EnTg_Idle(EnTg* this, PlayState* play) { + Vec3f heartStartPos; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8098FBD0.s") + this->actor.shape.rot.y += sREG(0) + 0x258; + this->actor.world.rot = this->actor.shape.rot; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/EnTg_Draw.s") + if (DECR(this->spawnHeartTimer) == 0) { + this->spawnHeartTimer = 12; + heartStartPos = this->actor.world.pos; + heartStartPos.y += 62.0f; + EnTg_SpawnHeart(this, this->effects, &heartStartPos, ARRAY_COUNT(this->effects)); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8098FD50.s") +void EnTg_Update(Actor* thisx, PlayState* play) { + EnTg* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8098FEA8.s") + this->actionFunc(this, play); + Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, 0.0f, 0.0f, 4); + EnTg_UpdateSkelAnime(this, play); + EnTg_UpdateHearts(play, this->effects, ARRAY_COUNT(this->effects)); + EnTg_UpdateCollider(this, play); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tg/func_8099000C.s") +s32 EnTg_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { + return false; +} + +void EnTg_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { + EnTg* this = THIS; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == HONEY_AND_DARLING_LIMB_MAN_HEAD) { + Matrix_MultVec3f(&zeroVec, &this->actor.focus.pos); + } +} + +void EnTg_Draw(Actor* thisx, PlayState* play) { + s32 pad; + EnTg* this = THIS; + + Matrix_Push(); + EnTg_DrawHearts(play, this->effects, ARRAY_COUNT(this->effects)); + Matrix_Pop(); + + OPEN_DISPS(play->state.gfxCtx); + func_8012C28C(play->state.gfxCtx); + + gDPPipeSync(POLY_OPA_DISP++); + gSPSegment(POLY_OPA_DISP++, 0x08, Gfx_EnvColor(play->state.gfxCtx, 0, 50, 160, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, Gfx_EnvColor(play->state.gfxCtx, 255, 255, 255, 0)); + + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnTg_OverrideLimbDraw, EnTg_PostLimbDraw, &this->actor); + + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * Spawns a heart at the first effects array index that's not enabled. + * Because of the frame counts, only two hearts are ever spawned at a time. + */ +void EnTg_SpawnHeart(EnTg* this, EnTgHeartEffect* effect, Vec3f* heartStartPos, s32 numEffects) { + Vec3f heartVelocity = { 0.0f, 1.5f, 0.0f }; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 i; + + for (i = 0; i < numEffects && effect->isEnabled; i++, effect++) {} + + if (i < numEffects) { + effect->isEnabled = true; + effect->pos = *heartStartPos; + effect->velocity = heartVelocity; + effect->unusedZeroVec = zeroVec; + effect->scale = 0.01f; + effect->pos.x += 4.0f * Math_SinS(this->actor.shape.rot.y); + effect->pos.z += 4.0f * Math_CosS(this->actor.shape.rot.y); + effect->timer = 16; + } +} + +/** + * The heart path is curvy as it floats up because of the use of Math_SinS and Math_CosS. + */ +void EnTg_UpdateHearts(PlayState* play, EnTgHeartEffect* effect, s32 numEffects) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s16 yaw = Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); + s32 i; + + for (i = 0; i < numEffects; i++, effect++) { + if (effect->isEnabled == true) { + if (DECR(effect->timer) == 0) { + effect->isEnabled = false; + } + effect->pos.y += effect->velocity.y; + effect->pos.x += 2.0f * Math_SinS(effect->angle); + effect->pos.z += 2.0f * Math_CosS(effect->angle); + + Matrix_Push(); + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateYS(yaw, MTXMODE_APPLY); + Matrix_MultVec3f(&zeroVec, &effect->pos); + Matrix_Pop(); + + effect->angle += 0x1770; + } + } +} + +void EnTg_DrawHearts(PlayState* play, EnTgHeartEffect* effect, s32 numEffects) { + s32 i; + s32 isMaterialApplied = false; + + OPEN_DISPS(play->state.gfxCtx); + + POLY_OPA_DISP = func_801660B8(play, POLY_OPA_DISP); + POLY_OPA_DISP = func_8012C724(POLY_OPA_DISP); + + for (i = 0; i < numEffects; i++, effect++) { + if (effect->isEnabled == true) { + if (!isMaterialApplied) { + gSPDisplayList(POLY_OPA_DISP++, gHoneyAndDarlingHeartMaterialDL); + isMaterialApplied = true; + } + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&play->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + + gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(gDropRecoveryHeartTex)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gHoneyAndDarlingHeartModelDL); + } + } + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Tg/z_en_tg.h b/src/overlays/actors/ovl_En_Tg/z_en_tg.h index 2798b99b3d..3cdcb57f1d 100644 --- a/src/overlays/actors/ovl_En_Tg/z_en_tg.h +++ b/src/overlays/actors/ovl_En_Tg/z_en_tg.h @@ -2,15 +2,33 @@ #define Z_EN_TG_H #include "global.h" +#include "objects/object_mu/object_mu.h" struct EnTg; typedef void (*EnTgActionFunc)(struct EnTg*, PlayState*); +typedef struct EnTgHeartEffect { + /* 0x00 */ u8 isEnabled; + /* 0x01 */ u8 timer; + /* 0x04 */ f32 scale; + /* 0x08 */ UNK_TYPE1 unk8[0xC]; + /* 0x14 */ Vec3f pos; + /* 0x20 */ Vec3f unusedZeroVec; + /* 0x2C */ Vec3f velocity; + /* 0x38 */ s16 angle; +} EnTgHeartEffect; // size = 0x3C + typedef struct EnTg { /* 0x000 */ Actor actor; /* 0x144 */ EnTgActionFunc actionFunc; - /* 0x148 */ char unk_148[0x400]; + /* 0x148 */ SkelAnime skelAnime; + /* 0x18C */ ColliderCylinder collider; + /* 0x1D8 */ UNK_TYPE1 unk1D8[0x18]; + /* 0x1F0 */ Vec3s jointTable[HONEY_AND_DARLING_LIMB_MAX]; + /* 0x26E */ Vec3s morphTable[HONEY_AND_DARLING_LIMB_MAX]; + /* 0x2EC */ s16 spawnHeartTimer; + /* 0x2F0 */ EnTgHeartEffect effects[10]; } EnTg; // size = 0x548 extern const ActorInit En_Tg_InitVars; diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 3984924be4..4526760d5e 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -8412,19 +8412,19 @@ 0x8098F66C:("func_8098F66C",), 0x8098F680:("func_8098F680",), 0x8098F6FC:("ObjMure3_Update",), - 0x8098F800:("func_8098F800",), - 0x8098F8A8:("func_8098F8A8",), - 0x8098F928:("func_8098F928",), + 0x8098F800:("EnTg_ChangeAnim",), + 0x8098F8A8:("EnTg_UpdateCollider",), + 0x8098F928:("EnTg_UpdateSkelAnime",), 0x8098F954:("EnTg_Init",), 0x8098FA44:("EnTg_Destroy",), - 0x8098FA70:("func_8098FA70",), + 0x8098FA70:("EnTg_Idle",), 0x8098FB28:("EnTg_Update",), - 0x8098FBB4:("func_8098FBB4",), - 0x8098FBD0:("func_8098FBD0",), + 0x8098FBB4:("EnTg_OverrideLimbDraw",), + 0x8098FBD0:("EnTg_PostLimbDraw",), 0x8098FC2C:("EnTg_Draw",), - 0x8098FD50:("func_8098FD50",), - 0x8098FEA8:("func_8098FEA8",), - 0x8099000C:("func_8099000C",), + 0x8098FD50:("EnTg_SpawnHeart",), + 0x8098FEA8:("EnTg_UpdateHearts",), + 0x8099000C:("EnTg_DrawHearts",), 0x80990310:("EnWf_Init",), 0x80990784:("EnWf_Destroy",), 0x809907D4:("func_809907D4",),