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 <noreply>
This commit is contained in:
Jeff Padgham 2024-04-06 10:05:04 -07:00 committed by GitHub
parent 0a65e85856
commit 47d43f2fa1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 891 additions and 98 deletions

View File

@ -1,10 +1,10 @@
<Root>
<File Name="object_dt" Segment="6">
<Animation Name="object_dt_Anim_0005A4" Offset="0x5A4" /> <!-- Original name is "dt_aiya" -->
<Animation Name="object_dt_Anim_000854" Offset="0x854" /> <!-- Original name is "dt_aiya2yomesan" -->
<Animation Name="object_dt_Anim_000BE0" Offset="0xBE0" /> <!-- Original name is "dt_arigato" -->
<Animation Name="object_dt_Anim_000DA8" Offset="0xDA8" /> <!-- Original name is "dt_mojimoji" -->
<Animation Name="object_dt_Anim_00112C" Offset="0x112C" /> <!-- Original name is "dt_wait" -->
<Animation Name="gDotourDistressAnim" Offset="0x5A4" /> <!-- Original name is "dt_aiya" -->
<Animation Name="gDotourShockAnim" Offset="0x854" /> <!-- Original name is "dt_aiya2yomesan" -->
<Animation Name="gDotourUprightAnim" Offset="0xBE0" /> <!-- Original name is "dt_arigato" -->
<Animation Name="gDotourTapDeskAnim" Offset="0xDA8" /> <!-- Original name is "dt_mojimoji" -->
<Animation Name="gDotourWaitAnim" Offset="0x112C" /> <!-- Original name is "dt_wait" -->
<DList Name="object_dt_DL_004DB0" Offset="0x4DB0" />
<DList Name="object_dt_DL_005130" Offset="0x5130" />
<DList Name="object_dt_DL_0054C8" Offset="0x54C8" />
@ -62,6 +62,6 @@
<Limb Name="object_dt_Standardlimb_00B07C" Type="Standard" EnumName="OBJECT_DT_LIMB_0D" Offset="0xB07C" />
<Limb Name="object_dt_Standardlimb_00B088" Type="Standard" EnumName="OBJECT_DT_LIMB_0E" Offset="0xB088" />
<Skeleton Name="object_dt_Skel_00B0CC" Type="Flex" LimbType="Standard" LimbNone="OBJECT_DT_LIMB_NONE" LimbMax="OBJECT_DT_LIMB_MAX" EnumName="ObjectDtLimb" Offset="0xB0CC" />
<Animation Name="object_dt_Anim_00B500" Offset="0xB500" /> <!-- Original name is "dt_yome2arigato" -->
<Animation Name="gDotourSitUpAnim" Offset="0xB500" /> <!-- Original name is "dt_yome2arigato" -->
</File>
</Root>

View File

@ -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)

3
spec
View File

@ -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

View File

@ -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),

View File

@ -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);
}

View File

@ -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

View File

@ -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) {

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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)

View File

@ -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",),

View File

@ -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),