Shipwright/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c

438 lines
18 KiB
C

/*
* 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);
}