Shipwright/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c

526 lines
20 KiB
C

/*
* File: z_en_arrow.c
* Overlay: ovl_En_Arrow
* Description: Arrow, Deku Seed, and Deku Nut Projectile
*/
#include "z_en_arrow.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_gi_nuts/object_gi_nuts.h"
#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5)
void EnArrow_Init(Actor* thisx, GlobalContext* globalCtx);
void EnArrow_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnArrow_Update(Actor* thisx, GlobalContext* globalCtx);
void EnArrow_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnArrow_Shoot(EnArrow* this, GlobalContext* globalCtx);
void EnArrow_Fly(EnArrow* this, GlobalContext* globalCtx);
void func_809B45E0(EnArrow* this, GlobalContext* globalCtx);
void func_809B4640(EnArrow* this, GlobalContext* globalCtx);
const ActorInit En_Arrow_InitVars = {
ACTOR_EN_ARROW,
ACTORCAT_ITEMACTION,
FLAGS,
OBJECT_GAMEPLAY_KEEP,
sizeof(EnArrow),
(ActorFunc)EnArrow_Init,
(ActorFunc)EnArrow_Destroy,
(ActorFunc)EnArrow_Update,
(ActorFunc)EnArrow_Draw,
NULL,
};
static ColliderQuadInit sColliderInit = {
{
COLTYPE_NONE,
AT_ON | AT_TYPE_PLAYER,
AC_NONE,
OC1_NONE,
OC2_TYPE_PLAYER,
COLSHAPE_QUAD,
},
{
ELEMTYPE_UNK2,
{ 0x00000020, 0x00, 0x01 },
{ 0xFFCFFFFF, 0x00, 0x00 },
TOUCH_ON | TOUCH_NEAREST | TOUCH_SFX_NONE,
BUMP_NONE,
OCELEM_NONE,
},
{ { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } },
};
static InitChainEntry sInitChain[] = {
ICHAIN_F32(minVelocityY, -150, ICHAIN_STOP),
};
void EnArrow_SetupAction(EnArrow* this, EnArrowActionFunc actionFunc) {
this->actionFunc = actionFunc;
}
void EnArrow_Init(Actor* thisx, GlobalContext* globalCtx) {
static EffectBlureInit2 blureNormal = {
0, 4, 0, { 0, 255, 200, 255 }, { 0, 255, 255, 255 }, { 0, 255, 200, 0 }, { 0, 255, 255, 0 }, 16,
0, 1, 0, { 255, 255, 170, 255 }, { 0, 150, 0, 0 }, 0,
};
static EffectBlureInit2 blureFire = {
0, 4, 0, { 0, 255, 200, 255 }, { 0, 255, 255, 255 }, { 0, 255, 200, 0 }, { 0, 255, 255, 0 }, 16,
0, 1, 0, { 255, 200, 0, 255 }, { 255, 0, 0, 0 }, 0,
};
static EffectBlureInit2 blureIce = {
0, 4, 0, { 0, 255, 200, 255 }, { 0, 255, 255, 255 }, { 0, 255, 200, 0 }, { 0, 255, 255, 0 }, 16,
0, 1, 0, { 170, 255, 255, 255 }, { 0, 100, 255, 0 }, 0,
};
static EffectBlureInit2 blureLight = {
0, 4, 0, { 0, 255, 200, 255 }, { 0, 255, 255, 255 }, { 0, 255, 200, 0 }, { 0, 255, 255, 0 }, 16,
0, 1, 0, { 255, 255, 170, 255 }, { 255, 255, 0, 0 }, 0,
};
static u32 dmgFlags[] = {
0x00000800, 0x00000020, 0x00000020, 0x00000800, 0x00001000,
0x00002000, 0x00010000, 0x00004000, 0x00008000, 0x00000004,
};
EnArrow* this = (EnArrow*)thisx;
Color_RGBA8 Arrow_env_ori = { 0, 150, 0, 0 };
Color_RGBA8 Arrow_col_ori = { 255, 255, 170, 255 };
Color_RGBA8 Light_env_ori = { 255, 255, 0, 255 };
Color_RGBA8 Light_col_ori = { 255, 255, 170, 0 };
Color_RGBA8 Fire_env_ori = { 255, 0, 0, 255 };
Color_RGBA8 Fire_col_ori = { 255, 200, 0, 0 };
Color_RGBA8 Ice_env_ori = { 0, 0, 255, 255 };
Color_RGBA8 Ice_col_ori = { 170, 255, 255, 0 };
if (CVar_GetS32("gUseArrowsCol", 0) != 0) {
blureNormal.altPrimColor = CVar_GetRGBA("gNormalArrowCol", Arrow_col_ori);
blureNormal.altEnvColor = CVar_GetRGBA("gNormalArrowColEnv", Arrow_env_ori);
blureFire.altPrimColor = CVar_GetRGBA("gFireArrowCol", Fire_col_ori);
blureFire.altEnvColor = CVar_GetRGBA("gFireArrowColEnv", Fire_env_ori);
blureIce.altPrimColor = CVar_GetRGBA("gIceArrowCol", Ice_col_ori);
blureIce.altEnvColor = CVar_GetRGBA("gIceArrowColEnv", Ice_env_ori);
blureLight.altPrimColor = CVar_GetRGBA("gLightArrowCol", Light_col_ori);
blureLight.altEnvColor = CVar_GetRGBA("gLightArrowColEnv", Light_env_ori);
//make sure the alpha values are correct.
blureNormal.altPrimColor.a = 255;
blureNormal.altEnvColor.a = 0;
blureFire.altPrimColor.a = 255;
blureFire.altEnvColor.a = 0;
blureIce.altPrimColor.a = 255;
blureIce.altEnvColor.a = 0;
blureLight.altPrimColor.a = 255;
blureLight.altEnvColor.a = 0;
}
Actor_ProcessInitChain(&this->actor, sInitChain);
if (this->actor.params == ARROW_CS_NUT) {
this->isCsNut = true;
this->actor.params = ARROW_NUT;
}
if (this->actor.params <= ARROW_SEED) {
if (this->actor.params <= ARROW_0E) {
SkelAnime_Init(globalCtx, &this->skelAnime, &gArrowSkel, &gArrow2Anim, NULL, NULL, 0);
}
if (this->actor.params <= ARROW_NORMAL) {
if (this->actor.params == ARROW_NORMAL_HORSE) {
blureNormal.elemDuration = 4;
} else {
blureNormal.elemDuration = 16;
}
Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE2, 0, 0, &blureNormal);
} else if (this->actor.params == ARROW_FIRE) {
Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE2, 0, 0, &blureFire);
} else if (this->actor.params == ARROW_ICE) {
Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE2, 0, 0, &blureIce);
} else if (this->actor.params == ARROW_LIGHT) {
Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE2, 0, 0, &blureLight);
}
Collider_InitQuad(globalCtx, &this->collider);
Collider_SetQuad(globalCtx, &this->collider, &this->actor, &sColliderInit);
if (this->actor.params <= ARROW_NORMAL) {
this->collider.info.toucherFlags &= ~0x18;
this->collider.info.toucherFlags |= 0;
}
if (this->actor.params < 0) {
this->collider.base.atFlags = (AT_ON | AT_TYPE_ENEMY);
} else if (this->actor.params <= ARROW_SEED) {
this->collider.info.toucher.dmgFlags = dmgFlags[this->actor.params];
LOG_HEX("this->at_info.cl_elem.at_btl_info.at_type", this->collider.info.toucher.dmgFlags);
}
}
EnArrow_SetupAction(this, EnArrow_Shoot);
}
void EnArrow_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnArrow* this = (EnArrow*)thisx;
if (this->actor.params <= ARROW_LIGHT) {
Effect_Delete(globalCtx, this->effectIndex);
}
SkelAnime_Free(&this->skelAnime, globalCtx);
Collider_DestroyQuad(globalCtx, &this->collider);
if ((this->hitActor != NULL) && (this->hitActor->update != NULL)) {
this->hitActor->flags &= ~ACTOR_FLAG_15;
}
}
void EnArrow_Shoot(EnArrow* this, GlobalContext* globalCtx) {
Player* player = GET_PLAYER(globalCtx);
if (this->actor.parent == NULL) {
if ((this->actor.params != ARROW_NUT) && (player->unk_A73 == 0)) {
Actor_Kill(&this->actor);
return;
}
switch (this->actor.params) {
case ARROW_SEED:
func_8002F7DC(&player->actor, NA_SE_IT_SLING_SHOT);
break;
case ARROW_NORMAL_LIT:
case ARROW_NORMAL_HORSE:
case ARROW_NORMAL:
func_8002F7DC(&player->actor, NA_SE_IT_ARROW_SHOT);
break;
case ARROW_FIRE:
case ARROW_ICE:
case ARROW_LIGHT:
func_8002F7DC(&player->actor, NA_SE_IT_MAGIC_ARROW_SHOT);
break;
}
EnArrow_SetupAction(this, EnArrow_Fly);
Math_Vec3f_Copy(&this->unk_210, &this->actor.world.pos);
if (this->actor.params >= ARROW_SEED) {
func_8002D9A4(&this->actor, 80.0f);
this->timer = 15;
this->actor.shape.rot.x = this->actor.shape.rot.y = this->actor.shape.rot.z = 0;
} else {
func_8002D9A4(&this->actor, 150.0f);
this->timer = 12;
}
}
}
void func_809B3CEC(GlobalContext* globalCtx, EnArrow* this) {
EnArrow_SetupAction(this, func_809B4640);
Animation_PlayOnce(&this->skelAnime, &gArrow1Anim);
this->actor.world.rot.y += (s32)(24576.0f * (Rand_ZeroOne() - 0.5f)) + 0x8000;
this->actor.velocity.y += (this->actor.speedXZ * (0.4f + (0.4f * Rand_ZeroOne())));
this->actor.speedXZ *= (0.04f + 0.3f * Rand_ZeroOne());
this->timer = 50;
this->actor.gravity = -1.5f;
}
void EnArrow_CarryActor(EnArrow* this, GlobalContext* globalCtx) {
CollisionPoly* hitPoly;
Vec3f posDiffLastFrame;
Vec3f actorNextPos;
Vec3f hitPos;
f32 temp_f12;
f32 scale;
s32 bgId;
Math_Vec3f_Diff(&this->actor.world.pos, &this->unk_210, &posDiffLastFrame);
temp_f12 = ((this->actor.world.pos.x - this->hitActor->world.pos.x) * posDiffLastFrame.x) +
((this->actor.world.pos.y - this->hitActor->world.pos.y) * posDiffLastFrame.y) +
((this->actor.world.pos.z - this->hitActor->world.pos.z) * posDiffLastFrame.z);
if (!(temp_f12 < 0.0f)) {
scale = Math3D_Vec3fMagnitudeSq(&posDiffLastFrame);
if (!(scale < 1.0f)) {
scale = temp_f12 / scale;
Math_Vec3f_Scale(&posDiffLastFrame, scale);
Math_Vec3f_Sum(&this->hitActor->world.pos, &posDiffLastFrame, &actorNextPos);
if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->hitActor->world.pos, &actorNextPos, &hitPos,
&hitPoly, true, true, true, true, &bgId)) {
this->hitActor->world.pos.x = hitPos.x + ((actorNextPos.x <= hitPos.x) ? 1.0f : -1.0f);
this->hitActor->world.pos.y = hitPos.y + ((actorNextPos.y <= hitPos.y) ? 1.0f : -1.0f);
this->hitActor->world.pos.z = hitPos.z + ((actorNextPos.z <= hitPos.z) ? 1.0f : -1.0f);
} else {
Math_Vec3f_Copy(&this->hitActor->world.pos, &actorNextPos);
}
}
}
}
void EnArrow_Fly(EnArrow* this, GlobalContext* globalCtx) {
CollisionPoly* hitPoly;
s32 bgId;
Vec3f hitPoint;
Vec3f posCopy;
s32 atTouched;
u16 sfxId;
Actor* hitActor;
Vec3f sp60;
Vec3f sp54;
if (DECR(this->timer) == 0) {
Actor_Kill(&this->actor);
return;
}
if (this->timer < 7.2000003f) {
this->actor.gravity = -0.4f;
}
atTouched = (this->actor.params != ARROW_NORMAL_LIT) && (this->actor.params <= ARROW_SEED) &&
(this->collider.base.atFlags & AT_HIT);
if (atTouched || this->touchedPoly) {
if (this->actor.params >= ARROW_SEED) {
if (atTouched) {
this->actor.world.pos.x = (this->actor.world.pos.x + this->actor.prevPos.x) * 0.5f;
this->actor.world.pos.y = (this->actor.world.pos.y + this->actor.prevPos.y) * 0.5f;
this->actor.world.pos.z = (this->actor.world.pos.z + this->actor.prevPos.z) * 0.5f;
}
if (this->actor.params == ARROW_NUT) {
iREG(50) = -1;
Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_M_FIRE1, this->actor.world.pos.x,
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0);
sfxId = NA_SE_IT_DEKU;
} else {
sfxId = NA_SE_IT_SLING_REFLECT;
}
EffectSsStone1_Spawn(globalCtx, &this->actor.world.pos, 0);
SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, sfxId);
Actor_Kill(&this->actor);
} else {
EffectSsHitMark_SpawnCustomScale(globalCtx, 0, 150, &this->actor.world.pos);
if (atTouched && (this->collider.info.atHitInfo->elemType != ELEMTYPE_UNK4)) {
hitActor = this->collider.base.at;
if ((hitActor->update != NULL) && (!(this->collider.base.atFlags & AT_BOUNCED)) &&
(hitActor->flags & ACTOR_FLAG_14)) {
this->hitActor = hitActor;
EnArrow_CarryActor(this, globalCtx);
Math_Vec3f_Diff(&hitActor->world.pos, &this->actor.world.pos, &this->unk_250);
hitActor->flags |= ACTOR_FLAG_15;
this->collider.base.atFlags &= ~AT_HIT;
this->actor.speedXZ /= 2.0f;
this->actor.velocity.y /= 2.0f;
} else {
this->hitFlags |= 1;
this->hitFlags |= 2;
if (this->collider.info.atHitInfo->bumperFlags & 2) {
this->actor.world.pos.x = this->collider.info.atHitInfo->bumper.hitPos.x;
this->actor.world.pos.y = this->collider.info.atHitInfo->bumper.hitPos.y;
this->actor.world.pos.z = this->collider.info.atHitInfo->bumper.hitPos.z;
}
func_809B3CEC(globalCtx, this);
Audio_PlayActorSound2(&this->actor, NA_SE_IT_ARROW_STICK_CRE);
}
} else if (this->touchedPoly) {
EnArrow_SetupAction(this, func_809B45E0);
Animation_PlayOnce(&this->skelAnime, &gArrow2Anim);
if (this->actor.params >= ARROW_NORMAL_LIT) {
this->timer = 60;
} else {
this->timer = 20;
}
Audio_PlayActorSound2(&this->actor, NA_SE_IT_ARROW_STICK_OBJ);
this->hitFlags |= 1;
}
}
} else {
Math_Vec3f_Copy(&this->unk_210, &this->actor.world.pos);
Actor_MoveForward(&this->actor);
if ((this->touchedPoly =
BgCheck_ProjectileLineTest(&globalCtx->colCtx, &this->actor.prevPos, &this->actor.world.pos, &hitPoint,
&this->actor.wallPoly, true, true, true, true, &bgId))) {
func_8002F9EC(globalCtx, &this->actor, this->actor.wallPoly, bgId, &hitPoint);
Math_Vec3f_Copy(&posCopy, &this->actor.world.pos);
Math_Vec3f_Copy(&this->actor.world.pos, &hitPoint);
}
if (this->actor.params <= ARROW_0E) {
this->actor.shape.rot.x = Math_Atan2S(this->actor.speedXZ, -this->actor.velocity.y);
}
}
if (this->hitActor != NULL) {
if (this->hitActor->update != NULL) {
Math_Vec3f_Sum(&this->unk_210, &this->unk_250, &sp60);
Math_Vec3f_Sum(&this->actor.world.pos, &this->unk_250, &sp54);
if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &sp60, &sp54, &hitPoint, &hitPoly, true, true, true, true,
&bgId)) {
this->hitActor->world.pos.x = hitPoint.x + ((sp54.x <= hitPoint.x) ? 1.0f : -1.0f);
this->hitActor->world.pos.y = hitPoint.y + ((sp54.y <= hitPoint.y) ? 1.0f : -1.0f);
this->hitActor->world.pos.z = hitPoint.z + ((sp54.z <= hitPoint.z) ? 1.0f : -1.0f);
Math_Vec3f_Diff(&this->hitActor->world.pos, &this->actor.world.pos, &this->unk_250);
this->hitActor->flags &= ~ACTOR_FLAG_15;
this->hitActor = NULL;
} else {
Math_Vec3f_Sum(&this->actor.world.pos, &this->unk_250, &this->hitActor->world.pos);
}
if (this->touchedPoly && (this->hitActor != NULL)) {
this->hitActor->flags &= ~ACTOR_FLAG_15;
this->hitActor = NULL;
}
} else {
this->hitActor = NULL;
}
}
}
void func_809B45E0(EnArrow* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
if (DECR(this->timer) == 0) {
Actor_Kill(&this->actor);
}
}
void func_809B4640(EnArrow* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
Actor_MoveForward(&this->actor);
if (DECR(this->timer) == 0) {
Actor_Kill(&this->actor);
}
}
void EnArrow_Update(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnArrow* this = (EnArrow*)thisx;
Player* player = GET_PLAYER(globalCtx);
if (this->isCsNut || ((this->actor.params >= ARROW_NORMAL_LIT) && (player->unk_A73 != 0)) ||
!Player_InBlockingCsMode(globalCtx, player)) {
this->actionFunc(this, globalCtx);
}
if ((this->actor.params >= ARROW_FIRE) && (this->actor.params <= ARROW_0E)) {
s16 elementalActorIds[] = { ACTOR_ARROW_FIRE, ACTOR_ARROW_ICE, ACTOR_ARROW_LIGHT,
ACTOR_ARROW_FIRE, ACTOR_ARROW_FIRE, ACTOR_ARROW_FIRE };
if (this->actor.child == NULL) {
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, elementalActorIds[this->actor.params - 3],
this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0);
}
} else if (this->actor.params == ARROW_NORMAL_LIT) {
static Vec3f velocity = { 0.0f, 0.5f, 0.0f };
static Vec3f accel = { 0.0f, 0.5f, 0.0f };
static Color_RGBA8 primColor = { 255, 255, 100, 255 };
static Color_RGBA8 envColor = { 255, 50, 0, 0 };
// spawn dust for the flame
func_8002836C(globalCtx, &this->unk_21C, &velocity, &accel, &primColor, &envColor, 100, 0, 8);
}
}
void func_809B4800(EnArrow* this, GlobalContext* globalCtx) {
static Vec3f D_809B4E88 = { 0.0f, 400.0f, 1500.0f };
static Vec3f D_809B4E94 = { 0.0f, -400.0f, 1500.0f };
static Vec3f D_809B4EA0 = { 0.0f, 0.0f, -300.0f };
Vec3f sp44;
Vec3f sp38;
s32 addBlureVertex;
Matrix_MultVec3f(&D_809B4EA0, &this->unk_21C);
if (EnArrow_Fly == this->actionFunc) {
Matrix_MultVec3f(&D_809B4E88, &sp44);
Matrix_MultVec3f(&D_809B4E94, &sp38);
if (this->actor.params <= ARROW_SEED) {
addBlureVertex = this->actor.params <= ARROW_LIGHT;
if (this->hitActor == NULL) {
addBlureVertex &= func_80090480(globalCtx, &this->collider, &this->weaponInfo, &sp44, &sp38);
} else {
if (addBlureVertex) {
if ((sp44.x == this->weaponInfo.tip.x) && (sp44.y == this->weaponInfo.tip.y) &&
(sp44.z == this->weaponInfo.tip.z) && (sp38.x == this->weaponInfo.base.x) &&
(sp38.y == this->weaponInfo.base.y) && (sp38.z == this->weaponInfo.base.z)) {
addBlureVertex = false;
}
}
}
if (addBlureVertex) {
EffectBlure_AddVertex(Effect_GetByIndex(this->effectIndex), &sp44, &sp38);
}
}
}
}
void EnArrow_Draw(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnArrow* this = (EnArrow*)thisx;
u8 alpha;
f32 scale;
if (this->actor.params <= ARROW_0E) {
func_80093D18(globalCtx->state.gfxCtx);
SkelAnime_DrawLod(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, this,
(this->actor.projectedPos.z < MREG(95)) ? 0 : 1);
} else if (this->actor.speedXZ != 0.0f) {
alpha = (Math_CosS(this->timer * 5000) * 127.5f) + 127.5f;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093C14(globalCtx->state.gfxCtx);
if (this->actor.params == ARROW_SEED) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 255, alpha);
scale = 50.0f;
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 12, 0, 0, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 250, 250, 0, alpha);
scale = 150.0f;
}
Matrix_Push();
Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY);
// redundant check because this is contained in an if block for non-zero speed
Matrix_RotateZ(
(this->actor.speedXZ == 0.0f) ? 0.0f : ((globalCtx->gameplayFrames & 0xFF) * 4000) * (M_PI / 0x8000),
MTXMODE_APPLY);
Matrix_Scale(scale, scale, scale, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gEffSparklesDL);
Matrix_Pop();
Matrix_RotateY(this->actor.world.rot.y * (M_PI / 0x8000), MTXMODE_APPLY);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
func_809B4800(this, globalCtx);
}