diff --git a/spec b/spec index 5b7d8bfa9..9f79e7ac1 100644 --- a/spec +++ b/spec @@ -4528,9 +4528,7 @@ beginseg name "ovl_Obj_Chan" compress include "build/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.o" - include "build/data/ovl_Obj_Chan/ovl_Obj_Chan.data.o" - include "build/data/ovl_Obj_Chan/ovl_Obj_Chan.bss.o" - include "build/data/ovl_Obj_Chan/ovl_Obj_Chan.reloc.o" + include "build/src/overlays/actors/ovl_Obj_Chan/ovl_Obj_Chan_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.c b/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.c index 465c660c6..16dd4943a 100644 --- a/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.c +++ b/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.c @@ -2,23 +2,32 @@ * File: z_obj_chan.c * Overlay: ovl_Obj_Chan * Description: Goron Shrine chandelier + * + * This actor class is used for both the chandelier and its sub-components. + * The distinction is made using the subtype actor parameter: + * - 0 for the chandelier itself + * - 1 for one of the 5 breakable pots that are attached to the chandelier */ #include "z_obj_chan.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_obj_chan/object_obj_chan.h" +#include "objects/object_tsubo/object_tsubo.h" #define FLAGS (ACTOR_FLAG_10 | ACTOR_FLAG_20) #define THIS ((ObjChan*)thisx) +#define OBJCHAN_ROTATION_SPEED 364 // == (65536 * 2/360) i.e. 2 degrees per second + void ObjChan_Init(Actor* thisx, GlobalContext* globalCtx); void ObjChan_Destroy(Actor* thisx, GlobalContext* globalCtx); void ObjChan_Update(Actor* thisx, GlobalContext* globalCtx); void ObjChan_Draw(Actor* thisx, GlobalContext* globalCtx); -void func_80BB9F24(ObjChan* this, GlobalContext* globalCtx); -void func_80BBA314(ObjChan* this, GlobalContext* globalCtx); +void ObjChan_ChandelierAction(ObjChan* this, GlobalContext* globalCtx); +void ObjChan_PotAction(ObjChan* this, GlobalContext* globalCtx); -#if 0 const ActorInit Obj_Chan_InitVars = { ACTOR_OBJ_CHAN, ACTORCAT_BG, @@ -31,49 +40,399 @@ const ActorInit Obj_Chan_InitVars = { (ActorFunc)ObjChan_Draw, }; -// static ColliderCylinderInit sCylinderInit = { -static ColliderCylinderInit D_80BBAB30 = { - { COLTYPE_HARD, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_NONE, OC2_TYPE_1, COLSHAPE_CYLINDER, }, - { ELEMTYPE_UNK1, { 0x00000000, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_NONE, }, +static ColliderCylinderInit sObjChanCylinderInit = { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, { 48, 76, -60, { 0, 0, 0 } }, }; -// static InitChainEntry sInitChain[] = { -static InitChainEntry D_80BBAB5C[] = { +static InitChainEntry sInitChain[] = { ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), }; -#endif +void ObjChan_InitChandelier(ObjChan* this, GlobalContext* globalCtx); +void ObjChan_InitPot(ObjChan* this, GlobalContext* globalCtx); +void ObjChan_CreateSmashParticles(ObjChan* this, GlobalContext* globalCtx); +void ObjChan_DrawPot(Actor* thisx, GlobalContext* globalCtx); +void ObjChan_DrawFire(ObjChan* this, GlobalContext* globalCtx); -extern ColliderCylinderInit D_80BBAB30; -extern InitChainEntry D_80BBAB5C[]; +static Vec3f sObjChanFlameSize[2] = { + { 0.16f, 0.11f, 1.0f }, + { 0.13f, 0.09f, 1.0f }, +}; +static f32 sObjChanFlameYOffset[2] = { 1800, 1800 }; +static s32 sObjChanLoaded; -extern UNK_TYPE D_06000AF0; -extern UNK_TYPE D_06001960; -extern UNK_TYPE D_06002358; +void ObjChan_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjChan* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/ObjChan_Init.s") + if (OBJCHAN_SUBTYPE(&this->actor) == OBJCHAN_SUBTYPE_CHANDELIER) { + if (sObjChanLoaded) { + Actor_MarkForDeath(&this->actor); + return; + } + this->actor.room = -1; + sObjChanLoaded = true; + } + Actor_ProcessInitChain(&this->actor, sInitChain); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sObjChanCylinderInit); + SubS_FillCutscenesList(&this->actor, this->cutscenes, ARRAY_COUNT(this->cutscenes)); + switch (OBJCHAN_SUBTYPE(&this->actor)) { + case OBJCHAN_SUBTYPE_CHANDELIER: + this->rotation = this->actor.shape.rot.y; + this->actor.shape.rot.y = 0; + ObjChan_InitChandelier(this, globalCtx); + break; + case OBJCHAN_SUBTYPE_POT: + this->actor.draw = ObjChan_DrawPot; + ObjChan_InitPot(this, globalCtx); + break; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/ObjChan_Destroy.s") +void ObjChan_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjChan* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BB9A1C.s") + Collider_DestroyCylinder(globalCtx, &this->collider); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BB9B40.s") +u32 func_80BB9A1C(ObjChan* this, f32 arg1) { + f32 temp_f6; + f32 sp20; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BB9C08.s") + sp20 = Math_SinS(this->unk1D4) * this->unk1D0; + temp_f6 = (Math_CosS(this->unk1D4) * 0.03834952f * this->unk1D0) + arg1; + if (temp_f6 != 0.0f) { + this->unk1D4 = RADF_TO_BINANG(func_80086B30(sp20 * 0.03834952f, temp_f6)); + } else if (sp20 >= 0.0f) { + this->unk1D4 = 0x4000; + } else { + this->unk1D4 = -0x4000; + } + if (Math_CosS(this->unk1D4) != 0.0f) { + this->unk1D0 = (temp_f6 / (Math_CosS(this->unk1D4) * 0.03834952f)); + } else { + this->unk1D0 = sp20; + } + return 0; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BB9F24.s") +void ObjChan_CalculatePotPosition(Vec3f* childPosOut, Vec3s* childRotOut, Vec3f* parentPos, Vec3s* parentRot, + s16 childAngle) { + Vec3f offset; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BBA2FC.s") + Matrix_RotateY(parentRot->y, MTXMODE_NEW); + Matrix_InsertXRotation_s(parentRot->x, MTXMODE_APPLY); + Matrix_InsertZRotation_s(parentRot->z, MTXMODE_APPLY); + Matrix_RotateY(childAngle, MTXMODE_APPLY); + Matrix_GetStateTranslationAndScaledX(-280.0f, &offset); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BBA314.s") + childPosOut->x = parentPos->x + offset.x; + childPosOut->y = parentPos->y + offset.y; + childPosOut->z = parentPos->z + offset.z; + Math_Vec3s_Copy(childRotOut, parentRot); + childRotOut->y += childAngle; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BBA488.s") +void ObjChan_InitChandelier(ObjChan* this, GlobalContext* globalCtx) { + s32 j; + s32 i; + ObjChan* temp_v0; + Vec3f childPos; + Vec3s childRot; + CollisionPoly* sp94; + s32 sp90; + Vec3f sp84; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/ObjChan_Update.s") + Math_Vec3f_Copy(&this->unk1C0, &this->actor.world.pos); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/ObjChan_Draw.s") + Math_Vec3f_Copy(&sp84, &this->actor.world.pos); + sp84.y += 1600.0f; + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &sp84, &this->unk1C0, &sp94, false, false, + true, true, &sp90)) { + this->unk1CC = this->actor.world.pos.y - this->unk1C0.y; + } else { + Actor_MarkForDeath(&this->actor); + return; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BBA894.s") + for (i = 0; i < 5; i++) { + temp_v0 = (ObjChan*)&globalCtx->actorCtx; // strange cast needed for matching + ObjChan_CalculatePotPosition(&childPos, &childRot, &this->actor.world.pos, &this->actor.shape.rot, + (s32)(i * 360.0f / 5.0f * (65536.0f / 360.0f)) + this->rotation); + temp_v0 = (ObjChan*)Actor_SpawnAsChildAndCutscene((ActorContext*)temp_v0, globalCtx, ACTOR_OBJ_CHAN, childPos.x, + childPos.y, childPos.z, childRot.x, childRot.y, childRot.z, + (this->actor.params & 0xFFF) | 0x1000, this->actor.cutscene, + this->actor.unk20, &this->actor); + if (temp_v0 != NULL) { + this->pots[i] = temp_v0; + temp_v0->myPotIndex = i; + temp_v0->actor.cutscene = this->actor.cutscene; + } else { + Actor_MarkForDeath(&this->actor); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Chan/func_80BBA930.s") + if (Flags_GetSwitch(globalCtx, this->actor.params & 0x7F)) { + this->stateFlags |= OBJCHAN_STATE_FIRE_DELAY; + this->stateFlags |= OBJCHAN_STATE_ON_FIRE; + + this->rotationSpeed = OBJCHAN_ROTATION_SPEED; + this->flameSize = 1.0f; + + for (i = 0; i < 5; i++) { + if (this->pots[i] != NULL) { + this->pots[i]->stateFlags |= OBJCHAN_STATE_FIRE_DELAY; + this->pots[i]->stateFlags |= OBJCHAN_STATE_ON_FIRE; + this->pots[i]->flameSize = 1.0f; + } + } + } + this->collider.dim.radius = 45; + this->collider.dim.height = 60; + this->collider.dim.yShift = -20; + this->actionFunc = ObjChan_ChandelierAction; +} + +void ObjChan_ChandelierAction(ObjChan* thisx, GlobalContext* globalCtx) { + ObjChan* this = thisx; + ObjChan* temp; + s32 i; + Vec3f sp60; + Vec3s sp58; + + if (this->unk1D0 > 0.0f) { + this->unk1D4 += 0x190; + if (this->unk1D0 <= 400.0f) { + this->unk1D0 = this->unk1D0 * 0.99f; + } else { + this->unk1D0 = this->unk1D0 - 1.0f; + } + if (this->unk1D0 < 0.0f) { + this->unk1D4 = 0; + this->unk1D0 = 0.0f; + } + } + this->actor.shape.rot.z = (Math_SinS(this->unk1D4) * this->unk1D0); + if ((this->stateFlags & OBJCHAN_STATE_START_CUTSCENE) && + SubS_StartActorCutscene(&this->actor, this->cutscenes[0], -1, 0)) { + this->stateFlags |= OBJCHAN_STATE_CUTSCENE; + this->stateFlags &= ~OBJCHAN_STATE_START_CUTSCENE; + } + if ((this->stateFlags & OBJCHAN_STATE_CUTSCENE) && this->rotationSpeed == OBJCHAN_ROTATION_SPEED) { + this->stateFlags &= ~OBJCHAN_STATE_CUTSCENE; + ActorCutscene_Stop(this->cutscenes[0]); + } + Matrix_RotateY(this->actor.shape.rot.y, MTXMODE_NEW); + Matrix_InsertXRotation_s(this->actor.shape.rot.x, MTXMODE_APPLY); + Matrix_InsertZRotation_s(this->actor.shape.rot.z, MTXMODE_APPLY); + Matrix_RotateY(this->rotation, MTXMODE_APPLY); + Matrix_GetStateTranslationAndScaledY(this->unk1CC, &this->actor.world.pos); + Math_Vec3f_Sum(&this->actor.world.pos, &this->unk1C0, &this->actor.world.pos); + Collider_UpdateCylinder(&this->actor, &this->collider); + for (i = 0; i < 5; i++) { + temp = this->pots[i]; + if (temp != NULL) { + ObjChan_CalculatePotPosition(&sp60, &sp58, &this->actor.world.pos, &this->actor.shape.rot, + (s32)(i * 360.0f / 5.0f * (65536.0f / 360.0f)) + this->rotation); + Math_Vec3f_Copy(&temp->actor.world.pos, &sp60); + Math_Vec3s_Copy(&temp->actor.shape.rot, &this->actor.shape.rot); + temp->actor.shape.rot.y = this->rotation; + Math_Vec3f_ToVec3s(&temp->collider.dim.pos, &temp->actor.world.pos); + } + } + if ((this->collider.base.acFlags & AC_HIT) && (this->collider.info.acHitInfo->toucher.dmgFlags & 0x800)) { + Flags_SetSwitch(globalCtx, this->actor.params & 0x7F); + } + if (Flags_GetSwitch(globalCtx, this->actor.params & 0x7F)) { + if (!(this->stateFlags & OBJCHAN_STATE_FIRE_DELAY)) { + this->rotationSpeed = 0; + this->flameSize = 0.0f; + for (i = 0; i < 5; i++) { + temp = this->pots[i]; + if (temp != NULL) { + this->pots[i]->stateFlags |= OBJCHAN_STATE_FIRE_DELAY; + this->pots[i]->fireDelayFrames = 20 + i * 5; + this->pots[i]->flameSize = 0.0f; + } + } + this->stateFlags |= OBJCHAN_STATE_FIRE_DELAY; + this->stateFlags |= OBJCHAN_STATE_ON_FIRE; + this->stateFlags |= OBJCHAN_STATE_START_CUTSCENE; + } else { + Math_StepToF(&this->flameSize, 1.0f, 0.05f); + this->rotation += this->rotationSpeed; + Math_StepToS(&this->rotationSpeed, OBJCHAN_ROTATION_SPEED, 5); + func_800B9010(&this->actor, NA_SE_EV_CHANDELIER_ROLL - SFX_FLAG); + } + } +} + +void ObjChan_InitPot(ObjChan* this, GlobalContext* globalCtx) { + this->actionFunc = ObjChan_PotAction; +} + +void ObjChan_PotAction(ObjChan* this, GlobalContext* globalCtx) { + s32 potBreaks; + s16 temp_v0_2; + s32 phi_v1; + + potBreaks = false; + if ((this->collider.base.acFlags & AC_HIT) && (this->collider.info.acHitInfo->toucher.dmgFlags & 0x4004000)) { + potBreaks = true; + } + if (this->stateFlags & OBJCHAN_STATE_ON_FIRE) { + Math_StepToF(&this->flameSize, 1.0f, 0.05f); + } else if (this->stateFlags & OBJCHAN_STATE_FIRE_DELAY) { + this->fireDelayFrames--; + if (this->fireDelayFrames <= 0) { + this->stateFlags |= OBJCHAN_STATE_ON_FIRE; + } + } + if (potBreaks) { + ObjChan_CreateSmashParticles(this, globalCtx); + ((ObjChan*)this->actor.parent)->pots[this->myPotIndex] = NULL; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_CHANDELIER_BROKEN); + func_80BB9A1C((ObjChan*)this->actor.parent, 40.0f); + if (this->myPotIndex == 4) { + temp_v0_2 = gSaveContext.weekEventReg[0x25]; + if (!(temp_v0_2 & 0x10)) { + gSaveContext.weekEventReg[0x25] = temp_v0_2 | 0x10; + Actor_SpawnAsChildAndCutscene(&globalCtx->actorCtx, globalCtx, ACTOR_EN_MM, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0x8000, + this->actor.cutscene, this->actor.unk20, NULL); + } + } + Actor_MarkForDeath(&this->actor); + } +} + +void ObjChan_CreateSmashParticles(ObjChan* this, GlobalContext* globalCtx) { + s16 new_var = 0; + s32 phi_s0; + Vec3f spDC; + Vec3f spD0; + f32 temp_f0; + f32 new_var2; + s16 temp_s1 = 0; + s32 temp_s2; + f32 spAC; + f32 spA8 = new_var + 15.0f; + f32 spA4 = new_var + (spAC = 110.0f); + + for (temp_s2 = 0, temp_s1 = 0; temp_s2 != 18; temp_s2++, temp_s1 += 0x4E20) { + f32 sin = Math_SinS(temp_s1); + f32 cos = Math_CosS(temp_s1); + spDC.x = sin * 8.0f; + spDC.y = Rand_ZeroOne() * 12.0f + 2.0f; + spDC.z = cos * 8.0f; + spD0.x = spDC.x * 0.23f; + spD0.y = Rand_ZeroOne() * 5.0f + 2.5f; + spD0.z = spDC.z * 0.23f; + temp_f0 = Rand_ZeroOne(); + if (temp_f0 < 0.2f) { + phi_s0 = 0x60; + } else if (temp_f0 < 0.6f) { + phi_s0 = 0x40; + } else { + phi_s0 = 0x20; + } + new_var2 = spA4 * Rand_ZeroOne(); + EffectSsKakera_Spawn(globalCtx, &spDC, &spD0, &this->actor.world.pos, -260, phi_s0, 20, 0, 0, spA8 + new_var2, + 0, 0, 50, -1, OBJECT_TSUBO, object_tsubo_DL_001960); + } + func_800BBFB0(globalCtx, &this->actor.world.pos, 30.0f, 2, 20, 50, true); + func_800BBFB0(globalCtx, &this->actor.world.pos, 30.0f, 2, 10, 80, true); +} + +void ObjChan_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjChan* this = THIS; + + this->actionFunc(this, globalCtx); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void ObjChan_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjChan* this = THIS; + Gfx* opa; + Gfx* xlu; + + OPEN_DISPS(globalCtx->state.gfxCtx); + Matrix_RotateY(this->rotation, MTXMODE_APPLY); + + opa = Gfx_CallSetupDL(POLY_OPA_DISP, 0x19); + gSPMatrix(&opa[0], Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_LOAD); + gSPDisplayList(&opa[1], object_obj_chan_DL_000AF0); + POLY_OPA_DISP = &opa[2]; + + xlu = func_8012C2B4(POLY_XLU_DISP); + gSPMatrix(&xlu[0], Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_LOAD); + gSPDisplayList(&xlu[1], object_obj_chan_DL_000A10); + POLY_XLU_DISP = &xlu[2]; + + CLOSE_DISPS(globalCtx->state.gfxCtx); + + Matrix_StatePush(); + if (this->stateFlags & OBJCHAN_STATE_ON_FIRE) { + ObjChan_DrawFire(this, globalCtx); + } + Matrix_StatePop(); +} + +void ObjChan_DrawPot(Actor* thisx, GlobalContext* globalCtx) { + ObjChan* this = THIS; + s32 pad; + Gfx* dl; + + OPEN_DISPS(globalCtx->state.gfxCtx); + dl = Gfx_CallSetupDL(POLY_OPA_DISP, 0x19); + gSPMatrix(&dl[0], Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_LOAD); + gSPDisplayList(&dl[1], object_obj_chan_DL_002358); + POLY_OPA_DISP = &dl[2]; + CLOSE_DISPS(globalCtx->state.gfxCtx); + + if (this->stateFlags & OBJCHAN_STATE_ON_FIRE) { + ObjChan_DrawFire(this, globalCtx); + } +} + +void ObjChan_DrawFire(ObjChan* this, GlobalContext* globalCtx) { + u32 sp4C; + Gfx* dl; + OPEN_DISPS(globalCtx->state.gfxCtx); + + sp4C = globalCtx->gameplayFrames; + + Matrix_RotateY(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->actor.shape.rot.y - this->rotation + 0x8000, + MTXMODE_APPLY); + Matrix_Scale(sObjChanFlameSize[OBJCHAN_SUBTYPE(&this->actor)].x * this->flameSize, + sObjChanFlameSize[OBJCHAN_SUBTYPE(&this->actor)].y * this->flameSize, 1.0f, MTXMODE_APPLY); + Matrix_InsertTranslation(0.0f, sObjChanFlameYOffset[OBJCHAN_SUBTYPE(&this->actor)], 0.0f, MTXMODE_APPLY); + + dl = func_8012C2B4(POLY_XLU_DISP); + gSPMatrix(&dl[0], Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_LOAD); + gMoveWd(&dl[1], 6, 32, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0U, -sp4C * 20, 32, 128)); + gDPSetPrimColor(&dl[2], 128, 128, 255, 255, 0, 255); + gDPSetEnvColor(&dl[3], 255, 0, 0, 0); + gSPDisplayList(&dl[4], &gGameplayKeepDrawFlameDL); + POLY_XLU_DISP = &dl[5]; + + CLOSE_DISPS(globalCtx->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.h b/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.h index 8b498f39e..2b11335b3 100644 --- a/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.h +++ b/src/overlays/actors/ovl_Obj_Chan/z_obj_chan.h @@ -5,12 +5,56 @@ struct ObjChan; +#define OBJCHAN_SUBTYPE(thisx) (((thisx)->params >> 12) & 0xF) +#define OBJCHAN_SUBTYPE_CHANDELIER 0 +#define OBJCHAN_SUBTYPE_POT 1 + +/*** stateFlags ***/ +/* + * After the chandelier is hit by a fire attack, each pot gets OBJCHAN_STATE_FIRE_DELAY state and counts down a number of frames until it catches fire. + * The delay is different for each pot. Remaining frame count is stored in fireDelayFrames. + * The main actor also gets this flag but it's always equivalent to OBJCHAN_STATE_ON_FIRE. + */ +#define OBJCHAN_STATE_FIRE_DELAY (1 << 0) +/** + * Pot is now on fire. After this state is set the flame grows to full size over 20 frames. Flame size is stored in flameSize. + * OBJCHAN_STATE_FIRE_DELAY is not cleared when this flag is set. + * The main actor also gets this flag. + */ +#define OBJCHAN_STATE_ON_FIRE (1 << 1) // Pot has fire. Flame grows to full size over about 1 second. Flame size is stored in fireSize. +/** + * Only for the main actor. + * Once set, the actor tries to start a cutscene. + * Once cutscene is started, this flag is unset and OBJCHAN_STATE_MAIN_STOP_CUTSCENE_AT_FULL_SPEED is set. + */ +#define OBJCHAN_STATE_START_CUTSCENE (1 << 2) +/** + * Only for the main actor. + * Cutscene is running. Once set, the actor waits for full rotation speed and then cancels the cutscene. + */ +#define OBJCHAN_STATE_CUTSCENE (1 << 3) + + + typedef void (*ObjChanActionFunc)(struct ObjChan*, GlobalContext*); typedef struct ObjChan { /* 0x0000 */ Actor actor; /* 0x0144 */ ObjChanActionFunc actionFunc; - /* 0x0148 */ char unk_148[0x9C]; + /* 0x0148 */ ColliderCylinder collider; + /* 0x0194 */ u8 myPotIndex; // For OBJCHAN_SUBTYPE_POT: which pot is this? + /* 0x0196 */ s16 fireDelayFrames; + /* 0x0198 */ UNK_TYPE1 unk198[0x12]; + /* 0x01AA */ u8 stateFlags; + /* 0x01AC */ struct ObjChan* pots[5]; // For OBJCHAN_SUBTYPE_BODY: pointers to pot actors + /* 0x01C0 */ Vec3f unk1C0; + /* 0x01CC */ f32 unk1CC; + /* 0x01D0 */ f32 unk1D0; + /* 0x01D4 */ s16 unk1D4; + /* 0x01D8 */ f32 flameSize; + /* 0x01DC */ s16 rotationSpeed; // Main object only. Slowly ramps up under some circumstances. In other circumstances it's immediately set to maximum speed. + /* 0x01DE */ s16 rotation; // Increases as the chandelier spins. Don't know why they didn't use the actor's rotation variable. + /* 0x01E0 */ s16 cutscenes[2]; } ObjChan; // size = 0x1E4 extern const ActorInit Obj_Chan_InitVars; diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 83923c05b..b676feb1c 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -15375,15 +15375,15 @@ 0x80BB98E0:("ObjChan_Init",), 0x80BB99F0:("ObjChan_Destroy",), 0x80BB9A1C:("func_80BB9A1C",), - 0x80BB9B40:("func_80BB9B40",), - 0x80BB9C08:("func_80BB9C08",), - 0x80BB9F24:("func_80BB9F24",), - 0x80BBA2FC:("func_80BBA2FC",), - 0x80BBA314:("func_80BBA314",), + 0x80BB9B40:("ObjChan_CalculatePotPosition",), + 0x80BB9C08:("ObjChan_InitMain",), + 0x80BB9F24:("ObjChan_MainAction",), + 0x80BBA2FC:("ObjChan_InitPot",), + 0x80BBA314:("ObjChan_PotAction",), 0x80BBA488:("func_80BBA488",), 0x80BBA738:("ObjChan_Update",), 0x80BBA78C:("ObjChan_Draw",), - 0x80BBA894:("func_80BBA894",), + 0x80BBA894:("ObjChan_DrawPot",), 0x80BBA930:("func_80BBA930",), 0x80BBACA0:("EnZos_Init",), 0x80BBAE60:("EnZos_Destroy",), diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt index d578b0544..f685ee576 100644 --- a/tools/disasm/variables.txt +++ b/tools/disasm/variables.txt @@ -15688,10 +15688,10 @@ 0x80BB97D0:("D_80BB97D0","f32","",0x4), 0x80BB97D4:("D_80BB97D4","f32","",0x4), 0x80BBAB10:("Obj_Chan_InitVars","UNK_TYPE1","",0x1), - 0x80BBAB30:("D_80BBAB30","UNK_TYPE1","",0x1), - 0x80BBAB5C:("D_80BBAB5C","UNK_TYPE1","",0x1), - 0x80BBAB60:("D_80BBAB60","UNK_TYPE4","",0x4), - 0x80BBAB78:("D_80BBAB78","f32","",0x4), + 0x80BBAB30:("sObjChanCylinderInit","UNK_TYPE1","",0x1), + 0x80BBAB5C:("sObjChanInitChain","UNK_TYPE1","",0x1), + 0x80BBAB60:("sObjChanFlameSize","UNK_TYPE4","",0x4), + 0x80BBAB78:("sObjChanFlameYOffset","f32","",0x4), 0x80BBAB80:("D_80BBAB80","f32","",0x4), 0x80BBAB84:("D_80BBAB84","f32","",0x4), 0x80BBAB88:("D_80BBAB88","f32","",0x4), @@ -15702,7 +15702,7 @@ 0x80BBAB9C:("D_80BBAB9C","f32","",0x4), 0x80BBABA0:("D_80BBABA0","f32","",0x4), 0x80BBABA4:("D_80BBABA4","f32","",0x4), - 0x80BBAC90:("D_80BBAC90","UNK_TYPE1","",0x1), + 0x80BBAC90:("sObjChanLoaded","UNK_TYPE1","",0x1), 0x80BBC6D0:("En_Zos_InitVars","UNK_TYPE1","",0x1), 0x80BBC6F0:("D_80BBC6F0","UNK_TYPE1","",0x1), 0x80BBC71C:("D_80BBC71C","UNK_TYPE1","",0x1), diff --git a/undefined_syms.txt b/undefined_syms.txt index a53d3388d..5230f6e2a 100644 --- a/undefined_syms.txt +++ b/undefined_syms.txt @@ -1781,13 +1781,6 @@ D_060014F0 = 0x060014F0; D_06007630 = 0x06007630; D_06009A88 = 0x06009A88; -// ovl_Obj_Chan - -D_06000A10 = 0x06000A10; -D_06000AF0 = 0x06000AF0; -D_06001960 = 0x06001960; -D_06002358 = 0x06002358; - // ovl_Obj_Chikuwa D_06000D10 = 0x06000D10;