/* * File: z_bg_spot00_hanebasi.c * Overlay: ovl_Bg_Spot00_Hanebasi * Description: Hyrule Field Drawbridge and Torches */ #include "z_bg_spot00_hanebasi.h" #include "objects/object_spot00_objects/object_spot00_objects.h" #include "objects/gameplay_keep/gameplay_keep.h" #include "soh/frame_interpolation.h" #define FLAGS ACTOR_FLAG_4 typedef enum { /* -1 */ DT_DRAWBRIDGE = -1, /* 0 */ DT_CHAIN_1, /* 1 */ DT_CHAIN_2 } DrawbridgeType; void BgSpot00Hanebasi_Init(Actor* thisx, GlobalContext* globalCtx); void BgSpot00Hanebasi_Destroy(Actor* thisx, GlobalContext* globalCtx); void BgSpot00Hanebasi_Update(Actor* thisx, GlobalContext* globalCtx); void BgSpot00Hanebasi_Draw(Actor* thisx, GlobalContext* globalCtx); void BgSpot00Hanebasi_DrawbridgeWait(BgSpot00Hanebasi* this, GlobalContext* globalCtx); void BgSpot00Hanebasi_DrawbridgeRiseAndFall(BgSpot00Hanebasi* this, GlobalContext* globalCtx); void BgSpot00Hanebasi_SetTorchLightInfo(BgSpot00Hanebasi* this, GlobalContext* globalCtx); const ActorInit Bg_Spot00_Hanebasi_InitVars = { ACTOR_BG_SPOT00_HANEBASI, ACTORCAT_BG, FLAGS, OBJECT_SPOT00_OBJECTS, sizeof(BgSpot00Hanebasi), (ActorFunc)BgSpot00Hanebasi_Init, (ActorFunc)BgSpot00Hanebasi_Destroy, (ActorFunc)BgSpot00Hanebasi_Update, (ActorFunc)BgSpot00Hanebasi_Draw, NULL, }; static f32 sTorchFlameScale = 0.0f; static InitChainEntry sInitChain[] = { ICHAIN_F32(uncullZoneScale, 550, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 2000, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneForward, 5000, ICHAIN_CONTINUE), ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), }; void BgSpot00Hanebasi_Init(Actor* thisx, GlobalContext* globalCtx) { BgSpot00Hanebasi* this = (BgSpot00Hanebasi*)thisx; s32 pad; Vec3f chainPos; CollisionHeader* colHeader = NULL; Actor_ProcessInitChain(&this->dyna.actor, sInitChain); DynaPolyActor_Init(&this->dyna, 1); if (this->dyna.actor.params == DT_DRAWBRIDGE) { CollisionHeader_GetVirtual(&gHyruleFieldCastleDrawbridgeCol, &colHeader); } else { CollisionHeader_GetVirtual(&gHyruleFieldCastleDrawbridgeChainsCol, &colHeader); } this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); if (this->dyna.actor.params == DT_DRAWBRIDGE) { if (LINK_IS_ADULT && (gSaveContext.sceneSetupIndex < 4)) { Actor_Kill(&this->dyna.actor); return; } if ((gSaveContext.sceneSetupIndex != 6) && ((gSaveContext.sceneSetupIndex == 4) || (gSaveContext.sceneSetupIndex == 5) || (!LINK_IS_ADULT && !IS_DAY))) { this->dyna.actor.shape.rot.x = -0x4000; } else { this->dyna.actor.shape.rot.x = 0; } if (gSaveContext.sceneSetupIndex != 6) { // Don't close the bridge in rando to accomodate hyrule castle exit if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY) && CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE) && !(gSaveContext.eventChkInf[8] & 1) && !(gSaveContext.n64ddFlag)) { this->dyna.actor.shape.rot.x = -0x4000; } } chainPos.y = (10.0f * Math_CosS(this->dyna.actor.shape.rot.x)) - (Math_SinS(this->dyna.actor.shape.rot.x) * 400.0f); chainPos.z = (10.0f * Math_SinS(this->dyna.actor.shape.rot.x)) - (Math_CosS(this->dyna.actor.shape.rot.x) * 400.0f); chainPos.x = (158.0f * Math_CosS(this->dyna.actor.shape.rot.y)) + (Math_SinS(this->dyna.actor.shape.rot.y) * chainPos.z); chainPos.z = (-158.0f * Math_SinS(this->dyna.actor.shape.rot.y)) + (Math_CosS(this->dyna.actor.shape.rot.y) * chainPos.z); if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_SPOT00_HANEBASI, this->dyna.actor.world.pos.x + chainPos.x, this->dyna.actor.world.pos.y + chainPos.y, this->dyna.actor.world.pos.z + chainPos.z, ((this->dyna.actor.shape.rot.x == 0) ? 0 : 0xF020), this->dyna.actor.shape.rot.y, 0, DT_CHAIN_1) == NULL) { Actor_Kill(&this->dyna.actor); } this->actionFunc = BgSpot00Hanebasi_DrawbridgeWait; this->destAngle = 40; } else if (this->dyna.actor.params == DT_CHAIN_1) { if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_SPOT00_HANEBASI, this->dyna.actor.world.pos.x - (Math_CosS(this->dyna.actor.shape.rot.y) * 316.0f), this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z + (Math_SinS(this->dyna.actor.shape.rot.y) * 316.0f), this->dyna.actor.shape.rot.x, this->dyna.actor.shape.rot.y, 0, DT_CHAIN_2) == NULL) { Actor_Kill(&this->dyna.actor); Actor_Kill(this->dyna.actor.parent); } this->actionFunc = BgSpot00Hanebasi_SetTorchLightInfo; } else { this->actionFunc = BgSpot00Hanebasi_SetTorchLightInfo; } if (this->dyna.actor.params >= DT_CHAIN_1) { this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); Lights_PointGlowSetInfo(&this->lightInfo, ((this->dyna.actor.params == DT_CHAIN_1) ? 260.0f : -260.0f), 168, 690, 255, 255, 0, 0); } } void BgSpot00Hanebasi_Destroy(Actor* thisx, GlobalContext* globalCtx) { BgSpot00Hanebasi* this = (BgSpot00Hanebasi*)thisx; DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); if (this->dyna.actor.params >= DT_CHAIN_1) { LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); } } void BgSpot00Hanebasi_DrawbridgeWait(BgSpot00Hanebasi* this, GlobalContext* globalCtx) { BgSpot00Hanebasi* child = (BgSpot00Hanebasi*)this->dyna.actor.child; if ((gSaveContext.sceneSetupIndex >= 4) || !CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) || !CHECK_QUEST_ITEM(QUEST_GORON_RUBY) || !CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE) || (gSaveContext.eventChkInf[8] & 1)) { if (this->dyna.actor.shape.rot.x != 0) { if (Flags_GetEnv(globalCtx, 0) || ((gSaveContext.sceneSetupIndex < 4) && IS_DAY)) { this->actionFunc = BgSpot00Hanebasi_DrawbridgeRiseAndFall; this->destAngle = 0; child->destAngle = 0; return; } if (this) {} // required to match } if ((this->dyna.actor.shape.rot.x == 0) && (gSaveContext.sceneSetupIndex < 4) && !LINK_IS_ADULT && !IS_DAY) { this->actionFunc = BgSpot00Hanebasi_DrawbridgeRiseAndFall; this->destAngle = -0x4000; child->destAngle = -0xFE0; } } } void BgSpot00Hanebasi_DoNothing(BgSpot00Hanebasi* this, GlobalContext* globalCtx) { } void BgSpot00Hanebasi_DrawbridgeRiseAndFall(BgSpot00Hanebasi* this, GlobalContext* globalCtx) { BgSpot00Hanebasi* child; Actor* childsChild; s16 angle = 80; if (Math_ScaledStepToS(&this->dyna.actor.shape.rot.x, this->destAngle, 80)) { this->actionFunc = BgSpot00Hanebasi_DrawbridgeWait; } if (this->dyna.actor.shape.rot.x >= -0x27D8) { child = (BgSpot00Hanebasi*)this->dyna.actor.child; angle *= 0.4f; Math_ScaledStepToS(&child->dyna.actor.shape.rot.x, child->destAngle, angle); childsChild = child->dyna.actor.child; Math_ScaledStepToS(&childsChild->shape.rot.x, child->destAngle, angle); } if (this->destAngle < 0) { if (this->actionFunc == BgSpot00Hanebasi_DrawbridgeWait) { Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BRIDGE_CLOSE_STOP); } else { func_8002F974(&this->dyna.actor, NA_SE_EV_BRIDGE_CLOSE - SFX_FLAG); } } else { if (this->actionFunc == BgSpot00Hanebasi_DrawbridgeWait) { Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BRIDGE_OPEN_STOP); } else { func_8002F974(&this->dyna.actor, NA_SE_EV_BRIDGE_OPEN - SFX_FLAG); } } } void BgSpot00Hanebasi_SetTorchLightInfo(BgSpot00Hanebasi* this, GlobalContext* globalCtx) { u8 lightColor = (u8)(Rand_ZeroOne() * 127.0f) + 128; // intensity of the red and green channels Lights_PointGlowSetInfo(&this->lightInfo, (this->dyna.actor.params == DT_CHAIN_1) ? 260.0f : -260.0f, (5000.0f * sTorchFlameScale) + 128.0f, 690, lightColor, lightColor, 0, sTorchFlameScale * 37500.0f); } void BgSpot00Hanebasi_Update(Actor* thisx, GlobalContext* globalCtx) { BgSpot00Hanebasi* this = (BgSpot00Hanebasi*)thisx; s32 pad; this->actionFunc(this, globalCtx); if (this->dyna.actor.params == DT_DRAWBRIDGE) { if (globalCtx->sceneNum == SCENE_SPOT00) { if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY) && CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE) && !(gSaveContext.eventChkInf[8] & 1) && LINK_IS_CHILD) { Player* player = GET_PLAYER(globalCtx); if ((player->actor.world.pos.x > -450.0f) && (player->actor.world.pos.x < 450.0f) && (player->actor.world.pos.z > 1080.0f) && (player->actor.world.pos.z < 1700.0f) && (!(Gameplay_InCsMode(globalCtx)))) { gSaveContext.eventChkInf[8] |= 1; Flags_SetEventChkInf(0x82); this->actionFunc = BgSpot00Hanebasi_DoNothing; func_8002DF54(globalCtx, &player->actor, 8); globalCtx->nextEntranceIndex = 0x00CD; gSaveContext.nextCutsceneIndex = 0xFFF1; globalCtx->sceneLoadFlag = 0x14; globalCtx->fadeTransition = 4; } else if (Actor_IsFacingAndNearPlayer(&this->dyna.actor, 3000.0f, 0x7530)) { globalCtx->envCtx.gloomySkyMode = 1; } } } if (gSaveContext.sceneSetupIndex == 5) { u16 dayTime; s32 tmp; if (gTimeIncrement == 50) { tmp = 0xD556; if (gSaveContext.dayTime >= 0xD557) { tmp = 0x1D556; } gTimeIncrement = (tmp - gSaveContext.dayTime) * (1.0f / 350.0f); } dayTime = gSaveContext.dayTime; if ((dayTime >= 0x2AAC) && (dayTime < 0x3000) && (gSaveContext.sceneSetupIndex == 5)) { gTimeIncrement = 0; } } } } void BgSpot00Hanebasi_DrawTorches(Actor* thisx, GlobalContext* globalCtx2) { GlobalContext* globalCtx = globalCtx2; f32 angle; s32 i; static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); func_80093D84(globalCtx->state.gfxCtx); if (gSaveContext.sceneSetupIndex >= 4) { sTorchFlameScale = 0.008f; } else { sTorchFlameScale = ((thisx->shape.rot.x * -1) - 0x2000) * (1.0f / 1024000.0f); } angle = (s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000) * (M_PI / 32768.0f); gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 0, 255); gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); for (i = 0; i < 2; i++) { FrameInterpolation_RecordOpenChild("Hanebasi Torch", epoch + i * 25); gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, ((globalCtx->gameplayFrames + i) * -20) & 0x1FF, 32, 128)); Matrix_Translate((i == 0) ? 260.0f : -260.0f, 128.0f, 690.0f, MTXMODE_NEW); Matrix_RotateY(angle, MTXMODE_APPLY); Matrix_Scale(sTorchFlameScale, sTorchFlameScale, sTorchFlameScale, MTXMODE_APPLY); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); FrameInterpolation_RecordCloseChild(); } CLOSE_DISPS(globalCtx->state.gfxCtx); } void BgSpot00Hanebasi_Draw(Actor* thisx, GlobalContext* globalCtx) { Vec3f basePos = { 158.0f, 10.0f, 400.0f }; Vec3f newPos; OPEN_DISPS(globalCtx->state.gfxCtx); func_80093D18(globalCtx->state.gfxCtx); gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); if (thisx->params == DT_DRAWBRIDGE) { gSPDisplayList(POLY_OPA_DISP++, gHyruleFieldCastleDrawbridgeDL); Matrix_MultVec3f(&basePos, &newPos); thisx->child->world.pos.x = newPos.x; thisx->child->world.pos.y = newPos.y; thisx->child->world.pos.z = newPos.z; basePos.x *= -1.0f; Matrix_MultVec3f(&basePos, &newPos); thisx->child->child->world.pos.x = newPos.x; thisx->child->child->world.pos.y = newPos.y; thisx->child->child->world.pos.z = newPos.z; if (gSaveContext.sceneSetupIndex != 12) { if ((gSaveContext.sceneSetupIndex >= 4) || (!LINK_IS_ADULT && (thisx->shape.rot.x < -0x2000))) { BgSpot00Hanebasi_DrawTorches(thisx, globalCtx); } else { sTorchFlameScale = 0.0f; } } } else { gSPDisplayList(POLY_OPA_DISP++, gHyruleFieldCastleDrawbridgeChainsDL); } CLOSE_DISPS(globalCtx->state.gfxCtx); }