Shipwright/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c

899 lines
29 KiB
C

#include "z_en_fz.h"
#include "objects/object_fz/object_fz.h"
#include "soh/frame_interpolation.h"
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_10)
void EnFz_Init(Actor* thisx, GlobalContext* globalCtx);
void EnFz_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnFz_Update(Actor* thisx, GlobalContext* globalCtx);
void EnFz_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnFz_UpdateTargetPos(EnFz* this, GlobalContext* globalCtx);
// Stationary Freezard
void EnFz_SetupBlowSmokeStationary(EnFz* this);
void EnFz_BlowSmokeStationary(EnFz* this, GlobalContext* globalCtx);
// Moving Freezard that can vanish and reappear
void EnFz_Wait(EnFz* this, GlobalContext* globalCtx);
void EnFz_SetupAppear(EnFz* this);
void EnFz_Appear(EnFz* this, GlobalContext* globalCtx);
void EnFz_SetupAimForMove(EnFz* this);
void EnFz_AimForMove(EnFz* this, GlobalContext* globalCtx);
void EnFz_SetupMoveTowardsPlayer(EnFz* this);
void EnFz_MoveTowardsPlayer(EnFz* this, GlobalContext* globalCtx);
void EnFz_SetupAimForFreeze(EnFz* this);
void EnFz_AimForFreeze(EnFz* this, GlobalContext* globalCtx);
void EnFz_SetupBlowSmoke(EnFz* this, GlobalContext* globalCtx);
void EnFz_BlowSmoke(EnFz* this, GlobalContext* globalCtx);
void EnFz_SetupDisappear(EnFz* this);
void EnFz_Disappear(EnFz* this, GlobalContext* globalCtx);
void EnFz_SetupWait(EnFz* this);
// Killed with fire source
void EnFz_SetupMelt(EnFz* this);
void EnFz_Melt(EnFz* this, GlobalContext* globalCtx);
// Death
void EnFz_SetupDespawn(EnFz* this, GlobalContext* globalCtx);
void EnFz_Despawn(EnFz* this, GlobalContext* globalCtx);
// Ice Smoke Effects
void EnFz_SpawnIceSmokeNoFreeze(EnFz* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 xyScale);
void EnFz_SpawnIceSmokeFreeze(EnFz* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 xyScale, f32 xyScaleTarget,
s16 primAlpha, u8 isTimerMod8);
void EnFz_UpdateIceSmoke(EnFz* this, GlobalContext* globalCtx);
void EnFz_DrawIceSmoke(EnFz* this, GlobalContext* globalCtx);
const ActorInit En_Fz_InitVars = {
ACTOR_EN_FZ,
ACTORCAT_ENEMY,
FLAGS,
OBJECT_FZ,
sizeof(EnFz),
(ActorFunc)EnFz_Init,
(ActorFunc)EnFz_Destroy,
(ActorFunc)EnFz_Update,
(ActorFunc)EnFz_Draw,
NULL,
};
static ColliderCylinderInitType1 sCylinderInit1 = {
{
COLTYPE_NONE,
AT_ON | AT_TYPE_ENEMY,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0xFFCFFFFF, 0x00, 0x00 },
{ 0xFFCE0FDB, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_ON | BUMP_HOOKABLE,
OCELEM_ON,
},
{ 30, 80, 0, { 0, 0, 0 } },
};
static ColliderCylinderInitType1 sCylinderInit2 = {
{
COLTYPE_METAL,
AT_NONE,
AC_ON | AC_HARD | AC_TYPE_PLAYER,
OC1_NONE,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0xFFCFFFFF, 0x00, 0x00 },
{ 0x0001F024, 0x00, 0x00 },
TOUCH_NONE,
BUMP_ON,
OCELEM_NONE,
},
{ 35, 80, 0, { 0, 0, 0 } },
};
static ColliderCylinderInitType1 sCylinderInit3 = {
{
COLTYPE_NONE,
AT_ON | AT_TYPE_ENEMY,
AC_NONE,
OC1_NONE,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0x20000000, 0x02, 0x08 },
{ 0x00000000, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_NONE,
OCELEM_NONE,
},
{ 20, 30, -15, { 0, 0, 0 } },
};
static DamageTable sDamageTable = {
/* Deku nut */ DMG_ENTRY(0, 0x0),
/* Deku stick */ DMG_ENTRY(0, 0xF),
/* Slingshot */ DMG_ENTRY(0, 0xF),
/* Explosive */ DMG_ENTRY(2, 0xF),
/* Boomerang */ DMG_ENTRY(0, 0xF),
/* Normal arrow */ DMG_ENTRY(0, 0xF),
/* Hammer swing */ DMG_ENTRY(2, 0xF),
/* Hookshot */ DMG_ENTRY(2, 0xF),
/* Kokiri sword */ DMG_ENTRY(0, 0xF),
/* Master sword */ DMG_ENTRY(2, 0xF),
/* Giant's Knife */ DMG_ENTRY(4, 0xF),
/* Fire arrow */ DMG_ENTRY(4, 0x2),
/* Ice arrow */ DMG_ENTRY(0, 0xF),
/* Light arrow */ DMG_ENTRY(0, 0xF),
/* Unk arrow 1 */ DMG_ENTRY(0, 0xF),
/* Unk arrow 2 */ DMG_ENTRY(0, 0xF),
/* Unk arrow 3 */ DMG_ENTRY(0, 0xF),
/* Fire magic */ DMG_ENTRY(4, 0x2),
/* Ice magic */ DMG_ENTRY(0, 0x0),
/* Light magic */ DMG_ENTRY(0, 0x0),
/* Shield */ DMG_ENTRY(0, 0x0),
/* Mirror Ray */ DMG_ENTRY(0, 0x0),
/* Kokiri spin */ DMG_ENTRY(0, 0xF),
/* Giant spin */ DMG_ENTRY(4, 0xF),
/* Master spin */ DMG_ENTRY(2, 0xF),
/* Kokiri jump */ DMG_ENTRY(0, 0xF),
/* Giant jump */ DMG_ENTRY(8, 0xF),
/* Master jump */ DMG_ENTRY(4, 0xF),
/* Unknown 1 */ DMG_ENTRY(0, 0x0),
/* Unblockable */ DMG_ENTRY(0, 0x0),
/* Hammer jump */ DMG_ENTRY(0, 0x0),
/* Unknown 2 */ DMG_ENTRY(0, 0x0),
};
static InitChainEntry sInitChain[] = {
ICHAIN_S8(naviEnemyId, 0x3B, ICHAIN_CONTINUE),
ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP),
};
void EnFz_Init(Actor* thisx, GlobalContext* globalCtx) {
EnFz* this = (EnFz*)thisx;
Actor_ProcessInitChain(&this->actor, sInitChain);
this->actor.colChkInfo.damageTable = &sDamageTable;
this->actor.colChkInfo.health = 6;
Collider_InitCylinder(globalCtx, &this->collider1);
Collider_SetCylinderType1(globalCtx, &this->collider1, &this->actor, &sCylinderInit1);
Collider_InitCylinder(globalCtx, &this->collider2);
Collider_SetCylinderType1(globalCtx, &this->collider2, &this->actor, &sCylinderInit2);
Collider_InitCylinder(globalCtx, &this->collider3);
Collider_SetCylinderType1(globalCtx, &this->collider3, &this->actor, &sCylinderInit3);
Actor_SetScale(&this->actor, 0.008f);
this->actor.colChkInfo.mass = MASS_IMMOVABLE;
this->actor.flags &= ~ACTOR_FLAG_0;
this->unusedTimer1 = 0;
this->unusedCounter = 0;
this->updateBgInfo = true;
this->isMoving = false;
this->isFreezing = false;
this->isActive = true;
this->isDespawning = false;
this->actor.speedXZ = 0.0f;
this->actor.gravity = 0.0f;
this->actor.velocity.y = 0.0f;
this->posOrigin.y = this->actor.world.pos.y;
this->iceSmokeFreezingSpawnHeight = this->actor.world.pos.y;
this->posOrigin.x = this->actor.world.pos.x;
this->posOrigin.z = this->actor.world.pos.z;
this->unusedFloat = 135.0f;
if (this->actor.params < 0) {
this->envAlpha = 0;
this->actor.scale.y = 0.0f;
EnFz_SetupWait(this);
} else {
this->envAlpha = 255;
EnFz_SetupBlowSmokeStationary(this);
}
EnFz_UpdateTargetPos(this, globalCtx);
}
void EnFz_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnFz* this = (EnFz*)thisx;
Collider_DestroyCylinder(globalCtx, &this->collider1);
Collider_DestroyCylinder(globalCtx, &this->collider2);
Collider_DestroyCylinder(globalCtx, &this->collider3);
}
void EnFz_UpdateTargetPos(EnFz* this, GlobalContext* globalCtx) {
Vec3f pos;
Vec3f hitPos;
Vec3f vec1;
s32 bgId;
CollisionPoly* hitPoly;
pos.x = this->actor.world.pos.x;
pos.y = this->actor.world.pos.y + 20.0f;
pos.z = this->actor.world.pos.z;
Matrix_Translate(pos.x, pos.y, pos.z, MTXMODE_NEW);
Matrix_RotateZYX(this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, MTXMODE_APPLY);
vec1.x = vec1.y = 0.0f;
vec1.z = 220.0f;
Matrix_MultVec3f(&vec1, &this->wallHitPos);
if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &pos, &this->wallHitPos, &hitPos, &hitPoly, true, false, false,
true, &bgId)) {
Math_Vec3f_Copy(&this->wallHitPos, &hitPos);
}
pos.x = this->actor.world.pos.x - this->wallHitPos.x;
pos.z = this->actor.world.pos.z - this->wallHitPos.z;
this->distToTargetSq = SQ(pos.x) + SQ(pos.z);
}
s32 EnFz_ReachedTarget(EnFz* this, Vec3f* vec) {
if (this->distToTargetSq <= (SQ(this->actor.world.pos.x - vec->x) + SQ(this->actor.world.pos.z - vec->z))) {
return true;
} else {
return false;
}
}
void EnFz_Damaged(EnFz* this, GlobalContext* globalCtx, Vec3f* vec, s32 numEffects, f32 unkFloat) {
s32 i;
Vec3f pos;
Vec3f vel;
Vec3f accel;
Color_RGBA8 primColor;
Color_RGBA8 envColor;
f32 scale;
s32 life;
primColor.r = 155;
primColor.g = 255;
primColor.b = 255;
primColor.a = 255;
envColor.r = 200;
envColor.g = 200;
envColor.b = 200;
accel.x = accel.z = 0.0f;
accel.y = -1.0f;
for (i = 0; i < numEffects; i++) {
scale = Rand_CenteredFloat(0.3f) + 0.6f;
life = (s32)Rand_CenteredFloat(5.0f) + 12;
pos.x = Rand_CenteredFloat(unkFloat) + vec->x;
pos.y = Rand_ZeroFloat(unkFloat) + vec->y;
pos.z = Rand_CenteredFloat(unkFloat) + vec->z;
vel.x = Rand_CenteredFloat(10.0f);
vel.y = Rand_ZeroFloat(10.0f) + 2.0f;
vel.z = Rand_CenteredFloat(10.0f);
EffectSsEnIce_Spawn(globalCtx, &pos, scale, &vel, &accel, &primColor, &envColor, life);
}
CollisionCheck_SpawnShieldParticles(globalCtx, vec);
}
void EnFz_SpawnIceSmokeHiddenState(EnFz* this) {
}
// Fully grown
void EnFz_SpawnIceSmokeGrowingState(EnFz* this) {
Vec3f pos;
Vec3f velocity;
Vec3f accel;
if ((this->counter % 16) == 0) {
pos.x = Rand_CenteredFloat(40.0f) + this->actor.world.pos.x;
pos.y = Rand_CenteredFloat(40.0f) + this->actor.world.pos.y + 30.0f;
pos.z = Rand_CenteredFloat(40.0f) + this->actor.world.pos.z;
accel.x = accel.z = 0.0f;
accel.y = 0.1f;
velocity.x = velocity.y = velocity.z = 0.0f;
EnFz_SpawnIceSmokeNoFreeze(this, &pos, &velocity, &accel, Rand_ZeroFloat(7.5f) + 15.0f);
}
}
// (2) Growing or Shrinking to/from hiding or (3) melting from fire
void EnFz_SpawnIceSmokeActiveState(EnFz* this) {
Vec3f pos;
Vec3f velocity;
Vec3f accel;
if ((this->counter % 4) == 0) {
pos.x = Rand_CenteredFloat(40.0f) + this->actor.world.pos.x;
pos.y = this->iceSmokeFreezingSpawnHeight;
pos.z = Rand_CenteredFloat(40.0f) + this->actor.world.pos.z;
accel.x = accel.z = 0.0f;
accel.y = 0.1f;
velocity.x = velocity.y = velocity.z = 0.0f;
EnFz_SpawnIceSmokeNoFreeze(this, &pos, &velocity, &accel, Rand_ZeroFloat(7.5f) + 15.0f);
}
}
void EnFz_ApplyDamage(EnFz* this, GlobalContext* globalCtx) {
Vec3f vec;
if (this->isMoving &&
((this->actor.bgCheckFlags & 8) ||
(Actor_TestFloorInDirection(&this->actor, globalCtx, 60.0f, this->actor.world.rot.y) == 0))) {
this->actor.bgCheckFlags &= ~8;
this->isMoving = false;
this->speedXZ = 0.0f;
this->actor.speedXZ = 0.0f;
}
if (this->isFreezing) {
if ((this->actor.params < 0) && (this->collider1.base.atFlags & 2)) {
this->isMoving = false;
this->collider1.base.acFlags &= ~2;
this->actor.speedXZ = this->speedXZ = 0.0f;
this->timer = 10;
EnFz_SetupDisappear(this);
} else if (this->collider2.base.acFlags & 0x80) {
this->collider2.base.acFlags &= ~0x80;
this->collider1.base.acFlags &= ~2;
} else if (this->collider1.base.acFlags & 2) {
this->collider1.base.acFlags &= ~2;
if (this->actor.colChkInfo.damageEffect != 2) {
if (this->actor.colChkInfo.damageEffect == 0xF) {
Actor_ApplyDamage(&this->actor);
Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 8);
if (this->actor.colChkInfo.health) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DAMAGE);
vec.x = this->actor.world.pos.x;
vec.y = this->actor.world.pos.y;
vec.z = this->actor.world.pos.z;
EnFz_Damaged(this, globalCtx, &vec, 10, 0.0f);
this->unusedCounter++;
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DEAD);
Audio_PlayActorSound2(&this->actor, NA_SE_EV_ICE_BROKEN);
vec.x = this->actor.world.pos.x;
vec.y = this->actor.world.pos.y;
vec.z = this->actor.world.pos.z;
EnFz_Damaged(this, globalCtx, &vec, 30, 10.0f);
EnFz_SetupDespawn(this, globalCtx);
}
}
} else {
Actor_ApplyDamage(&this->actor);
Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 8);
if (this->actor.colChkInfo.health == 0) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DEAD);
EnFz_SetupMelt(this);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DAMAGE);
}
}
}
}
}
void EnFz_SetYawTowardsPlayer(EnFz* this) {
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 10, 2000, 0);
this->actor.world.rot.y = this->actor.shape.rot.y;
}
void EnFz_SetupDisappear(EnFz* this) {
this->state = 2;
this->isFreezing = false;
this->actor.flags &= ~ACTOR_FLAG_0;
this->actionFunc = EnFz_Disappear;
}
void EnFz_Disappear(EnFz* this, GlobalContext* globalCtx) {
this->envAlpha -= 16;
if (this->envAlpha > 255) {
this->envAlpha = 0;
}
if (Math_SmoothStepToF(&this->actor.scale.y, 0.0f, 1.0f, 0.0005f, 0) == 0.0f) {
EnFz_SetupWait(this);
}
}
void EnFz_SetupWait(EnFz* this) {
this->state = 0;
this->unusedNum2 = 0;
this->unusedNum1 = 0;
this->timer = 100;
this->actionFunc = EnFz_Wait;
this->actor.world.pos.x = this->posOrigin.x;
this->actor.world.pos.y = this->posOrigin.y;
this->actor.world.pos.z = this->posOrigin.z;
}
void EnFz_Wait(EnFz* this, GlobalContext* globalCtx) {
if ((this->timer == 0) && (this->actor.xzDistToPlayer < 400.0f)) {
EnFz_SetupAppear(this);
}
}
void EnFz_SetupAppear(EnFz* this) {
this->state = 2;
this->timer = 20;
this->unusedNum2 = 4000;
this->actionFunc = EnFz_Appear;
}
void EnFz_Appear(EnFz* this, GlobalContext* globalCtx) {
if (this->timer == 0) {
this->envAlpha += 8;
if (this->envAlpha > 255) {
this->envAlpha = 255;
}
if (Math_SmoothStepToF(&this->actor.scale.y, 0.008f, 1.0f, 0.0005f, 0.0f) == 0.0f) {
EnFz_SetupAimForMove(this);
}
}
}
void EnFz_SetupAimForMove(EnFz* this) {
this->state = 1;
this->timer = 40;
this->updateBgInfo = true;
this->isFreezing = true;
this->actor.flags |= ACTOR_FLAG_0;
this->actionFunc = EnFz_AimForMove;
this->actor.gravity = -1.0f;
}
void EnFz_AimForMove(EnFz* this, GlobalContext* globalCtx) {
EnFz_SetYawTowardsPlayer(this);
if (this->timer == 0) {
EnFz_SetupMoveTowardsPlayer(this);
}
}
void EnFz_SetupMoveTowardsPlayer(EnFz* this) {
this->state = 1;
this->isMoving = true;
this->timer = 100;
this->actionFunc = EnFz_MoveTowardsPlayer;
this->speedXZ = 4.0f;
}
void EnFz_MoveTowardsPlayer(EnFz* this, GlobalContext* globalCtx) {
if ((this->timer == 0) || !this->isMoving) {
EnFz_SetupAimForFreeze(this);
}
}
void EnFz_SetupAimForFreeze(EnFz* this) {
this->state = 1;
this->timer = 40;
this->actionFunc = EnFz_AimForFreeze;
this->speedXZ = 0.0f;
this->actor.speedXZ = 0.0f;
}
void EnFz_AimForFreeze(EnFz* this, GlobalContext* globalCtx) {
EnFz_SetYawTowardsPlayer(this);
if (this->timer == 0) {
EnFz_SetupBlowSmoke(this, globalCtx);
}
}
void EnFz_SetupBlowSmoke(EnFz* this, GlobalContext* globalCtx) {
this->state = 1;
this->timer = 80;
this->actionFunc = EnFz_BlowSmoke;
EnFz_UpdateTargetPos(this, globalCtx);
}
void EnFz_BlowSmoke(EnFz* this, GlobalContext* globalCtx) {
Vec3f vec1;
Vec3f pos;
Vec3f velocity;
Vec3f accel;
u8 isTimerMod8;
s16 primAlpha;
if (this->timer == 0) {
EnFz_SetupDisappear(this);
} else if (this->timer >= 11) {
isTimerMod8 = false;
primAlpha = 150;
func_8002F974(&this->actor, NA_SE_EN_FREEZAD_BREATH - SFX_FLAG);
if ((this->timer - 10) < 16) { // t < 26
primAlpha = (this->timer * 10) - 100;
}
accel.x = accel.z = 0.0f;
accel.y = 0.6f;
pos.x = this->actor.world.pos.x;
pos.y = this->actor.world.pos.y + 20.0f;
pos.z = this->actor.world.pos.z;
Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW);
vec1.x = 0.0f;
vec1.y = -2.0f;
vec1.z = 20.0f; // xz velocity
Matrix_MultVec3f(&vec1, &velocity);
if ((this->timer % 8) == 0) {
isTimerMod8 = true;
}
EnFz_SpawnIceSmokeFreeze(this, &pos, &velocity, &accel, 2.0f, 25.0f, primAlpha, isTimerMod8);
pos.x += (velocity.x * 0.5f);
pos.y += (velocity.y * 0.5f);
pos.z += (velocity.z * 0.5f);
EnFz_SpawnIceSmokeFreeze(this, &pos, &velocity, &accel, 2.0f, 25.0f, primAlpha, false);
}
}
void EnFz_SetupDespawn(EnFz* this, GlobalContext* globalCtx) {
this->state = 0;
this->updateBgInfo = true;
this->isFreezing = false;
this->isDespawning = true;
this->actor.flags &= ~ACTOR_FLAG_0;
this->isActive = false;
this->timer = 60;
this->speedXZ = 0.0f;
this->actor.gravity = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.speedXZ = 0.0f;
Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP);
Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x60);
this->actionFunc = EnFz_Despawn;
}
void EnFz_Despawn(EnFz* this, GlobalContext* globalCtx) {
if (this->timer == 0) {
Actor_Kill(&this->actor);
}
}
void EnFz_SetupMelt(EnFz* this) {
this->state = 3;
this->isFreezing = false;
this->isDespawning = true;
this->actor.flags &= ~ACTOR_FLAG_0;
this->actionFunc = EnFz_Melt;
this->actor.speedXZ = 0.0f;
this->speedXZ = 0.0f;
}
void EnFz_Melt(EnFz* this, GlobalContext* globalCtx) {
Math_StepToF(&this->actor.scale.y, 0.0006f, 0.0002f);
if (this->actor.scale.y < 0.006f) {
this->actor.scale.x += 0.0004f;
this->actor.scale.z += 0.0004f;
}
if (this->actor.scale.y < 0.004f) {
this->envAlpha -= 8;
if (this->envAlpha > 255) {
this->envAlpha = 0;
}
}
if (this->envAlpha == 0) {
EnFz_SetupDespawn(this, globalCtx);
}
}
void EnFz_SetupBlowSmokeStationary(EnFz* this) {
this->state = 1;
this->timer = 40;
this->updateBgInfo = true;
this->isFreezing = true;
this->actor.flags |= ACTOR_FLAG_0;
this->actionFunc = EnFz_BlowSmokeStationary;
this->actor.gravity = -1.0f;
}
void EnFz_BlowSmokeStationary(EnFz* this, GlobalContext* globalCtx) {
Vec3f vec1;
Vec3f pos;
Vec3f velocity;
Vec3f accel;
u8 isTimerMod8;
s16 primAlpha;
if (this->counter & 0xC0) {
EnFz_SetYawTowardsPlayer(this);
EnFz_UpdateTargetPos(this, globalCtx);
} else {
isTimerMod8 = false;
primAlpha = 150;
func_8002F974(&this->actor, NA_SE_EN_FREEZAD_BREATH - SFX_FLAG);
if ((this->counter & 0x3F) >= 48) {
primAlpha = 630 - ((this->counter & 0x3F) * 10);
}
accel.x = accel.z = 0.0f;
accel.y = 0.6f;
pos.x = this->actor.world.pos.x;
pos.y = this->actor.world.pos.y + 20.0f;
pos.z = this->actor.world.pos.z;
Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW);
vec1.x = 0.0f;
vec1.y = -2.0f;
vec1.z = 20.0f;
Matrix_MultVec3f(&vec1, &velocity);
if ((this->counter % 8) == 0) {
isTimerMod8 = true;
}
EnFz_SpawnIceSmokeFreeze(this, &pos, &velocity, &accel, 2.0f, 25.0f, primAlpha, isTimerMod8);
pos.x += (velocity.x * 0.5f);
pos.y += (velocity.y * 0.5f);
pos.z += (velocity.z * 0.5f);
EnFz_SpawnIceSmokeFreeze(this, &pos, &velocity, &accel, 2.0f, 25.0f, primAlpha, false);
}
}
static EnFzSpawnIceSmokeFunc iceSmokeSpawnFuncs[] = {
EnFz_SpawnIceSmokeHiddenState,
EnFz_SpawnIceSmokeGrowingState,
EnFz_SpawnIceSmokeActiveState,
EnFz_SpawnIceSmokeActiveState,
};
void EnFz_Update(Actor* thisx, GlobalContext* globalCtx) {
EnFz* this = (EnFz*)thisx;
s32 pad;
this->counter++;
if (this->unusedTimer1 != 0) {
this->unusedTimer1--;
}
if (this->timer != 0) {
this->timer--;
}
if (this->unusedTimer2 != 0) {
this->unusedTimer2--;
}
Actor_SetFocus(&this->actor, 50.0f);
EnFz_ApplyDamage(this, globalCtx);
this->actionFunc(this, globalCtx);
if (this->isDespawning == false) {
Collider_UpdateCylinder(&this->actor, &this->collider1);
Collider_UpdateCylinder(&this->actor, &this->collider2);
if (this->isFreezing) {
if (this->actor.colorFilterTimer == 0) {
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base);
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base);
}
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base);
}
}
Math_StepToF(&this->actor.speedXZ, this->speedXZ, 0.2f);
Actor_MoveForward(&this->actor);
if (this->updateBgInfo) {
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 20.0f, 5);
}
iceSmokeSpawnFuncs[this->state](this);
EnFz_UpdateIceSmoke(this, globalCtx);
}
void EnFz_Draw(Actor* thisx, GlobalContext* globalCtx) {
static Gfx* displayLists[] = {
gFreezardIntactDL, // Body fully intact (5 or 6 health)
gFreezardTopRightHornChippedDL, // Top right horn chipped off (from Freezards perspective) (3 or 4 health)
gFreezardHeadChippedDL, // Entire head chipped off (1 or 2 health)
};
EnFz* this = (EnFz*)thisx;
s32 pad;
s32 index;
index = (6 - this->actor.colChkInfo.health) >> 1;
OPEN_DISPS(globalCtx->state.gfxCtx);
if (this->actor.colChkInfo.health == 0) {
index = 2;
}
if (this->isActive) {
func_8002ED80(&this->actor, globalCtx, 0);
func_80093D84(globalCtx->state.gfxCtx);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, globalCtx->state.frames & 0x7F, 32, 32, 1, 0,
(2 * globalCtx->state.frames) & 0x7F, 32, 32));
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gDPSetCombineLERP(POLY_XLU_DISP++, TEXEL1, PRIMITIVE, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIMITIVE, TEXEL0,
PRIMITIVE, ENVIRONMENT, COMBINED, ENVIRONMENT, COMBINED, 0, ENVIRONMENT, 0);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, 155, 255, 255, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 200, this->envAlpha);
gSPDisplayList(POLY_XLU_DISP++, displayLists[index]);
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
EnFz_DrawIceSmoke(this, globalCtx);
}
void EnFz_SpawnIceSmokeNoFreeze(EnFz* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 xyScale) {
EnFzEffectSsIceSmoke* iceSmoke = this->iceSmoke;
s16 i;
for (i = 0; i < ARRAY_COUNT(this->iceSmoke); i++) {
if (iceSmoke->type == 0) {
iceSmoke->type = 1;
iceSmoke->pos = *pos;
iceSmoke->velocity = *velocity;
iceSmoke->accel = *accel;
iceSmoke->primAlphaState = 0;
iceSmoke->xyScale = xyScale / 1000.0f;
iceSmoke->primAlpha = 0;
iceSmoke->timer = 0;
iceSmoke->epoch++;
break;
}
iceSmoke++;
}
}
void EnFz_SpawnIceSmokeFreeze(EnFz* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 xyScale, f32 xyScaleTarget,
s16 primAlpha, u8 isTimerMod8) {
EnFzEffectSsIceSmoke* iceSmoke = this->iceSmoke;
s16 i;
for (i = 0; i < ARRAY_COUNT(this->iceSmoke); i++) {
if (iceSmoke->type == 0) {
iceSmoke->type = 2;
iceSmoke->pos = *pos;
iceSmoke->velocity = *velocity;
iceSmoke->accel = *accel;
iceSmoke->primAlphaState = 0;
iceSmoke->xyScale = xyScale / 1000.0f;
iceSmoke->xyScaleTarget = xyScaleTarget / 1000.0f;
iceSmoke->primAlpha = primAlpha;
iceSmoke->timer = 0;
iceSmoke->isTimerMod8 = isTimerMod8;
iceSmoke->epoch++;
break;
}
iceSmoke++;
}
}
void EnFz_UpdateIceSmoke(EnFz* this, GlobalContext* globalCtx) {
EnFzEffectSsIceSmoke* iceSmoke = this->iceSmoke;
s16 i;
Vec3f pos;
for (i = 0; i < ARRAY_COUNT(this->iceSmoke); i++) {
if (iceSmoke->type) {
iceSmoke->pos.x += iceSmoke->velocity.x;
iceSmoke->pos.y += iceSmoke->velocity.y;
iceSmoke->pos.z += iceSmoke->velocity.z;
iceSmoke->timer++;
iceSmoke->velocity.x += iceSmoke->accel.x;
iceSmoke->velocity.y += iceSmoke->accel.y;
iceSmoke->velocity.z += iceSmoke->accel.z;
if (iceSmoke->type == 1) {
if (iceSmoke->primAlphaState == 0) { // Becoming more opaque
iceSmoke->primAlpha += 10;
if (iceSmoke->primAlpha >= 100) {
iceSmoke->primAlphaState++;
}
} else { // Becoming more transparent
iceSmoke->primAlpha -= 3;
if (iceSmoke->primAlpha <= 0) {
iceSmoke->primAlpha = 0;
iceSmoke->type = 0;
}
}
} else if (iceSmoke->type == 2) { // Freezing
Math_ApproachF(&iceSmoke->xyScale, iceSmoke->xyScaleTarget, 0.1f, iceSmoke->xyScaleTarget / 10.0f);
if (iceSmoke->primAlphaState == 0) { // Becoming more opaque
if (iceSmoke->timer >= 7) {
iceSmoke->primAlphaState++;
}
} else { // Becoming more transparent, slows down
iceSmoke->accel.y = 2.0f;
iceSmoke->primAlpha -= 17;
iceSmoke->velocity.x *= 0.75f;
iceSmoke->velocity.z *= 0.75f;
if (iceSmoke->primAlpha <= 0) {
iceSmoke->primAlpha = 0;
iceSmoke->type = 0;
}
}
if ((this->unusedTimer2 == 0) && (iceSmoke->primAlpha >= 101) && iceSmoke->isTimerMod8) {
this->collider3.dim.pos.x = (s16)iceSmoke->pos.x;
this->collider3.dim.pos.y = (s16)iceSmoke->pos.y;
this->collider3.dim.pos.z = (s16)iceSmoke->pos.z;
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider3.base);
}
pos.x = iceSmoke->pos.x;
pos.y = iceSmoke->pos.y + 10.0f;
pos.z = iceSmoke->pos.z;
if ((iceSmoke->primAlphaState != 2) && EnFz_ReachedTarget(this, &pos)) {
iceSmoke->primAlphaState = 2;
iceSmoke->velocity.x = 0.0f;
iceSmoke->velocity.z = 0.0f;
}
}
}
iceSmoke++;
}
}
void EnFz_DrawIceSmoke(EnFz* this, GlobalContext* globalCtx) {
EnFzEffectSsIceSmoke* iceSmoke = this->iceSmoke;
s16 i;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
u8 texLoaded = false;
OPEN_DISPS(gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
for (i = 0; i < ARRAY_COUNT(this->iceSmoke); i++) {
FrameInterpolation_RecordOpenChild(iceSmoke, iceSmoke->epoch);
if (iceSmoke->type > 0) {
gDPPipeSync(POLY_XLU_DISP++);
if (!texLoaded) {
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gFreezardSteamStartDL));
texLoaded++;
}
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, iceSmoke->primAlpha);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 3 * (iceSmoke->timer + (3 * i)),
15 * (iceSmoke->timer + (3 * i)), 32, 64, 1, 0, 0, 32, 32));
Matrix_Translate(iceSmoke->pos.x, iceSmoke->pos.y, iceSmoke->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(iceSmoke->xyScale, iceSmoke->xyScale, 1.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gFreezardSteamDL));
}
FrameInterpolation_RecordCloseChild();
iceSmoke++;
}
CLOSE_DISPS(gfxCtx);
}