/* * File: z_en_po_relay.c * Overlay: ovl_En_Po_Relay * Description: Dampé's Ghost */ #include "z_en_po_relay.h" #include "overlays/actors/ovl_En_Honotrap/z_en_honotrap.h" #include "objects/object_tk/object_tk.h" #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_12 | ACTOR_FLAG_16) void EnPoRelay_Init(Actor* thisx, PlayState* play); void EnPoRelay_Destroy(Actor* thisx, PlayState* play); void EnPoRelay_Update(Actor* thisx, PlayState* play); void EnPoRelay_Draw(Actor* thisx, PlayState* play); void EnPoRelay_Idle(EnPoRelay* this, PlayState* play); void EnPoRelay_Race(EnPoRelay* this, PlayState* play); void EnPoRelay_EndRace(EnPoRelay* this, PlayState* play); void EnPoRelay_Talk(EnPoRelay* this, PlayState* play); void EnPoRelay_Talk2(EnPoRelay* this, PlayState* play); void EnPoRelay_DisappearAndReward(EnPoRelay* this, PlayState* play); void EnPoRelay_SetupIdle(EnPoRelay* this); static Vec3s D_80AD8C30[] = { { 0xFFC4, 0xFDEE, 0xF47A }, { 0x0186, 0xFE0C, 0xF47A }, { 0x0186, 0xFE0C, 0xF0F6 }, { 0x00D2, 0xFDEE, 0xF0F6 }, { 0x00D2, 0xFD9E, 0xEEDA }, { 0x023A, 0xFDC6, 0xEEDA }, { 0x023A, 0xFDC6, 0xED18 }, { 0x00D2, 0xFDC6, 0xED18 }, { 0x00D2, 0xFDC6, 0xEBCE }, { 0x00D2, 0xFDC6, 0xEAA2 }, { 0x023A, 0xFDC6, 0xEAA2 }, { 0x023A, 0xFDC6, 0xEBB0 }, { 0x04EC, 0xFD9E, 0xEBB0 }, { 0x0672, 0xFD62, 0xED18 }, { 0x0672, 0xFD30, 0xEE80 }, { 0x07DA, 0xFD26, 0xEE80 }, { 0x07DA, 0xFD26, 0xEF70 }, { 0x07DA, 0xFD26, 0xF204 }, { 0x0672, 0xFD44, 0xF204 }, { 0x0672, 0xFD6C, 0xF3C6 }, { 0x088E, 0xFD6C, 0xF3C6 }, { 0x088E, 0xFDB2, 0xF5E2 }, { 0x099C, 0xFDD0, 0xF5E2 }, { 0x0B54, 0xFE66, 0xF772 }, { 0x0B4E, 0xFE66, 0xF87E }, { 0x0B4A, 0xFE66, 0xF97A }, { 0x0B4A, 0xFE98, 0xF9FC }, { 0x0BAE, 0xFE98, 0xF9FC }, }; const ActorInit En_Po_Relay_InitVars = { ACTOR_EN_PO_RELAY, ACTORCAT_NPC, FLAGS, OBJECT_TK, sizeof(EnPoRelay), (ActorFunc)EnPoRelay_Init, (ActorFunc)EnPoRelay_Destroy, (ActorFunc)EnPoRelay_Update, (ActorFunc)EnPoRelay_Draw, NULL, }; static ColliderCylinderInit sCylinderInit = { { COLTYPE_NONE, AT_NONE, AC_NONE, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_CYLINDER, }, { ELEMTYPE_UNK0, { 0x00000000, 0x00, 0x00 }, { 0x00000000, 0x00, 0x00 }, TOUCH_NONE, BUMP_NONE, OCELEM_ON, }, { 30, 52, 0, { 0, 0, 0 } }, }; static s32 D_80AD8D24 = 0; static InitChainEntry sInitChain[] = { ICHAIN_S8(naviEnemyId, 0x4F, ICHAIN_CONTINUE), ICHAIN_F32(targetArrowOffset, 1500, ICHAIN_STOP), }; static Vec3f D_80AD8D30 = { 0.0f, 1.5f, 0.0f }; static Vec3f D_80AD8D3C = { 0.0f, 0.0f, 0.0f }; static Vec3f D_80AD8D48 = { 0.0f, 1200.0f, 0.0f }; static void* sEyesTextures[] = { gDampeEyeOpenTex, gDampeEyeHalfTex, gDampeEyeClosedTex, }; void EnPoRelay_Init(Actor* thisx, PlayState* play) { EnPoRelay* this = (EnPoRelay*)thisx; s32 temp; Actor_ProcessInitChain(&this->actor, sInitChain); ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 42.0f); SkelAnime_InitFlex(play, &this->skelAnime, &gDampeSkel, &gDampeFloatAnim, this->jointTable, this->morphTable, 18); Collider_InitCylinder(play, &this->collider); Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit); this->lightNode = LightContext_InsertLight(play, &play->lightCtx, &this->lightInfo); Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, 255, 255, 255, 200); this->lightColor.a = 255; temp = 1; if (D_80AD8D24 != 0) { Actor_Kill(&this->actor); } else { D_80AD8D24 = temp; Actor_SetTextWithPrefix(play, &this->actor, 65); this->textId = this->actor.textId; EnPoRelay_SetupIdle(this); } this->actor.params &= 0x3F; } void EnPoRelay_Destroy(Actor* thisx, PlayState* play) { EnPoRelay* this = (EnPoRelay*)thisx; D_80AD8D24 = 0; LightContext_RemoveLight(play, &play->lightCtx, this->lightNode); Collider_DestroyCylinder(play, &this->collider); } void EnPoRelay_SetupIdle(EnPoRelay* this) { this->unk_195 = 32; this->pathIndex = 0; this->actor.room = -1; this->actor.shape.rot.y = 0; this->actor.world.rot.y = -0x8000; this->actor.colChkInfo.mass = MASS_HEAVY; this->actionFunc = EnPoRelay_Idle; } void EnPoRelay_Vec3sToVec3f(Vec3f* dest, Vec3s* src) { dest->x = src->x; dest->y = src->y; dest->z = src->z; } void EnPoRelay_SetupRace(EnPoRelay* this) { Vec3f vec; EnPoRelay_Vec3sToVec3f(&vec, &D_80AD8C30[this->pathIndex]); this->actionTimer = ((s16)(this->actor.shape.rot.y - this->actor.world.rot.y - 0x8000) >> 0xB) % 32U; func_80088B34(0); this->hookshotSlotFull = (INV_CONTENT(ITEM_HOOKSHOT) != ITEM_NONE && !gSaveContext.n64ddFlag) || (gSaveContext.n64ddFlag && Flags_GetTreasure(gPlayState, 0x1E)); this->unk_19A = Actor_WorldYawTowardPoint(&this->actor, &vec); this->actor.flags |= ACTOR_FLAG_27; Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); this->actionFunc = EnPoRelay_Race; } void EnPoRelay_SetupEndRace(EnPoRelay* this) { this->actor.world.rot.y = this->actor.home.rot.y + 0xC000; this->actor.flags &= ~ACTOR_FLAG_27; this->actor.speedXZ = 0.0f; this->actionFunc = EnPoRelay_EndRace; } void EnPoRelay_CorrectY(EnPoRelay* this) { Math_StepToF(&this->actor.home.pos.y, D_80AD8C30[(this->pathIndex >= 28) ? 27 : this->pathIndex].y + 45.0f, 2.0f); this->actor.world.pos.y = Math_SinS(this->unk_195 * 0x800) * 8.0f + this->actor.home.pos.y; } void EnPoRelay_Idle(EnPoRelay* this, PlayState* play) { Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x100); if (Actor_ProcessTalkRequest(&this->actor, play)) { this->actor.flags &= ~ACTOR_FLAG_16; this->actionFunc = EnPoRelay_Talk; } else if (this->actor.xzDistToPlayer < 250.0f) { this->actor.flags |= ACTOR_FLAG_16; this->actor.textId = this->textId; func_8002F2CC(&this->actor, play, 250.0f); } func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); } void EnPoRelay_Talk(EnPoRelay* this, PlayState* play) { Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x100); if (Actor_TextboxIsClosing(&this->actor, play)) { Actor_SetTextWithPrefix(play, &this->actor, 0x2F); this->textId = this->actor.textId; EnPoRelay_SetupRace(this); } func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); } void EnPoRelay_Race(EnPoRelay* this, PlayState* play) { Player* player = GET_PLAYER(play); Vec3f vec; f32 speed; f32 multiplier; if (this->actionTimer != 0) { this->actionTimer--; } if (this->actionTimer == 0 && Rand_ZeroOne() < 0.03f) { this->actionTimer = 32; if (this->pathIndex < 23) { speed = Rand_ZeroOne() * 3.0f; if (speed < 1.0f) { multiplier = 1.0f; } else if (speed < 2.0f) { multiplier = -1.0f; } else { multiplier = 0.0f; } speed = 30.0f * multiplier; Actor_Spawn(&play->actorCtx, play, ACTOR_EN_HONOTRAP, Math_CosS(this->unk_19A) * speed + this->actor.world.pos.x, this->actor.world.pos.y, Math_SinS(this->unk_19A) * speed + this->actor.world.pos.z, 0, (this->unk_19A + 0x8000) - (0x2000 * multiplier), 0, HONOTRAP_FLAME_DROP, true); } } Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_19A, 2, 0x1000, 0x100); this->actor.shape.rot.y = this->actor.world.rot.y + (this->actionTimer * 0x800) + 0x8000; if (this->pathIndex < 23) { // If the player travels along a different path to Dampé that converges later if ((Math3D_PointInSquare2D(660.0f, 840.0f, -4480.0f, -3760.0f, player->actor.world.pos.x, player->actor.world.pos.z) != 0) || (Math3D_PointInSquare2D(1560.0f, 1740.0f, -4030.0f, -3670.0f, player->actor.world.pos.x, player->actor.world.pos.z) != 0) || (Math3D_PointInSquare2D(1580.0f, 2090.0f, -3030.0f, -2500.0f, player->actor.world.pos.x, player->actor.world.pos.z) != 0)) { speed = (this->hookshotSlotFull) ? player->actor.speedXZ * 1.4f : player->actor.speedXZ * 1.2f; } else if (this->actor.xzDistToPlayer < 150.0f) { speed = (this->hookshotSlotFull) ? player->actor.speedXZ * 1.2f : player->actor.speedXZ; } else if (this->actor.xzDistToPlayer < 300.0f) { speed = (this->hookshotSlotFull) ? player->actor.speedXZ : player->actor.speedXZ * 0.8f; } else if (this->hookshotSlotFull) { speed = 4.5f; } else { speed = 3.5f; } multiplier = 250.0f - this->actor.xzDistToPlayer; multiplier = CLAMP_MIN(multiplier, 0.0f); speed += multiplier * 0.02f + 1.0f; Math_ApproachF(&this->actor.speedXZ, speed, 0.5f, 1.5f); } else { Math_ApproachF(&this->actor.speedXZ, 3.5f, 0.5f, 1.5f); } EnPoRelay_Vec3sToVec3f(&vec, &D_80AD8C30[this->pathIndex]); if (Actor_WorldDistXZToPoint(&this->actor, &vec) < 40.0f) { this->pathIndex++; EnPoRelay_Vec3sToVec3f(&vec, &D_80AD8C30[this->pathIndex]); if (this->pathIndex == 28) { EnPoRelay_SetupEndRace(this); } else if (this->pathIndex == 9) { Flags_SetSwitch(play, 0x35); } else if (this->pathIndex == 17) { Flags_SetSwitch(play, 0x36); } else if (this->pathIndex == 25) { Flags_SetSwitch(play, 0x37); } } this->unk_19A = Actor_WorldYawTowardPoint(&this->actor, &vec); func_8002F974(&this->actor, NA_SE_EN_PO_AWAY - SFX_FLAG); } void EnPoRelay_EndRace(EnPoRelay* this, PlayState* play) { Math_ScaledStepToS(&this->actor.shape.rot.y, -0x4000, 0x800); if (Actor_ProcessTalkRequest(&this->actor, play)) { this->actionFunc = EnPoRelay_Talk2; } else if (play->roomCtx.curRoom.num == 5) { Actor_Kill(&this->actor); gSaveContext.timer1State = 0; } else if (Actor_IsFacingAndNearPlayer(&this->actor, 150.0f, 0x3000)) { this->actor.textId = this->textId; func_8002F2CC(&this->actor, play, 250.0f); } func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); } void EnPoRelay_Talk2(EnPoRelay* this, PlayState* play) { Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x100); if (Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) { if (Message_ShouldAdvance(play)) { if (this->hookshotSlotFull != 0) { Actor_SetTextWithPrefix(play, &this->actor, 0x2E); } else { Actor_SetTextWithPrefix(play, &this->actor, 0x2D); } this->textId = this->actor.textId; Message_ContinueTextbox(play, this->actor.textId); } } else if (Actor_TextboxIsClosing(&this->actor, play)) { gSaveContext.timer1State = 0; this->actionTimer = 0; this->actionFunc = EnPoRelay_DisappearAndReward; } func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); } void EnPoRelay_DisappearAndReward(EnPoRelay* this, PlayState* play) { Vec3f vec; f32 multiplier; s32 pad; Vec3f sp60; s32 pad1; this->actionTimer++; if (this->actionTimer < 8) { if (this->actionTimer < 5) { vec.y = Math_SinS((this->actionTimer * 0x1000) - 0x4000) * 23.0f + (this->actor.world.pos.y + 40.0f); multiplier = Math_CosS((this->actionTimer * 0x1000) - 0x4000) * 23.0f; vec.x = (Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x4800) * multiplier) + this->actor.world.pos.x; vec.z = (Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x4800) * multiplier) + this->actor.world.pos.z; } else { vec.y = this->actor.world.pos.y + 40.0f + 15.0f * (this->actionTimer - 5); vec.x = (Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x4800) * 23.0f) + this->actor.world.pos.x; vec.z = (Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x4800) * 23.0f) + this->actor.world.pos.z; } EffectSsDeadDb_Spawn(play, &vec, &D_80AD8D30, &D_80AD8D3C, this->actionTimer * 10 + 80, 0, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); vec.x = (this->actor.world.pos.x + this->actor.world.pos.x) - vec.x; vec.z = (this->actor.world.pos.z + this->actor.world.pos.z) - vec.z; EffectSsDeadDb_Spawn(play, &vec, &D_80AD8D30, &D_80AD8D3C, this->actionTimer * 10 + 80, 0, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); vec.x = this->actor.world.pos.x; vec.z = this->actor.world.pos.z; EffectSsDeadDb_Spawn(play, &vec, &D_80AD8D30, &D_80AD8D3C, this->actionTimer * 10 + 80, 0, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); if (this->actionTimer == 1) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_EXTINCT); } } if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.001f) != 0) { if(!gSaveContext.n64ddFlag) { if (this->hookshotSlotFull != 0) { sp60.x = this->actor.world.pos.x; sp60.y = this->actor.floorHeight; sp60.z = this->actor.world.pos.z; if (gSaveContext.timer1Value < HIGH_SCORE(HS_DAMPE_RACE)) { HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timer1Value; } if (Flags_GetCollectible(play, this->actor.params) == 0 && gSaveContext.timer1Value <= 60) { Item_DropCollectible2(play, &sp60, (this->actor.params << 8) + (0x4000 | ITEM00_HEART_PIECE)); } else { Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, sp60.x, sp60.y, sp60.z, 0, 0, 0, 2, true); } } else { Flags_SetTempClear(play, 4); HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timer1Value; } } else { sp60.x = this->actor.world.pos.x; sp60.y = this->actor.floorHeight; sp60.z = this->actor.world.pos.z; if (this->hookshotSlotFull == 0) { Flags_SetTempClear(play, 4); Flags_SetTreasure(gPlayState, 0x1E); HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timer1Value; } if (gSaveContext.timer1Value < HIGH_SCORE(HS_DAMPE_RACE)) { HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timer1Value; } if (Flags_GetCollectible(play, this->actor.params) == 0 && gSaveContext.timer1Value <= 60) { Item_DropCollectible2(play, &sp60, (this->actor.params << 8) + (0x4000 | ITEM00_HEART_PIECE)); } else if (Flags_GetCollectible(play, this->actor.params) != 0) { Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, sp60.x, sp60.y, sp60.z, 0, 0, 0, 2, true); } } Actor_Kill(&this->actor); } this->actor.scale.y = this->actor.scale.x; this->actor.scale.z = this->actor.scale.x; this->actor.world.pos.y += 10.0f; } void EnPoRelay_Update(Actor* thisx, PlayState* play) { EnPoRelay* this = (EnPoRelay*)thisx; s32 pad; SkelAnime_Update(&this->skelAnime); this->actionFunc(this, play); Actor_MoveForward(&this->actor); EnPoRelay_CorrectY(this); Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, 27.0f, 60.0f, 4); Collider_UpdateCylinder(&this->actor, &this->collider); CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); Actor_SetFocus(&this->actor, 50.0f); if (this->unk_195 != 0) { this->unk_195 -= 1; } if (this->unk_195 == 0) { this->unk_195 = 32; } this->eyeTextureIdx++; if (this->eyeTextureIdx == 3) { this->eyeTextureIdx = 0; } } void EnPoRelay_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { EnPoRelay* this = (EnPoRelay*)thisx; if (limbIndex == 14) { f32 rand; Vec3f vec; OPEN_DISPS(play->state.gfxCtx); rand = Rand_ZeroOne(); this->lightColor.r = (s16)(rand * 30.0f) + 225; this->lightColor.g = (s16)(rand * 100.0f) + 155; this->lightColor.b = (s16)(rand * 160.0f) + 95; gDPPipeSync(POLY_OPA_DISP++); gDPSetEnvColor(POLY_OPA_DISP++, this->lightColor.r, this->lightColor.g, this->lightColor.b, 128); gSPDisplayList(POLY_OPA_DISP++, gDampeLanternDL); CLOSE_DISPS(play->state.gfxCtx); Matrix_MultVec3f(&D_80AD8D48, &vec); Lights_PointNoGlowSetInfo(&this->lightInfo, vec.x, vec.y, vec.z, this->lightColor.r, this->lightColor.g, this->lightColor.b, 200); } else if (limbIndex == 8) { OPEN_DISPS(play->state.gfxCtx); gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gDampeHaloDL); CLOSE_DISPS(play->state.gfxCtx); } } void EnPoRelay_Draw(Actor* thisx, PlayState* play) { EnPoRelay* this = (EnPoRelay*)thisx; OPEN_DISPS(play->state.gfxCtx); Gfx_SetupDL_25Opa(play->state.gfxCtx); gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyesTextures[this->eyeTextureIdx])); SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, NULL, EnPoRelay_PostLimbDraw, &this->actor); CLOSE_DISPS(play->state.gfxCtx); }