Shipwright/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.c

324 lines
11 KiB
C

/*
* File: z_en_jj.c
* Overlay: ovl_En_Jj
* Description: Lord Jabu-Jabu
*/
#include "z_en_jj.h"
#include "objects/object_jj/object_jj.h"
#include "overlays/actors/ovl_Eff_Dust/z_eff_dust.h"
#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5)
typedef enum {
/* 0 */ JABUJABU_EYE_OPEN,
/* 1 */ JABUJABU_EYE_HALF,
/* 2 */ JABUJABU_EYE_CLOSED,
/* 3 */ JABUJABU_EYE_MAX
} EnJjEyeState;
void EnJj_Init(Actor* thisx, GlobalContext* globalCtx);
void EnJj_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnJj_Update(Actor* thisx, GlobalContext* globalCtx);
void EnJj_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnJj_UpdateStaticCollision(Actor* thisx, GlobalContext* globalCtx);
void EnJj_WaitToOpenMouth(EnJj* this, GlobalContext* globalCtx);
void EnJj_WaitForFish(EnJj* this, GlobalContext* globalCtx);
void EnJj_BeginCutscene(EnJj* this, GlobalContext* globalCtx);
void EnJj_RemoveDust(EnJj* this, GlobalContext* globalCtx);
const ActorInit En_Jj_InitVars = {
ACTOR_EN_JJ,
ACTORCAT_ITEMACTION,
FLAGS,
OBJECT_JJ,
sizeof(EnJj),
(ActorFunc)EnJj_Init,
(ActorFunc)EnJj_Destroy,
(ActorFunc)EnJj_Update,
(ActorFunc)EnJj_Draw,
NULL,
};
static s32 sUnused = 0;
#include "z_en_jj_cutscene_data.c" EARLY
static s32 sUnused2[] = { 0, 0 };
static ColliderCylinderInit sCylinderInit = {
{
COLTYPE_NONE,
AT_NONE,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_1,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0x00000000, 0x00, 0x00 },
{ 0x00000004, 0x00, 0x00 },
TOUCH_NONE,
BUMP_ON,
OCELEM_ON,
},
{ 170, 150, 0, { 0, 0, 0 } },
};
static InitChainEntry sInitChain[] = {
ICHAIN_VEC3F_DIV1000(scale, 87, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneScale, 3300, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneDownward, 1100, ICHAIN_STOP),
};
void EnJj_SetupAction(EnJj* this, EnJjActionFunc actionFunc) {
this->actionFunc = actionFunc;
}
void EnJj_Init(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
EnJj* this = (EnJj*)thisx;
CollisionHeader* colHeader = NULL;
Actor_ProcessInitChain(&this->dyna.actor, sInitChain);
ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f);
switch (this->dyna.actor.params) {
case JABUJABU_MAIN:
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gJabuJabuSkel, &gJabuJabuAnim, this->jointTable,
this->morphTable, 22);
Animation_PlayLoop(&this->skelAnime, &gJabuJabuAnim);
this->unk_30A = 0;
this->eyeIndex = 0;
this->blinkTimer = 0;
this->extraBlinkCounter = 0;
this->extraBlinkTotal = 0;
if (gSaveContext.eventChkInf[3] & 0x400) { // Fish given
EnJj_SetupAction(this, EnJj_WaitToOpenMouth);
} else {
EnJj_SetupAction(this, EnJj_WaitForFish);
}
this->bodyCollisionActor = (DynaPolyActor*)Actor_SpawnAsChild(
&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_JJ, this->dyna.actor.world.pos.x - 10.0f,
this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, this->dyna.actor.world.rot.y, 0,
JABUJABU_COLLISION);
DynaPolyActor_Init(&this->dyna, 0);
CollisionHeader_GetVirtual(&gJabuJabuHeadCol, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit);
this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE;
break;
case JABUJABU_COLLISION:
DynaPolyActor_Init(&this->dyna, 0);
CollisionHeader_GetVirtual(&gJabuJabuBodyCol, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
func_8003ECA8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId);
this->dyna.actor.update = EnJj_UpdateStaticCollision;
this->dyna.actor.draw = NULL;
Actor_SetScale(&this->dyna.actor, 0.087f);
break;
case JABUJABU_UNUSED_COLLISION:
DynaPolyActor_Init(&this->dyna, 0);
CollisionHeader_GetVirtual(&gJabuJabuUnusedCol, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
this->dyna.actor.update = EnJj_UpdateStaticCollision;
this->dyna.actor.draw = NULL;
Actor_SetScale(&this->dyna.actor, 0.087f);
break;
}
}
void EnJj_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnJj* this = (EnJj*)thisx;
switch (this->dyna.actor.params) {
case JABUJABU_MAIN:
DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId);
Collider_DestroyCylinder(globalCtx, &this->collider);
break;
case JABUJABU_COLLISION:
case JABUJABU_UNUSED_COLLISION:
DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId);
break;
}
}
/**
* Blink routine. Blinks at the end of each randomised blinkTimer cycle. If extraBlinkCounter is not zero, blink that
* many more times before resuming random blinkTimer cycles. extraBlinkTotal can be set to a positive number to blink
* that many extra times at the end of every blinkTimer cycle, but the actor always sets it to zero, so only one
* multiblink happens when extraBlinkCounter is nonzero.
*/
void EnJj_Blink(EnJj* this) {
if (this->blinkTimer > 0) {
this->blinkTimer--;
} else {
this->eyeIndex++;
if (this->eyeIndex >= JABUJABU_EYE_MAX) {
this->eyeIndex = JABUJABU_EYE_OPEN;
if (this->extraBlinkCounter > 0) {
this->extraBlinkCounter--;
} else {
this->blinkTimer = Rand_S16Offset(20, 20);
this->extraBlinkCounter = this->extraBlinkTotal;
}
}
}
}
void EnJj_OpenMouth(EnJj* this, GlobalContext* globalCtx) {
DynaPolyActor* bodyCollisionActor = this->bodyCollisionActor;
if (this->mouthOpenAngle >= -5200) {
this->mouthOpenAngle -= 102;
if (this->mouthOpenAngle < -2600) {
func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, bodyCollisionActor->bgId);
}
}
}
void EnJj_WaitToOpenMouth(EnJj* this, GlobalContext* globalCtx) {
if (this->dyna.actor.xzDistToPlayer < 300.0f) {
EnJj_SetupAction(this, EnJj_OpenMouth);
}
}
void EnJj_WaitForFish(EnJj* this, GlobalContext* globalCtx) {
static Vec3f feedingSpot = { -1589.0f, 53.0f, -43.0f };
Player* player = GET_PLAYER(globalCtx);
if ((Math_Vec3f_DistXZ(&feedingSpot, &player->actor.world.pos) < 300.0f) &&
globalCtx->isPlayerDroppingFish(globalCtx)) {
this->cutsceneCountdownTimer = 100;
EnJj_SetupAction(this, EnJj_BeginCutscene);
}
this->collider.dim.pos.x = -1245;
this->collider.dim.pos.y = 20;
this->collider.dim.pos.z = -48;
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
void EnJj_BeginCutscene(EnJj* this, GlobalContext* globalCtx) {
DynaPolyActor* bodyCollisionActor = this->bodyCollisionActor;
if (this->cutsceneCountdownTimer > 0) {
this->cutsceneCountdownTimer--;
} else {
EnJj_SetupAction(this, EnJj_RemoveDust);
globalCtx->csCtx.segment = &D_80A88164;
gSaveContext.cutsceneTrigger = 1;
func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, bodyCollisionActor->bgId);
func_8005B1A4(GET_ACTIVE_CAM(globalCtx));
gSaveContext.eventChkInf[3] |= 0x400;
func_80078884(NA_SE_SY_CORRECT_CHIME);
}
}
void EnJj_CutsceneUpdate(EnJj* this, GlobalContext* globalCtx) {
switch (globalCtx->csCtx.npcActions[2]->action) {
case 1:
if (this->unk_30A & 2) {
this->eyeIndex = 0;
this->blinkTimer = Rand_S16Offset(20, 20);
this->extraBlinkCounter = 0;
this->extraBlinkTotal = 0;
this->unk_30A ^= 2;
}
break;
case 2:
this->unk_30A |= 1;
if (!(this->unk_30A & 8)) {
this->dust = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EFF_DUST,
-1100.0f, 105.0f, -27.0f, 0, 0, 0, EFF_DUST_TYPE_0);
this->unk_30A |= 8;
}
break;
case 3:
if (!(this->unk_30A & 2)) {
this->eyeIndex = 0;
this->blinkTimer = 0;
this->extraBlinkCounter = 1;
this->extraBlinkTotal = 0;
this->unk_30A |= 2;
}
break;
}
if (this->unk_30A & 1) {
Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_JABJAB_BREATHE - SFX_FLAG);
if (this->mouthOpenAngle >= -5200) {
this->mouthOpenAngle -= 102;
}
}
}
void EnJj_RemoveDust(EnJj* this, GlobalContext* globalCtx) {
Actor* dust;
if (!(this->unk_30A & 4)) {
this->unk_30A |= 4;
dust = this->dust;
if (dust != NULL) {
Actor_Kill(dust);
this->dyna.actor.child = NULL;
}
}
}
void EnJj_UpdateStaticCollision(Actor* thisx, GlobalContext* globalCtx) {
}
void EnJj_Update(Actor* thisx, GlobalContext* globalCtx) {
EnJj* this = (EnJj*)thisx;
if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[2] != NULL)) {
EnJj_CutsceneUpdate(this, globalCtx);
} else {
this->actionFunc(this, globalCtx);
if (this->skelAnime.curFrame == 41.0f) {
Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_JABJAB_GROAN);
}
}
EnJj_Blink(this);
SkelAnime_Update(&this->skelAnime);
Actor_SetScale(&this->dyna.actor, 0.087f);
// Head
this->skelAnime.jointTable[10].z = this->mouthOpenAngle;
}
void EnJj_Draw(Actor* thisx, GlobalContext* globalCtx2) {
static void* eyeTextures[] = { gJabuJabuEyeOpenTex, gJabuJabuEyeHalfTex, gJabuJabuEyeClosedTex };
GlobalContext* globalCtx = globalCtx2;
EnJj* this = (EnJj*)thisx;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_800943C8(globalCtx->state.gfxCtx);
Matrix_Translate(0.0f, (cosf(this->skelAnime.curFrame * (M_PI / 41.0f)) * 10.0f) - 10.0f, 0.0f, MTXMODE_APPLY);
Matrix_Scale(10.0f, 10.0f, 10.0f, MTXMODE_APPLY);
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex]));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
NULL, NULL, this);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}