From 527c54b66e1c1c8c7264bb4f1d26141c9c2456c9 Mon Sep 17 00:00:00 2001 From: Tom Overton Date: Mon, 27 Dec 2021 18:10:15 -0800 Subject: [PATCH] Obj_Tokeidai (Clock Tower) OK and mostly documented (#442) * Migrate data to C * ObjTokeidai_Init OK * ObjTokeidai_Destroy OK * func_80AB2BBC OK * func_80AB3BB0 OK * func_80AB29F8 OK * func_80AB27B4 OK * func_80AB2834 OK * func_80AB2790 OK * func_80AB28C8 OK * ObjTokeidai_Update OK * func_80AB4040 OK * func_80AB3010 OK * func_80AB3CCC OK * func_80AB3240 OK * func_80AB3C50 OK * func_80AB4080 OK * func_80AB4160 OK * func_80AB319C OK * func_80AB3BE8 OK * func_80AB3BD8 OK * func_80AB3B34 OK * func_80AB3A7C OK * func_80AB3ED0 OK * func_80AB39BC OK * func_80AB38B0 OK * func_80AB3880 OK * func_80AB363C OK * func_80AB36C4 OK * func_80AB3808 OK * func_80AB365C OK * func_80AB3598 OK * func_80AB3544 OK * func_80AB34CC OK * func_80AB3370 OK * func_80AB32F0 OK * ObjTokeidai_Draw OK * func_80AB4894 OK * func_80AB4664 OK * func_80AB4394 OK * Use compiled reloc * Initial documentation pass * Little more documentation * Last bit of first pass documentation * Finish documentation * Respond to review feedback * Respond to Anghelo's feedback * Incorporate new enum * Respond to engineer's feedback * Respond to engineer's feedback * Update comments because the things I thought were unused ARE actually used * Switch to using "Open" instead of "Transform" * Run ./format.sh * Respond to engineer + hensldm by removing some macros * Fix func_801A3F54 argument * Fix func_801A3F54 argument Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com> * This is a legacy PR, so using autogenned object symbols * Move enum to .c * Revert "Move enum to .c" This reverts commit de13c50f0aac35470cec29bc90665c780055651f. * Move tokeidai-specific defines to .c Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com> --- include/functions.h | 2 +- spec | 3 +- .../actors/ovl_Obj_Tokeidai/z_obj_tokeidai.c | 849 ++++++++++++++++-- .../actors/ovl_Obj_Tokeidai/z_obj_tokeidai.h | 55 +- tools/disasm/functions.txt | 70 +- undefined_syms.txt | 15 - 6 files changed, 871 insertions(+), 123 deletions(-) diff --git a/include/functions.h b/include/functions.h index ba6ca5fc9..e8db8432e 100644 --- a/include/functions.h +++ b/include/functions.h @@ -3884,7 +3884,7 @@ void func_801A3CD8(s8 param_1); // void func_801A3D98(void); // void func_801A3E38(void); // void func_801A3EC0(void); -// void func_801A3F54(void); +void func_801A3F54(s32 arg0); // void func_801A3F6C(UNK_TYPE1 param_1, UNK_TYPE1 param_2, UNK_TYPE1 param_3, UNK_TYPE1 param_4, UNK_TYPE4 param_5, UNK_TYPE4 param_6); // void func_801A3FB4(void); // void func_801A3FFC(UNK_TYPE1 param_1); diff --git a/spec b/spec index 884a236c7..c437583d8 100644 --- a/spec +++ b/spec @@ -3361,8 +3361,7 @@ beginseg name "ovl_Obj_Tokeidai" compress include "build/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.o" - include "build/data/ovl_Obj_Tokeidai/ovl_Obj_Tokeidai.data.o" - include "build/data/ovl_Obj_Tokeidai/ovl_Obj_Tokeidai.reloc.o" + include "build/src/overlays/actors/ovl_Obj_Tokeidai/ovl_Obj_Tokeidai_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.c b/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.c index 13fbb074b..729464350 100644 --- a/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.c +++ b/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.c @@ -1,43 +1,51 @@ /* * File: z_obj_tokeidai.c * Overlay: ovl_Obj_Tokeidai - * Description: Components of the Clock Tower (gears, clock face, counterweight, etc). + * Description: Components of the Clock Tower (gears, clock face, counterweight, etc). Also used for wall clocks. + * + * This actor handles most of the functionality related to the Clock Tower and the various + * wall clocks around Clock Town. Among its responsibilities are making the clocks correctly + * tell the time by spinning the various pieces and gears and controlling the spotlight that + * is emitted from the Clock Tower at night. + * + * On the midnight of the Final Day, the Clock Tower opens up, and the various pieces of it + * are moved around. This actor is responsible for managing all of this movement. Additionally, + * if the moon crashes into Termina, this actor is responsible for making the pieces of the + * Clock Tower collapse. */ #include "z_obj_tokeidai.h" +#include "objects/object_obj_tokeidai/object_obj_tokeidai.h" #define FLAGS 0x00000030 #define THIS ((ObjTokeidai*)thisx) +#define GET_CURRENT_HOUR(this) ((s32)((this)->currentTime * (24.0f / 0x10000))) +#define GET_CURRENT_MINUTE(this) ((s32)((this)->currentTime * (360 * 2.0f / 0x10000)) % 30) +#define GET_CLOCK_FACE_ROTATION(currentHour) ((s16)(currentHour * (0x10000 / 24.0f))) +#define GET_OUTER_RING_OR_GEAR_ROTATION(currentMinute) ((s16)(currentMinute * (0x10000 * 12.0f / 360))) + void ObjTokeidai_Init(Actor* thisx, GlobalContext* globalCtx); void ObjTokeidai_Destroy(Actor* thisx, GlobalContext* globalCtx); void ObjTokeidai_Update(Actor* thisx, GlobalContext* globalCtx); void ObjTokeidai_Draw(Actor* thisx, GlobalContext* globalCtx); -void func_80AB319C(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3240(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB32F0(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3370(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB34CC(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3544(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3598(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB363C(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB365C(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3808(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3880(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB38B0(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB39BC(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3A7C(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3B34(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3BD8(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3BE8(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB3ED0(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB4040(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB4080(ObjTokeidai* this, GlobalContext* globalCtx); -void func_80AB4160(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_DoNothing(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_TowerClock_OpenedIdle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_TowerGear_OpenedIdle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_Counterweight_OpenedIdle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_TowerClock_Idle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_WallClock_Idle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_TowerGear_Idle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_Counterweight_Idle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_Walls_Idle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_StaircaseIntoTower_Idle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_SetupTowerOpening(ObjTokeidai* this); +void ObjTokeidai_Clock_Draw(Actor* thisx, GlobalContext* globalCtx); +void ObjTokeidai_Counterweight_Draw(Actor* thisx, GlobalContext* globalCtx); +void ObjTokeidai_TowerGear_Draw(Actor* thisx, GlobalContext* globalCtx); -#if 0 const ActorInit Obj_Tokeidai_InitVars = { ACTOR_OBJ_TOKEIDAI, ACTORCAT_PROP, @@ -50,87 +58,794 @@ const ActorInit Obj_Tokeidai_InitVars = { (ActorFunc)ObjTokeidai_Draw, }; -#endif +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 3300, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1100, ICHAIN_STOP), +}; -extern UNK_TYPE D_0600B208; -extern UNK_TYPE D_0600BA78; -extern UNK_TYPE D_0600CF28; -extern UNK_TYPE D_0600D388; +/** + * Returns the angle necessary to show the correct side of + * the sun and moon disk based on the time of day. The actual + * angle can differ from the target angle if the disk is in + * the middle of rotating. + */ +s32 ObjTokeidai_GetTargetSunMoonDiskRotation() { + if (gSaveContext.isNight) { + return 0x8000; + } + return 0; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB2790.s") +void ObjTokeidai_SetupClockOrGear(ObjTokeidai* this) { + s32 currentMinute = GET_CURRENT_MINUTE(this); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB27B4.s") + this->clockMinute = currentMinute; + this->outerRingOrGearRotation = GET_OUTER_RING_OR_GEAR_ROTATION(currentMinute); + this->outerRingOrGearRotationalVelocity = 0x3C; + this->outerRingOrGearRotationTimer = 0; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB2834.s") +void ObjTokeidai_Clock_Init(ObjTokeidai* this) { + s32 currentHour; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB28C8.s") + ObjTokeidai_SetupClockOrGear(this); + currentHour = GET_CURRENT_HOUR(this); + this->clockHour = currentHour; + this->clockFaceRotation = GET_CLOCK_FACE_ROTATION(currentHour); + this->clockFaceRotationalVelocity = 0; + this->clockFaceRotationTimer = 0; + this->sunMoonDiskRotationalVelocity = 0; + this->sunMoonDiskRotation = ObjTokeidai_GetTargetSunMoonDiskRotation(); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB29F8.s") +void ObjTokeidai_TowerGear_Init(ObjTokeidai* this, GlobalContext* globalCtx) { + this->actor.draw = ObjTokeidai_TowerGear_Draw; + this->opaDList = object_obj_tokeidai_DL_00BA78; + ObjTokeidai_SetupClockOrGear(this); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB2BBC.s") + if (((globalCtx->sceneNum == SCENE_CLOCKTOWER) && (gSaveContext.sceneSetupIndex == 2) && + (globalCtx->csCtx.unk_12 == 0)) || + ((globalCtx->sceneNum == SCENE_00KEIKOKU) && (gSaveContext.sceneSetupIndex == 2) && + (globalCtx->csCtx.unk_12 == 0))) { + ObjTokeidai_SetupTowerOpening(this); + } else if ((CURRENT_DAY == 3 && gSaveContext.time < CLOCK_TIME(6, 0)) || CURRENT_DAY >= 4) { + this->actionFunc = ObjTokeidai_TowerGear_OpenedIdle; + this->actor.world.pos.y += this->actor.scale.y * 1900.0f; + this->actor.shape.yOffset = 1500.0f; + gSaveContext.weekEventReg[8] |= 0x40; + } else { + this->actionFunc = ObjTokeidai_TowerGear_Idle; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/ObjTokeidai_Init.s") +void ObjTokeidai_TowerClock_Init(ObjTokeidai* this, GlobalContext* globalCtx) { + this->actor.draw = ObjTokeidai_Clock_Draw; + ObjTokeidai_Clock_Init(this); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/ObjTokeidai_Destroy.s") + if (((globalCtx->sceneNum == SCENE_CLOCKTOWER) && (gSaveContext.sceneSetupIndex == 2) && + (globalCtx->csCtx.unk_12 == 0)) || + ((globalCtx->sceneNum == SCENE_00KEIKOKU) && (gSaveContext.sceneSetupIndex == 2) && + (globalCtx->csCtx.unk_12 == 0))) { + ObjTokeidai_SetupTowerOpening(this); + } else if ((CURRENT_DAY == 3 && gSaveContext.time < CLOCK_TIME(6, 0)) || CURRENT_DAY >= 4) { + this->actor.world.pos.y += (this->actor.scale.y * 5191.0f) - 50.0f; + this->actor.world.pos.x += Math_SinS(this->actor.world.rot.y) * this->actor.scale.z * 1791.0f; + this->actor.world.pos.z += -Math_CosS(this->actor.world.rot.y) * this->actor.scale.z * 1791.0f; + this->clockFaceZTranslation = -0x140; + this->actor.shape.rot.x = -0x4000; + this->actor.home.pos = this->actor.world.pos; + this->actor.home.pos.y -= 1178.0f; + this->actor.home.pos.z += 13.0f; + this->actionFunc = ObjTokeidai_TowerClock_OpenedIdle; + } else { + this->actionFunc = ObjTokeidai_TowerClock_Idle; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3010.s") +void ObjTokeidai_Counterweight_Init(ObjTokeidai* this, GlobalContext* globalCtx) { + s32 type; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB319C.s") + this->actor.draw = ObjTokeidai_Counterweight_Draw; + this->opaDList = object_obj_tokeidai_DL_00B208; + this->xluDList = object_obj_tokeidai_DL_00B0C0; + if (gSaveContext.isNight) { + this->spotlightIntensity = 100; + } else { + this->spotlightIntensity = 0; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3240.s") + if (((globalCtx->sceneNum == SCENE_CLOCKTOWER) && (gSaveContext.sceneSetupIndex == 2) && + (globalCtx->csCtx.unk_12 == 0)) || + ((globalCtx->sceneNum == SCENE_00KEIKOKU) && (gSaveContext.sceneSetupIndex == 2) && + (globalCtx->csCtx.unk_12 == 0))) { + this->spotlightIntensity = 0; + ObjTokeidai_SetupTowerOpening(this); + if (this->actor.child == NULL) { + type = OBJ_TOKEIDAI_TYPE(&this->actor); + if (type == OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_CLOCK_TOWN || + type == OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_TERMINA_FIELD) { + this->actor.child = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HANABI, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + } + } + if (this->actor.child != NULL) { + if (OBJ_TOKEIDAI_TYPE(&this->actor) == OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_TERMINA_FIELD) { + this->actor.child->home.rot.x = 0x384; + } else { + this->actor.child->home.rot.x = 0x12C; + } + } + } else if ((CURRENT_DAY == 3 && gSaveContext.time < CLOCK_TIME(6, 0)) || CURRENT_DAY >= 4) { + this->spotlightIntensity = 0; + this->actor.world.pos.y += this->actor.scale.y * -2160.0f; + this->actor.world.pos.x += Math_SinS(this->actor.world.rot.y) * this->actor.scale.z * 5400.0f; + this->actor.world.pos.z += -Math_CosS(this->actor.world.rot.y) * this->actor.scale.z * 5400.0f; + this->actor.shape.rot.x = -0x4000; + this->actionFunc = ObjTokeidai_Counterweight_OpenedIdle; + } else { + this->actionFunc = ObjTokeidai_Counterweight_Idle; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB32F0.s") +void ObjTokeidai_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjTokeidai* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3370.s") + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actionFunc = ObjTokeidai_DoNothing; + this->opaDList = NULL; + this->xluDList = NULL; + this->xRotation = 0; + this->yTranslation = 0; + this->clockFaceZTranslation = 0; + this->currentTime = gSaveContext.time; + this->actor.home.rot.x = 0; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB34CC.s") + switch (OBJ_TOKEIDAI_TYPE(&this->actor)) { + case OBJ_TOKEIDAI_TYPE_TOWER_GEAR_TERMINA_FIELD: + Actor_SetScale(&this->actor, 0.15f); + ObjTokeidai_TowerGear_Init(this, globalCtx); + break; + case OBJ_TOKEIDAI_TYPE_TOWER_GEAR_CLOCK_TOWN: + ObjTokeidai_TowerGear_Init(this, globalCtx); + break; + case OBJ_TOKEIDAI_TYPE_UNUSED_WALL: + this->opaDList = object_obj_tokeidai_DL_00D388; + break; + case OBJ_TOKEIDAI_TYPE_TOWER_WALLS_TERMINA_FIELD: + Actor_SetScale(&this->actor, 1.0f); + this->opaDList = object_obj_tokeidai_DL_009A08; + this->actionFunc = ObjTokeidai_Walls_Idle; + break; + case OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_TERMINA_FIELD: + Actor_SetScale(&this->actor, 0.15f); + ObjTokeidai_TowerClock_Init(this, globalCtx); + break; + case OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_CLOCK_TOWN: + ObjTokeidai_TowerClock_Init(this, globalCtx); + break; + case OBJ_TOKEIDAI_TYPE_WALL_CLOCK: + Actor_SetScale(&this->actor, 0.02f); + this->actor.draw = ObjTokeidai_Clock_Draw; + ObjTokeidai_Clock_Init(this); + this->actionFunc = ObjTokeidai_WallClock_Idle; + break; + case OBJ_TOKEIDAI_TYPE_SMALL_WALL_CLOCK: + Actor_SetScale(&this->actor, 0.01f); + this->actor.draw = ObjTokeidai_Clock_Draw; + ObjTokeidai_Clock_Init(this); + this->actionFunc = ObjTokeidai_WallClock_Idle; + break; + case OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_TERMINA_FIELD: + Actor_SetScale(&this->actor, 0.15f); + ObjTokeidai_Counterweight_Init(this, globalCtx); + break; + case OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_CLOCK_TOWN: + ObjTokeidai_Counterweight_Init(this, globalCtx); + break; + case OBJ_TOKEIDAI_TYPE_STAIRCASE_INTO_TOWER: + this->opaDList = object_obj_tokeidai_DL_00D8E8; + this->xluDList = object_obj_tokeidai_DL_00D8E0; + this->actionFunc = ObjTokeidai_StaircaseIntoTower_Idle; + break; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3544.s") +void ObjTokeidai_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3598.s") +void ObjTokeidai_RotateOnMinuteChange(ObjTokeidai* this, s32 playSfx) { + s32 currentMinute = GET_CURRENT_MINUTE(this); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB363C.s") + if (currentMinute != this->clockMinute) { + if (this->outerRingOrGearRotationTimer == 8 && playSfx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CLOCK_TOWER_SECOND_HAND); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB365C.s") + if (this->outerRingOrGearRotationTimer > 8) { + // This actually performs the rotation to the next minute + // for the outer ring or gear. + this->outerRingOrGearRotationalVelocity += 0x3C; + this->outerRingOrGearRotation += this->outerRingOrGearRotationalVelocity; + } else { + // This makes the outer ring or gear wiggle in place for a bit + // before rotating to the next position. + if ((this->outerRingOrGearRotationTimer & 3) == 0) { + this->outerRingOrGearRotation += 0x5A; + } + if ((this->outerRingOrGearRotationTimer & 3) == 1) { + this->outerRingOrGearRotation -= 0x5A; + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB36C4.s") + this->outerRingOrGearRotationTimer++; + if ((currentMinute == 15 && this->outerRingOrGearRotation < 0) || + (currentMinute != 15 && this->outerRingOrGearRotation > GET_OUTER_RING_OR_GEAR_ROTATION(currentMinute))) { + this->outerRingOrGearRotation = GET_OUTER_RING_OR_GEAR_ROTATION(currentMinute); + this->clockMinute = currentMinute; + this->outerRingOrGearRotationalVelocity = 0x5A; + this->outerRingOrGearRotationTimer = 0; + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3808.s") +void ObjTokeidai_TowerGear_Collapse(ObjTokeidai* this, GlobalContext* globalCtx) { + if ((this->actor.bgCheckFlags & 1) || this->actor.world.pos.y < 0.0f) { + this->actionFunc = ObjTokeidai_DoNothing; + } else { + this->actor.shape.rot.x += 0x50; + this->actor.shape.rot.z += 0x50; + Actor_SetVelocityAndMoveYRotationAndGravity(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3880.s") +void ObjTokeidai_TowerGear_OpenedIdle(ObjTokeidai* this, GlobalContext* globalCtx) { + if (func_800EE29C(globalCtx, 0x84) && globalCtx->csCtx.npcActions[func_800EE200(globalCtx, 0x84)]->unk0 == 2) { + this->actionFunc = ObjTokeidai_TowerGear_Collapse; + this->actor.speedXZ = this->actor.scale.y * 5.0f; + this->actor.velocity.y = 0.0f; + this->actor.minVelocityY = this->actor.scale.y * -50.0f; + this->actor.gravity = this->actor.scale.y * -5.0f; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB38B0.s") +/** + * This is hard to see in the final game. This, used in conjunction + * with the following function, makes the tower's clock slide off the + * tower and spin through the air when the moon crashes. + */ +void ObjTokeidai_TowerClock_Fall(ObjTokeidai* this, GlobalContext* globalCtx) { + this->actor.shape.rot.x += this->fallingClockFaceRotationalVelocity; + if (this->fallingClockFaceRotationalVelocity > 0xA0) { + this->fallingClockFaceRotationalVelocity -= 5; + } + this->actor.world.pos.z += 4.0f; + Actor_SetVelocityAndMoveYRotationAndGravity(&this->actor); + if (this->actor.world.pos.y < 0.0f) { + this->actionFunc = ObjTokeidai_DoNothing; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB39BC.s") +/** + * This is hard to see in the final game. This makes the + * tower's clock slide off as the moon crashes into it. + */ +void ObjTokeidai_TowerClock_SlideOff(ObjTokeidai* this, GlobalContext* globalCtx) { + f32 cos; + f32 sin; + Actor* thisx = &this->actor; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3A7C.s") + if (this->slidingClockFaceAngle < 0x4000) { + this->slidingClockFaceAngle += 0x28; + } + if (this->slidingClockFaceAngle > 0x800) { + this->aerialClockFaceSpeed += 4; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3B34.s") + if (this->aerialClockFaceSpeed < 0x80) { + thisx->shape.rot.x = this->slidingClockFaceAngle - 0x4000; + this->fallingClockFaceRotationalVelocity = 0x28; + } else { + if (thisx->shape.rot.x < -0x1000) { + thisx->shape.rot.x += this->fallingClockFaceRotationalVelocity; + if (this->fallingClockFaceRotationalVelocity < 0x1E0) { + this->fallingClockFaceRotationalVelocity += 0xA; + } + } else { + thisx->shape.rot.x += this->fallingClockFaceRotationalVelocity; + this->actionFunc = ObjTokeidai_TowerClock_Fall; + thisx->minVelocityY = -7.5f; + thisx->gravity = -0.75f; + thisx->velocity.y = -2.0f; + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3BB0.s") + sin = Math_SinS(this->slidingClockFaceAngle); + cos = Math_CosS(this->slidingClockFaceAngle); + thisx->world.pos.y = (1178.0f * cos) - (this->aerialClockFaceSpeed * sin) + thisx->home.pos.y; + thisx->world.pos.z = (1178.0f * sin) + (this->aerialClockFaceSpeed * cos) + thisx->home.pos.z; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3BD8.s") +void ObjTokeidai_TowerClock_OpenedIdle(ObjTokeidai* this, GlobalContext* globalCtx) { + if (func_800EE29C(globalCtx, 0x84) && globalCtx->csCtx.npcActions[func_800EE200(globalCtx, 0x84)]->unk0 == 1) { + this->actionFunc = ObjTokeidai_TowerClock_SlideOff; + this->slidingClockFaceAngle = 0; + this->aerialClockFaceSpeed = -0xD; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3BE8.s") +void ObjTokeidai_Counterweight_Collapse(ObjTokeidai* this, GlobalContext* globalCtx) { + if (this->actor.world.pos.y < 0.0f) { + this->actionFunc = ObjTokeidai_DoNothing; + } else { + this->xRotation += 0x64; + Actor_SetVelocityAndMoveYRotationAndGravity(&this->actor); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3C50.s") +void ObjTokeidai_Counterweight_OpenedIdle(ObjTokeidai* this, GlobalContext* globalCtx) { + if (func_800EE29C(globalCtx, 0x84) && globalCtx->csCtx.npcActions[func_800EE200(globalCtx, 0x84)]->unk0 == 3) { + this->actionFunc = ObjTokeidai_Counterweight_Collapse; + this->xRotation = 0; + this->actor.velocity.y = 0.0f; + this->actor.minVelocityY = this->actor.scale.y * -50.0f; + this->actor.gravity = this->actor.scale.y * -5.0f; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3CCC.s") +/** + * This is hard to see in the final game. It makes the walls of + * the clock tower tip over as the moon crashes into it, but the + * giant moon mostly obscures it. + */ +void ObjTokeidai_Walls_Collapse(ObjTokeidai* this, GlobalContext* globalCtx) { + if (this->actor.shape.rot.x < 0x4000) { + this->actor.shape.rot.x += 0x28; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB3ED0.s") +void ObjTokeidai_Walls_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { + if (func_800EE29C(globalCtx, 0x84) != 0 && globalCtx->csCtx.npcActions[func_800EE200(globalCtx, 0x84)]->unk0 == 1) { + this->actionFunc = ObjTokeidai_Walls_Collapse; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB4040.s") +void ObjTokeidai_TowerOpening_EndCutscene(ObjTokeidai* this, GlobalContext* globalCtx) { + if (func_800EE29C(globalCtx, 0x84) != 0 && globalCtx->csCtx.npcActions[func_800EE200(globalCtx, 0x84)]->unk0 == 5) { + gSaveContext.weekEventReg[8] |= 0x40; + if (((globalCtx->sceneNum == SCENE_CLOCKTOWER) && (gSaveContext.sceneSetupIndex == 2) && + (globalCtx->csCtx.unk_12 == 0)) || + ((globalCtx->sceneNum == SCENE_00KEIKOKU) && (gSaveContext.sceneSetupIndex == 2) && + (globalCtx->csCtx.unk_12 == 0))) { + func_801A3F54(false); + gSaveContext.cutscene = 0; + gSaveContext.nextCutsceneIndex = 0; + gSaveContext.respawnFlag = 2; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->nextEntranceIndex = gSaveContext.respawn[1].entranceIndex; + globalCtx->unk_1887F = 2; + if (gSaveContext.respawn[1].playerParams == 0xCFF) { + gSaveContext.nextTransition = 0x15; + } else { + gSaveContext.nextTransition = 2; + } + } + this->actionFunc = ObjTokeidai_DoNothing; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB4080.s") +void ObjTokeidai_TowerOpening_FinishOpening(ObjTokeidai* this, GlobalContext* globalCtx) { + s32 type; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB4160.s") + if (this->clockFaceZTranslation > -320) { + // Recess the clock face before ending the cutscene, since the Termina + // Field version of the cutscene looks right at it. + this->clockFaceZTranslation -= 10; + } else { + type = OBJ_TOKEIDAI_TYPE(&this->actor); + if ((type == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_CLOCK_TOWN) || + (type == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_TERMINA_FIELD)) { + ObjTokeidai_TowerOpening_EndCutscene(this, globalCtx); + } else { + this->actionFunc = ObjTokeidai_DoNothing; + if (this->actor.child != NULL) { + this->actor.child->home.rot.x = 0; + } + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/ObjTokeidai_Update.s") +void ObjTokeidai_TowerOpening_Wait(ObjTokeidai* this, GlobalContext* globalCtx) { + if (this->openingWaitTimer > 0) { + this->openingWaitTimer--; + } else { + this->actionFunc = ObjTokeidai_TowerOpening_FinishOpening; + this->openingWaitTimer = 0; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/ObjTokeidai_Draw.s") +void ObjTokeidai_TowerOpening_DropCounterweight(ObjTokeidai* this, GlobalContext* globalCtx) { + this->xRotation += this->counterweightRotationalVelocity; + if (this->xRotation < 0x4000) { + this->counterweightRotationalVelocity += this->counterweightRotationalAcceleration; + return; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB4394.s") + this->counterweightRotationalAcceleration = 0x14; + this->xRotation = 0x4000; + switch (this->boundCount) { + case 0: + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CLOCK_TOWER_BOUND_0); + break; + case 1: + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CLOCK_TOWER_BOUND_1); + break; + case 2: + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CLOCK_TOWER_BOUND_2); + break; + } + this->boundCount++; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB4664.s") + if (this->counterweightRotationalVelocity > 0x190) { + // This condition is met for the first bound, causing the counterweight + // to rebound upwards quickly. + this->counterweightRotationalVelocity = -0xC8; + } else if (this->counterweightRotationalVelocity > 0x32) { + // This condition is met for the second bound, causing the counterweight + // to rebound upwards slowly. + this->counterweightRotationalVelocity = -(this->counterweightRotationalVelocity >> 1); + } else { + // This condition is met for the third bound, causing the counterweight + // to stop moving. + this->actionFunc = ObjTokeidai_TowerOpening_Wait; + this->openingWaitTimer = 10; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tokeidai/func_80AB4894.s") +void ObjTokeidai_TowerOpening_FinishRaise(ObjTokeidai* this, GlobalContext* globalCtx) { + s32 type; + + if (this->settleTimer > 0) { + // Jiggle the parts of the tower up and down to give it the appearance + // of "settling" into place. + if (this->settleTimer & 1) { + this->yTranslation = this->settleAmount + 3400; + } else { + this->yTranslation = 3400 - this->settleAmount; + if (this->settleAmount > 0) { + this->settleAmount -= 4; + } + } + this->settleTimer--; + } else { + type = OBJ_TOKEIDAI_TYPE(&this->actor); + if ((type == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_CLOCK_TOWN) || + (type == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_TERMINA_FIELD)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CLOCK_TOWER_FALL); + } + this->yTranslation = 3400; + this->actionFunc = ObjTokeidai_TowerOpening_DropCounterweight; + this->counterweightRotationalVelocity = 0; + this->counterweightRotationalAcceleration = 0xA; + this->boundCount = 0; + } +} + +void ObjTokeidai_TowerOpening_RaiseTower(ObjTokeidai* this, GlobalContext* globalCtx) { + s32 type; + + if (this->yTranslation < 3400) { + type = OBJ_TOKEIDAI_TYPE(&this->actor); + this->yTranslation += 25; + if ((type == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_CLOCK_TOWN) || + (type == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_TERMINA_FIELD)) { + func_800B9010(&this->actor, NA_SE_EV_CLOCK_TOWER_UP - SFX_FLAG); + } + } else { + type = OBJ_TOKEIDAI_TYPE(&this->actor); + if ((type == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_CLOCK_TOWN) || + (type == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_TERMINA_FIELD)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CLOCK_TOWER_STOP); + } + this->yTranslation = 3400; + this->actionFunc = ObjTokeidai_TowerOpening_FinishRaise; + this->settleTimer = 10; + this->settleAmount = 20; + } +} + +void ObjTokeidai_TowerOpening_Start(ObjTokeidai* this, GlobalContext* globalCtx) { + if ((func_800EE29C(globalCtx, 0x84) && globalCtx->csCtx.npcActions[func_800EE200(globalCtx, 0x84)]->unk0 == 4) || + (gSaveContext.weekEventReg[8] & 0x40)) { + this->actionFunc = ObjTokeidai_TowerOpening_RaiseTower; + } +} + +/** + * Sets up the sequence where the Clock Tower "transforms" and opens up + * on the midnight of the Final Day. + */ +void ObjTokeidai_SetupTowerOpening(ObjTokeidai* this) { + this->actionFunc = ObjTokeidai_TowerOpening_Start; + this->clockFaceRotationalVelocity = 0; + this->clockFaceRotationTimer = 0; + this->yTranslation = 0; + this->xRotation = 0; + this->clockFaceZTranslation = 0; +} + +void ObjTokeidai_DoNothing(ObjTokeidai* this, GlobalContext* globalCtx) { +} + +void ObjTokeidai_StaircaseIntoTower_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { + if (((CURRENT_DAY == 3 && gSaveContext.time < CLOCK_TIME(6, 0)) || CURRENT_DAY >= 4) || + (gSaveContext.weekEventReg[8] & 0x40)) { + this->actor.draw = ObjTokeidai_Draw; + } else { + this->actor.draw = NULL; + } +} + +s32 ObjTokeidai_IsPostFirstCycleFinalHours(ObjTokeidai* this, GlobalContext* globalCtx) { + if (gSaveContext.inventory.items[SLOT_OCARINA] == ITEM_NONE) { + return false; + } + if (CURRENT_DAY == 3 && gSaveContext.time < CLOCK_TIME(6, 0)) { + ObjTokeidai_SetupTowerOpening(this); + return true; + } + return false; +} + +void ObjTokeidai_RotateOnHourChange(ObjTokeidai* this, GlobalContext* globalCtx) { + s32 currentHour = GET_CURRENT_HOUR(this); + + if (currentHour != this->clockHour) { + if (this->clockFaceRotationTimer > 12) { + // This actually performs the rotation to the next hour + // for the clock face. + this->clockFaceRotationalVelocity += 0xA; + this->clockFaceRotation += this->clockFaceRotationalVelocity; + } else { + // This makes the clock face wiggle in place for a bit + // before rotating to the next position. + if ((this->clockFaceRotationTimer & 3) == 0) { + this->clockFaceRotation += 0x3C; + } + if ((this->clockFaceRotationTimer & 3) == 1) { + this->clockFaceRotation -= 0x3C; + } + } + + this->clockFaceRotationTimer++; + if ((currentHour == 12 && this->clockFaceRotation < 0) || + (currentHour != 12 && this->clockFaceRotation > GET_CLOCK_FACE_ROTATION(currentHour))) { + this->clockFaceRotation = GET_CLOCK_FACE_ROTATION(currentHour); + this->clockHour = currentHour; + this->clockFaceRotationalVelocity = 0; + this->clockFaceRotationTimer = 0; + } + } + + // If the sun and moon disk doesn't have the target rotation (e.g., the time of day + // just changed), rotate it until it matches the target. + if (this->sunMoonDiskRotation != ObjTokeidai_GetTargetSunMoonDiskRotation()) { + if (this->clockHour == 6) { + this->sunMoonDiskRotationalVelocity += 0x222; + this->sunMoonDiskRotation += this->sunMoonDiskRotationalVelocity; + if (this->sunMoonDiskRotation > 0x10000) { + this->sunMoonDiskRotation = ObjTokeidai_GetTargetSunMoonDiskRotation(); + this->sunMoonDiskRotationalVelocity = 0; + } + } + if (this->clockHour == 18) { + this->sunMoonDiskRotationalVelocity += 0x222; + this->sunMoonDiskRotation += this->sunMoonDiskRotationalVelocity; + if (this->sunMoonDiskRotation > 0x8000) { + this->sunMoonDiskRotation = ObjTokeidai_GetTargetSunMoonDiskRotation(); + this->sunMoonDiskRotationalVelocity = 0; + } + } + } +} + +void ObjTokeidai_TowerClock_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { + if (CURRENT_DAY == 3 && this->clockHour < 6 && gSaveContext.time < CLOCK_TIME(6, 0)) { + this->actor.draw = ObjTokeidai_Clock_Draw; + ObjTokeidai_SetupTowerOpening(this); + gSaveContext.weekEventReg[8] |= 0x40; + return; + } + + if (globalCtx->csCtx.state != 0) { + this->actor.home.rot.x = 1; + this->currentTime += 3; + this->actor.draw = ObjTokeidai_Clock_Draw; + } else { + if (!(globalCtx->actorCtx.unk5 & 2) && + OBJ_TOKEIDAI_TYPE(&this->actor) == OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_TERMINA_FIELD && + ActorCutscene_GetCurrentIndex() == -1) { + this->actor.draw = NULL; + } + this->currentTime = gSaveContext.time; + if (this->actor.home.rot.x != 0) { + ObjTokeidai_Clock_Init(this); + this->actor.home.rot.x = 0; + } + } + + if (CURRENT_DAY != 3 || gSaveContext.time >= CLOCK_TIME(6, 0)) { + ObjTokeidai_RotateOnMinuteChange(this, true); + } + ObjTokeidai_RotateOnHourChange(this, globalCtx); +} + +void ObjTokeidai_WallClock_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { + this->currentTime = gSaveContext.time; + ObjTokeidai_RotateOnMinuteChange(this, true); + ObjTokeidai_RotateOnHourChange(this, globalCtx); +} + +void ObjTokeidai_TowerGear_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { + if (ObjTokeidai_IsPostFirstCycleFinalHours(this, globalCtx)) { + this->actor.draw = ObjTokeidai_TowerGear_Draw; + } else { + if (globalCtx->csCtx.state != 0) { + this->actor.home.rot.x = 1; + this->currentTime += 3; + this->actor.draw = ObjTokeidai_TowerGear_Draw; + } else { + if ((globalCtx->actorCtx.unk5 & 2) == 0 && + OBJ_TOKEIDAI_TYPE(&this->actor) == OBJ_TOKEIDAI_TYPE_TOWER_GEAR_TERMINA_FIELD && + ActorCutscene_GetCurrentIndex() == -1) { + this->actor.draw = NULL; + } + this->currentTime = gSaveContext.time; + if (this->actor.home.rot.x != 0) { + ObjTokeidai_SetupClockOrGear(this); + this->actor.home.rot.x = 0; + } + } + ObjTokeidai_RotateOnMinuteChange(this, false); + } +} + +void ObjTokeidai_Counterweight_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { + s32 type; + + if (ObjTokeidai_IsPostFirstCycleFinalHours(this, globalCtx)) { + this->spotlightIntensity = 0; + if (this->actor.child == NULL) { + type = OBJ_TOKEIDAI_TYPE(&this->actor); + if (type == OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_CLOCK_TOWN || + type == OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_TERMINA_FIELD) { + this->actor.child = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HANABI, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + } + } + if (this->actor.child != NULL) { + if (OBJ_TOKEIDAI_TYPE(&this->actor) == OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_TERMINA_FIELD) { + this->actor.child->home.rot.x = 0x384; + } else { + this->actor.child->home.rot.x = 0x12C; + } + } + } else { + this->actor.shape.rot.y -= 0x40; + if (gSaveContext.isNight) { + if (this->spotlightIntensity < 100) { + this->spotlightIntensity += 4; + } + } else if (this->spotlightIntensity > 0) { + this->spotlightIntensity -= 4; + } + } +} + +void ObjTokeidai_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjTokeidai* this = THIS; + this->actionFunc(this, globalCtx); +} + +void ObjTokeidai_Draw(Actor* thisx, GlobalContext* globalCtx) { + ObjTokeidai* this = THIS; + + OPEN_DISPS(globalCtx->state.gfxCtx); + + if (this->opaDList != NULL) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_8012C28C(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_OPA_DISP++, this->opaDList); + } + if (this->xluDList != NULL) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_8012C2DC(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, this->xluDList); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx); +} + +void ObjTokeidai_Clock_Draw(Actor* thisx, GlobalContext* globalCtx) { + ObjTokeidai* this = THIS; + + OPEN_DISPS(globalCtx->state.gfxCtx); + + func_8012C28C(globalCtx->state.gfxCtx); + Matrix_InsertTranslation(0.0f, this->yTranslation, 0.0f, MTXMODE_APPLY); + Matrix_InsertTranslation(0.0f, 0.0f, -1791.0f, MTXMODE_APPLY); + Matrix_InsertXRotation_s(-this->xRotation, MTXMODE_APPLY); + Matrix_InsertTranslation(0.0f, 0.0f, 1791.0f, MTXMODE_APPLY); + Matrix_StatePush(); + Matrix_InsertZRotation_s(-this->outerRingOrGearRotation, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_obj_tokeidai_DL_00CF28); + Matrix_StatePop(); + Matrix_InsertTranslation(0.0f, 0.0f, this->clockFaceZTranslation, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_obj_tokeidai_DL_00BEE8); + Matrix_InsertZRotation_s(-this->clockFaceRotation * 2, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (OBJ_TOKEIDAI_TYPE(&this->actor) == OBJ_TOKEIDAI_TYPE_WALL_CLOCK || + OBJ_TOKEIDAI_TYPE(&this->actor) == OBJ_TOKEIDAI_TYPE_SMALL_WALL_CLOCK) { + gSPDisplayList(POLY_OPA_DISP++, object_obj_tokeidai_DL_00F518); + } else { + gSPDisplayList(POLY_OPA_DISP++, object_obj_tokeidai_DL_00E818); + } + Matrix_InsertTranslation(0.0f, -1112.0f, -19.6f, MTXMODE_APPLY); + Matrix_RotateY((s16)this->sunMoonDiskRotation, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_obj_tokeidai_DL_00C368); + + CLOSE_DISPS(globalCtx->state.gfxCtx); +} + +void ObjTokeidai_Counterweight_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + u32 gameplayFrames = globalCtx->gameplayFrames; + ObjTokeidai* this = THIS; + + Matrix_RotateY(-this->actor.shape.rot.y, MTXMODE_APPLY); + Matrix_InsertTranslation(0.0f, this->yTranslation, 0.0f, MTXMODE_APPLY); + Matrix_InsertTranslation(0.0f, -5480.0f, 80.0f, MTXMODE_APPLY); + Matrix_InsertXRotation_s(-this->xRotation, MTXMODE_APPLY); + Matrix_InsertTranslation(0.0f, 5480.0f, -80.0f, MTXMODE_APPLY); + Matrix_RotateY(this->actor.shape.rot.y, MTXMODE_APPLY); + + OPEN_DISPS(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, -gameplayFrames, 0, 0x20, 0x20)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_8012C28C(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_OPA_DISP++, this->opaDList); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_8012C2DC(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 255, 255, 235, 180, (s32)(this->spotlightIntensity * 2.55f)); + gSPDisplayList(POLY_XLU_DISP++, this->xluDList); + + CLOSE_DISPS(globalCtx->state.gfxCtx); +} + +void ObjTokeidai_TowerGear_Draw(Actor* thisx, GlobalContext* globalCtx) { + ObjTokeidai* this = THIS; + + OPEN_DISPS(globalCtx->state.gfxCtx); + + Matrix_InsertTranslation(0.0f, this->yTranslation, 0.0f, MTXMODE_APPLY); + Matrix_InsertTranslation(0.0f, 0.0f, -1791.0f, MTXMODE_APPLY); + Matrix_RotateY(-this->actor.shape.rot.y, MTXMODE_APPLY); + Matrix_InsertXRotation_s(-this->xRotation, MTXMODE_APPLY); + Matrix_RotateY(thisx->shape.rot.y, MTXMODE_APPLY); + Matrix_InsertTranslation(0.0f, 0.0f, 1791.0f, MTXMODE_APPLY); + Matrix_InsertZRotation_s(this->outerRingOrGearRotation, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_8012C28C(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_OPA_DISP++, object_obj_tokeidai_DL_00BA78); + + CLOSE_DISPS(globalCtx->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.h b/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.h index f550606f0..c5cf8d70c 100644 --- a/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.h +++ b/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.h @@ -3,14 +3,63 @@ #include "global.h" +#define OBJ_TOKEIDAI_TYPE(thisx) (((thisx)->params & 0xF000) >> 12) + +typedef enum { + /* 0 */ OBJ_TOKEIDAI_TYPE_TOWER_GEAR_CLOCK_TOWN, + /* 1 */ OBJ_TOKEIDAI_TYPE_UNUSED_WALL, + /* 2 */ OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_CLOCK_TOWN, + /* 3 */ OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_CLOCK_TOWN, + /* 4 */ OBJ_TOKEIDAI_TYPE_TOWER_GEAR_TERMINA_FIELD, + /* 5 */ OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_TERMINA_FIELD, + /* 6 */ OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_TERMINA_FIELD, + /* 8 */ OBJ_TOKEIDAI_TYPE_TOWER_WALLS_TERMINA_FIELD = 8, + /* 9 */ OBJ_TOKEIDAI_TYPE_WALL_CLOCK, + /* 10 */ OBJ_TOKEIDAI_TYPE_SMALL_WALL_CLOCK, + /* 11 */ OBJ_TOKEIDAI_TYPE_STAIRCASE_INTO_TOWER, +} ObjTokeidaiType; + struct ObjTokeidai; typedef void (*ObjTokeidaiActionFunc)(struct ObjTokeidai*, GlobalContext*); typedef struct ObjTokeidai { - /* 0x0000 */ Actor actor; - /* 0x0144 */ char unk_144[0x30]; - /* 0x0174 */ ObjTokeidaiActionFunc actionFunc; + /* 0x000 */ Actor actor; + /* 0x144 */ Gfx* opaDList; + /* 0x148 */ Gfx* xluDList; + /* 0x14C */ s16 outerRingOrGearRotation; + /* 0x14E */ s16 outerRingOrGearRotationalVelocity; + /* 0x150 */ s16 outerRingOrGearRotationTimer; + /* 0x152 */ s16 clockFaceRotation; + /* 0x154 */ union { + s16 clockFaceRotationalVelocity; + s16 settleTimer; + s16 counterweightRotationalVelocity; + s16 openingWaitTimer; + s16 slidingClockFaceAngle; + }; + /* 0x156 */ union { + s16 clockFaceRotationTimer; + s16 settleAmount; + s16 counterweightRotationalAcceleration; + s16 aerialClockFaceSpeed; + }; + /* 0x158 */ s32 sunMoonDiskRotation; + /* 0x15C */ union { + s16 sunMoonDiskRotationalVelocity; + s16 fallingClockFaceRotationalVelocity; + }; + /* 0x15E */ s16 yTranslation; + /* 0x160 */ s16 xRotation; + /* 0x162 */ s16 clockFaceZTranslation; // amount the clock face recesses inwards once it transforms + /* 0x164 */ s16 boundCount; + /* 0x168 */ s32 clockMinute; // only 30 minutes in an hour + /* 0x16C */ union { + s32 clockHour; + s32 spotlightIntensity; + }; + /* 0x170 */ u16 currentTime; + /* 0x174 */ ObjTokeidaiActionFunc actionFunc; } ObjTokeidai; // size = 0x178 extern const ActorInit Obj_Tokeidai_InitVars; diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 63f698282..d13347d4f 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -11667,45 +11667,45 @@ 0x80AB2544:("DmChar09_Update",), 0x80AB25D8:("func_80AB25D8",), 0x80AB261C:("DmChar09_Draw",), - 0x80AB2790:("func_80AB2790",), - 0x80AB27B4:("func_80AB27B4",), - 0x80AB2834:("func_80AB2834",), - 0x80AB28C8:("func_80AB28C8",), - 0x80AB29F8:("func_80AB29F8",), - 0x80AB2BBC:("func_80AB2BBC",), + 0x80AB2790:("ObjTokeidai_GetTargetSunMoonDiskRotation",), + 0x80AB27B4:("ObjTokeidai_SetupClockOrGear",), + 0x80AB2834:("ObjTokeidai_Clock_Init",), + 0x80AB28C8:("ObjTokeidai_TowerGear_Init",), + 0x80AB29F8:("ObjTokeidai_TowerClock_Init",), + 0x80AB2BBC:("ObjTokeidai_Counterweight_Init",), 0x80AB2DEC:("ObjTokeidai_Init",), 0x80AB3000:("ObjTokeidai_Destroy",), - 0x80AB3010:("func_80AB3010",), - 0x80AB319C:("func_80AB319C",), - 0x80AB3240:("func_80AB3240",), - 0x80AB32F0:("func_80AB32F0",), - 0x80AB3370:("func_80AB3370",), - 0x80AB34CC:("func_80AB34CC",), - 0x80AB3544:("func_80AB3544",), - 0x80AB3598:("func_80AB3598",), - 0x80AB363C:("func_80AB363C",), - 0x80AB365C:("func_80AB365C",), - 0x80AB36C4:("func_80AB36C4",), - 0x80AB3808:("func_80AB3808",), - 0x80AB3880:("func_80AB3880",), - 0x80AB38B0:("func_80AB38B0",), - 0x80AB39BC:("func_80AB39BC",), - 0x80AB3A7C:("func_80AB3A7C",), - 0x80AB3B34:("func_80AB3B34",), - 0x80AB3BB0:("func_80AB3BB0",), - 0x80AB3BD8:("func_80AB3BD8",), - 0x80AB3BE8:("func_80AB3BE8",), - 0x80AB3C50:("func_80AB3C50",), - 0x80AB3CCC:("func_80AB3CCC",), - 0x80AB3ED0:("func_80AB3ED0",), - 0x80AB4040:("func_80AB4040",), - 0x80AB4080:("func_80AB4080",), - 0x80AB4160:("func_80AB4160",), + 0x80AB3010:("ObjTokeidai_RotateOnMinuteChange",), + 0x80AB319C:("ObjTokeidai_TowerGear_Collapse",), + 0x80AB3240:("ObjTokeidai_TowerGear_OpenedIdle",), + 0x80AB32F0:("ObjTokeidai_TowerClock_Fall",), + 0x80AB3370:("ObjTokeidai_TowerClock_SlideOff",), + 0x80AB34CC:("ObjTokeidai_TowerClock_OpenedIdle",), + 0x80AB3544:("ObjTokeidai_Counterweight_Collapse",), + 0x80AB3598:("ObjTokeidai_Counterweight_OpenedIdle",), + 0x80AB363C:("ObjTokeidai_Walls_Collapse",), + 0x80AB365C:("ObjTokeidai_Walls_Idle",), + 0x80AB36C4:("ObjTokeidai_TowerTransformation_EndCutscene",), + 0x80AB3808:("ObjTokeidai_TowerOpening_FinishOpening",), + 0x80AB3880:("ObjTokeidai_TowerOpening_Wait",), + 0x80AB38B0:("ObjTokeidai_TowerOpening_DropCounterweight",), + 0x80AB39BC:("ObjTokeidai_TowerOpening_FinishRaise",), + 0x80AB3A7C:("ObjTokeidai_TowerOpening_RaiseTower",), + 0x80AB3B34:("ObjTokeidai_TowerOpening_Start",), + 0x80AB3BB0:("ObjTokeidai_SetupTowerOpening",), + 0x80AB3BD8:("ObjTokeidai_DoNothing",), + 0x80AB3BE8:("ObjTokeidai_StaircaseIntoTower_Idle",), + 0x80AB3C50:("ObjTokeidai_IsPostFirstCycleFinalHours",), + 0x80AB3CCC:("ObjTokeidai_RotateOnHourChange",), + 0x80AB3ED0:("ObjTokeidai_TowerClock_Idle",), + 0x80AB4040:("ObjTokeidai_WallClock_Idle",), + 0x80AB4080:("ObjTokeidai_TowerGear_Idle",), + 0x80AB4160:("ObjTokeidai_Counterweight_Idle",), 0x80AB4278:("ObjTokeidai_Update",), 0x80AB429C:("ObjTokeidai_Draw",), - 0x80AB4394:("func_80AB4394",), - 0x80AB4664:("func_80AB4664",), - 0x80AB4894:("func_80AB4894",), + 0x80AB4394:("ObjTokeidai_Clock_Draw",), + 0x80AB4664:("ObjTokeidai_Counterweight_Draw",), + 0x80AB4894:("ObjTokeidai_TowerGear_Draw",), 0x80AB4D10:("func_80AB4D10",), 0x80AB4E34:("func_80AB4E34",), 0x80AB4E58:("func_80AB4E58",), diff --git a/undefined_syms.txt b/undefined_syms.txt index c1f879369..50342166c 100644 --- a/undefined_syms.txt +++ b/undefined_syms.txt @@ -4273,21 +4273,6 @@ D_06001CB0 = 0x06001CB0; D_06001400 = 0x06001400; -// ovl_Obj_Tokeidai - -D_06009A08 = 0x06009A08; -D_0600B0C0 = 0x0600B0C0; -D_0600B208 = 0x0600B208; -D_0600BA78 = 0x0600BA78; -D_0600BEE8 = 0x0600BEE8; -D_0600C368 = 0x0600C368; -D_0600CF28 = 0x0600CF28; -D_0600D388 = 0x0600D388; -D_0600D8E0 = 0x0600D8E0; -D_0600D8E8 = 0x0600D8E8; -D_0600E818 = 0x0600E818; -D_0600F518 = 0x0600F518; - // ovl_Obj_Tokei_Turret D_06002508 = 0x06002508;