Shipwright/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c

383 lines
12 KiB
C

/*
* File: z_en_bom.c
* Overlay: ovl_En_Bom
* Description: Bomb
*/
#include "z_en_bom.h"
#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5)
void EnBom_Init(Actor* thisx, GlobalContext* globalCtx);
void EnBom_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnBom_Update(Actor* thisx, GlobalContext* globalCtx);
void EnBom_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnBom_Move(EnBom* this, GlobalContext* globalCtx);
void EnBom_WaitForRelease(EnBom* this, GlobalContext* globalCtx);
const ActorInit En_Bom_InitVars = {
ACTOR_EN_BOM,
ACTORCAT_EXPLOSIVE,
FLAGS,
OBJECT_GAMEPLAY_KEEP,
sizeof(EnBom),
(ActorFunc)EnBom_Init,
(ActorFunc)EnBom_Destroy,
(ActorFunc)EnBom_Update,
(ActorFunc)EnBom_Draw,
NULL,
};
static ColliderCylinderInit sCylinderInit = {
{
COLTYPE_HIT0,
AT_NONE,
AC_ON | AC_TYPE_PLAYER | AC_TYPE_OTHER,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_2,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK2,
{ 0x00000000, 0x00, 0x00 },
{ 0x0003F828, 0x00, 0x00 },
TOUCH_NONE,
BUMP_ON,
OCELEM_ON,
},
{ 6, 11, 14, { 0, 0, 0 } },
};
static ColliderJntSphElementInit sJntSphElementsInit[1] = {
{
{
ELEMTYPE_UNK0,
{ 0x00000008, 0x00, 0x08 },
{ 0x00000000, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NONE,
BUMP_NONE,
OCELEM_NONE,
},
{ 0, { { 0, 0, 0 }, 0 }, 100 },
},
};
static ColliderJntSphInit sJntSphInit = {
{
COLTYPE_HIT0,
AT_ON | AT_TYPE_ALL,
AC_NONE,
OC1_NONE,
OC2_NONE,
COLSHAPE_JNTSPH,
},
1,
sJntSphElementsInit,
};
static InitChainEntry sInitChain[] = {
ICHAIN_VEC3F(scale, 0, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE),
ICHAIN_F32_DIV1000(gravity, -4000, ICHAIN_STOP),
};
void EnBom_SetupAction(EnBom* this, EnBomActionFunc actionFunc) {
this->actionFunc = actionFunc;
}
void EnBom_Init(Actor* thisx, GlobalContext* globalCtx) {
EnBom* this = (EnBom*)thisx;
Actor_ProcessInitChain(thisx, sInitChain);
ActorShape_Init(&thisx->shape, 700.0f, ActorShadow_DrawCircle, 16.0f);
thisx->colChkInfo.mass = 200;
thisx->colChkInfo.cylRadius = 5;
thisx->colChkInfo.cylHeight = 10;
this->timer = 70;
this->flashSpeedScale = 7;
Collider_InitCylinder(globalCtx, &this->bombCollider);
Collider_InitJntSph(globalCtx, &this->explosionCollider);
Collider_SetCylinder(globalCtx, &this->bombCollider, thisx, &sCylinderInit);
Collider_SetJntSph(globalCtx, &this->explosionCollider, thisx, &sJntSphInit, &this->explosionColliderItems[0]);
this->explosionColliderItems[0].info.toucher.damage += (thisx->shape.rot.z & 0xFF00) >> 8;
thisx->shape.rot.z &= 0xFF;
if (thisx->shape.rot.z & 0x80) {
thisx->shape.rot.z |= 0xFF00;
}
EnBom_SetupAction(this, EnBom_Move);
}
void EnBom_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnBom* this = (EnBom*)thisx;
Collider_DestroyJntSph(globalCtx, &this->explosionCollider);
Collider_DestroyCylinder(globalCtx, &this->bombCollider);
}
void EnBom_Move(EnBom* this, GlobalContext* globalCtx) {
// if bomb has a parent actor, the bomb hasnt been released yet
if (Actor_HasParent(&this->actor, globalCtx)) {
EnBom_SetupAction(this, EnBom_WaitForRelease);
this->actor.room = -1;
return;
}
if ((this->actor.velocity.y > 0.0f) && (this->actor.bgCheckFlags & 0x10)) {
this->actor.velocity.y = -this->actor.velocity.y;
}
// rebound bomb off the wall it hits
if ((this->actor.speedXZ != 0.0f) && (this->actor.bgCheckFlags & 8)) {
if (ABS((s16)(this->actor.wallYaw - this->actor.world.rot.y)) > 0x4000) {
this->actor.world.rot.y = ((this->actor.wallYaw - this->actor.world.rot.y) + this->actor.wallYaw) - 0x8000;
}
Audio_PlayActorSound2(&this->actor, NA_SE_EV_BOMB_BOUND);
Actor_MoveForward(&this->actor);
this->actor.speedXZ *= 0.7f;
this->actor.bgCheckFlags &= ~8;
}
if (!(this->actor.bgCheckFlags & 1)) {
Math_StepToF(&this->actor.speedXZ, 0.0f, 0.08f);
} else {
Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f);
if ((this->actor.bgCheckFlags & 2) && (this->actor.velocity.y < -3.0f)) {
func_8002F850(globalCtx, &this->actor);
this->actor.velocity.y *= -0.3f;
this->actor.bgCheckFlags &= ~2;
} else if (this->timer >= 4) {
func_8002F580(&this->actor, globalCtx);
}
}
Actor_MoveForward(&this->actor);
}
void EnBom_WaitForRelease(EnBom* this, GlobalContext* globalCtx) {
// if parent is NULL bomb has been released
if (Actor_HasNoParent(&this->actor, globalCtx)) {
EnBom_SetupAction(this, EnBom_Move);
EnBom_Move(this, globalCtx);
}
}
void EnBom_Explode(EnBom* this, GlobalContext* globalCtx) {
Player* player;
if (this->explosionCollider.elements[0].dim.modelSphere.radius == 0) {
this->actor.flags |= ACTOR_FLAG_5;
func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96);
}
this->explosionCollider.elements[0].dim.worldSphere.radius += this->actor.shape.rot.z + 8;
if (this->actor.params == BOMB_EXPLOSION) {
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->explosionCollider.base);
}
if (globalCtx->envCtx.adjLight1Color[0] != 0) {
globalCtx->envCtx.adjLight1Color[0] -= 25;
}
if (globalCtx->envCtx.adjLight1Color[1] != 0) {
globalCtx->envCtx.adjLight1Color[1] -= 25;
}
if (globalCtx->envCtx.adjLight1Color[2] != 0) {
globalCtx->envCtx.adjLight1Color[2] -= 25;
}
if (globalCtx->envCtx.adjAmbientColor[0] != 0) {
globalCtx->envCtx.adjAmbientColor[0] -= 25;
}
if (globalCtx->envCtx.adjAmbientColor[1] != 0) {
globalCtx->envCtx.adjAmbientColor[1] -= 25;
}
if (globalCtx->envCtx.adjAmbientColor[2] != 0) {
globalCtx->envCtx.adjAmbientColor[2] -= 25;
}
if (this->timer == 0) {
player = GET_PLAYER(globalCtx);
if ((player->stateFlags1 & 0x800) && (player->heldActor == &this->actor)) {
player->actor.child = NULL;
player->heldActor = NULL;
player->interactRangeActor = NULL;
player->stateFlags1 &= ~0x800;
}
Actor_Kill(&this->actor);
}
}
void EnBom_Update(Actor* thisx, GlobalContext* globalCtx2) {
Vec3f effVelocity = { 0.0f, 0.0f, 0.0f };
Vec3f bomb2Accel = { 0.0f, 0.1f, 0.0f };
Vec3f effAccel = { 0.0f, 0.0f, 0.0f };
Vec3f effPos;
Vec3f dustAccel = { 0.0f, 0.6f, 0.0f };
Color_RGBA8 dustColor = { 255, 255, 255, 255 };
s32 pad;
GlobalContext* globalCtx = globalCtx2;
EnBom* this = (EnBom*)thisx;
thisx->gravity = -1.2f;
if (this->timer != 0) {
this->timer--;
}
if (this->timer == 67) {
Audio_PlayActorSound2(thisx, NA_SE_PL_TAKE_OUT_SHIELD);
Actor_SetScale(thisx, 0.01f);
}
if ((thisx->xzDistToPlayer >= 20.0f) || (ABS(thisx->yDistToPlayer) >= 80.0f)) {
this->bumpOn = true;
}
this->actionFunc(this, globalCtx);
Actor_UpdateBgCheckInfo(globalCtx, thisx, 5.0f, 10.0f, 15.0f, 0x1F);
if (thisx->params == BOMB_BODY) {
if (this->timer < 63) {
dustAccel.y = 0.2f;
// spawn spark effect on even frames
effPos = thisx->world.pos;
effPos.y += 17.0f;
if ((globalCtx->gameplayFrames % 2) == 0) {
EffectSsGSpk_SpawnFuse(globalCtx, thisx, &effPos, &effVelocity, &effAccel);
}
Audio_PlayActorSound2(thisx, NA_SE_IT_BOMB_IGNIT - SFX_FLAG);
effPos.y += 3.0f;
func_8002829C(globalCtx, &effPos, &effVelocity, &dustAccel, &dustColor, &dustColor, 50, 5);
}
if ((this->bombCollider.base.acFlags & AC_HIT) || ((this->bombCollider.base.ocFlags1 & OC1_HIT) &&
(this->bombCollider.base.oc->category == ACTORCAT_ENEMY))) {
this->timer = 0;
thisx->shape.rot.z = 0;
} else {
// if a lit stick touches the bomb, set timer to 100
// these bombs never have a timer over 70, so this isnt used
if ((this->timer > 100) && Player_IsBurningStickInRange(globalCtx, &thisx->world.pos, 30.0f, 50.0f)) {
this->timer = 100;
}
}
dustAccel.y = 0.2f;
effPos = thisx->world.pos;
effPos.y += 10.0f;
// double bomb flash speed and adjust red color at certain times during the countdown
if ((this->timer == 3) || (this->timer == 20) || (this->timer == 40)) {
thisx->shape.rot.z = 0;
this->flashSpeedScale >>= 1;
}
if ((this->timer < 100) && ((this->timer & (this->flashSpeedScale + 1)) != 0)) {
Math_SmoothStepToF(&this->flashIntensity, 140.0f, 1.0f, 140.0f / this->flashSpeedScale, 0.0f);
} else {
Math_SmoothStepToF(&this->flashIntensity, 0.0f, 1.0f, 140.0f / this->flashSpeedScale, 0.0f);
}
if (this->timer < 3) {
Actor_SetScale(thisx, thisx->scale.x + 0.002f);
}
if (this->timer == 0) {
effPos = thisx->world.pos;
effPos.y += 10.0f;
if (Actor_HasParent(thisx, globalCtx)) {
effPos.y += 30.0f;
}
EffectSsBomb2_SpawnLayered(globalCtx, &effPos, &effVelocity, &bomb2Accel, 100,
(thisx->shape.rot.z * 6) + 19);
effPos.y = thisx->floorHeight;
if (thisx->floorHeight > BGCHECK_Y_MIN) {
EffectSsBlast_SpawnWhiteShockwave(globalCtx, &effPos, &effVelocity, &effAccel);
}
Audio_PlayActorSound2(thisx, NA_SE_IT_BOMB_EXPLOSION);
globalCtx->envCtx.adjLight1Color[0] = globalCtx->envCtx.adjLight1Color[1] =
globalCtx->envCtx.adjLight1Color[2] = 250;
globalCtx->envCtx.adjAmbientColor[0] = globalCtx->envCtx.adjAmbientColor[1] =
globalCtx->envCtx.adjAmbientColor[2] = 250;
Camera_AddQuake(&globalCtx->mainCamera, 2, 0xB, 8);
thisx->params = BOMB_EXPLOSION;
this->timer = 10;
thisx->flags |= ACTOR_FLAG_5;
EnBom_SetupAction(this, EnBom_Explode);
}
}
Actor_SetFocus(thisx, 20.0f);
if (thisx->params <= BOMB_BODY) {
Collider_UpdateCylinder(thisx, &this->bombCollider);
// if link is not holding the bomb anymore and bump conditions are met, subscribe to OC
if (!Actor_HasParent(thisx, globalCtx) && this->bumpOn) {
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bombCollider.base);
}
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bombCollider.base);
}
if ((thisx->scale.x >= 0.01f) && (thisx->params != BOMB_EXPLOSION)) {
if (thisx->yDistToWater >= 20.0f) {
EffectSsDeadSound_SpawnStationary(globalCtx, &thisx->projectedPos, NA_SE_IT_BOMB_UNEXPLOSION, true,
DEADSOUND_REPEAT_MODE_OFF, 10);
Actor_Kill(thisx);
return;
}
if (thisx->bgCheckFlags & 0x40) {
thisx->bgCheckFlags &= ~0x40;
Audio_PlayActorSound2(thisx, NA_SE_EV_BOMB_DROP_WATER);
}
}
}
void EnBom_Draw(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnBom* this = (EnBom*)thisx;
OPEN_DISPS(globalCtx->state.gfxCtx);
if (thisx->params == BOMB_BODY) {
func_80093D18(globalCtx->state.gfxCtx);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
func_8002EBCC(thisx, globalCtx, 0);
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gBombCapDL);
Matrix_RotateZYX(0x4000, 0, 0, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gDPPipeSync(POLY_OPA_DISP++);
gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->flashIntensity, 0, 40, 255);
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, (s16)this->flashIntensity, 0, 40, 255);
gSPDisplayList(POLY_OPA_DISP++, gBombBodyDL);
Collider_UpdateSpheres(0, &this->explosionCollider);
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}