diff --git a/assets/xml/objects/object_zl4.xml b/assets/xml/objects/object_zl4.xml index dafdef8cda..65596c3708 100644 --- a/assets/xml/objects/object_zl4.xml +++ b/assets/xml/objects/object_zl4.xml @@ -1,125 +1,164 @@  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec b/spec index 1e34c3bf29..d43222b06b 100644 --- a/spec +++ b/spec @@ -2704,8 +2704,7 @@ beginseg name "ovl_Dm_Zl" compress include "build/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.o" - include "build/data/ovl_Dm_Zl/ovl_Dm_Zl.data.o" - include "build/data/ovl_Dm_Zl/ovl_Dm_Zl.reloc.o" + include "build/src/overlays/actors/ovl_Dm_Zl/ovl_Dm_Zl_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.c b/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.c index fc9d21b3d0..a4c2852334 100644 --- a/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.c +++ b/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.c @@ -1,7 +1,7 @@ /* * File: z_dm_zl.c * Overlay: ovl_Dm_Zl - * Description: Child Zelda (Cutscenes) + * Description: Child Zelda (Re-learning Song of Time cutscene) */ #include "z_dm_zl.h" @@ -15,9 +15,8 @@ void DmZl_Destroy(Actor* thisx, GlobalContext* globalCtx); void DmZl_Update(Actor* thisx, GlobalContext* globalCtx); void DmZl_Draw(Actor* thisx, GlobalContext* globalCtx); -void func_80A382FC(DmZl* this, GlobalContext* globalCtx); +void DmZl_DoNothing(DmZl* this, GlobalContext* globalCtx); -#if 0 const ActorInit Dm_Zl_InitVars = { ACTOR_DM_ZL, ACTORCAT_ITEMACTION, @@ -30,27 +29,269 @@ const ActorInit Dm_Zl_InitVars = { (ActorFunc)DmZl_Draw, }; -#endif +static AnimationInfo sAnimations[] = { + { &gDmZl4FacingAwayHandsOverEmblemLoop, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDmZl4TurningAround2Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -10.0f }, + { &gDmZl4HandsOverEmblemLoopAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDmZl4GivingItemStartAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -10.0f }, + { &gDmZl4GivingItemLoopAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDmZl4RaisingOcarinaToPlayAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -10.0f }, + { &gDmZl4PlayingOcarinaAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, +}; -extern UNK_TYPE D_0600DE08; -extern UNK_TYPE D_0600E038; +typedef enum { + /* 0 */ ZELDA_ANIM_FACING_AWAY, + /* 1 */ ZELDA_ANIM_TURNING_TOWARD_PLAYER, + /* 2 */ ZELDA_ANIM_FACING_PLAYER, + /* 3 */ ZELDA_ANIM_GIVING_OCARINA_START, + /* 4 */ ZELDA_ANIM_GIVING_OCARINA, + /* 5 */ ZELDA_ANIM_PLAYING_OCARINA_START, + /* 6 */ ZELDA_ANIM_PLAYING_OCARINA, + /* 7 */ ZELDA_ANIM_MAX, +} DmZlAnimations; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/func_80A38190.s") +static TexturePtr sMouthTextures[] = { + gZl4MouthNeutralTex, + gZl4MouthOpenSmilingTex, + gZl4MouthFrowningTex, + gZl4MouthOpenTex, +}; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/DmZl_Init.s") +typedef enum { + /* 0 */ ZELDA_MOUTH_NEUTRAL, + /* 1 */ ZELDA_MOUTH_SMILING, + /* 2 */ ZELDA_MOUTH_FROWNING, + /* 3 */ ZELDA_MOUTH_OPEN, +} DmZlMouthTextures; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/DmZl_Destroy.s") +static TexturePtr sEyeTextures[] = { + gDmZl4EyeOpenNormalTex, + gDmZl4EyeHalfTex, + gDmZl4EyeClosedTex, + gDmZl4EyeWideTex, + gDmZl4EyeHappyTex, + gDmZl4EyeOpenLookingLeftTex, + gDmZl4EyeOpenLookingRightTex, +}; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/func_80A382FC.s") +typedef enum { + /* 0 */ ZELDA_EYE_OPEN_NORMAL, + /* 1 */ ZELDA_EYE_BLINKING, + /* 2 */ ZELDA_EYE_CLOSED, + /* 3 */ ZELDA_EYE_WIDE, + /* 4 */ ZELDA_EYE_HAPPY, + /* 5 */ ZELDA_EYE_OPEN_LOOKING_LEFT, + /* 6 */ ZELDA_EYE_OPEN_LOOKING_RIGHT, +} DmZlEyeTextures; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/func_80A3830C.s") +// Unused in MM +typedef enum { + /* 0 */ ZELDA_EYE_STATE_NORMAL, + /* 1 */ ZELDA_EYE_STATE_CLOSED, + /* 2 */ ZELDA_EYE_STATE_LOOKING_LEFT, + /* 3 */ ZELDA_EYE_STATE_LOOKING_RIGHT, + /* 4 */ ZELDA_EYE_STATE_WIDE, + /* 5 */ ZELDA_EYE_STATE_HAPPY, + /* 6 */ ZELDA_EYE_STATE_CLOSED2, +} DmZlEyeStates; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/func_80A38468.s") +/** + * This function is always called with unusedExtraOffset = 0. + */ +void DmZl_ChangeAnimation(SkelAnime* skelAnime, AnimationInfo animation[], u16 unusedExtraOffset) { + f32 endFrame; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/DmZl_Update.s") + animation += unusedExtraOffset; + endFrame = (animation->frameCount < 0.0f) ? Animation_GetLastFrame(animation->animation) : animation->frameCount; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/func_80A3862C.s") + Animation_Change(skelAnime, animation->animation, animation->playSpeed, animation->startFrame, endFrame, + animation->mode, animation->morphFrames); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/func_80A38648.s") +void DmZl_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DmZl* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Dm_Zl/DmZl_Draw.s") + this->animationIndex = ZELDA_ANIM_FACING_AWAY; + this->unk_2BA = 0; + this->actor.targetArrowOffset = 1000.0f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 24.0f); + // these three set to NULL should mean they are dynamically allocated + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gZl4Skeleton, NULL, NULL, NULL, 0); + DmZl_ChangeAnimation(&this->skelAnime, &sAnimations[this->animationIndex], 0); + Actor_SetScale(&this->actor, 0.01f); + this->actionFunc = DmZl_DoNothing; +} + +void DmZl_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void DmZl_DoNothing(DmZl* this, GlobalContext* globalCtx) { +} + +void DmZl_UpdateCutscene(DmZl* this, GlobalContext* globalCtx) { + s32 actionIndex; + + if (Cutscene_CheckActorAction(globalCtx, 0x66)) { + actionIndex = Cutscene_GetActorActionIndex(globalCtx, 0x66); + if (globalCtx->csCtx.frames == globalCtx->csCtx.actorActions[actionIndex]->startFrame) { + s16 nextAnimationIndex = ZELDA_ANIM_FACING_AWAY; + + switch (globalCtx->csCtx.actorActions[actionIndex]->action) { + default: + case 1: + break; + case 2: + nextAnimationIndex = ZELDA_ANIM_TURNING_TOWARD_PLAYER; + break; + case 3: + nextAnimationIndex = ZELDA_ANIM_GIVING_OCARINA_START; + break; + case 4: + nextAnimationIndex = ZELDA_ANIM_PLAYING_OCARINA_START; + break; + } + + if (nextAnimationIndex != this->animationIndex) { + this->animationIndex = nextAnimationIndex; + DmZl_ChangeAnimation(&this->skelAnime, &sAnimations[this->animationIndex], 0); + } + } + + Cutscene_ActorTranslateAndYaw(&this->actor, globalCtx, actionIndex); + } + + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + actionIndex = this->animationIndex; + + if ((actionIndex == ZELDA_ANIM_TURNING_TOWARD_PLAYER) || (actionIndex == ZELDA_ANIM_GIVING_OCARINA_START) || + (actionIndex == ZELDA_ANIM_PLAYING_OCARINA_START)) { + // these animations don't loop at the end, they lead into the next animation + this->animationIndex++; + DmZl_ChangeAnimation(&this->skelAnime, &sAnimations[this->animationIndex], 0); + } + } +} + +/** + * Updates the eye blinking and state, and mouth textures. + */ +void DmZl_UpdateFace(DmZl* this) { + if (this->blinkTimer > 0) { + this->blinkTimer--; + } else { + this->blinkTimer = 0; + } + + if (this->blinkTimer < 3) { + this->eyeTextureIndexRight = this->blinkTimer; + this->eyeTextureIndexLeft = this->blinkTimer; + } + + // nextEyeState is never changed by this actor in MM, only ZELDA_EYE_STATE_NORMAL used. + switch (this->nextEyeState) { + case ZELDA_EYE_STATE_NORMAL: + if (this->blinkTimer == 0) { + this->blinkTimer = Rand_S16Offset(30, 30); + } + break; + case ZELDA_EYE_STATE_CLOSED: + if (this->blinkTimer == 0) { + this->eyeTextureIndexLeft = this->eyeTextureIndexRight = ZELDA_EYE_CLOSED; + } + break; + case ZELDA_EYE_STATE_LOOKING_LEFT: + if (this->blinkTimer == 0) { + this->eyeTextureIndexLeft = ZELDA_EYE_OPEN_LOOKING_LEFT; + this->eyeTextureIndexRight = ZELDA_EYE_OPEN_LOOKING_RIGHT; + } + break; + case ZELDA_EYE_STATE_LOOKING_RIGHT: + if (this->blinkTimer == 0) { + this->eyeTextureIndexLeft = ZELDA_EYE_OPEN_LOOKING_RIGHT; + this->eyeTextureIndexRight = ZELDA_EYE_OPEN_LOOKING_LEFT; + } + break; + case ZELDA_EYE_STATE_WIDE: + if (this->blinkTimer == 0) { + this->eyeTextureIndexLeft = this->eyeTextureIndexRight = ZELDA_EYE_WIDE; + } + break; + case ZELDA_EYE_STATE_HAPPY: + if (this->blinkTimer == 0) { + this->eyeTextureIndexLeft = this->eyeTextureIndexRight = ZELDA_EYE_HAPPY; + } + break; + case ZELDA_EYE_STATE_CLOSED2: + if (this->blinkTimer >= 3) { + this->blinkTimer = 0; + } + break; + } + + // nextMouthState is never changed by this actor in MM, only ZELDA_MOUTH_NEUTRAL used. + switch (this->nextMouthState) { + default: + this->mouthTextureIndex = ZELDA_MOUTH_NEUTRAL; + break; + case ZELDA_MOUTH_SMILING: + this->mouthTextureIndex = ZELDA_MOUTH_SMILING; + break; + case ZELDA_MOUTH_FROWNING: + this->mouthTextureIndex = ZELDA_MOUTH_FROWNING; + break; + case ZELDA_MOUTH_OPEN: + this->mouthTextureIndex = ZELDA_MOUTH_OPEN; + break; + } + + if (this->animationIndex == ZELDA_ANIM_PLAYING_OCARINA) { + this->eyeTextureIndexLeft = this->eyeTextureIndexRight = ZELDA_EYE_CLOSED; + } +} + +void DmZl_Update(Actor* thisx, GlobalContext* globalCtx) { + DmZl* this = THIS; + + DmZl_UpdateFace(this); + SkelAnime_Update(&this->skelAnime); + DmZl_UpdateCutscene(this, globalCtx); + this->actionFunc(this, globalCtx); +} + +s32 DmZl_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { + return false; +} + +void DmZl_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { + DmZl* this = THIS; + + if (limbIndex == ZL4_LIMB_RIGHT_HAND) { + if ((this->animationIndex >= ZELDA_ANIM_GIVING_OCARINA_START) && + (this->animationIndex <= ZELDA_ANIM_PLAYING_OCARINA)) { + OPEN_DISPS(globalCtx->state.gfxCtx); + + gSPDisplayList(POLY_OPA_DISP++, gDmZl4OcarinaDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx); + } + } +} + +void DmZl_Draw(Actor* thisx, GlobalContext* globalCtx) { + DmZl* this = THIS; + + OPEN_DISPS(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(sEyeTextures[this->eyeTextureIndexRight])); + + gSPSegment(POLY_OPA_DISP++, 0x09, Lib_SegmentedToVirtual(sEyeTextures[this->eyeTextureIndexLeft])); + + gSPSegment(POLY_OPA_DISP++, 0x0A, Lib_SegmentedToVirtual(sMouthTextures[this->mouthTextureIndex])); + + func_8012C28C(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + DmZl_OverrideLimbDraw, DmZl_PostLimbDraw, &this->actor); + + CLOSE_DISPS(globalCtx->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.h b/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.h index c3b9858502..ec3e442960 100644 --- a/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.h +++ b/src/overlays/actors/ovl_Dm_Zl/z_dm_zl.h @@ -2,16 +2,28 @@ #define Z_DM_ZL_H #include "global.h" +#include "objects/object_zl4/object_zl4.h" struct DmZl; typedef void (*DmZlActionFunc)(struct DmZl*, GlobalContext*); typedef struct DmZl { - /* 0x0000 */ Actor actor; - /* 0x0144 */ char unk_144[0x11C]; - /* 0x0260 */ DmZlActionFunc actionFunc; - /* 0x0264 */ char unk_264[0x70]; + /* 0x000 */ Actor actor; + /* 0x144 */ SkelAnime skelAnime; + /* 0x188 */ Vec3s jointTable[ZL4_LIMB_MAX]; // these tables are not referenced directly, loaded dynamically + /* 0x1F4 */ Vec3s morphTable[ZL4_LIMB_MAX]; + /* 0x260 */ DmZlActionFunc actionFunc; + /* 0x264 */ ColliderCylinder collider; // unused, assumed to be here based on OoT documentation + /* 0x2B0 */ s16 animationIndex; + /* 0x2B2 */ u8 eyeTextureIndexLeft; + /* 0x2B3 */ u8 eyeTextureIndexRight; + /* 0x2B4 */ u8 mouthTextureIndex; + /* 0x2B5 */ u8 nextEyeState; // used to control eye state, but not set by our actor, outside of actor? + /* 0x2B6 */ u8 nextMouthState; // used to control mouth state, but not set by our actor, outside of actor? + /* 0x2B8 */ s16 blinkTimer; + /* 0x2BA */ s16 unk_2BA; // set but not used by this actor. + /* 0x2BC */ UNK_TYPE1 pad2BC[0x18]; // unused by DmZl } DmZl; // size = 0x2D4 extern const ActorInit Dm_Zl_InitVars; diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 4307c8e379..e59790cf3b 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -10244,15 +10244,15 @@ 0x80A37EE0:("ObjShutter_Destroy",), 0x80A37EF0:("ObjShutter_Update",), 0x80A3803C:("ObjShutter_Draw",), - 0x80A38190:("func_80A38190",), + 0x80A38190:("DmZl_ChangeAnimation",), 0x80A3822C:("DmZl_Init",), 0x80A382EC:("DmZl_Destroy",), - 0x80A382FC:("func_80A382FC",), - 0x80A3830C:("func_80A3830C",), - 0x80A38468:("func_80A38468",), + 0x80A382FC:("DmZl_DoNothing",), + 0x80A3830C:("DmZl_UpdateCutscene",), + 0x80A38468:("DmZl_UpdateFace",), 0x80A385D4:("DmZl_Update",), - 0x80A3862C:("func_80A3862C",), - 0x80A38648:("func_80A38648",), + 0x80A3862C:("DmZl_OverrideLimbDraw",), + 0x80A38648:("DmZl_PostLimbDraw",), 0x80A3869C:("DmZl_Draw",), 0x80A389A0:("func_80A389A0",), 0x80A38A68:("func_80A38A68",),