diff --git a/assets/xml/objects/object_obj_tokeidai.xml b/assets/xml/objects/object_obj_tokeidai.xml index 9141b67ff3..09e0702540 100644 --- a/assets/xml/objects/object_obj_tokeidai.xml +++ b/assets/xml/objects/object_obj_tokeidai.xml @@ -1,59 +1,81 @@  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 0bdb9a6ea8..ae43514c73 100644 --- a/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.c +++ b/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.c @@ -1,17 +1,32 @@ /* * File: z_obj_tokeidai.c * Overlay: ovl_Obj_Tokeidai - * Description: Components of the Clock Tower (gears, clock face, counterweight, etc). Also used for wall clocks. + * Description: Components of the Clock Tower. Also used for wall clocks. The specific components are: + * - ClockFace: The decorated face of the clock that spins every hour. + * - MinuteRing: The ring around the clock face that spins every two minutes. + * - ExteriorGear: The two gears on the side of the tower that spin every two minutes. + * - SunAndMoonPanel: A panel with a sun and moon image that tells whether it is day or night. + * - Counterweight: The spinning weight on top of the tower that emits a spotlight at night. + * - StaircaseToRooftop: The staircase to tower's rooftop that opens after midnight on the Final Day. + * - TerminaFieldWalls: The walls of the Clock Tower visible from Termina Field. + * - UnusedWall: A single wall of the Clock Tower, unused in the final game. * - * 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. + * Sometimes, the ClockFace, MinuteRing, and SunAndMoonPanel are treated as a single clock entity. + * This is called Clock if it applies to both the Clock Tower clock and wall clocks, and + * TowerClock/WallClock, respectively, if it only applies to one of them. * * 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. + * are moved around. This actor is responsible for managing all of this movement, which is handled + * via the series of action functions labeled with TowerOpening. + * + * All components have at least one of these three actions: + * - Idle: The "default" action, and for some components the only action. + * The component is either completely still, or it's telling the time, depending on the component. + * - OpenedIdle: The action for certain Clock Tower components after midnight on the Final Day. + * The TowerClock, ExteriorGears, and Counterweight no longer tell time and are moved to a different position. + * Called "opened" because it corresponds to when the staircase to the tower's rooftop is opened up. + * - Collapse: The action for certain Clock Tower components when the moon crashes into Termina. + * This action is responsible for making the component fall during the moon crash cutscene. */ #include "z_obj_tokeidai.h" @@ -21,10 +36,11 @@ #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))) +#define GET_CURRENT_CLOCK_HOUR(this) ((s32)((this)->clockTime * (24.0f / 0x10000))) +#define GET_CURRENT_CLOCK_MINUTE(this) ((s32)((this)->clockTime * (360 * 2.0f / 0x10000)) % 30) +#define GET_CLOCK_FACE_ROTATION(currentClockHour) ((s16)(currentClockHour * (0x10000 / 24.0f))) +#define GET_MINUTE_RING_OR_EXTERIOR_GEAR_ROTATION(currentClockMinute) \ + ((s16)(currentClockMinute * (0x10000 * 12.0f / 360))) void ObjTokeidai_Init(Actor* thisx, GlobalContext* globalCtx); void ObjTokeidai_Destroy(Actor* thisx, GlobalContext* globalCtx); @@ -33,18 +49,18 @@ void ObjTokeidai_Draw(Actor* thisx, 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_ExteriorGear_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_ExteriorGear_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_TerminaFieldWalls_Idle(ObjTokeidai* this, GlobalContext* globalCtx); +void ObjTokeidai_StaircaseToRooftop_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); +void ObjTokeidai_ExteriorGear_Draw(Actor* thisx, GlobalContext* globalCtx); const ActorInit Obj_Tokeidai_InitVars = { ACTOR_OBJ_TOKEIDAI, @@ -67,43 +83,43 @@ static InitChainEntry sInitChain[] = { /** * 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 sun and moon panel based on the time of day. The actual + * angle can differ from the target angle if the panel is in * the middle of rotating. */ -s32 ObjTokeidai_GetTargetSunMoonDiskRotation() { +s32 ObjTokeidai_GetTargetSunMoonPanelRotation() { if (gSaveContext.isNight) { return 0x8000; } return 0; } -void ObjTokeidai_SetupClockOrGear(ObjTokeidai* this) { - s32 currentMinute = GET_CURRENT_MINUTE(this); +void ObjTokeidai_SetupClockOrExteriorGear(ObjTokeidai* this) { + s32 currentClockMinute = GET_CURRENT_CLOCK_MINUTE(this); - this->clockMinute = currentMinute; - this->outerRingOrGearRotation = GET_OUTER_RING_OR_GEAR_ROTATION(currentMinute); - this->outerRingOrGearRotationalVelocity = 0x3C; - this->outerRingOrGearRotationTimer = 0; + this->clockMinute = currentClockMinute; + this->minuteRingOrExteriorGearRotation = GET_MINUTE_RING_OR_EXTERIOR_GEAR_ROTATION(currentClockMinute); + this->minuteRingOrExteriorGearRotationalVelocity = 0x3C; + this->minuteRingOrExteriorGearRotationTimer = 0; } void ObjTokeidai_Clock_Init(ObjTokeidai* this) { - s32 currentHour; + s32 currentClockHour; - ObjTokeidai_SetupClockOrGear(this); - currentHour = GET_CURRENT_HOUR(this); - this->clockHour = currentHour; - this->clockFaceRotation = GET_CLOCK_FACE_ROTATION(currentHour); + ObjTokeidai_SetupClockOrExteriorGear(this); + currentClockHour = GET_CURRENT_CLOCK_HOUR(this); + this->clockHour = currentClockHour; + this->clockFaceRotation = GET_CLOCK_FACE_ROTATION(currentClockHour); this->clockFaceRotationalVelocity = 0; this->clockFaceRotationTimer = 0; - this->sunMoonDiskRotationalVelocity = 0; - this->sunMoonDiskRotation = ObjTokeidai_GetTargetSunMoonDiskRotation(); + this->sunMoonPanelRotationalVelocity = 0; + this->sunMoonPanelRotation = ObjTokeidai_GetTargetSunMoonPanelRotation(); } -void ObjTokeidai_TowerGear_Init(ObjTokeidai* this, GlobalContext* globalCtx) { - this->actor.draw = ObjTokeidai_TowerGear_Draw; - this->opaDList = object_obj_tokeidai_DL_00BA78; - ObjTokeidai_SetupClockOrGear(this); +void ObjTokeidai_ExteriorGear_Init(ObjTokeidai* this, GlobalContext* globalCtx) { + this->actor.draw = ObjTokeidai_ExteriorGear_Draw; + this->opaDList = gClockTowerExteriorGearDL; + ObjTokeidai_SetupClockOrExteriorGear(this); if (((globalCtx->sceneNum == SCENE_CLOCKTOWER) && (gSaveContext.sceneSetupIndex == 2) && (globalCtx->csCtx.unk_12 == 0)) || @@ -111,12 +127,12 @@ void ObjTokeidai_TowerGear_Init(ObjTokeidai* this, GlobalContext* globalCtx) { (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->actionFunc = ObjTokeidai_ExteriorGear_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; + this->actionFunc = ObjTokeidai_ExteriorGear_Idle; } } @@ -133,11 +149,14 @@ void ObjTokeidai_TowerClock_Init(ObjTokeidai* this, GlobalContext* globalCtx) { 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; @@ -148,8 +167,8 @@ void ObjTokeidai_Counterweight_Init(ObjTokeidai* this, GlobalContext* globalCtx) s32 type; this->actor.draw = ObjTokeidai_Counterweight_Draw; - this->opaDList = object_obj_tokeidai_DL_00B208; - this->xluDList = object_obj_tokeidai_DL_00B0C0; + this->opaDList = gClockTowerCounterweightDL; + this->xluDList = gClockTowerSpotlightDL; if (gSaveContext.isNight) { this->spotlightIntensity = 100; } else { @@ -162,6 +181,7 @@ void ObjTokeidai_Counterweight_Init(ObjTokeidai* this, GlobalContext* globalCtx) (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 || @@ -171,6 +191,7 @@ void ObjTokeidai_Counterweight_Init(ObjTokeidai* this, GlobalContext* globalCtx) 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; @@ -200,55 +221,65 @@ void ObjTokeidai_Init(Actor* thisx, GlobalContext* globalCtx) { this->xRotation = 0; this->yTranslation = 0; this->clockFaceZTranslation = 0; - this->currentTime = gSaveContext.time; + this->clockTime = gSaveContext.time; this->actor.home.rot.x = 0; switch (OBJ_TOKEIDAI_TYPE(&this->actor)) { - case OBJ_TOKEIDAI_TYPE_TOWER_GEAR_TERMINA_FIELD: + case OBJ_TOKEIDAI_TYPE_EXTERIOR_GEAR_TERMINA_FIELD: Actor_SetScale(&this->actor, 0.15f); - ObjTokeidai_TowerGear_Init(this, globalCtx); + ObjTokeidai_ExteriorGear_Init(this, globalCtx); break; - case OBJ_TOKEIDAI_TYPE_TOWER_GEAR_CLOCK_TOWN: - ObjTokeidai_TowerGear_Init(this, globalCtx); + + case OBJ_TOKEIDAI_TYPE_EXTERIOR_GEAR_CLOCK_TOWN: + ObjTokeidai_ExteriorGear_Init(this, globalCtx); break; + case OBJ_TOKEIDAI_TYPE_UNUSED_WALL: - this->opaDList = object_obj_tokeidai_DL_00D388; + this->opaDList = gClockTowerUnusedTowerWallDL; 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; + this->opaDList = gClockTowerTerminaFieldWallsDL; + this->actionFunc = ObjTokeidai_TerminaFieldWalls_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; + + case OBJ_TOKEIDAI_TYPE_STAIRCASE_TO_ROOFTOP: + this->opaDList = gClockTowerStaircaseToRooftopDL; + this->xluDList = gClockTowerEmptyDL; + this->actionFunc = ObjTokeidai_StaircaseToRooftop_Idle; break; } } @@ -257,41 +288,43 @@ void ObjTokeidai_Destroy(Actor* thisx, GlobalContext* globalCtx) { } void ObjTokeidai_RotateOnMinuteChange(ObjTokeidai* this, s32 playSfx) { - s32 currentMinute = GET_CURRENT_MINUTE(this); + s32 currentClockMinute = GET_CURRENT_CLOCK_MINUTE(this); - if (currentMinute != this->clockMinute) { - if (this->outerRingOrGearRotationTimer == 8 && playSfx) { + if (currentClockMinute != this->clockMinute) { + if (this->minuteRingOrExteriorGearRotationTimer == 8 && playSfx) { Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_CLOCK_TOWER_SECOND_HAND); } - if (this->outerRingOrGearRotationTimer > 8) { + if (this->minuteRingOrExteriorGearRotationTimer > 8) { // This actually performs the rotation to the next minute - // for the outer ring or gear. - this->outerRingOrGearRotationalVelocity += 0x3C; - this->outerRingOrGearRotation += this->outerRingOrGearRotationalVelocity; + // for the minute ring or exterior gear. + this->minuteRingOrExteriorGearRotationalVelocity += 0x3C; + this->minuteRingOrExteriorGearRotation += this->minuteRingOrExteriorGearRotationalVelocity; } else { - // This makes the outer ring or gear wiggle in place for a bit + // This makes the minute ring or exterior gear wiggle in place for a bit // before rotating to the next position. - if ((this->outerRingOrGearRotationTimer & 3) == 0) { - this->outerRingOrGearRotation += 0x5A; + if ((this->minuteRingOrExteriorGearRotationTimer & 3) == 0) { + this->minuteRingOrExteriorGearRotation += 0x5A; } - if ((this->outerRingOrGearRotationTimer & 3) == 1) { - this->outerRingOrGearRotation -= 0x5A; + if ((this->minuteRingOrExteriorGearRotationTimer & 3) == 1) { + this->minuteRingOrExteriorGearRotation -= 0x5A; } } - 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; + this->minuteRingOrExteriorGearRotationTimer++; + + if ((currentClockMinute == 15 && this->minuteRingOrExteriorGearRotation < 0) || + (currentClockMinute != 15 && + this->minuteRingOrExteriorGearRotation > GET_MINUTE_RING_OR_EXTERIOR_GEAR_ROTATION(currentClockMinute))) { + this->minuteRingOrExteriorGearRotation = GET_MINUTE_RING_OR_EXTERIOR_GEAR_ROTATION(currentClockMinute); + this->clockMinute = currentClockMinute; + this->minuteRingOrExteriorGearRotationalVelocity = 0x5A; + this->minuteRingOrExteriorGearRotationTimer = 0; } } } -void ObjTokeidai_TowerGear_Collapse(ObjTokeidai* this, GlobalContext* globalCtx) { +void ObjTokeidai_ExteriorGear_Collapse(ObjTokeidai* this, GlobalContext* globalCtx) { if ((this->actor.bgCheckFlags & 1) || this->actor.world.pos.y < 0.0f) { this->actionFunc = ObjTokeidai_DoNothing; } else { @@ -302,9 +335,9 @@ void ObjTokeidai_TowerGear_Collapse(ObjTokeidai* this, GlobalContext* globalCtx) } } -void ObjTokeidai_TowerGear_OpenedIdle(ObjTokeidai* this, GlobalContext* globalCtx) { +void ObjTokeidai_ExteriorGear_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->actionFunc = ObjTokeidai_ExteriorGear_Collapse; this->actor.speedXZ = this->actor.scale.y * 5.0f; this->actor.velocity.y = 0.0f; this->actor.terminalVelocity = this->actor.scale.y * -50.0f; @@ -313,15 +346,15 @@ void ObjTokeidai_TowerGear_OpenedIdle(ObjTokeidai* this, GlobalContext* globalCt } /** - * 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. + * 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_MoveWithGravity(&this->actor); if (this->actor.world.pos.y < 0.0f) { @@ -330,8 +363,8 @@ void ObjTokeidai_TowerClock_Fall(ObjTokeidai* this, GlobalContext* globalCtx) { } /** - * This is hard to see in the final game. This makes the - * tower's clock slide off as the moon crashes into it. + * This makes the tower's clock slide off as the moon crashes into it. It's + * the tower clock's equivalent to other components' Collapse functions. */ void ObjTokeidai_TowerClock_SlideOff(ObjTokeidai* this, GlobalContext* globalCtx) { f32 cos; @@ -396,20 +429,15 @@ void ObjTokeidai_Counterweight_OpenedIdle(ObjTokeidai* this, GlobalContext* glob } } -/** - * 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) { +void ObjTokeidai_TerminaFieldWalls_Collapse(ObjTokeidai* this, GlobalContext* globalCtx) { if (this->actor.shape.rot.x < 0x4000) { this->actor.shape.rot.x += 0x28; } } -void ObjTokeidai_Walls_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { +void ObjTokeidai_TerminaFieldWalls_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; + this->actionFunc = ObjTokeidai_TerminaFieldWalls_Collapse; } } @@ -480,9 +508,11 @@ void ObjTokeidai_TowerOpening_DropCounterweight(ObjTokeidai* this, GlobalContext case 0: Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_CLOCK_TOWER_BOUND_0); break; + case 1: Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_CLOCK_TOWER_BOUND_1); break; + case 2: Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_CLOCK_TOWER_BOUND_2); break; @@ -565,7 +595,7 @@ void ObjTokeidai_TowerOpening_Start(ObjTokeidai* this, GlobalContext* globalCtx) } /** - * Sets up the sequence where the Clock Tower "transforms" and opens up + * Sets up the sequence where the Clock Tower opens up * on the midnight of the Final Day. */ void ObjTokeidai_SetupTowerOpening(ObjTokeidai* this) { @@ -580,7 +610,7 @@ void ObjTokeidai_SetupTowerOpening(ObjTokeidai* this) { void ObjTokeidai_DoNothing(ObjTokeidai* this, GlobalContext* globalCtx) { } -void ObjTokeidai_StaircaseIntoTower_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { +void ObjTokeidai_StaircaseToRooftop_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; @@ -601,9 +631,9 @@ s32 ObjTokeidai_IsPostFirstCycleFinalHours(ObjTokeidai* this, GlobalContext* glo } void ObjTokeidai_RotateOnHourChange(ObjTokeidai* this, GlobalContext* globalCtx) { - s32 currentHour = GET_CURRENT_HOUR(this); + s32 currentClockHour = GET_CURRENT_CLOCK_HOUR(this); - if (currentHour != this->clockHour) { + if (currentClockHour != this->clockHour) { if (this->clockFaceRotationTimer > 12) { // This actually performs the rotation to the next hour // for the clock face. @@ -621,32 +651,33 @@ void ObjTokeidai_RotateOnHourChange(ObjTokeidai* this, GlobalContext* globalCtx) } 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; + + if ((currentClockHour == 12 && this->clockFaceRotation < 0) || + (currentClockHour != 12 && this->clockFaceRotation > GET_CLOCK_FACE_ROTATION(currentClockHour))) { + this->clockFaceRotation = GET_CLOCK_FACE_ROTATION(currentClockHour); + this->clockHour = currentClockHour; this->clockFaceRotationalVelocity = 0; this->clockFaceRotationTimer = 0; } } - // If the sun and moon disk doesn't have the target rotation (e.g., the time of day + // If the sun and moon panel 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->sunMoonPanelRotation != ObjTokeidai_GetTargetSunMoonPanelRotation()) { if (this->clockHour == 6) { - this->sunMoonDiskRotationalVelocity += 0x222; - this->sunMoonDiskRotation += this->sunMoonDiskRotationalVelocity; - if (this->sunMoonDiskRotation > 0x10000) { - this->sunMoonDiskRotation = ObjTokeidai_GetTargetSunMoonDiskRotation(); - this->sunMoonDiskRotationalVelocity = 0; + this->sunMoonPanelRotationalVelocity += 0x222; + this->sunMoonPanelRotation += this->sunMoonPanelRotationalVelocity; + if (this->sunMoonPanelRotation > 0x10000) { + this->sunMoonPanelRotation = ObjTokeidai_GetTargetSunMoonPanelRotation(); + this->sunMoonPanelRotationalVelocity = 0; } } if (this->clockHour == 18) { - this->sunMoonDiskRotationalVelocity += 0x222; - this->sunMoonDiskRotation += this->sunMoonDiskRotationalVelocity; - if (this->sunMoonDiskRotation > 0x8000) { - this->sunMoonDiskRotation = ObjTokeidai_GetTargetSunMoonDiskRotation(); - this->sunMoonDiskRotationalVelocity = 0; + this->sunMoonPanelRotationalVelocity += 0x222; + this->sunMoonPanelRotation += this->sunMoonPanelRotationalVelocity; + if (this->sunMoonPanelRotation > 0x8000) { + this->sunMoonPanelRotation = ObjTokeidai_GetTargetSunMoonPanelRotation(); + this->sunMoonPanelRotationalVelocity = 0; } } } @@ -662,7 +693,7 @@ void ObjTokeidai_TowerClock_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { if (globalCtx->csCtx.state != 0) { this->actor.home.rot.x = 1; - this->currentTime += 3; + this->clockTime += 3; this->actor.draw = ObjTokeidai_Clock_Draw; } else { if (!(globalCtx->actorCtx.unk5 & 2) && @@ -670,7 +701,7 @@ void ObjTokeidai_TowerClock_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { ActorCutscene_GetCurrentIndex() == -1) { this->actor.draw = NULL; } - this->currentTime = gSaveContext.time; + this->clockTime = gSaveContext.time; if (this->actor.home.rot.x != 0) { ObjTokeidai_Clock_Init(this); this->actor.home.rot.x = 0; @@ -684,28 +715,28 @@ void ObjTokeidai_TowerClock_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { } void ObjTokeidai_WallClock_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { - this->currentTime = gSaveContext.time; + this->clockTime = gSaveContext.time; ObjTokeidai_RotateOnMinuteChange(this, true); ObjTokeidai_RotateOnHourChange(this, globalCtx); } -void ObjTokeidai_TowerGear_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { +void ObjTokeidai_ExteriorGear_Idle(ObjTokeidai* this, GlobalContext* globalCtx) { if (ObjTokeidai_IsPostFirstCycleFinalHours(this, globalCtx)) { - this->actor.draw = ObjTokeidai_TowerGear_Draw; + this->actor.draw = ObjTokeidai_ExteriorGear_Draw; } else { if (globalCtx->csCtx.state != 0) { this->actor.home.rot.x = 1; - this->currentTime += 3; - this->actor.draw = ObjTokeidai_TowerGear_Draw; + this->clockTime += 3; + this->actor.draw = ObjTokeidai_ExteriorGear_Draw; } else { if ((globalCtx->actorCtx.unk5 & 2) == 0 && - OBJ_TOKEIDAI_TYPE(&this->actor) == OBJ_TOKEIDAI_TYPE_TOWER_GEAR_TERMINA_FIELD && + OBJ_TOKEIDAI_TYPE(&this->actor) == OBJ_TOKEIDAI_TYPE_EXTERIOR_GEAR_TERMINA_FIELD && ActorCutscene_GetCurrentIndex() == -1) { this->actor.draw = NULL; } - this->currentTime = gSaveContext.time; + this->clockTime = gSaveContext.time; if (this->actor.home.rot.x != 0) { - ObjTokeidai_SetupClockOrGear(this); + ObjTokeidai_SetupClockOrExteriorGear(this); this->actor.home.rot.x = 0; } } @@ -718,6 +749,7 @@ void ObjTokeidai_Counterweight_Idle(ObjTokeidai* this, GlobalContext* globalCtx) 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 || @@ -727,6 +759,7 @@ void ObjTokeidai_Counterweight_Idle(ObjTokeidai* this, GlobalContext* globalCtx) 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; @@ -751,6 +784,9 @@ void ObjTokeidai_Update(Actor* thisx, GlobalContext* globalCtx) { this->actionFunc(this, globalCtx); } +/** + * Used for TerminaFieldWalls StaircaseToRooftop, and UnusedWall + */ void ObjTokeidai_Draw(Actor* thisx, GlobalContext* globalCtx) { ObjTokeidai* this = THIS; @@ -761,6 +797,7 @@ void ObjTokeidai_Draw(Actor* thisx, GlobalContext* globalCtx) { 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); @@ -780,26 +817,30 @@ void ObjTokeidai_Clock_Draw(Actor* thisx, GlobalContext* globalCtx) { 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); + Matrix_InsertZRotation_s(-this->minuteRingOrExteriorGearRotation, 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); + gSPDisplayList(POLY_OPA_DISP++, gClockTowerMinuteRingDL); 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); + gSPDisplayList(POLY_OPA_DISP++, gClockTowerClockCenterAndHandDL); + 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); + gSPDisplayList(POLY_OPA_DISP++, gWallClockClockFace); } else { - gSPDisplayList(POLY_OPA_DISP++, object_obj_tokeidai_DL_00E818); + gSPDisplayList(POLY_OPA_DISP++, gClockTowerClockFace); } + Matrix_InsertTranslation(0.0f, -1112.0f, -19.6f, MTXMODE_APPLY); - Matrix_RotateY((s16)this->sunMoonDiskRotation, MTXMODE_APPLY); + Matrix_RotateY((s16)this->sunMoonPanelRotation, 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); + gSPDisplayList(POLY_OPA_DISP++, gClockTowerSunAndMoonPanelDL); CLOSE_DISPS(globalCtx->state.gfxCtx); } @@ -818,11 +859,16 @@ void ObjTokeidai_Counterweight_Draw(Actor* thisx, GlobalContext* globalCtx) { OPEN_DISPS(globalCtx->state.gfxCtx); + // For scrolling the spotlight's mask texture down the length of the light beam. gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, -gameplayFrames, 0, 0x20, 0x20)); + + // Draws the counterweight 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); + + // Draws the spotlight 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)); @@ -831,7 +877,7 @@ void ObjTokeidai_Counterweight_Draw(Actor* thisx, GlobalContext* globalCtx) { CLOSE_DISPS(globalCtx->state.gfxCtx); } -void ObjTokeidai_TowerGear_Draw(Actor* thisx, GlobalContext* globalCtx) { +void ObjTokeidai_ExteriorGear_Draw(Actor* thisx, GlobalContext* globalCtx) { ObjTokeidai* this = THIS; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -842,10 +888,10 @@ void ObjTokeidai_TowerGear_Draw(Actor* thisx, GlobalContext* globalCtx) { 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); + Matrix_InsertZRotation_s(this->minuteRingOrExteriorGearRotation, 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); + gSPDisplayList(POLY_OPA_DISP++, gClockTowerExteriorGearDL); 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 c5cf8d70cf..48e44614dc 100644 --- a/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.h +++ b/src/overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.h @@ -6,17 +6,17 @@ #define OBJ_TOKEIDAI_TYPE(thisx) (((thisx)->params & 0xF000) >> 12) typedef enum { - /* 0 */ OBJ_TOKEIDAI_TYPE_TOWER_GEAR_CLOCK_TOWN, + /* 0 */ OBJ_TOKEIDAI_TYPE_EXTERIOR_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, + /* 4 */ OBJ_TOKEIDAI_TYPE_EXTERIOR_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, + /* 11 */ OBJ_TOKEIDAI_TYPE_STAIRCASE_TO_ROOFTOP, } ObjTokeidaiType; struct ObjTokeidai; @@ -27,9 +27,9 @@ typedef struct ObjTokeidai { /* 0x000 */ Actor actor; /* 0x144 */ Gfx* opaDList; /* 0x148 */ Gfx* xluDList; - /* 0x14C */ s16 outerRingOrGearRotation; - /* 0x14E */ s16 outerRingOrGearRotationalVelocity; - /* 0x150 */ s16 outerRingOrGearRotationTimer; + /* 0x14C */ s16 minuteRingOrExteriorGearRotation; + /* 0x14E */ s16 minuteRingOrExteriorGearRotationalVelocity; + /* 0x150 */ s16 minuteRingOrExteriorGearRotationTimer; /* 0x152 */ s16 clockFaceRotation; /* 0x154 */ union { s16 clockFaceRotationalVelocity; @@ -44,9 +44,9 @@ typedef struct ObjTokeidai { s16 counterweightRotationalAcceleration; s16 aerialClockFaceSpeed; }; - /* 0x158 */ s32 sunMoonDiskRotation; + /* 0x158 */ s32 sunMoonPanelRotation; /* 0x15C */ union { - s16 sunMoonDiskRotationalVelocity; + s16 sunMoonPanelRotationalVelocity; s16 fallingClockFaceRotationalVelocity; }; /* 0x15E */ s16 yTranslation; @@ -58,7 +58,7 @@ typedef struct ObjTokeidai { s32 clockHour; s32 spotlightIntensity; }; - /* 0x170 */ u16 currentTime; + /* 0x170 */ u16 clockTime; // can differ from the actual time, e.g., in cutscenes /* 0x174 */ ObjTokeidaiActionFunc actionFunc; } ObjTokeidai; // size = 0x178 diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 9eb2125557..15ac9dc2a5 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -11667,24 +11667,24 @@ 0x80AB2544:("DmChar09_Update",), 0x80AB25D8:("func_80AB25D8",), 0x80AB261C:("DmChar09_Draw",), - 0x80AB2790:("ObjTokeidai_GetTargetSunMoonDiskRotation",), - 0x80AB27B4:("ObjTokeidai_SetupClockOrGear",), + 0x80AB2790:("ObjTokeidai_GetTargetSunMoonPanelRotation",), + 0x80AB27B4:("ObjTokeidai_SetupClockOrExteriorGear",), 0x80AB2834:("ObjTokeidai_Clock_Init",), - 0x80AB28C8:("ObjTokeidai_TowerGear_Init",), + 0x80AB28C8:("ObjTokeidai_ExteriorGear_Init",), 0x80AB29F8:("ObjTokeidai_TowerClock_Init",), 0x80AB2BBC:("ObjTokeidai_Counterweight_Init",), 0x80AB2DEC:("ObjTokeidai_Init",), 0x80AB3000:("ObjTokeidai_Destroy",), 0x80AB3010:("ObjTokeidai_RotateOnMinuteChange",), - 0x80AB319C:("ObjTokeidai_TowerGear_Collapse",), - 0x80AB3240:("ObjTokeidai_TowerGear_OpenedIdle",), + 0x80AB319C:("ObjTokeidai_ExteriorGear_Collapse",), + 0x80AB3240:("ObjTokeidai_ExteriorGear_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",), + 0x80AB363C:("ObjTokeidai_TerminaFieldWalls_Collapse",), + 0x80AB365C:("ObjTokeidai_TerminaFieldWalls_Idle",), 0x80AB36C4:("ObjTokeidai_TowerTransformation_EndCutscene",), 0x80AB3808:("ObjTokeidai_TowerOpening_FinishOpening",), 0x80AB3880:("ObjTokeidai_TowerOpening_Wait",), @@ -11694,18 +11694,18 @@ 0x80AB3B34:("ObjTokeidai_TowerOpening_Start",), 0x80AB3BB0:("ObjTokeidai_SetupTowerOpening",), 0x80AB3BD8:("ObjTokeidai_DoNothing",), - 0x80AB3BE8:("ObjTokeidai_StaircaseIntoTower_Idle",), + 0x80AB3BE8:("ObjTokeidai_StaircaseToRooftop_Idle",), 0x80AB3C50:("ObjTokeidai_IsPostFirstCycleFinalHours",), 0x80AB3CCC:("ObjTokeidai_RotateOnHourChange",), 0x80AB3ED0:("ObjTokeidai_TowerClock_Idle",), 0x80AB4040:("ObjTokeidai_WallClock_Idle",), - 0x80AB4080:("ObjTokeidai_TowerGear_Idle",), + 0x80AB4080:("ObjTokeidai_ExteriorGear_Idle",), 0x80AB4160:("ObjTokeidai_Counterweight_Idle",), 0x80AB4278:("ObjTokeidai_Update",), 0x80AB429C:("ObjTokeidai_Draw",), 0x80AB4394:("ObjTokeidai_Clock_Draw",), 0x80AB4664:("ObjTokeidai_Counterweight_Draw",), - 0x80AB4894:("ObjTokeidai_TowerGear_Draw",), + 0x80AB4894:("ObjTokeidai_ExteriorGear_Draw",), 0x80AB4D10:("func_80AB4D10",), 0x80AB4E34:("func_80AB4E34",), 0x80AB4E58:("func_80AB4E58",),