Shipwright/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c

1013 lines
43 KiB
C

/*
* File: z_en_tite.c
* Overlay: ovl_En_Tite
* Description: Tektite
*/
#include "z_en_tite.h"
#include "overlays/actors/ovl_En_Encount1/z_en_encount1.h"
#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h"
#include "vt.h"
#include "objects/object_tite/object_tite.h"
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4)
// EnTite_Idle
#define vIdleTimer actionVar1
// EnTite_Attack (vQueuedJumps also used by EnTite_MoveTowardPlayer)
#define vAttackState actionVar1
#define vQueuedJumps actionVar2
// EnTite_FlipOnBack
#define vOnBackTimer actionVar1
#define vLegTwitchTimer actionVar2
typedef enum {
/* 0x0 */ TEKTITE_DEATH_CRY,
/* 0x1 */ TEKTITE_UNK_1,
/* 0x2 */ TEKTITE_UNK_2,
/* 0x3 */ TEKTITE_RECOIL,
/* 0x4 */ TEKTITE_UNK_4,
/* 0x5 */ TEKTITE_FALL_APART,
/* 0x6 */ TEKTITE_IDLE,
/* 0x7 */ TEKTITE_STUNNED,
/* 0x8 */ TEKTITE_UNK_8,
/* 0x9 */ TEKTITE_ATTACK,
/* 0xA */ TEKTITE_TURN_TOWARD_PLAYER,
/* 0xB */ TEKTITE_UNK9,
/* 0xC */ TEKTITE_MOVE_TOWARD_PLAYER
} EnTiteAction;
typedef enum {
/* 0x0 */ TEKTITE_BEGIN_LUNGE,
/* 0x1 */ TEKTITE_MID_LUNGE,
/* 0x2 */ TEKTITE_LANDED,
/* 0x2 */ TEKTITE_SUBMERGED
} EnTiteAttackState;
typedef enum {
/* 0x0 */ TEKTITE_INITIAL,
/* 0x1 */ TEKTITE_UNFLIPPED,
/* 0x2 */ TEKTITE_FLIPPED
} EnTiteFlipState;
void EnTite_Init(Actor* thisx, GlobalContext* globalCtx);
void EnTite_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnTite_Update(Actor* thisx, GlobalContext* globalCtx);
void EnTite_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnTite_SetupIdle(EnTite* this);
void EnTite_SetupTurnTowardPlayer(EnTite* this);
void EnTite_SetupMoveTowardPlayer(EnTite* this);
void EnTite_SetupDeathCry(EnTite* this);
void EnTite_SetupFlipUpright(EnTite* this);
void EnTite_Idle(EnTite* this, GlobalContext* globalCtx);
void EnTite_Attack(EnTite* this, GlobalContext* globalCtx);
void EnTite_TurnTowardPlayer(EnTite* this, GlobalContext* globalCtx);
void EnTite_MoveTowardPlayer(EnTite* this, GlobalContext* globalCtx);
void EnTite_Recoil(EnTite* this, GlobalContext* globalCtx);
void EnTite_Stunned(EnTite* this, GlobalContext* globalCtx);
void EnTite_DeathCry(EnTite* this, GlobalContext* globalCtx);
void EnTite_FallApart(EnTite* this, GlobalContext* globalCtx);
void EnTite_FlipOnBack(EnTite* this, GlobalContext* globalCtx);
void EnTite_FlipUpright(EnTite* this, GlobalContext* globalCtx);
const ActorInit En_Tite_InitVars = {
ACTOR_EN_TITE,
ACTORCAT_ENEMY,
FLAGS,
OBJECT_TITE,
sizeof(EnTite),
(ActorFunc)EnTite_Init,
(ActorFunc)EnTite_Destroy,
(ActorFunc)EnTite_Update,
(ActorFunc)EnTite_Draw,
NULL,
};
static ColliderJntSphElementInit sJntSphElementsInit[1] = {
{
{
ELEMTYPE_UNK0,
{ 0xFFCFFFFF, 0x00, 0x08 },
{ 0xFFCFFFFF, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_ON | BUMP_HOOKABLE,
OCELEM_ON,
},
{ 0, { { 0, 1500, 0 }, 20 }, 100 },
},
};
static ColliderJntSphInit sJntSphInit = {
{
COLTYPE_HIT6,
AT_ON | AT_TYPE_ENEMY,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_1,
COLSHAPE_JNTSPH,
},
1,
sJntSphElementsInit,
};
static DamageTable sDamageTable[] = {
/* Deku nut */ DMG_ENTRY(0, 0x1),
/* Deku stick */ DMG_ENTRY(2, 0x0),
/* Slingshot */ DMG_ENTRY(1, 0x0),
/* Explosive */ DMG_ENTRY(2, 0x0),
/* Boomerang */ DMG_ENTRY(0, 0x1),
/* Normal arrow */ DMG_ENTRY(2, 0x0),
/* Hammer swing */ DMG_ENTRY(2, 0x0),
/* Hookshot */ DMG_ENTRY(0, 0x1),
/* Kokiri sword */ DMG_ENTRY(1, 0x0),
/* Master sword */ DMG_ENTRY(2, 0x0),
/* Giant's Knife */ DMG_ENTRY(4, 0x0),
/* Fire arrow */ DMG_ENTRY(2, 0x0),
/* Ice arrow */ DMG_ENTRY(4, 0xF),
/* Light arrow */ DMG_ENTRY(2, 0x0),
/* Unk arrow 1 */ DMG_ENTRY(2, 0x0),
/* Unk arrow 2 */ DMG_ENTRY(2, 0x0),
/* Unk arrow 3 */ DMG_ENTRY(2, 0x0),
/* Fire magic */ DMG_ENTRY(0, 0xE),
/* Ice magic */ DMG_ENTRY(3, 0xF),
/* Light magic */ DMG_ENTRY(0, 0xE),
/* Shield */ DMG_ENTRY(0, 0x0),
/* Mirror Ray */ DMG_ENTRY(0, 0x0),
/* Kokiri spin */ DMG_ENTRY(1, 0x0),
/* Giant spin */ DMG_ENTRY(4, 0x0),
/* Master spin */ DMG_ENTRY(2, 0x0),
/* Kokiri jump */ DMG_ENTRY(2, 0x0),
/* Giant jump */ DMG_ENTRY(8, 0x0),
/* Master jump */ DMG_ENTRY(4, 0x0),
/* Unknown 1 */ DMG_ENTRY(0, 0x0),
/* Unblockable */ DMG_ENTRY(0, 0x0),
/* Hammer jump */ DMG_ENTRY(4, 0x0),
/* Unknown 2 */ DMG_ENTRY(0, 0x0),
};
static InitChainEntry sInitChain[] = {
ICHAIN_S8(naviEnemyId, 0x45, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE),
ICHAIN_F32(minVelocityY, -40, ICHAIN_CONTINUE),
ICHAIN_F32_DIV1000(gravity, -1000, ICHAIN_STOP),
};
static AnimationHeader* D_80B1B634[] = {
&object_tite_Anim_00083C, &object_tite_Anim_0004F8, &object_tite_Anim_00069C, NULL, NULL, NULL,
};
// Some kind of offset for the position of each tektite foot
static Vec3f sFootOffset = { 2800.0f, -200.0f, 0.0f };
// Relative positions to spawn ice chunks when tektite is frozen
static Vec3f sIceChunks[12] = {
{ 20.0f, 20.0f, 0.0f }, { 10.0f, 40.0f, 10.0f }, { -10.0f, 40.0f, 10.0f }, { -20.0f, 20.0f, 0.0f },
{ 10.0f, 40.0f, -10.0f }, { -10.0f, 40.0f, -10.0f }, { 0.0f, 20.0f, -20.0f }, { 10.0f, 0.0f, 10.0f },
{ 10.0f, 0.0f, -10.0f }, { 0.0f, 20.0f, 20.0f }, { -10.0f, 0.0f, 10.0f }, { -10.0f, 0.0f, -10.0f },
};
void EnTite_SetupAction(EnTite* this, EnTiteActionFunc actionFunc) {
this->actionFunc = actionFunc;
}
void EnTite_Init(Actor* thisx, GlobalContext* globalCtx) {
EnTite* this = (EnTite*)thisx;
Actor_ProcessInitChain(thisx, sInitChain);
thisx->targetMode = 3;
Actor_SetScale(thisx, 0.01f);
SkelAnime_Init(globalCtx, &this->skelAnime, &object_tite_Skel_003A20, &object_tite_Anim_0012E4, this->jointTable,
this->morphTable, 25);
ActorShape_Init(&thisx->shape, -200.0f, ActorShadow_DrawCircle, 70.0f);
this->flipState = TEKTITE_INITIAL;
thisx->colChkInfo.damageTable = sDamageTable;
this->actionVar1 = 0;
this->bodyBreak.val = BODYBREAK_STATUS_FINISHED;
thisx->focus.pos = thisx->world.pos;
thisx->focus.pos.y += 20.0f;
thisx->colChkInfo.health = 2;
thisx->colChkInfo.mass = MASS_HEAVY;
Collider_InitJntSph(globalCtx, &this->collider);
Collider_SetJntSph(globalCtx, &this->collider, thisx, &sJntSphInit, &this->colliderItem);
this->unk_2DC = 0x1D;
if (this->actor.params == TEKTITE_BLUE) {
this->unk_2DC |= 0x40; // Don't use the actor engine's ripple spawning code
thisx->colChkInfo.health = 4;
thisx->naviEnemyId += 1;
}
EnTite_SetupIdle(this);
}
void EnTite_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnTite* this = (EnTite*)thisx;
EnEncount1* spawner;
if (thisx->parent != NULL) {
spawner = (EnEncount1*)thisx->parent;
if (spawner->curNumSpawn > 0) {
spawner->curNumSpawn--;
}
osSyncPrintf("\n\n");
// "Number of simultaneous occurrences"
osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 同時発生数 ☆☆☆☆☆%d\n" VT_RST, spawner->curNumSpawn);
osSyncPrintf("\n\n");
}
Collider_DestroyJntSph(globalCtx, &this->collider);
}
void EnTite_SetupIdle(EnTite* this) {
Animation_MorphToLoop(&this->skelAnime, &object_tite_Anim_0012E4, 4.0f);
this->action = TEKTITE_IDLE;
this->vIdleTimer = Rand_S16Offset(15, 30);
this->actor.speedXZ = 0.0f;
EnTite_SetupAction(this, EnTite_Idle);
}
void EnTite_Idle(EnTite* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f);
if (this->actor.params == TEKTITE_BLUE) {
if (this->actor.bgCheckFlags & 0x20) {
// Float on water surface
this->actor.gravity = 0.0f;
Math_SmoothStepToF(&this->actor.velocity.y, 0.0f, 1.0f, 2.0f, 0.0f);
Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.world.pos.y + this->actor.yDistToWater, 1.0f, 2.0f,
0.0f);
} else {
this->actor.gravity = -1.0f;
}
}
if ((this->actor.bgCheckFlags & 3) && (this->actor.velocity.y <= 0.0f)) {
this->actor.velocity.y = 0.0f;
}
if (this->vIdleTimer > 0) {
this->vIdleTimer--;
} else if ((this->actor.xzDistToPlayer < 300.0f) && (this->actor.yDistToPlayer <= 80.0f)) {
EnTite_SetupTurnTowardPlayer(this);
}
}
void EnTite_SetupAttack(EnTite* this) {
Animation_PlayOnce(&this->skelAnime, &object_tite_Anim_00083C);
this->action = TEKTITE_ATTACK;
this->vAttackState = TEKTITE_BEGIN_LUNGE;
this->vQueuedJumps = Rand_S16Offset(1, 3);
this->actor.speedXZ = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.world.rot.y = this->actor.shape.rot.y;
EnTite_SetupAction(this, EnTite_Attack);
}
void EnTite_Attack(EnTite* this, GlobalContext* globalCtx) {
s16 angleToPlayer;
s32 attackState;
Vec3f ripplePos;
if (SkelAnime_Update(&this->skelAnime) != 0) {
attackState = this->vAttackState; // for deciding whether to change animation
switch (this->vAttackState) {
case TEKTITE_BEGIN_LUNGE:
// Snap to ground or water, then lunge into the air with some initial speed
this->vAttackState = TEKTITE_MID_LUNGE;
if ((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20)) {
if (this->actor.floorHeight > BGCHECK_Y_MIN) {
this->actor.world.pos.y = this->actor.floorHeight;
}
Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP);
} else {
this->actor.world.pos.y += this->actor.yDistToWater;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_JUMP_WATER);
}
this->actor.velocity.y = 8.0f;
this->actor.gravity = -1.0f;
this->actor.speedXZ = 4.0f;
break;
case TEKTITE_MID_LUNGE:
// Continue trajectory until tektite has negative velocity and has landed on ground/water surface
// Snap to ground/water surface, or if falling fast dip into the water and slow fall speed
this->actor.flags |= ACTOR_FLAG_24;
if ((this->actor.bgCheckFlags & 3) ||
((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20))) {
if (this->actor.velocity.y <= 0.0f) {
this->vAttackState = TEKTITE_LANDED;
if ((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20)) {
if (BGCHECK_Y_MIN < this->actor.floorHeight) {
this->actor.world.pos.y = this->actor.floorHeight;
}
this->actor.velocity.y = 0.0f;
this->actor.speedXZ = 0.0f;
} else {
this->actor.gravity = 0.0f;
if (this->actor.velocity.y < -8.0f) {
ripplePos = this->actor.world.pos;
ripplePos.y += this->actor.yDistToWater;
this->vAttackState++; // TEKTITE_SUBMERGED
this->actor.velocity.y *= 0.75f;
attackState = this->vAttackState;
EffectSsGRipple_Spawn(globalCtx, &ripplePos, 0, 500, 0);
} else {
this->actor.velocity.y = 0.0f;
this->actor.speedXZ = 0.0f;
}
}
this->actor.world.rot.y = this->actor.shape.rot.y;
}
}
break;
case TEKTITE_LANDED:
// Get ready to begin another lunge if more lunges are queued, otherwise start turning
if (this->vQueuedJumps != 0) {
this->vQueuedJumps--;
this->vAttackState = TEKTITE_BEGIN_LUNGE;
this->collider.base.atFlags &= ~AT_HIT;
} else {
EnTite_SetupTurnTowardPlayer(this);
}
break;
case TEKTITE_SUBMERGED:
// Check if floated to surface
if (this->actor.yDistToWater == 0.0f) {
this->vAttackState = TEKTITE_LANDED;
attackState = this->vAttackState;
}
break;
}
// If switching attack state, change animation (unless tektite is switching between submerged and landed)
if (attackState != this->vAttackState) {
Animation_PlayOnce(&this->skelAnime, D_80B1B634[this->vAttackState]);
}
}
switch (this->vAttackState) {
case TEKTITE_BEGIN_LUNGE:
// Slightly turn to player and switch to turning/idling action if the player is too far
Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 1000, 0);
this->actor.shape.rot.y = this->actor.world.rot.y;
angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
if ((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f)) {
EnTite_SetupIdle(this);
} else if (ABS(angleToPlayer) >= 9000) {
EnTite_SetupTurnTowardPlayer(this);
}
break;
case TEKTITE_MID_LUNGE:
// Generate sparkles at feet upon landing, set jumping animation and hurtbox and check if hit player
if (this->actor.velocity.y >= 5.0f) {
if (this->actor.bgCheckFlags & 1) {
func_800355B8(globalCtx, &this->frontLeftFootPos);
func_800355B8(globalCtx, &this->frontRightFootPos);
func_800355B8(globalCtx, &this->backRightFootPos);
func_800355B8(globalCtx, &this->backLeftFootPos);
}
}
if (!(this->collider.base.atFlags & AT_HIT) && (this->actor.flags & ACTOR_FLAG_6)) {
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
} else {
Player* player = GET_PLAYER(globalCtx);
this->collider.base.atFlags &= ~AT_HIT;
Animation_MorphToLoop(&this->skelAnime, &object_tite_Anim_0012E4, 4.0f);
this->actor.speedXZ = -6.0f;
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
if (&player->actor == this->collider.base.at) {
if (!(this->collider.base.atFlags & AT_BOUNCED)) {
Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT);
}
}
EnTite_SetupAction(this, EnTite_Recoil);
}
break;
case TEKTITE_LANDED:
// Slightly turn to player
Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 1500, 0);
break;
case TEKTITE_SUBMERGED:
// Float up to water surface
Math_SmoothStepToF(&this->actor.velocity.y, 0.0f, 1.0f, 2.0f, 0.0f);
Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f);
Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.world.pos.y + this->actor.yDistToWater, 1.0f, 2.0f,
0.0f);
break;
}
// Create ripples on water surface where tektite feet landed
if (this->actor.bgCheckFlags & 2) {
if (!(this->actor.bgCheckFlags & 0x20)) {
func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1);
}
}
// if landed, kill XZ speed and play appropriate sounds
if (this->actor.params == TEKTITE_BLUE) {
if (this->actor.bgCheckFlags & 0x40) {
this->actor.speedXZ = 0.0f;
if (this->vAttackState == TEKTITE_SUBMERGED) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER2);
}
this->actor.bgCheckFlags &= ~0x40;
} else if (this->actor.bgCheckFlags & 2) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND);
}
} else if (this->actor.bgCheckFlags & 2) {
this->actor.speedXZ = 0.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND);
}
}
void EnTite_SetupTurnTowardPlayer(EnTite* this) {
Animation_PlayLoop(&this->skelAnime, &object_tite_Anim_000A14);
this->action = TEKTITE_TURN_TOWARD_PLAYER;
if ((this->actor.bgCheckFlags & 3) || ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20))) {
if (this->actor.velocity.y <= 0.0f) {
this->actor.gravity = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.speedXZ = 0.0f;
}
}
EnTite_SetupAction(this, EnTite_TurnTowardPlayer);
}
void EnTite_TurnTowardPlayer(EnTite* this, GlobalContext* globalCtx) {
s16 angleToPlayer;
s16 turnVelocity;
if (((this->actor.bgCheckFlags & 3) ||
((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20))) &&
(this->actor.velocity.y <= 0.0f)) {
this->actor.gravity = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.speedXZ = 0.0f;
}
// Calculate turn velocity and animation speed based on angle to player
if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) {
this->actor.world.pos.y += this->actor.yDistToWater;
}
angleToPlayer = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.world.rot.y;
if (angleToPlayer > 0) {
turnVelocity = (angleToPlayer / 42.0f) + 10.0f;
this->actor.world.rot.y += (turnVelocity * 2);
} else {
turnVelocity = (angleToPlayer / 42.0f) - 10.0f;
this->actor.world.rot.y += (turnVelocity * 2);
}
if (angleToPlayer > 0) {
this->skelAnime.playSpeed = turnVelocity * 0.01f;
} else {
this->skelAnime.playSpeed = turnVelocity * 0.01f;
}
/**
* Play sounds once every animation cycle
*/
SkelAnime_Update(&this->skelAnime);
if (((s16)this->skelAnime.curFrame & 7) == 0) {
if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_WALK_WATER);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_WALK);
}
}
// Idle if player is far enough away from the tektite, move or attack if almost facing player
this->actor.shape.rot.y = this->actor.world.rot.y;
if ((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f)) {
EnTite_SetupIdle(this);
} else if (Actor_IsFacingPlayer(&this->actor, 3640)) {
if ((this->actor.xzDistToPlayer <= 180.0f) && (this->actor.yDistToPlayer <= 80.0f)) {
EnTite_SetupAttack(this);
} else {
EnTite_SetupMoveTowardPlayer(this);
}
}
}
void EnTite_SetupMoveTowardPlayer(EnTite* this) {
Animation_PlayLoop(&this->skelAnime, &object_tite_Anim_000C70);
this->action = TEKTITE_MOVE_TOWARD_PLAYER;
this->actor.velocity.y = 10.0f;
this->actor.gravity = -1.0f;
this->actor.speedXZ = 4.0f;
this->vQueuedJumps = Rand_S16Offset(1, 3);
if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_JUMP_WATER);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP);
}
EnTite_SetupAction(this, EnTite_MoveTowardPlayer);
}
/**
* Jumping toward player as a method of travel (different from attacking, has no hitbox)
*/
void EnTite_MoveTowardPlayer(EnTite* this, GlobalContext* globalCtx) {
Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 1.0f, 0.0f);
SkelAnime_Update(&this->skelAnime);
if (this->actor.bgCheckFlags & 0x42) {
if (!(this->actor.bgCheckFlags & 0x40)) {
func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER);
}
}
if ((this->actor.bgCheckFlags & 2) || ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x40))) {
if (this->vQueuedJumps != 0) {
this->vQueuedJumps--;
} else {
EnTite_SetupIdle(this);
}
}
if (((this->actor.bgCheckFlags & 3) || (this->actor.params == TEKTITE_BLUE && (this->actor.bgCheckFlags & 0x60))) &&
(this->actor.velocity.y <= 0.0f)) {
// slightly turn toward player upon landing and snap to ground or water.
this->actor.speedXZ = 0.0f;
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 0);
this->actor.world.rot.y = this->actor.shape.rot.y;
if ((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20)) {
if (this->actor.floorHeight > BGCHECK_Y_MIN) {
this->actor.world.pos.y = this->actor.floorHeight;
}
} else if (this->actor.bgCheckFlags & 0x40) {
Vec3f ripplePos = this->actor.world.pos;
this->actor.bgCheckFlags &= ~0x40;
ripplePos.y += this->actor.yDistToWater;
this->actor.gravity = 0.0f;
this->actor.velocity.y *= 0.75f;
EffectSsGRipple_Spawn(globalCtx, &ripplePos, 0, 500, 0);
return;
} else {
// If submerged, float to surface
Math_SmoothStepToF(&this->actor.velocity.y, 0.0f, 1.0f, 2.0f, 0.0f);
Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.world.pos.y + this->actor.yDistToWater, 1.0f, 2.0f,
0.0f);
if (this->actor.yDistToWater != 0.0f) {
// Do not change state until tekite has floated to surface
return;
}
}
// Idle or turn if player is too far away, otherwise keep jumping
if (((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f))) {
EnTite_SetupIdle(this);
} else if (((this->actor.xzDistToPlayer <= 180.0f)) && ((this->actor.yDistToPlayer <= 80.0f))) {
if (this->vQueuedJumps <= 0) {
EnTite_SetupTurnTowardPlayer(this);
} else {
this->actor.velocity.y = 10.0f;
this->actor.speedXZ = 4.0f;
this->actor.flags |= ACTOR_FLAG_24;
this->actor.gravity = -1.0f;
if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_JUMP_WATER);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP);
}
}
} else {
this->actor.velocity.y = 10.0f;
this->actor.speedXZ = 4.0f;
this->actor.flags |= ACTOR_FLAG_24;
this->actor.gravity = -1.0f;
if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_JUMP_WATER);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP);
}
}
// If in midair:
} else {
// Turn slowly toward player
this->actor.flags |= ACTOR_FLAG_24;
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 1000, 0);
if (this->actor.velocity.y >= 6.0f) {
if (this->actor.bgCheckFlags & 1) {
func_800355B8(globalCtx, &this->frontLeftFootPos);
func_800355B8(globalCtx, &this->frontRightFootPos);
func_800355B8(globalCtx, &this->backRightFootPos);
func_800355B8(globalCtx, &this->backLeftFootPos);
}
}
}
}
void EnTite_SetupRecoil(EnTite* this) {
this->action = TEKTITE_RECOIL;
Animation_MorphToLoop(&this->skelAnime, &object_tite_Anim_0012E4, 4.0f);
this->actor.speedXZ = -6.0f;
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
this->actor.gravity = -1.0f;
EnTite_SetupAction(this, EnTite_Recoil);
}
/**
* After tektite hits or gets hit, recoils backwards and slides a bit upon landing
*/
void EnTite_Recoil(EnTite* this, GlobalContext* globalCtx) {
s16 angleToPlayer;
// Snap to ground or water surface upon landing
Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f);
if (((this->actor.bgCheckFlags & 3) || (this->actor.params == TEKTITE_BLUE && (this->actor.bgCheckFlags & 0x20))) &&
(this->actor.velocity.y <= 0.0f)) {
if ((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20)) {
if (this->actor.floorHeight > BGCHECK_Y_MIN) {
this->actor.world.pos.y = this->actor.floorHeight;
}
} else {
this->actor.velocity.y = 0.0f;
this->actor.gravity = 0.0f;
this->actor.world.pos.y += this->actor.yDistToWater;
}
}
// play sound and generate ripples
if (this->actor.bgCheckFlags & 0x42) {
if (!(this->actor.bgCheckFlags & 0x40)) {
func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND);
} else {
this->actor.bgCheckFlags &= ~0x40;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER2);
}
}
// If player is far away, idle. Otherwise attack or move
angleToPlayer = (this->actor.yawTowardsPlayer - this->actor.shape.rot.y);
if ((this->actor.speedXZ == 0.0f) && ((this->actor.bgCheckFlags & 1) || ((this->actor.params == TEKTITE_BLUE) &&
(this->actor.bgCheckFlags & 0x20)))) {
this->actor.world.rot.y = this->actor.shape.rot.y;
this->collider.base.atFlags &= ~AT_HIT;
if ((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f) &&
(ABS(this->actor.shape.rot.x) < 4000) && (ABS(this->actor.shape.rot.z) < 4000) &&
((this->actor.bgCheckFlags & 1) ||
((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)))) {
EnTite_SetupIdle(this);
} else if ((this->actor.xzDistToPlayer < 180.0f) && (this->actor.yDistToPlayer <= 80.0f) &&
(ABS(angleToPlayer) <= 6000)) {
EnTite_SetupAttack(this);
} else {
EnTite_SetupMoveTowardPlayer(this);
}
}
SkelAnime_Update(&this->skelAnime);
}
void EnTite_SetupStunned(EnTite* this) {
Animation_Change(&this->skelAnime, &object_tite_Anim_0012E4, 0.0f, 0.0f,
(f32)Animation_GetLastFrame(&object_tite_Anim_0012E4), ANIMMODE_LOOP, 4.0f);
this->action = TEKTITE_STUNNED;
this->actor.speedXZ = -6.0f;
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
if (this->damageEffect == 0xF) {
this->spawnIceTimer = 48;
}
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE);
EnTite_SetupAction(this, EnTite_Stunned);
}
/**
* stunned or frozen
*/
void EnTite_Stunned(EnTite* this, GlobalContext* globalCtx) {
s16 angleToPlayer;
Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f);
// Snap to ground or water
if (((this->actor.bgCheckFlags & 3) ||
((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20))) &&
(this->actor.velocity.y <= 0.0f)) {
if (((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20))) {
if (this->actor.floorHeight > BGCHECK_Y_MIN) {
this->actor.world.pos.y = this->actor.floorHeight;
}
} else {
this->actor.velocity.y = 0.0f;
this->actor.gravity = 0.0f;
this->actor.world.pos.y += this->actor.yDistToWater;
}
}
// Play sounds and spawn dirt effects upon landing
if (this->actor.bgCheckFlags & 0x42) {
if (!(this->actor.bgCheckFlags & 0x40)) {
func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND);
} else {
this->actor.bgCheckFlags &= ~0x40;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER2);
}
}
// Decide on next action based on health, flip state and player distance
angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
if (((this->actor.colorFilterTimer == 0) && (this->actor.speedXZ == 0.0f)) &&
((this->actor.bgCheckFlags & 1) ||
((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)))) {
this->actor.world.rot.y = this->actor.shape.rot.y;
if (this->actor.colChkInfo.health == 0) {
EnTite_SetupDeathCry(this);
} else if (this->flipState == TEKTITE_FLIPPED) {
EnTite_SetupFlipUpright(this);
} else if (((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f) &&
(ABS(this->actor.shape.rot.x) < 4000) && (ABS(this->actor.shape.rot.z) < 4000)) &&
((this->actor.bgCheckFlags & 1) ||
((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)))) {
EnTite_SetupIdle(this);
} else if ((this->actor.xzDistToPlayer < 180.0f) && (this->actor.yDistToPlayer <= 80.0f) &&
(ABS(angleToPlayer) <= 6000)) {
EnTite_SetupAttack(this);
} else {
EnTite_SetupMoveTowardPlayer(this);
}
}
SkelAnime_Update(&this->skelAnime);
}
void EnTite_SetupDeathCry(EnTite* this) {
this->action = TEKTITE_DEATH_CRY;
this->actor.colorFilterTimer = 0;
this->actor.speedXZ = 0.0f;
EnTite_SetupAction(this, EnTite_DeathCry);
}
/**
* First frame of death. Scream in pain and allocate memory for EnPart data
*/
void EnTite_DeathCry(EnTite* this, GlobalContext* globalCtx) {
EffectSsDeadSound_SpawnStationary(globalCtx, &this->actor.projectedPos, NA_SE_EN_TEKU_DEAD, true,
DEADSOUND_REPEAT_MODE_OFF, 40);
this->action = TEKTITE_FALL_APART;
EnTite_SetupAction(this, EnTite_FallApart);
BodyBreak_Alloc(&this->bodyBreak, 24, globalCtx);
}
/**
* Spawn EnPart and drop items
*/
void EnTite_FallApart(EnTite* this, GlobalContext* globalCtx) {
if (BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, this->actor.params + 0xB)) {
if (this->actor.params == TEKTITE_BLUE) {
Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xE0);
} else {
Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x40);
}
Actor_Kill(&this->actor);
}
}
void EnTite_SetupFlipOnBack(EnTite* this) {
Animation_PlayLoopSetSpeed(&this->skelAnime, &object_tite_Anim_000A14, 1.5f);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_REVERSE);
this->flipState = TEKTITE_FLIPPED;
this->vOnBackTimer = 500;
this->actor.speedXZ = 0.0f;
this->actor.gravity = -1.0f;
this->vLegTwitchTimer = (Rand_ZeroOne() * 50.0f);
this->actor.velocity.y = 11.0f;
EnTite_SetupAction(this, EnTite_FlipOnBack);
}
/**
* During the flip animation and also while idling on back
*/
void EnTite_FlipOnBack(EnTite* this, GlobalContext* globalCtx) {
Math_SmoothStepToS(&this->actor.shape.rot.z, 0x7FFF, 1, 4000, 0);
// randomly reset the leg wiggling animation whenever timer reaches 0 to give illusion of twitching legs
this->vLegTwitchTimer--;
if (this->vLegTwitchTimer == 0) {
this->vLegTwitchTimer = Rand_ZeroOne() * 30.0f;
this->skelAnime.curFrame = Rand_ZeroOne() * 5.0f;
}
SkelAnime_Update(&this->skelAnime);
if (this->actor.bgCheckFlags & 3) {
// Upon landing, spawn dust and make noise
if (this->actor.bgCheckFlags & 2) {
Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 20.0f, 11, 4.0f, 0, 0, false);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND);
}
this->vOnBackTimer--;
if (this->vOnBackTimer == 0) {
EnTite_SetupFlipUpright(this);
}
} else {
// Gradually increase y offset during flip so that the actor position is at tektite's back instead of feet
if (this->actor.shape.yOffset < 2800.0f) {
this->actor.shape.yOffset += 400.0f;
}
}
}
void EnTite_SetupFlipUpright(EnTite* this) {
this->flipState = TEKTITE_UNFLIPPED;
this->actionVar1 = 1000; // value unused here and overwritten in SetupIdle
//! @bug flying tektite: water sets gravity to 0 so y velocity will never decrease from 13
this->actor.velocity.y = 13.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_REVERSE);
EnTite_SetupAction(this, EnTite_FlipUpright);
}
void EnTite_FlipUpright(EnTite* this, GlobalContext* globalCtx) {
Math_SmoothStepToS(&this->actor.shape.rot.z, 0, 1, 0xFA0, 0);
SkelAnime_Update(&this->skelAnime);
//! @bug flying tektite: the following condition is never met and tektite stays stuck in this action forever
if (this->actor.bgCheckFlags & 2) {
func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1);
func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1);
this->actor.shape.yOffset = 0.0f;
this->actor.world.pos.y = this->actor.floorHeight;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND);
EnTite_SetupIdle(this);
}
}
void EnTite_CheckDamage(Actor* thisx, GlobalContext* globalCtx) {
EnTite* this = (EnTite*)thisx;
if ((this->collider.base.acFlags & AC_HIT) && (this->action >= TEKTITE_IDLE)) {
this->collider.base.acFlags &= ~AC_HIT;
if (thisx->colChkInfo.damageEffect != 0xE) { // Immune to fire magic
this->damageEffect = thisx->colChkInfo.damageEffect;
Actor_SetDropFlag(thisx, &this->collider.elements[0].info, 0);
// Stun if Tektite hit by nut, boomerang, hookshot, ice arrow or ice magic
if ((thisx->colChkInfo.damageEffect == 1) || (thisx->colChkInfo.damageEffect == 0xF)) {
if (this->action != TEKTITE_STUNNED) {
Actor_SetColorFilter(thisx, 0, 0x78, 0, 0x50);
Actor_ApplyDamage(thisx);
EnTite_SetupStunned(this);
}
// Otherwise apply damage and handle death where necessary
} else {
if ((thisx->colorFilterTimer == 0) || ((thisx->colorFilterParams & 0x4000) == 0)) {
Actor_SetColorFilter(thisx, 0x4000, 0xFF, 0, 8);
Actor_ApplyDamage(thisx);
}
if (thisx->colChkInfo.health == 0) {
EnTite_SetupDeathCry(this);
} else {
// Flip tektite back up if it's on its back
Audio_PlayActorSound2(thisx, NA_SE_EN_TEKU_DAMAGE);
if (this->flipState != TEKTITE_FLIPPED) {
EnTite_SetupRecoil(this);
} else {
EnTite_SetupFlipUpright(this);
}
}
}
}
// If hammer has recently hit the floor and player is close to tektite, flip over
} else if ((thisx->colChkInfo.health != 0) && (globalCtx->actorCtx.unk_02 != 0) &&
(thisx->xzDistToPlayer <= 400.0f) && (thisx->bgCheckFlags & 1)) {
if (this->flipState == TEKTITE_FLIPPED) {
EnTite_SetupFlipUpright(this);
} else if ((this->action >= TEKTITE_IDLE) || (this->action >= TEKTITE_IDLE)) {
EnTite_SetupFlipOnBack(this);
}
}
}
void EnTite_Update(Actor* thisx, GlobalContext* globalCtx) {
EnTite* this = (EnTite*)thisx;
char pad[0x4];
CollisionPoly* floorPoly;
WaterBox* waterBox;
f32 waterSurfaceY;
EnTite_CheckDamage(thisx, globalCtx);
// Stay still if hit by immunity damage type this frame
if (thisx->colChkInfo.damageEffect != 0xE) {
this->actionFunc(this, globalCtx);
Actor_MoveForward(thisx);
Actor_UpdateBgCheckInfo(globalCtx, thisx, 25.0f, 40.0f, 20.0f, this->unk_2DC);
// If on water, snap feet to surface and spawn ripples
if ((this->actor.params == TEKTITE_BLUE) && (thisx->bgCheckFlags & 0x20)) {
floorPoly = thisx->floorPoly;
if ((((globalCtx->gameplayFrames % 8) == 0) || (thisx->velocity.y < 0.0f)) &&
(WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->backRightFootPos.x,
this->backRightFootPos.z, &waterSurfaceY, &waterBox)) &&
(this->backRightFootPos.y <= waterSurfaceY)) {
this->backRightFootPos.y = waterSurfaceY;
EffectSsGRipple_Spawn(globalCtx, &this->backRightFootPos, 0, 220, 0);
}
if (((((globalCtx->gameplayFrames + 2) % 8) == 0) || (thisx->velocity.y < 0.0f)) &&
(WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->backLeftFootPos.x,
this->backLeftFootPos.z, &waterSurfaceY, &waterBox)) &&
(this->backLeftFootPos.y <= waterSurfaceY)) {
this->backLeftFootPos.y = waterSurfaceY;
EffectSsGRipple_Spawn(globalCtx, &this->backLeftFootPos, 0, 220, 0);
}
if (((((globalCtx->gameplayFrames + 4) % 8) == 0) || (thisx->velocity.y < 0.0f)) &&
(WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->frontLeftFootPos.x,
this->frontLeftFootPos.z, &waterSurfaceY, &waterBox)) &&
(this->frontLeftFootPos.y <= waterSurfaceY)) {
this->frontLeftFootPos.y = waterSurfaceY;
EffectSsGRipple_Spawn(globalCtx, &this->frontLeftFootPos, 0, 220, 0);
}
if (((((globalCtx->gameplayFrames + 1) % 8) == 0) || (thisx->velocity.y < 0.0f)) &&
(WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->frontRightFootPos.x,
this->frontRightFootPos.z, &waterSurfaceY, &waterBox)) &&
(this->frontRightFootPos.y <= waterSurfaceY)) {
this->frontRightFootPos.y = waterSurfaceY;
EffectSsGRipple_Spawn(globalCtx, &this->frontRightFootPos, 0, 220, 0);
}
thisx->floorPoly = floorPoly;
}
// If on ground and currently flipped over, set tektite to be fully upside-down
if (thisx->bgCheckFlags & 3) {
func_800359B8(thisx, thisx->shape.rot.y, &thisx->shape.rot);
if (this->flipState >= TEKTITE_FLIPPED) {
thisx->shape.rot.z += 0x7FFF;
}
// Otherwise ensure the tektite is rotating back upright
} else {
Math_SmoothStepToS(&thisx->shape.rot.x, 0, 1, 1000, 0);
if (this->flipState <= TEKTITE_UNFLIPPED) {
Math_SmoothStepToS(&thisx->shape.rot.z, 0, 1, 1000, 0);
if (thisx->shape.yOffset > 0) {
thisx->shape.yOffset -= 400.0f;
}
}
}
}
thisx->focus.pos = thisx->world.pos;
thisx->focus.pos.y += 20.0f;
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
void EnTite_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** limbDList, Vec3s* rot, void* thisx) {
EnTite* this = (EnTite*)thisx;
switch (limbIndex) {
case 8:
Matrix_MultVec3f(&sFootOffset, &this->backRightFootPos);
break;
case 13:
Matrix_MultVec3f(&sFootOffset, &this->frontRightFootPos);
break;
case 18:
Matrix_MultVec3f(&sFootOffset, &this->backLeftFootPos);
break;
case 23:
Matrix_MultVec3f(&sFootOffset, &this->frontLeftFootPos);
break;
}
BodyBreak_SetInfo(&this->bodyBreak, limbIndex, 0, 24, 24, limbDList, BODYBREAK_OBJECT_DEFAULT);
}
void EnTite_Draw(Actor* thisx, GlobalContext* globalCtx) {
EnTite* this = (EnTite*)thisx;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
Collider_UpdateSpheres(0, &this->collider);
if (this->actor.params == TEKTITE_BLUE) {
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001300));
gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001700));
gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001900));
} else {
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001B00));
gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001F00));
gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(object_tite_Tex_002100));
}
SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, EnTite_PostLimbDraw,
thisx);
CLOSE_DISPS(globalCtx->state.gfxCtx);
if (this->spawnIceTimer != 0) {
// Spawn chunks of ice all over the tektite's body
thisx->colorFilterTimer++;
this->spawnIceTimer--;
if ((this->spawnIceTimer & 3) == 0) {
Vec3f iceChunk;
s32 idx = this->spawnIceTimer >> 2;
iceChunk.x = thisx->world.pos.x + sIceChunks[idx].x;
iceChunk.y = thisx->world.pos.y + sIceChunks[idx].y;
iceChunk.z = thisx->world.pos.z + sIceChunks[idx].z;
EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &iceChunk, 150, 150, 150, 250, 235, 245, 255, 1.0f);
}
}
}