From 47d43f2fa1b0a35b02a1186fb94e5bd227c92219 Mon Sep 17 00:00:00 2001 From: Jeff Padgham Date: Sat, 6 Apr 2024 10:05:04 -0700 Subject: [PATCH] Mayor Dotour (ovl_en_dt) OK and documented (#1607) * EnDt deecomp first pass, two nonmatching * Some cleanup, got the big boy matching * Some light cleanup, documenting blinking behaviour * Fixed last non-matching with some permuter silliness * Decompilation complete * Mayor documentation, first pass * Updating some unknown vars in baisen * Added an enum for mayor's cutscene state * Formatting pass for mayor * Cleaning up unk var in mayor meeting method * Some cleanup and changes based on PR feedback * Another cleanup and fix pass based on PR feedback * Updated mayor event flags according to their usage * All the parentheses * Another round of PR feedback cleanup * Having another crack at naming/cleanup based on PR feedback * Renaming animation symbols * Fixing broken references in other actors from anim rename --------- Co-authored-by: GinNoOokami --- assets/xml/objects/object_dt.xml | 12 +- include/z64save.h | 10 +- spec | 3 +- src/code/z_sram_NES.c | 2 +- .../actors/ovl_En_Baisen/z_en_baisen.c | 34 +- .../actors/ovl_En_Baisen/z_en_baisen.h | 4 +- src/overlays/actors/ovl_En_Daiku/z_en_daiku.c | 3 +- src/overlays/actors/ovl_En_Dt/z_en_dt.c | 824 +++++++++++++++++- src/overlays/actors/ovl_En_Dt/z_en_dt.h | 28 +- .../ovl_En_Ending_Hero/z_en_ending_hero.c | 2 +- .../ovl_En_Ending_Hero6/z_en_ending_hero6.c | 2 +- .../actors/ovl_En_Heishi/z_en_heishi.c | 6 +- src/overlays/actors/ovl_En_Muto/z_en_muto.c | 5 +- .../actors/ovl_En_Recepgirl/z_en_recepgirl.c | 2 +- tools/disasm/functions.txt | 36 +- tools/disasm/variables.txt | 16 +- 16 files changed, 891 insertions(+), 98 deletions(-) diff --git a/assets/xml/objects/object_dt.xml b/assets/xml/objects/object_dt.xml index 3acf0c4426..6c4f5c68ef 100644 --- a/assets/xml/objects/object_dt.xml +++ b/assets/xml/objects/object_dt.xml @@ -1,10 +1,10 @@  - - - - - + + + + + @@ -62,6 +62,6 @@ - + diff --git a/include/z64save.h b/include/z64save.h index 8478e98801..dc31596c27 100644 --- a/include/z64save.h +++ b/include/z64save.h @@ -1108,10 +1108,10 @@ typedef enum { #define WEEKEVENTREG_60_01 PACK_WEEKEVENTREG_FLAG(60, 0x01) #define WEEKEVENTREG_60_02 PACK_WEEKEVENTREG_FLAG(60, 0x02) #define WEEKEVENTREG_60_04 PACK_WEEKEVENTREG_FLAG(60, 0x04) -#define WEEKEVENTREG_60_08 PACK_WEEKEVENTREG_FLAG(60, 0x08) -#define WEEKEVENTREG_60_10 PACK_WEEKEVENTREG_FLAG(60, 0x10) +#define WEEKEVENTREG_ATTENDED_MAYOR_MEETING PACK_WEEKEVENTREG_FLAG(60, 0x08) +#define WEEKEVENTREG_RECEIVED_MAYOR_REWARD PACK_WEEKEVENTREG_FLAG(60, 0x10) #define WEEKEVENTREG_60_20 PACK_WEEKEVENTREG_FLAG(60, 0x20) -#define WEEKEVENTREG_60_40 PACK_WEEKEVENTREG_FLAG(60, 0x40) +#define WEEKEVENTREG_TALKED_MAYOR_NIGHT_3 PACK_WEEKEVENTREG_FLAG(60, 0x40) #define WEEKEVENTREG_60_80 PACK_WEEKEVENTREG_FLAG(60, 0x80) #define WEEKEVENTREG_61_01 PACK_WEEKEVENTREG_FLAG(61, 0x01) #define WEEKEVENTREG_61_02 PACK_WEEKEVENTREG_FLAG(61, 0x02) @@ -1141,8 +1141,8 @@ typedef enum { #define WEEKEVENTREG_63_20 PACK_WEEKEVENTREG_FLAG(63, 0x20) #define WEEKEVENTREG_63_40 PACK_WEEKEVENTREG_FLAG(63, 0x40) -// showed Couple's Mask to meeting -#define WEEKEVENTREG_63_80 PACK_WEEKEVENTREG_FLAG(63, 0x80) +// Showed Couple's Mask at meeting +#define WEEKEVENTREG_RESOLVED_MAYOR_MEETING PACK_WEEKEVENTREG_FLAG(63, 0x80) #define WEEKEVENTREG_64_01 PACK_WEEKEVENTREG_FLAG(64, 0x01) #define WEEKEVENTREG_64_02 PACK_WEEKEVENTREG_FLAG(64, 0x02) diff --git a/spec b/spec index bca5a7c0bb..fe38ebac88 100644 --- a/spec +++ b/spec @@ -4529,8 +4529,7 @@ beginseg name "ovl_En_Dt" compress include "build/src/overlays/actors/ovl_En_Dt/z_en_dt.o" - include "build/data/ovl_En_Dt/ovl_En_Dt.data.o" - include "build/data/ovl_En_Dt/ovl_En_Dt.reloc.o" + include "build/src/overlays/actors/ovl_En_Dt/ovl_En_Dt_reloc.o" endseg beginseg diff --git a/src/code/z_sram_NES.c b/src/code/z_sram_NES.c index af75b9a1d4..6966bd356b 100644 --- a/src/code/z_sram_NES.c +++ b/src/code/z_sram_NES.c @@ -152,7 +152,7 @@ u16 sPersistentCycleWeekEventRegs[ARRAY_COUNT(gSaveContext.save.saveInfo.weekEve PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_59_04) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_59_08) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_SWAMP_SHOOTING_GALLERY_QUIVER_UPGRADE) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_TOWN_SHOOTING_GALLERY_QUIVER_UPGRADE), - /* 60 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_60_10), + /* 60 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_MAYOR_REWARD), /* 61 */ 0, /* 62 */ 0, /* 63 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_63_20), diff --git a/src/overlays/actors/ovl_En_Baisen/z_en_baisen.c b/src/overlays/actors/ovl_En_Baisen/z_en_baisen.c index d0641b54d5..05b4cfb564 100644 --- a/src/overlays/actors/ovl_En_Baisen/z_en_baisen.c +++ b/src/overlays/actors/ovl_En_Baisen/z_en_baisen.c @@ -86,14 +86,16 @@ void EnBaisen_Init(Actor* thisx, PlayState* play) { this->paramCopy = this->actor.params; if (this->actor.params == 0) { this->unk290 = true; - if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_63_80) && ((gSaveContext.save.day != 3) || !gSaveContext.save.isNight)) { + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) && + ((gSaveContext.save.day != 3) || !gSaveContext.save.isNight)) { Actor_Kill(&this->actor); } } else { this->collider.dim.radius = 30; this->collider.dim.height = 60; this->collider.dim.yShift = 0; - if (CHECK_WEEKEVENTREG(WEEKEVENTREG_63_80) || ((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) || + ((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { Actor_Kill(&this->actor); } } @@ -168,13 +170,13 @@ void func_80BE887C(EnBaisen* this, PlayState* play) { } else { if (this->paramCopy != 0) { this->textIdIndex = 0; - if (CHECK_WEEKEVENTREG(WEEKEVENTREG_60_08)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_ATTENDED_MAYOR_MEETING)) { this->textIdIndex = 1; } if (Player_GetMask(play) == PLAYER_MASK_COUPLE) { this->textIdIndex = 6; } - if (this->unk2AC == 1) { + if (this->cutsceneState == 1) { func_80BE895C(this, play); return; } @@ -185,14 +187,14 @@ void func_80BE887C(EnBaisen* this, PlayState* play) { } void func_80BE895C(EnBaisen* this, PlayState* play) { - if (this->unk2A4 != NULL) { + if (this->targetActor != NULL) { this->unk290 = true; - this->unk2AC = 1; - Actor_ChangeFocus(this->unk2A4, play, this->unk2A4); + this->cutsceneState = 1; + Actor_ChangeFocus(this->targetActor, play, this->targetActor); } this->unk29C = 1; if (this->paramCopy == 0) { - this->unk2A4 = this->heishiPointer; + this->targetActor = this->heishiPointer; this->actionFunc = func_80BE8AAC; } else { this->actionFunc = func_80BE89D8; @@ -200,13 +202,13 @@ void func_80BE895C(EnBaisen* this, PlayState* play) { } void func_80BE89D8(EnBaisen* this, PlayState* play) { - if (&this->actor == this->unk2A4) { + if (&this->actor == this->targetActor) { this->unk29E = this->actor.world.rot.y; if (this->animIndex == ENBAISEN_ANIM_0) { EnBaisen_ChangeAnim(this, ENBAISEN_ANIM_1); } } else { - this->unk29E = Math_Vec3f_Yaw(&this->actor.world.pos, &this->unk2A4->world.pos); + this->unk29E = Math_Vec3f_Yaw(&this->actor.world.pos, &this->targetActor->world.pos); if (this->animIndex != ENBAISEN_ANIM_0) { EnBaisen_ChangeAnim(this, ENBAISEN_ANIM_0); } @@ -216,7 +218,7 @@ void func_80BE89D8(EnBaisen* this, PlayState* play) { this->skelAnime.playSpeed = 0.0f; this->unk29E = this->actor.yawTowardsPlayer; } - if (this->unk2AC == 2) { // Note: This variable is only ever set to 1. + if (this->cutsceneState == 2) { // Note: This variable is also set by EnDt. func_80BE87FC(this); } } @@ -228,8 +230,8 @@ void func_80BE8AAC(EnBaisen* this, PlayState* play) { EnBaisen_ChangeAnim(this, ENBAISEN_ANIM_1); } } else { - if (this->unk2A4 != NULL) { - this->unk29E = Math_Vec3f_Yaw(&this->actor.world.pos, &this->unk2A4->world.pos); + if (this->targetActor != NULL) { + this->unk29E = Math_Vec3f_Yaw(&this->actor.world.pos, &this->targetActor->world.pos); } if (this->animIndex != ENBAISEN_ANIM_0) { EnBaisen_ChangeAnim(this, ENBAISEN_ANIM_0); @@ -241,11 +243,11 @@ void func_80BE8AAC(EnBaisen* this, PlayState* play) { if (this->textIdIndex < 6) { Message_ContinueTextbox(play, sTextIds[this->textIdIndex]); if ((this->textIdIndex % 2) == 0) { - this->unk2A4 = this->heishiPointer; + this->targetActor = this->heishiPointer; } else { - this->unk2A4 = &this->actor; + this->targetActor = &this->actor; } - Actor_ChangeFocus(this->unk2A4, play, this->unk2A4); + Actor_ChangeFocus(this->targetActor, play, this->targetActor); } else { func_80BE87FC(this); } diff --git a/src/overlays/actors/ovl_En_Baisen/z_en_baisen.h b/src/overlays/actors/ovl_En_Baisen/z_en_baisen.h index 25e747e381..c44573930c 100644 --- a/src/overlays/actors/ovl_En_Baisen/z_en_baisen.h +++ b/src/overlays/actors/ovl_En_Baisen/z_en_baisen.h @@ -27,9 +27,9 @@ typedef struct EnBaisen { /* 0x29C */ s16 unk29C; /* 0x29E */ s16 unk29E; /* 0x2A0 */ s16 textIdIndex; - /* 0x2A4 */ Actor* unk2A4; + /* 0x2A4 */ Actor* targetActor; /* 0x2A8 */ Actor* heishiPointer; - /* 0x2AC */ s32 unk2AC; + /* 0x2AC */ s32 cutsceneState; /* 0x2B0 */ ColliderCylinder collider; } EnBaisen; // size = 0x2FC diff --git a/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c b/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c index baff81da39..c7000ecb87 100644 --- a/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c +++ b/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c @@ -114,7 +114,8 @@ void EnDaiku_Init(Actor* thisx, PlayState* play) { this->collider.dim.height = 60; this->collider.dim.yShift = 0; this->actor.flags |= ACTOR_FLAG_CANT_LOCK_ON; - if (CHECK_WEEKEVENTREG(WEEKEVENTREG_63_80) || ((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) || + ((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { Actor_Kill(&this->actor); } } else if ((gSaveContext.save.day == 3) && gSaveContext.save.isNight) { diff --git a/src/overlays/actors/ovl_En_Dt/z_en_dt.c b/src/overlays/actors/ovl_En_Dt/z_en_dt.c index 605c527054..b0e5dd0f83 100644 --- a/src/overlays/actors/ovl_En_Dt/z_en_dt.c +++ b/src/overlays/actors/ovl_En_Dt/z_en_dt.c @@ -2,10 +2,17 @@ * File: z_en_dt.c * Overlay: ovl_En_Dt * Description: Mayor Dotour + * + * This actor handles the logic for the meeting event between the mayor, Mutoh, and Baisen. + * It is also the source of a heart piece and two bomber notebook events. */ #include "z_en_dt.h" +#include "objects/object_dt/object_dt.h" +#include "overlays/actors/ovl_En_Baisen/z_en_baisen.h" +#include "overlays/actors/ovl_En_Muto/z_en_muto.h" + #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define THIS ((EnDt*)thisx) @@ -15,7 +22,80 @@ void EnDt_Destroy(Actor* thisx, PlayState* play); void EnDt_Update(Actor* thisx, PlayState* play); void EnDt_Draw(Actor* thisx, PlayState* play); -#if 0 +void EnDt_ChangeAnim(EnDt* this, s32 animIndex); +void EnDt_SetupRegularState(EnDt* this, PlayState* play); +void EnDt_SetupCutsceneNpcs(EnDt* this, PlayState* play); +void EnDt_OfferRegularTalk(EnDt* this, PlayState* play); +void EnDt_SetupMeetingCutscene(EnDt* this, PlayState* play); +void EnDt_UpdateMeetingCutscene(EnDt* this, PlayState* play); +void EnDt_FinishMeetingCutscene(EnDt* this, PlayState* play); +void EnDt_OfferMeetingReward(EnDt* this, PlayState* play); +void EnDt_OfferFinalNightTalk(EnDt* this, PlayState* play); +void EnDt_TriggerFinalNightTalkEvent(EnDt* this, PlayState* play); +void EnDt_TriggerMeetingRewardEvent(EnDt* this, PlayState* play); +void EnDt_TriggerMeetingNotebookEvent(EnDt* this, PlayState* play); +void EnDt_SetupFinalNightState(EnDt* this, PlayState* play); +void EnDt_UpdateAppearance(EnDt* this); +void EnDt_StartFinalNightTalk(EnDt* this); +void EnDt_UpdateCutsceneFocusTarget(EnDt* this); + +typedef enum { + /* 0 */ EN_DT_NPC_STATE_DEFAULT, + /* 1 */ EN_DT_NPC_STATE_IDLE, + /* 2 */ EN_DT_NPC_STATE_VIEWING_MEETING, + /* 3 */ EN_DT_NPC_STATE_OFFERED_MEETING_REWARD, + /* 4 */ EN_DT_NPC_STATE_WAIT_FINAL_NIGHT_TALK, + /* 5 */ EN_DT_NPC_STATE_DONE_FINAL_NIGHT_TALK +} EnDtNpcState; + +typedef enum { + /* 0 */ EN_DT_CS_STATE_NONE, + /* 1 */ EN_DT_CS_STATE_WAITING, + /* 2 */ EN_DT_CS_STATE_PLAYING +} EnDtCutsceneState; + +typedef enum { + /* 0 */ EN_DT_CS_FOCUS_TARGET_MUTO, + /* 1 */ EN_DT_CS_FOCUS_TARGET_BAISEN, + /* 2 */ EN_DT_CS_FOCUS_TARGET_MAYOR +} EnDtCutsceneFocusTarget; + +typedef enum { + /* 0 */ EN_DT_APPEARANCE_DEFAULT, + /* 1 */ EN_DT_APPEARANCE_PRE_MEETING, + /* 2 */ EN_DT_APPEARANCE_POST_MEETING, + /* 3 */ EN_DT_APPEARANCE_UNUSED, + /* 4 */ EN_DT_APPEARANCE_RESOLUTION_TALK, + /* 5 */ EN_DT_APPEARANCE_RESOLVED_MEETING, + /* 6 */ EN_DT_APPEARANCE_MAX +} EnDtAppearance; + +typedef enum { + /* 0 */ EN_DT_ANIMATION_WAIT, + /* 1 */ EN_DT_ANIMATION_DISTRESS, + /* 2 */ EN_DT_ANIMATION_SHOCK, + /* 3 */ EN_DT_ANIMATION_TAP_DESK, + /* 4 */ EN_DT_ANIMATION_UPRIGHT, + /* 5 */ EN_DT_ANIMATION_SIT_UP, + /* 6 */ EN_DT_ANIMATION_MAX +} EnDtAnimation; + +typedef enum { + /* 0 */ EN_DT_EYE_TEXTURE_SHOCK, + /* 1 */ EN_DT_EYE_TEXTURE_OPEN, + /* 2 */ EN_DT_EYE_TEXTURE_CLOSED, + /* 3 */ EN_DT_EYE_TEXTURE_LOOK_DOWN, + /* 4 */ EN_DT_EYE_TEXTURE_SQUINT, + /* 5 */ EN_DT_EYE_TEXTURE_MAX +} EnDtEyeTextures; + +typedef enum { + /* 0 */ EN_DT_BROW_TEXTURE_HIGH, + /* 1 */ EN_DT_BROW_TEXTURE_MID, + /* 2 */ EN_DT_BROW_TEXTURE_LOW, + /* 3 */ EN_DT_BROW_TEXTURE_MAX +} EnDtBrowTextures; + ActorInit En_Dt_InitVars = { /**/ ACTOR_EN_DT, /**/ ACTORCAT_NPC, @@ -28,59 +108,743 @@ ActorInit En_Dt_InitVars = { /**/ EnDt_Draw, }; -// static ColliderCylinderInit sCylinderInit = { -static ColliderCylinderInit D_80BEB29C = { - { COLTYPE_NONE, AT_NONE, AC_NONE, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_2, COLSHAPE_CYLINDER, }, - { ELEMTYPE_UNK0, { 0x00000000, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_NONE, OCELEM_ON, }, +static u16 sTextIds[] = { + 0x2ABD, 0x2ABE, 0x2ABF, 0x2AC0, 0x2ABE, 0x2AC1, 0x2AC2, 0x2AC3, 0x2AC4, 0x2ABB, 0x2ABC, 0x2AC6, 0x2AC7, 0x2AC8, + 0x2AC9, 0x2ACA, 0x2ACB, 0x2ACC, 0x2ACD, 0x2ACE, 0x2ACF, 0x2AD0, 0x2AD1, 0x2AC5, 0x2AD3, 0x2AD4, 0x2AD2, +}; + +// The code uses EnDt::textIdIndex as an index into this lookup table +static s32 sCutsceneFocusTargetTable[] = { + // Start of initial mayor meeting cutscene + /* 0 */ EN_DT_CS_FOCUS_TARGET_BAISEN, + /* 1 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + /* 2 */ EN_DT_CS_FOCUS_TARGET_MUTO, + /* 3 */ EN_DT_CS_FOCUS_TARGET_MUTO, + /* 4 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + /* 5 */ EN_DT_CS_FOCUS_TARGET_BAISEN, + /* 6 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + /* 7 */ EN_DT_CS_FOCUS_TARGET_MUTO, + /* 8 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + // End of intitial mayor meeting cutscene + + // Start of repeat mayor meeting cutscene + /* 9 */ EN_DT_CS_FOCUS_TARGET_BAISEN, + /* 10 */ EN_DT_CS_FOCUS_TARGET_MUTO, + // End of repeat mayor meeting cutscene + + // Start of meeting resolution cutscene + /* 11 */ EN_DT_CS_FOCUS_TARGET_BAISEN, + /* 12 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + /* 13 */ EN_DT_CS_FOCUS_TARGET_MUTO, + /* 14 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + /* 15 */ EN_DT_CS_FOCUS_TARGET_MUTO, + /* 16 */ EN_DT_CS_FOCUS_TARGET_BAISEN, + /* 17 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + /* 18 */ EN_DT_CS_FOCUS_TARGET_BAISEN, + /* 19 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + /* 20 */ EN_DT_CS_FOCUS_TARGET_MUTO, + /* 21 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + /* 22 */ EN_DT_CS_FOCUS_TARGET_MAYOR, + // End of meeting resolution cutscene + + // Final night cutscene with mayor + /* 23 */ EN_DT_CS_FOCUS_TARGET_MAYOR +}; + +static s16 sStringIdCsIndexTable[] = { + 0x2ABB, 0, // + 0x2ABD, 0, // + 0x2AC0, 1, // + 0x2AC1, 2, // + 0x2AC4, 3, // + 0x2AC6, 4, // + 0x2AC7, 5, // + 0x2AC8, 6, // + 0x2AC9, 7, // + 0x2ACC, 8, // + 0x2ACF, 9, // + 0x2AD0, 10, // +}; + +static s16 sUnused[] = { 777, 777 }; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, { 25, 70, 0, { 0, 0, 0 } }, }; -#endif +static AnimationHeader* sAnimations[EN_DT_ANIMATION_MAX] = { + &gDotourWaitAnim, // EN_DT_ANIMATION_WAIT + &gDotourDistressAnim, // EN_DT_ANIMATION_DISTRESS + &gDotourShockAnim, // EN_DT_ANIMATION_SHOCK + &gDotourTapDeskAnim, // EN_DT_ANIMATION_TAP_DESK + &gDotourUprightAnim, // EN_DT_ANIMATION_UPRIGHT + &gDotourSitUpAnim, // EN_DT_ANIMATION_SIT_UP +}; -extern ColliderCylinderInit D_80BEB29C; +static u8 sAnimationModes[EN_DT_ANIMATION_MAX] = { + ANIMMODE_LOOP, // EN_DT_ANIMATION_WAIT + ANIMMODE_LOOP, // EN_DT_ANIMATION_DISTRESS + ANIMMODE_ONCE, // EN_DT_ANIMATION_SHOCK + ANIMMODE_LOOP, // EN_DT_ANIMATION_TAP_DESK + ANIMMODE_LOOP, // EN_DT_ANIMATION_UPRIGHT + ANIMMODE_ONCE, // EN_DT_ANIMATION_SIT_UP +}; -extern UNK_TYPE D_0600112C; +// Based on the usage in its accompanying method, this must be an array where each row +// is 4 values wide. The first value is never used and always 0, while indexes 1, 2, +// and 3 are the animation index, the eye texture index, and a flag determining if +// the actor should have a dead-panned expression (not blinking). +static s32 sAppearancePropertiesTable[] = { + 0, EN_DT_ANIMATION_DISTRESS, EN_DT_EYE_TEXTURE_LOOK_DOWN, true, // EN_DT_APPEARANCE_DEFAULT + 0, EN_DT_ANIMATION_SHOCK, EN_DT_EYE_TEXTURE_LOOK_DOWN, true, // EN_DT_APPEARANCE_PRE_MEETING + 0, EN_DT_ANIMATION_TAP_DESK, EN_DT_EYE_TEXTURE_LOOK_DOWN, true, // EN_DT_APPEARANCE_POST_MEETING + 0, EN_DT_ANIMATION_TAP_DESK, EN_DT_EYE_TEXTURE_SHOCK, false, // EN_DT_APPEARANCE_UNUSED + 0, EN_DT_ANIMATION_SIT_UP, EN_DT_EYE_TEXTURE_SHOCK, false, // EN_DT_APPEARANCE_RESOLUTION_TALK + 0, EN_DT_ANIMATION_UPRIGHT, EN_DT_EYE_TEXTURE_SHOCK, false // EN_DT_APPEARANCE_RESOLVED_MEETING +}; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/EnDt_Init.s") +static TexturePtr sEyeTextures[] = { + gDotourEyeShockTex, // EN_DT_EYE_TEXTURE_SHOCK + gDotourEyeOpenTex, // EN_DT_EYE_TEXTURE_OPEN + gDotourEyeClosedTex, // EN_DT_EYE_TEXTURE_CLOSED + gDotourEyeLookDownTex, // EN_DT_EYE_TEXTURE_LOOK_DOWN + gDotourEyeSquintTex, // EN_DT_EYE_TEXTURE_SQUINT +}; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/EnDt_Destroy.s") +static TexturePtr sEyebrowTextures[] = { + gDotourEyebrowHighTex, // EN_DT_BROW_TEXTURE_HIGH + gDotourEyebrowMidTex, // EN_DT_BROW_TEXTURE_MID + gDotourEyebrowLowTex, // EN_DT_BROW_TEXTURE_LOW +}; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BE9C74.s") +void EnDt_Init(Actor* thisx, PlayState* play) { + EnDt* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BE9CE8.s") + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 19.0f); + SkelAnime_InitFlex(play, &this->skelAnime, &object_dt_Skel_00B0CC, &gDotourWaitAnim, this->jointTable, + this->morphTable, OBJECT_DT_LIMB_MAX); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BE9D9C.s") + this->actor.targetMode = TARGET_MODE_6; + this->npcEnMuto = NULL; + this->npcEnBaisen = NULL; + Collider_InitAndSetCylinder(play, &this->collider, &this->actor, &sCylinderInit); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BE9DF8.s") + if ((gSaveContext.save.day == 3) && gSaveContext.save.isNight) { + EnDt_SetupFinalNightState(this, play); + } else { + s32 csId = this->actor.csId; + s32 i = 0; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BE9E94.s") + // clang-format off + while (csId != CS_ID_NONE) { this->csIds[i] = csId; csId = CutsceneManager_GetAdditionalCsId(csId); i++; } + // clang-format on + } + this->actionFunc = EnDt_SetupCutsceneNpcs; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BE9EF8.s") +void EnDt_Destroy(Actor* thisx, PlayState* play) { + EnDt* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEA088.s") + Collider_DestroyCylinder(play, &this->collider); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEA254.s") +void EnDt_UpdateHeadRotate(EnDt* this) { + s32 yaw = ABS_ALT(BINANG_SUB(this->actor.yawTowardsPlayer, this->actor.world.rot.y)); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEA394.s") + this->headRotTarget.y = 0; + if (yaw < 0x4E20) { + this->headRotTarget.y = BINANG_SUB(this->actor.yawTowardsPlayer, this->actor.world.rot.y); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEA8F0.s") + if (this->headRotTarget.y > 0x2710) { + this->headRotTarget.y = 0x2710; + } else if (this->headRotTarget.y < -0x2710) { + this->headRotTarget.y = -0x2710; + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEAAF8.s") +void EnDt_ChangeAnim(EnDt* this, s32 animIndex) { + f32 morphFrames; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEAB44.s") + this->animIndex = animIndex; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEABF8.s") + morphFrames = -4.0f; + if ((this->animIndex == EN_DT_ANIMATION_SHOCK) || (this->animIndex == EN_DT_ANIMATION_SIT_UP)) { + morphFrames = 0.0f; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEAC84.s") + this->animEndFrame = Animation_GetLastFrame(sAnimations[this->animIndex]); + Animation_Change(&this->skelAnime, sAnimations[this->animIndex], 1.0f, 0.0f, this->animEndFrame, + sAnimationModes[this->animIndex], morphFrames); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEAD2C.s") +void EnDt_UpdateAppearance(EnDt* this) { + s32 index = this->appearancePhase * 4; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEADB8.s") + index++; + EnDt_ChangeAnim(this, sAppearancePropertiesTable[index]); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEADD4.s") + index++; + this->eyeTexIndex = sAppearancePropertiesTable[index]; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/EnDt_Update.s") + index++; + this->disableBlinking = sAppearancePropertiesTable[index]; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/func_80BEB06C.s") +void EnDt_UpdateCutsceneFocusTarget(EnDt* this) { + if ((this->npcEnMuto != NULL) && (this->npcEnBaisen != NULL)) { + EnMuto* npcEnMuto = (EnMuto*)this->npcEnMuto; + EnBaisen* npcEnBaisen = (EnBaisen*)this->npcEnBaisen; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Dt/EnDt_Draw.s") + switch (sCutsceneFocusTargetTable[this->textIdIndex]) { + case EN_DT_CS_FOCUS_TARGET_MUTO: + npcEnMuto->targetActor = this->npcEnMuto; + npcEnBaisen->targetActor = this->npcEnMuto; + this->targetActor = this->npcEnMuto; + break; + + case EN_DT_CS_FOCUS_TARGET_BAISEN: + npcEnMuto->targetActor = this->npcEnBaisen; + npcEnBaisen->targetActor = this->npcEnBaisen; + this->targetActor = this->npcEnBaisen; + break; + + case EN_DT_CS_FOCUS_TARGET_MAYOR: + npcEnMuto->targetActor = &this->actor; + npcEnBaisen->targetActor = &this->actor; + this->targetActor = &this->actor; + break; + + default: + break; + } + } +} + +void EnDt_SetupCutsceneNpcs(EnDt* this, PlayState* play) { + Actor* npc = play->actorCtx.actorLists[ACTORCAT_NPC].first; + + while (npc != NULL) { + if (npc->id == ACTOR_EN_MUTO) { + this->npcEnMuto = npc; + } else if (npc->id == ACTOR_EN_BAISEN) { + this->npcEnBaisen = npc; + } + npc = npc->next; + } + + EnDt_SetupRegularState(this, play); +} + +void EnDt_SetupRegularState(EnDt* this, PlayState* play) { + this->textIdIndex = 0; + + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_MAYOR_REWARD)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING)) { + this->textIdIndex = 21; + this->appearancePhase = EN_DT_APPEARANCE_RESOLVED_MEETING; + EnDt_UpdateAppearance(this); + + this->actor.textId = sTextIds[this->textIdIndex]; + Message_StartTextbox(play, this->actor.textId, &this->actor); + Player_SetCsActionWithHaltedActors(play, &this->actor, PLAYER_CSACTION_WAIT); + + this->state = EN_DT_NPC_STATE_IDLE; + this->actionFunc = EnDt_UpdateMeetingCutscene; + return; + } + } else if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING)) { + this->textIdIndex = 23; + this->appearancePhase = EN_DT_APPEARANCE_RESOLVED_MEETING; + this->meetingFinished = true; + EnDt_UpdateAppearance(this); + Message_BombersNotebookQueueEvent(play, BOMBERS_NOTEBOOK_PERSON_MAYOR_DOTOUR); + } + + if (this->appearancePhase == EN_DT_APPEARANCE_DEFAULT) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_ATTENDED_MAYOR_MEETING)) { + this->textIdIndex = 9; + this->appearancePhase = EN_DT_APPEARANCE_POST_MEETING; + } + + EnDt_UpdateAppearance(this); + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_ATTENDED_MAYOR_MEETING)) { + this->appearancePhase = EN_DT_APPEARANCE_PRE_MEETING; + } + } + + this->actor.textId = sTextIds[this->textIdIndex]; + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING)) { + EnDt_UpdateCutsceneFocusTarget(this); + } + + this->state = EN_DT_NPC_STATE_IDLE; + this->actionFunc = EnDt_OfferRegularTalk; +} + +void EnDt_OfferRegularTalk(EnDt* this, PlayState* play) { + EnMuto* npcMuto = NULL; + EnBaisen* npcBaisen = NULL; + + if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { + EnDt_SetupMeetingCutscene(this, play); + return; + } + + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING)) { + if ((this->npcEnMuto != NULL) && (this->npcEnBaisen)) { + npcMuto = (EnMuto*)this->npcEnMuto; + npcBaisen = (EnBaisen*)this->npcEnBaisen; + } + + this->showedCouplesMask = false; + this->textIdIndex = 0; + + if (Player_GetMask(play) == PLAYER_MASK_COUPLE) { + this->textIdIndex = 11; + this->showedCouplesMask = true; + + if (this->npcEnMuto && this->npcEnBaisen) { + npcMuto->textIdIndex = 4; + npcBaisen->textIdIndex = 6; + } + } else if (CHECK_WEEKEVENTREG(WEEKEVENTREG_ATTENDED_MAYOR_MEETING)) { + this->textIdIndex = 9; + } + } + + this->actor.textId = sTextIds[this->textIdIndex]; + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) && (this->npcEnMuto != NULL) && + (this->npcEnBaisen != NULL) && (npcMuto->cutsceneState == 1 || npcBaisen->cutsceneState == 1)) { + EnDt_SetupMeetingCutscene(this, play); + } + + // After completing Couple's Mask event and wearing Kafeis Mask + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) && (Player_GetMask(play) == PLAYER_MASK_KAFEIS_MASK)) { + this->actor.textId = 0x2368; // "My wife hired you? Go ask the Curiosity Shop" + } + + Actor_OfferTalk(&this->actor, play, 150.0f); +} + +void EnDt_SetupMeetingCutscene(EnDt* this, PlayState* play) { + EnMuto* npcMuto; + EnBaisen* npcBaisen; + s32 index; + + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING)) { + if ((this->npcEnMuto != NULL) && (this->npcEnBaisen != NULL)) { + npcMuto = (EnMuto*)this->npcEnMuto; + npcBaisen = (EnBaisen*)this->npcEnBaisen; + } + + if ((this->npcEnMuto != NULL) && (this->npcEnBaisen != NULL)) { + npcMuto->cutsceneState = 1; + npcBaisen->cutsceneState = 1; + + if (Player_GetMask(play) == PLAYER_MASK_COUPLE) { + npcMuto->textIdIndex = 4; + npcBaisen->textIdIndex = 6; + this->appearancePhase = EN_DT_APPEARANCE_RESOLVED_MEETING; + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_ATTENDED_MAYOR_MEETING)) { + this->appearancePhase = EN_DT_APPEARANCE_RESOLUTION_TALK; + } + EnDt_UpdateAppearance(this); + } + } + } + + this->cutsceneState = EN_DT_CS_STATE_NONE; + for (index = 0; index < ARRAY_COUNT(sStringIdCsIndexTable); index += 2) { + if ((play->msgCtx.currentTextId == sStringIdCsIndexTable[index]) || + (this->actor.textId == sStringIdCsIndexTable[index])) { + this->cutsceneState = EN_DT_CS_STATE_WAITING; + this->csIdIndex = index; + break; + } + } + + this->state = EN_DT_NPC_STATE_VIEWING_MEETING; + this->actionFunc = EnDt_UpdateMeetingCutscene; +} + +void EnDt_UpdateMeetingCutscene(EnDt* this, PlayState* play) { + EnMuto* muto = NULL; + EnBaisen* baisen = NULL; + s32 index = sStringIdCsIndexTable[this->csIdIndex + 1]; + s32 csIdIndex = sStringIdCsIndexTable[this->csIdIndex + 1]; + + if (this->cutsceneState == EN_DT_CS_STATE_WAITING) { + if (CutsceneManager_GetCurrentCsId() == CS_ID_GLOBAL_TALK) { + CutsceneManager_Stop(CS_ID_GLOBAL_TALK); + CutsceneManager_Queue(this->csIds[index]); + return; + } + + if (!CutsceneManager_IsNext(this->csIds[index])) { + CutsceneManager_Queue(this->csIds[index]); + return; + } + + CutsceneManager_StartWithPlayerCs(this->csIds[index], this->targetActor); + Actor_ChangeFocus(&this->actor, play, this->targetActor); + this->cutsceneState = EN_DT_CS_STATE_PLAYING; + } + + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) && + ((this->npcEnMuto != NULL) || (this->npcEnBaisen != NULL))) { + muto = (EnMuto*)this->npcEnMuto; + baisen = (EnBaisen*)this->npcEnBaisen; + } + + if ((this->timer == 0) && (Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { + if (this->textIdIndex == 21) { + Player_SetCsActionWithHaltedActors(play, &this->actor, PLAYER_CSACTION_END); + if (this->cutsceneState == EN_DT_CS_STATE_PLAYING) { + CutsceneManager_Stop(this->csIds[index]); + this->cutsceneState = EN_DT_CS_STATE_NONE; + } + EnDt_OfferMeetingReward(this, play); + return; + } + + // After Muto and Baisen react to the Couple's Mask, + // they leave the office and trigger a transition. + if (this->textIdIndex == 20) { // Muto reacts to Mayor's final comments + Message_CloseTextbox(play); + play->nextEntrance = ENTRANCE(MAYORS_RESIDENCE, 1); + Scene_SetExitFade(play); + play->transitionTrigger = TRANS_TRIGGER_START; + + SET_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING); + this->meetingFinished = true; + + if (this->cutsceneState == EN_DT_CS_STATE_PLAYING) { + CutsceneManager_Stop(this->csIds[index]); + this->cutsceneState = EN_DT_CS_STATE_NONE; + } + } else { + // At the end of each cutscene dialog, trigger an event flag + if ((this->textIdIndex == 8) || (this->textIdIndex == 10) || (this->textIdIndex == 22) || + (this->textIdIndex == 23)) { + SET_WEEKEVENTREG(WEEKEVENTREG_ATTENDED_MAYOR_MEETING); + Message_CloseTextbox(play); + + // If the meeting is ongoing, reset all of the npc targets + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING)) { + if ((this->npcEnMuto != NULL) && (this->npcEnBaisen != NULL)) { + muto->cutsceneState = 2; + baisen->cutsceneState = 2; + muto->targetActor = this->npcEnMuto; + baisen->targetActor = this->npcEnBaisen; + } + this->targetActor = &this->actor; + } + + if (this->cutsceneState == EN_DT_CS_STATE_PLAYING) { + CutsceneManager_Stop(this->csIds[index]); + this->cutsceneState = EN_DT_CS_STATE_NONE; + } + EnDt_SetupRegularState(this, play); + return; + } + + this->textIdIndex++; + + // End of initial meeting cutscene + if (this->textIdIndex == 8) { + play->msgCtx.msgLength = 0; + EnDt_UpdateCutsceneFocusTarget(this); + Actor_ChangeFocus(&this->actor, play, this->targetActor); + Camera_SetTargetActor(Play_GetCamera(play, CutsceneManager_GetCurrentSubCamId(this->csIds[index])), + this->targetActor); + + this->timer = 2; + this->actionFunc = EnDt_FinishMeetingCutscene; + return; + } + + if (this->textIdIndex == 12) { // Mayor reacts to Couple's mask + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_ATTENDED_MAYOR_MEETING)) { + EnDt_UpdateAppearance(this); + this->timer = 25; + } else { + EnDt_ChangeAnim(this, EN_DT_ANIMATION_UPRIGHT); + SkelAnime_Update(&this->skelAnime); + } + } + + if (this->textIdIndex == 13) { // Muto reacts to Couple's mask + EnDt_ChangeAnim(this, EN_DT_ANIMATION_UPRIGHT); + SkelAnime_Update(&this->skelAnime); + } + + EnDt_UpdateCutsceneFocusTarget(this); + Actor_ChangeFocus(&this->actor, play, this->targetActor); + Camera_SetTargetActor(Play_GetCamera(play, CutsceneManager_GetCurrentSubCamId(this->csIds[index])), + this->targetActor); + + this->actor.textId = sTextIds[this->textIdIndex]; + Message_CloseTextbox(play); + Message_StartTextbox(play, this->actor.textId, &this->actor); + + // Sets the cutscene ID index based off the current textId + for (index = 0; index < ARRAY_COUNT(sStringIdCsIndexTable); index += 2) { + if (play->msgCtx.currentTextId == sStringIdCsIndexTable[index] || + this->actor.textId == sStringIdCsIndexTable[index]) { + this->csIdIndex = index; + break; + } + } + + index = sStringIdCsIndexTable[this->csIdIndex + 1]; + if (this->cutsceneState == EN_DT_CS_STATE_PLAYING && (index != csIdIndex)) { + this->cutsceneState = EN_DT_CS_STATE_WAITING; + CutsceneManager_Stop(this->csIds[csIdIndex]); + CutsceneManager_Queue(this->csIds[index]); + } + + // Mutoh turns his head to focus on the Mayor after saying "Isn't that right Mayor?" + if ((this->textIdIndex == 3) && (this->npcEnMuto != NULL) && (this->npcEnBaisen != NULL)) { + muto->targetActor = &this->actor; + } + } + } +} + +void EnDt_FinishMeetingCutscene(EnDt* this, PlayState* play) { + f32 currFrame = this->skelAnime.curFrame; + + if (this->timer != 0) { + if (this->timer == 1) { + if ((this->textIdIndex == 8) || (this->appearancePhase == EN_DT_APPEARANCE_RESOLUTION_TALK)) { + EnDt_UpdateAppearance(this); + } + + this->appearancePhase++; + if (this->appearancePhase >= EN_DT_APPEARANCE_MAX) { + this->appearancePhase = EN_DT_APPEARANCE_RESOLVED_MEETING; + } + } + } else if (this->animEndFrame <= currFrame) { + Camera* subCam; + s32 index; + s32 csIdIndex = sStringIdCsIndexTable[this->csIdIndex + 1]; + + EnDt_UpdateAppearance(this); + + Message_CloseTextbox(play); + this->actor.textId = sTextIds[this->textIdIndex]; + Message_StartTextbox(play, this->actor.textId, &this->actor); + + if (this->textIdIndex == 8) { // End of initial meeting cutscene + Message_BombersNotebookQueueEvent(play, BOMBERS_NOTEBOOK_PERSON_MAYOR_DOTOUR); + } + + // Sets the cutscene ID index based off the current textId + //! @bug Bug Relatively harmless, but the index should actually be incremented by 2 like similar loops + for (index = 0; index < ARRAY_COUNT(sStringIdCsIndexTable); index++) { + if ((play->msgCtx.currentTextId == sStringIdCsIndexTable[index]) || + (this->actor.textId == sStringIdCsIndexTable[index])) { + this->csIdIndex = index; + } + } + + index = sStringIdCsIndexTable[this->csIdIndex + 1]; + if ((this->cutsceneState == EN_DT_CS_STATE_PLAYING) && (index != csIdIndex)) { + this->cutsceneState = EN_DT_CS_STATE_WAITING; + CutsceneManager_Stop(this->csIds[csIdIndex]); + CutsceneManager_Queue(this->csIds[index]); + } + + EnDt_UpdateCutsceneFocusTarget(this); + Actor_ChangeFocus(&this->actor, play, this->targetActor); + + subCam = Play_GetCamera(play, CutsceneManager_GetCurrentSubCamId(this->csIds[index])); + Camera_SetTargetActor(subCam, this->targetActor); + + this->actionFunc = EnDt_UpdateMeetingCutscene; + } +} + +void EnDt_OfferMeetingReward(EnDt* this, PlayState* play) { + Actor_OfferGetItem(&this->actor, play, GI_HEART_PIECE, 300.0f, 300.0f); + this->state = EN_DT_NPC_STATE_OFFERED_MEETING_REWARD; + this->actionFunc = EnDt_TriggerMeetingRewardEvent; +} + +void EnDt_TriggerMeetingRewardEvent(EnDt* this, PlayState* play) { + if (Actor_HasParent(&this->actor, play)) { + this->textIdIndex = 22; + this->actor.parent = NULL; + this->actor.textId = sTextIds[this->textIdIndex]; + + Actor_OfferTalkExchange(&this->actor, play, 400.0f, 400.0f, PLAYER_IA_MINUS1); + + SET_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_MAYOR_REWARD); + this->actionFunc = EnDt_TriggerMeetingNotebookEvent; + } else { + Actor_OfferGetItem(&this->actor, play, GI_HEART_PIECE, 300.0f, 300.0f); + } +} + +void EnDt_TriggerMeetingNotebookEvent(EnDt* this, PlayState* play) { + SkelAnime_Update(&this->skelAnime); + + if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { + Message_BombersNotebookQueueEvent(play, BOMBERS_NOTEBOOK_EVENT_RECEIVED_MAYOR_HP); + Message_BombersNotebookQueueEvent(play, BOMBERS_NOTEBOOK_PERSON_MAYOR_DOTOUR); + this->actionFunc = EnDt_UpdateMeetingCutscene; + } else { + Actor_OfferTalkExchange(&this->actor, play, 400.0f, 400.0f, PLAYER_IA_MINUS1); + } +} + +void EnDt_SetupFinalNightState(EnDt* this, PlayState* play) { + EnDt_ChangeAnim(this, EN_DT_ANIMATION_TAP_DESK); + + this->disableBlinking = true; + this->textIdIndex = 24; + Message_BombersNotebookQueueEvent(play, BOMBERS_NOTEBOOK_PERSON_MAYOR_DOTOUR); + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TALKED_MAYOR_NIGHT_3)) { + this->textIdIndex = 26; + } + + this->actor.textId = sTextIds[this->textIdIndex]; + this->eyeTexIndex = EN_DT_EYE_TEXTURE_LOOK_DOWN; + this->disableBlinking = true; + this->state = EN_DT_NPC_STATE_WAIT_FINAL_NIGHT_TALK; + this->actionFunc = EnDt_OfferFinalNightTalk; +} + +void EnDt_OfferFinalNightTalk(EnDt* this, PlayState* play) { + EnDt_UpdateHeadRotate(this); + + if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { + EnDt_StartFinalNightTalk(this); + return; + } + + // After completing Couple's Mask event and wearing Kafeis Mask + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) && (Player_GetMask(play) == PLAYER_MASK_KAFEIS_MASK)) { + this->actor.textId = 0x2368; // "My wife hired you? Go ask the Curiosity Shop" + } + + Actor_OfferTalk(&this->actor, play, 150.0f); +} + +void EnDt_StartFinalNightTalk(EnDt* this) { + this->state = EN_DT_NPC_STATE_DONE_FINAL_NIGHT_TALK; + this->actionFunc = EnDt_TriggerFinalNightTalkEvent; +} + +void EnDt_TriggerFinalNightTalkEvent(EnDt* this, PlayState* play) { + EnDt_UpdateHeadRotate(this); + + if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { + Message_CloseTextbox(play); + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_TALKED_MAYOR_NIGHT_3)) { + this->textIdIndex = 25; + Message_ContinueTextbox(play, sTextIds[this->textIdIndex]); + SET_WEEKEVENTREG(WEEKEVENTREG_TALKED_MAYOR_NIGHT_3); + } else { + EnDt_SetupFinalNightState(this, play); + } + } +} + +void EnDt_Update(Actor* thisx, PlayState* play) { + s32 pad; + EnDt* this = THIS; + + SkelAnime_Update(&this->skelAnime); + Actor_SetScale(&this->actor, 0.01f); + + if ((this->state != 4) && (this->state != 5) && (gSaveContext.save.day == 3) && gSaveContext.save.isNight) { + EnDt_SetupFinalNightState(this, play); + } + + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) && + ((gSaveContext.save.day != 3) || ((gSaveContext.save.day == 3) && !gSaveContext.save.isNight))) { + Audio_PlaySequenceAtPos(SEQ_PLAYER_BGM_SUB, &gSfxDefaultPos, NA_BGM_MAYORS_OFFICE, 1000.0f); + Actor_PlaySfx(&this->actor, NA_SE_EV_CROWD - SFX_FLAG); + } + + DECR(this->blinkTimer); + DECR(this->timer); + + if (this->meetingFinished) { + EnDt_UpdateHeadRotate(this); + } + + // Blinking update + if (!this->disableBlinking && (this->blinkTimer == 0)) { + this->eyeTexIndex++; + if (this->eyeTexIndex >= EN_DT_EYE_TEXTURE_LOOK_DOWN) { + this->eyeTexIndex = EN_DT_EYE_TEXTURE_SHOCK; + this->blinkTimer = (s32)Rand_ZeroFloat(60.0f) + 20; + } + } + + this->actionFunc(this, play); + + this->actor.shape.rot.y = this->actor.world.rot.y; + Math_SmoothStepToS(&this->headRotValue.y, this->headRotTarget.y, 1, 3000, 0); + + Actor_SetFocus(&this->actor, 60.0f); + Actor_MoveWithGravity(&this->actor); + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); +} + +s32 EnDt_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { + EnDt* this = THIS; + + if (limbIndex == OBJECT_DT_LIMB_0C) { + rot->y += -1 * this->headRotValue.y; + } + + return false; +} + +void EnDt_Draw(Actor* thisx, PlayState* play) { + EnDt* this = THIS; + s32 eyebrowIndex = 0; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Opa(play->state.gfxCtx); + Gfx_SetupDL25_Xlu(play->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(sEyeTextures[this->eyeTexIndex])); + + if (this->eyeTexIndex < EN_DT_BROW_TEXTURE_MAX) { + eyebrowIndex = this->eyeTexIndex; + } + + gSPSegment(POLY_OPA_DISP++, 0x09, Lib_SegmentedToVirtual(sEyebrowTextures[eyebrowIndex])); + + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnDt_OverrideLimbDraw, NULL, &this->actor); + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Dt/z_en_dt.h b/src/overlays/actors/ovl_En_Dt/z_en_dt.h index 6afbf8cd22..03aaad14ef 100644 --- a/src/overlays/actors/ovl_En_Dt/z_en_dt.h +++ b/src/overlays/actors/ovl_En_Dt/z_en_dt.h @@ -2,6 +2,9 @@ #define Z_EN_DT_H #include "global.h" +#include "objects/object_dt/object_dt.h" + +#define EN_DT_CUTSCENE_COUNT 11 struct EnDt; @@ -9,9 +12,30 @@ typedef void (*EnDtActionFunc)(struct EnDt*, PlayState*); typedef struct EnDt { /* 0x000 */ Actor actor; - /* 0x144 */ char unk_144[0xF8]; + /* 0x144 */ SkelAnime skelAnime; + /* 0x188 */ Vec3s jointTable[OBJECT_DT_LIMB_MAX]; + /* 0x1E2 */ Vec3s morphTable[OBJECT_DT_LIMB_MAX]; /* 0x23C */ EnDtActionFunc actionFunc; - /* 0x240 */ char unk_240[0xA0]; + /* 0x240 */ s32 showedCouplesMask; // Is set, but never read from + /* 0x244 */ s16 timer; + /* 0x246 */ s16 animIndex; + /* 0x248 */ s16 disableBlinking; + /* 0x24A */ s16 blinkTimer; + /* 0x24C */ s16 eyeTexIndex; + /* 0x250 */ f32 animEndFrame; + /* 0x254 */ s16 state; + /* 0x256 */ s16 textIdIndex; + /* 0x258 */ s16 csIds[EN_DT_CUTSCENE_COUNT]; + /* 0x26E */ s16 csIdIndex; + /* 0x270 */ s16 cutsceneState; + /* 0x274 */ Actor* npcEnMuto; + /* 0x278 */ Actor* npcEnBaisen; + /* 0x27C */ Actor* targetActor; + /* 0x280 */ s32 appearancePhase; + /* 0x284 */ Vec3s headRotValue; + /* 0x28A */ Vec3s headRotTarget; + /* 0x290 */ s32 meetingFinished; + /* 0x294 */ ColliderCylinder collider; } EnDt; // size = 0x2E0 #endif // Z_EN_DT_H diff --git a/src/overlays/actors/ovl_En_Ending_Hero/z_en_ending_hero.c b/src/overlays/actors/ovl_En_Ending_Hero/z_en_ending_hero.c index 6b47c734ad..b8f61cf22f 100644 --- a/src/overlays/actors/ovl_En_Ending_Hero/z_en_ending_hero.c +++ b/src/overlays/actors/ovl_En_Ending_Hero/z_en_ending_hero.c @@ -37,7 +37,7 @@ void EnEndingHero_Init(Actor* thisx, PlayState* play) { Actor_SetScale(&this->actor, 0.01f); this->actor.targetMode = TARGET_MODE_6; this->actor.gravity = -3.0f; - SkelAnime_InitFlex(play, &this->skelAnime, &object_dt_Skel_00B0CC, &object_dt_Anim_000BE0, this->jointTable, + SkelAnime_InitFlex(play, &this->skelAnime, &object_dt_Skel_00B0CC, &gDotourUprightAnim, this->jointTable, this->morphTable, OBJECT_DT_LIMB_MAX); ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 25.0f); EnEndingHero1_SetupIdle(this); diff --git a/src/overlays/actors/ovl_En_Ending_Hero6/z_en_ending_hero6.c b/src/overlays/actors/ovl_En_Ending_Hero6/z_en_ending_hero6.c index 6344370c53..e2b8e8d4ec 100644 --- a/src/overlays/actors/ovl_En_Ending_Hero6/z_en_ending_hero6.c +++ b/src/overlays/actors/ovl_En_Ending_Hero6/z_en_ending_hero6.c @@ -56,7 +56,7 @@ static FlexSkeletonHeader* sSkeletons[ENDING_HERO6_TYPE_MAX] = { }; static AnimationHeader* sAnimations[ENDING_HERO6_TYPE_MAX] = { - &object_dt_Anim_000BE0, // ENDING_HERO6_TYPE_DT + &gDotourUprightAnim, // ENDING_HERO6_TYPE_DT &object_bai_Anim_0011C0, // ENDING_HERO6_TYPE_BAI &object_toryo_Anim_000E50, // ENDING_HERO6_TYPE_TORYO &gSoldierCheerWithSpearAnim, // ENDING_HERO6_TYPE_SOLDIER diff --git a/src/overlays/actors/ovl_En_Heishi/z_en_heishi.c b/src/overlays/actors/ovl_En_Heishi/z_en_heishi.c index c694033f6d..5e904225f7 100644 --- a/src/overlays/actors/ovl_En_Heishi/z_en_heishi.c +++ b/src/overlays/actors/ovl_En_Heishi/z_en_heishi.c @@ -64,14 +64,16 @@ void EnHeishi_Init(Actor* thisx, PlayState* play) { if (this->paramsCopy == 0) { this->shouldSetHeadRotation = 1; - if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_63_80) && !((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) && + !((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { Actor_Kill(&this->actor); } } else { this->colliderCylinder.dim.radius = 30; this->colliderCylinder.dim.height = 60; this->colliderCylinder.dim.yShift = 0; - if (CHECK_WEEKEVENTREG(WEEKEVENTREG_63_80) || ((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) || + ((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { Actor_Kill(&this->actor); } } diff --git a/src/overlays/actors/ovl_En_Muto/z_en_muto.c b/src/overlays/actors/ovl_En_Muto/z_en_muto.c index 7a307a72c0..f047891bf1 100644 --- a/src/overlays/actors/ovl_En_Muto/z_en_muto.c +++ b/src/overlays/actors/ovl_En_Muto/z_en_muto.c @@ -78,7 +78,8 @@ void EnMuto_Init(Actor* thisx, PlayState* play) { this->collider.dim.height = 60; this->collider.dim.yShift = 0; - if (CHECK_WEEKEVENTREG(WEEKEVENTREG_63_80) || ((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING) || + ((gSaveContext.save.day == 3) && gSaveContext.save.isNight)) { Actor_Kill(&this->actor); } } @@ -173,7 +174,7 @@ void EnMuto_Idle(EnMuto* this, PlayState* play) { } } else { this->textIdIndex = 0; - if (CHECK_WEEKEVENTREG(WEEKEVENTREG_60_08)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_ATTENDED_MAYOR_MEETING)) { this->textIdIndex = 1; } if (Player_GetMask(play) == PLAYER_MASK_COUPLE) { diff --git a/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.c b/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.c index 50120b412d..5ae3bbb3f2 100644 --- a/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.c +++ b/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.c @@ -152,7 +152,7 @@ void EnRecepgirl_Talk(EnRecepgirl* this, PlayState* play) { Flags_SetSwitch(play, ENRECEPGIRL_GET_SWITCH_FLAG(&this->actor)); Animation_MorphToPlayOnce(&this->skelAnime, &object_bg_Anim_00AD98, 10.0f); - if (CHECK_WEEKEVENTREG(WEEKEVENTREG_63_80)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RESOLVED_MAYOR_MEETING)) { this->actor.textId = 0x2ADF; // Mayor's office is on the left (meeting ended) } else { this->actor.textId = 0x2ADA; // Mayor's office is on the left (meeting ongoing) diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 20cb19b6a9..661005c811 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -16181,25 +16181,25 @@ 0x80BE99CC:("EnDemoheishi_Draw",), 0x80BE9B20:("EnDt_Init",), 0x80BE9C48:("EnDt_Destroy",), - 0x80BE9C74:("func_80BE9C74",), - 0x80BE9CE8:("func_80BE9CE8",), - 0x80BE9D9C:("func_80BE9D9C",), - 0x80BE9DF8:("func_80BE9DF8",), - 0x80BE9E94:("func_80BE9E94",), - 0x80BE9EF8:("func_80BE9EF8",), - 0x80BEA088:("func_80BEA088",), - 0x80BEA254:("func_80BEA254",), - 0x80BEA394:("func_80BEA394",), - 0x80BEA8F0:("func_80BEA8F0",), - 0x80BEAAF8:("func_80BEAAF8",), - 0x80BEAB44:("func_80BEAB44",), - 0x80BEABF8:("func_80BEABF8",), - 0x80BEAC84:("func_80BEAC84",), - 0x80BEAD2C:("func_80BEAD2C",), - 0x80BEADB8:("func_80BEADB8",), - 0x80BEADD4:("func_80BEADD4",), + 0x80BE9C74:("EnDt_UpdateHeadRotate",), + 0x80BE9CE8:("EnDt_ChangeAnim",), + 0x80BE9D9C:("EnDt_UpdateAppearance",), + 0x80BE9DF8:("EnDt_UpdateCutsceneFocusTarget",), + 0x80BE9E94:("EnDt_SetupCutsceneNpcs",), + 0x80BE9EF8:("EnDt_SetupRegularState",), + 0x80BEA088:("EnDt_OfferRegularTalk",), + 0x80BEA254:("EnDt_SetupMeetingCutscene",), + 0x80BEA394:("EnDt_UpdateMeetingCutscene",), + 0x80BEA8F0:("EnDt_FinishMeetingCutscene",), + 0x80BEAAF8:("EnDt_OfferMeetingReward",), + 0x80BEAB44:("EnDt_TriggerMeetingRewardEvent",), + 0x80BEABF8:("EnDt_TriggerMeetingNotebookEvent",), + 0x80BEAC84:("EnDt_SetupFinalNightState",), + 0x80BEAD2C:("EnDt_OfferFinalNightTalk",), + 0x80BEADB8:("EnDt_StartFinalNightTalk",), + 0x80BEADD4:("EnDt_TriggerFinalNightTalkEvent",), 0x80BEAE94:("EnDt_Update",), - 0x80BEB06C:("func_80BEB06C",), + 0x80BEB06C:("EnDt_OverrideLimbDraw",), 0x80BEB0A8:("EnDt_Draw",), 0x80BEB520:("EnCha_Init",), 0x80BEB5B0:("EnCha_Destroy",), diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt index 1d7378e4c5..1f28ec606d 100644 --- a/tools/disasm/variables.txt +++ b/tools/disasm/variables.txt @@ -15862,16 +15862,16 @@ 0x80BE9A80:("sAnimations","UNK_TYPE1","",0x1), 0x80BE9A94:("sAnimationModes","UNK_TYPE1","",0x1), 0x80BEB1B0:("En_Dt_InitVars","UNK_TYPE1","",0x1), - 0x80BEB1D0:("D_80BEB1D0","UNK_TYPE1","",0x1), - 0x80BEB208:("D_80BEB208","UNK_TYPE1","",0x1), + 0x80BEB1D0:("sTextIds","UNK_TYPE1","",0x1), + 0x80BEB208:("sCutsceneFocusTargetTable","UNK_TYPE1","",0x1), 0x80BEB268:("D_80BEB268","UNK_TYPE2","",0x2), 0x80BEB26A:("D_80BEB26A","UNK_TYPE2","",0x2), - 0x80BEB29C:("D_80BEB29C","UNK_TYPE1","",0x1), - 0x80BEB2C8:("D_80BEB2C8","UNK_TYPE1","",0x1), - 0x80BEB2E0:("D_80BEB2E0","UNK_TYPE1","",0x1), - 0x80BEB2E8:("D_80BEB2E8","UNK_TYPE1","",0x1), - 0x80BEB348:("D_80BEB348","UNK_TYPE1","",0x1), - 0x80BEB35C:("D_80BEB35C","UNK_TYPE1","",0x1), + 0x80BEB29C:("sCylinderInit","UNK_TYPE1","",0x1), + 0x80BEB2C8:("sAnimations","UNK_TYPE1","",0x1), + 0x80BEB2E0:("sAnimationModes","UNK_TYPE1","",0x1), + 0x80BEB2E8:("sAppearancePropertiesTable","UNK_TYPE1","",0x1), + 0x80BEB348:("sEyeTextures","UNK_TYPE1","",0x1), + 0x80BEB35C:("sBrowTextures","UNK_TYPE1","",0x1), 0x80BEB860:("En_Cha_InitVars","UNK_TYPE1","",0x1), 0x80BEB880:("D_80BEB880","UNK_TYPE1","",0x1), 0x80BEB8B0:("D_80BEB8B0","f32","",0x4),