Shipwright/soh/src/overlays/actors/ovl_Bg_Haka_Tubo/z_bg_haka_tubo.c

257 lines
10 KiB
C

/*
* File: z_bg_haka_tubo.c
* Overlay: ovl_Bg_Haka_Tubo
* Description: Shadow Temple Giant Skull Jar
*/
#include "z_bg_haka_tubo.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_haka_objects/object_haka_objects.h"
#define FLAGS ACTOR_FLAG_4
void BgHakaTubo_Init(Actor* thisx, GlobalContext* globalCtx);
void BgHakaTubo_Destroy(Actor* thisx, GlobalContext* globalCtx);
void BgHakaTubo_Update(Actor* thisx, GlobalContext* globalCtx);
void BgHakaTubo_Draw(Actor* thisx, GlobalContext* globalCtx);
void BgHakaTubo_Idle(BgHakaTubo* this, GlobalContext* globalCtx);
void BgHakaTubo_DropCollectible(BgHakaTubo* this, GlobalContext* globalCtx);
const ActorInit Bg_Haka_Tubo_InitVars = {
ACTOR_BG_HAKA_TUBO,
ACTORCAT_BG,
FLAGS,
OBJECT_HAKA_OBJECTS,
sizeof(BgHakaTubo),
(ActorFunc)BgHakaTubo_Init,
(ActorFunc)BgHakaTubo_Destroy,
(ActorFunc)BgHakaTubo_Update,
(ActorFunc)BgHakaTubo_Draw,
NULL,
};
static ColliderCylinderInit sPotColliderInit = {
{
COLTYPE_NONE,
AT_NONE,
AC_ON | AC_TYPE_PLAYER,
OC1_NONE,
OC2_TYPE_2,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0x00000000, 0x00, 0x00 },
{ 0x00000008, 0x00, 0x00 },
TOUCH_NONE,
BUMP_ON,
OCELEM_NONE,
},
{ 25, 60, 30, { 0, 0, 0 } },
};
static ColliderCylinderInit sFlamesColliderInit = {
{
COLTYPE_NONE,
AT_ON | AT_TYPE_ENEMY,
AC_NONE,
OC1_ON | OC1_TYPE_PLAYER,
OC2_TYPE_2,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0x20000000, 0x01, 0x04 },
{ 0x00000008, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NONE,
BUMP_NONE,
OCELEM_ON,
},
{ 60, 45, 235, { 0, 0, 0 } },
};
static s32 sPotsDestroyed = 0;
static InitChainEntry sInitChain[] = {
ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP),
};
void BgHakaTubo_Init(Actor* thisx, GlobalContext* globalCtx) {
BgHakaTubo* this = (BgHakaTubo*)thisx;
s32 pad;
CollisionHeader* colHeader = NULL;
Actor_ProcessInitChain(&this->dyna.actor, sInitChain);
DynaPolyActor_Init(&this->dyna, DPM_UNK3);
CollisionHeader_GetVirtual(&object_haka_objects_Col_0108B8, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
Collider_InitCylinder(globalCtx, &this->potCollider);
Collider_SetCylinder(globalCtx, &this->potCollider, &this->dyna.actor, &sPotColliderInit);
Collider_InitCylinder(globalCtx, &this->flamesCollider);
Collider_SetCylinder(globalCtx, &this->flamesCollider, &this->dyna.actor, &sFlamesColliderInit);
this->fireScroll = Rand_ZeroOne() * 15.0f;
sPotsDestroyed = 0;
this->actionFunc = BgHakaTubo_Idle;
}
void BgHakaTubo_Destroy(Actor* thisx, GlobalContext* globalCtx) {
BgHakaTubo* this = (BgHakaTubo*)thisx;
DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId);
Collider_DestroyCylinder(globalCtx, &this->potCollider);
Collider_DestroyCylinder(globalCtx, &this->flamesCollider);
}
void BgHakaTubo_Idle(BgHakaTubo* this, GlobalContext* globalCtx) {
static Vec3f sZeroVector = { 0.0f, 0.0f, 0.0f };
Vec3f pos;
if (this->dyna.actor.room == 12) { // 3 spinning pots room in Shadow Temple
this->dyna.actor.shape.rot.y += 0x180;
this->dyna.actor.world.pos.x = Math_SinS(this->dyna.actor.shape.rot.y - 0x4000) * 145.0f + -5559.0f;
this->dyna.actor.world.pos.z = Math_CosS(this->dyna.actor.shape.rot.y - 0x4000) * 145.0f + -1587.0f;
}
// Colliding with flame circle
if (this->flamesCollider.base.atFlags & AT_HIT) {
this->flamesCollider.base.atFlags &= ~AT_HIT;
func_8002F71C(globalCtx, &this->dyna.actor, 5.0f, this->dyna.actor.yawTowardsPlayer, 5.0f);
}
// Colliding with hitbox inside the pot
if (this->potCollider.base.acFlags & AC_HIT) {
this->potCollider.base.acFlags &= ~AC_HIT;
// If the colliding actor is within a 50 unit radius and 50 unit height cylinder centered
// on the actor's position, break the pot
if (Actor_WorldDistXZToPoint(&this->dyna.actor, &this->potCollider.base.ac->world.pos) < 50.0f &&
(this->potCollider.base.ac->world.pos.y - this->dyna.actor.world.pos.y) < 50.0f) {
pos.x = this->dyna.actor.world.pos.x;
pos.z = this->dyna.actor.world.pos.z;
pos.y = this->dyna.actor.world.pos.y + 80.0f;
EffectSsBomb2_SpawnLayered(globalCtx, &pos, &sZeroVector, &sZeroVector, 100, 45);
SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 50, NA_SE_EV_BOX_BREAK);
EffectSsHahen_SpawnBurst(globalCtx, &pos, 20.0f, 0, 350, 100, 50, OBJECT_HAKA_OBJECTS, 40,
gEffFragments2DL);
this->dropTimer = 5;
this->dyna.actor.draw = NULL;
Actor_SetScale(&this->dyna.actor, 0.0f);
this->actionFunc = BgHakaTubo_DropCollectible;
}
} else {
Collider_UpdateCylinder(&this->dyna.actor, &this->flamesCollider);
Collider_UpdateCylinder(&this->dyna.actor, &this->potCollider);
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->potCollider.base);
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->flamesCollider.base);
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->flamesCollider.base);
}
}
void BgHakaTubo_DropCollectible(BgHakaTubo* this, GlobalContext* globalCtx) {
EnItem00* collectible;
f32 rnd;
Vec3f spawnPos;
s32 i;
s32 collectibleParams;
this->dropTimer--;
if (this->dropTimer == 0) { // Creates a delay between destroying the pot and dropping the collectible
spawnPos.x = this->dyna.actor.world.pos.x;
spawnPos.y = this->dyna.actor.world.pos.y + 200.0f;
spawnPos.z = this->dyna.actor.world.pos.z;
if (this->dyna.actor.room == 12) { // 3 spinning pots room in Shadow Temple
rnd = Rand_ZeroOne();
sPotsDestroyed++;
if (sPotsDestroyed == 3) {
// All 3 pots destroyed
collectibleParams = -1;
func_80078884(NA_SE_SY_CORRECT_CHIME);
// Drop rupees
for (i = 0; i < 9; i++) {
collectible = Item_DropCollectible(globalCtx, &spawnPos, i % 3);
if (collectible != NULL) {
collectible->actor.velocity.y = 15.0f;
collectible->actor.world.rot.y = this->dyna.actor.shape.rot.y + (i * 0x1C71);
}
}
} else if (rnd < 0.2f) {
// Unlucky, no reward and spawn keese
collectibleParams = -1;
Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_FIREFLY, this->dyna.actor.world.pos.x,
this->dyna.actor.world.pos.y + 80.0f, this->dyna.actor.world.pos.z, 0,
this->dyna.actor.shape.rot.y, 0, 2);
func_80078884(NA_SE_SY_ERROR);
} else {
// Random rewards
if (CVar_GetS32("gNoRandomDrops", 0)) {
collectibleParams = -1;
} else if(rnd < 0.4f) {
collectibleParams = ITEM00_BOMBS_A;
} else if (rnd < 0.6f) {
collectibleParams = ITEM00_MAGIC_LARGE;
} else if (rnd < 0.8f) {
collectibleParams = ITEM00_MAGIC_SMALL;
} else {
collectibleParams = ITEM00_ARROWS_SMALL;
}
func_80078884(NA_SE_SY_TRE_BOX_APPEAR);
}
} else if (Flags_GetCollectible(globalCtx, this->dyna.actor.params) != 0) {
// If small key already collected, drop recovery heart instead
if (CVar_GetS32("gNoHeartDrops", 0)) {
collectibleParams = -1;
}
else {
collectibleParams = ITEM00_HEART;
}
func_80078884(NA_SE_SY_TRE_BOX_APPEAR);
} else {
// Drops a small key and sets a collect flag
collectibleParams = ((this->dyna.actor.params & 0x3F) << 8) | ITEM00_SMALL_KEY;
func_80078884(NA_SE_SY_CORRECT_CHIME);
}
if (collectibleParams != -1) {
collectible = Item_DropCollectible(globalCtx, &spawnPos, collectibleParams);
if (collectible != NULL) {
collectible->actor.velocity.y = 15.0f;
collectible->actor.world.rot.y = this->dyna.actor.shape.rot.y;
}
}
Actor_Kill(&this->dyna.actor);
}
}
void BgHakaTubo_Update(Actor* thisx, GlobalContext* globalCtx) {
BgHakaTubo* this = (BgHakaTubo*)thisx;
this->actionFunc(this, globalCtx);
this->fireScroll++;
}
void BgHakaTubo_DrawFlameCircle(BgHakaTubo* this, GlobalContext* globalCtx) {
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
Matrix_Translate(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y + 235.0f, this->dyna.actor.world.pos.z,
MTXMODE_NEW);
Matrix_RotateY(this->dyna.actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY);
Matrix_Scale(0.07f, 0.04f, 0.07f, MTXMODE_APPLY);
gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 0, 170, 255, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 255, 255);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->fireScroll & 127, 0, 32, 64, 1, 0,
(this->fireScroll * -15) & 0xFF, 32, 64));
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gEffFireCircleDL);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void BgHakaTubo_Draw(Actor* thisx, GlobalContext* globalCtx) {
BgHakaTubo* this = (BgHakaTubo*)thisx;
Gfx_DrawDListOpa(globalCtx, object_haka_objects_DL_00FE40);
BgHakaTubo_DrawFlameCircle(this, globalCtx);
}