/* * File: z_en_zf.c * Overlay: ovl_En_Zf * Description: Lizalfos and Dinolfos */ #include "z_en_zf.h" #include "objects/object_zf/object_zf.h" #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) void EnZf_Init(Actor* thisx, GlobalContext* globalCtx); void EnZf_Destroy(Actor* thisx, GlobalContext* globalCtx); void EnZf_Update(Actor* thisx, GlobalContext* globalCtx); void EnZf_Draw(Actor* thisx, GlobalContext* globalCtx); void EnZf_Reset(void); s16 EnZf_FindPlatform(Vec3f* pos, s16 preferredIndex); void EnZf_SetupDropIn(EnZf* this); void EnZf_DropIn(EnZf* this, GlobalContext* globalCtx); void func_80B45384(EnZf* this); void func_80B4543C(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupApproachPlayer(EnZf* this, GlobalContext* globalCtx); void EnZf_ApproachPlayer(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupJumpForward(EnZf* this); void EnZf_JumpForward(EnZf* this, GlobalContext* globalCtx); void func_80B4604C(EnZf* this); void func_80B46098(EnZf* this, GlobalContext* globalCtx); void func_80B462E4(EnZf* this, GlobalContext* globalCtx); void func_80B463E4(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupSlash(EnZf* this); void EnZf_Slash(EnZf* this, GlobalContext* globalCtx); void EnZf_RecoilFromBlockedSlash(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupJumpBack(EnZf* this); void EnZf_JumpBack(EnZf* this, GlobalContext* globalCtx); void EnZf_Stunned(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupSheatheSword(EnZf* this, GlobalContext* globalCtx); void EnZf_SheatheSword(EnZf* this, GlobalContext* globalCtx); void EnZf_HopAndTaunt(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupHopAway(EnZf* this, GlobalContext* globalCtx); void EnZf_HopAway(EnZf* this, GlobalContext* globalCtx); void EnZf_DrawSword(EnZf* this, GlobalContext* globalCtx); void EnZf_Damaged(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupJumpUp(EnZf* this); void EnZf_JumpUp(EnZf* this, GlobalContext* globalCtx); void func_80B483E4(EnZf* this, GlobalContext* globalCtx); void EnZf_CircleAroundPlayer(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupDie(EnZf* this); void EnZf_Die(EnZf* this, GlobalContext* globalCtx); void EnZf_SetupCircleAroundPlayer(EnZf* this, f32 speed); s32 EnZf_DodgeRangedEngaging(GlobalContext* globalCtx, EnZf* this); s32 EnZf_DodgeRangedWaiting(GlobalContext* globalCtx, EnZf* this); #define PLATFORM_INDEX_DOWNSTAIRS_MIN 0 #define PLATFORM_INDEX_DOWNSTAIRS_INNER_MAX 5 #define PLATFORM_INDEX_DOWNSTAIRS_MAX 7 #define PLATFORM_INDEX_UPSTAIRS_MIN (PLATFORM_INDEX_DOWNSTAIRS_MAX + 1) #define PLATFORM_INDEX_UPSTAIRS_INNER_MAX 15 /** * Array of platform positions in Dodongo's Cavern miniboss room. * 0 - 7 : Downstairs * 8 - 15 : Upstairs inner platforms * 16 - 23 : Upstairs outer platforms (including several points on the long thin one) */ static Vec3f sPlatformPositions[] = { // Downstairs { 3560.0f, 100.0f, -1517.0f }, { 3170.0f, 100.0f, -1767.0f }, { 3165.0f, 100.0f, -2217.0f }, { 3563.0f, 100.0f, -2437.0f }, { 3946.0f, 100.0f, -2217.0f }, { 3942.0f, 100.0f, -1765.0f }, // Westmost downstairs two { 2861.0f, 100.0f, -2394.0f }, { 2776.0f, 100.0f, -1987.0f }, // Upstairs inner { 4527.0f, 531.0f, -1146.0f }, { 4442.0f, 531.0f, -1405.0f }, { 4170.0f, 531.0f, -1395.0f }, { 4030.0f, 531.0f, -1162.0f }, { 4010.0f, 531.0f, -883.0f }, { 4270.0f, 531.0f, -810.0f }, { 4520.0f, 531.0f, -880.0f }, { 4260.0f, 531.0f, -1035.0f }, // Upstairs outer { 4757.0f, 531.0f, -1146.0f }, { 3850.0f, 531.0f, -883.0f }, { 4380.0f, 531.0f, -690.0f }, { 4197.0f, 531.0f, -646.0f }, { 4070.0f, 531.0f, -1575.0f }, { 3930.0f, 531.0f, -1705.0f }, { 3780.0f, 531.0f, -1835.0f }, { 3560.0f, 531.0f, -1985.0f }, }; // These seem to relate to the tagging in/out the minibosses do s16 D_80B4A1B0 = 0; s16 D_80B4A1B4 = 1; const ActorInit En_Zf_InitVars = { ACTOR_EN_ZF, ACTORCAT_ENEMY, FLAGS, OBJECT_ZF, sizeof(EnZf), (ActorFunc)EnZf_Init, (ActorFunc)EnZf_Destroy, (ActorFunc)EnZf_Update, (ActorFunc)EnZf_Draw, (ActorResetFunc)EnZf_Reset, }; static ColliderCylinderInit sBodyCylinderInit = { { COLTYPE_HIT0, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER, OC2_TYPE_1, COLSHAPE_CYLINDER, }, { ELEMTYPE_UNK1, { 0x00000000, 0x00, 0x00 }, { 0xFFCFFFFF, 0x00, 0x00 }, TOUCH_NONE, BUMP_ON, OCELEM_ON, }, { 20, 70, 0, { 0, 0, 0 } }, }; static ColliderQuadInit sSwordQuadInit = { { COLTYPE_NONE, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_HARD | AC_TYPE_PLAYER, OC1_NONE, OC2_NONE, COLSHAPE_QUAD, }, { ELEMTYPE_UNK0, { 0xFFCFFFFF, 0x00, 0x08 }, { 0x00000000, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, BUMP_ON, 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 } } }, }; typedef enum { /* 0x0 */ ENZF_DMGEFF_NONE, /* 0x1 */ ENZF_DMGEFF_STUN, /* 0x6 */ ENZF_DMGEFF_IMMUNE = 6, // Skips damage code, but also skips the top half of Update /* 0xD */ ENZF_DMGEFF_PROJECTILE = 0xD, // Projectiles that don't have another damageeffect /* 0xF */ ENZF_DMGEFF_ICE = 0xF } EnZfDamageEffect; static DamageTable sDamageTable = { /* Deku nut */ DMG_ENTRY(0, ENZF_DMGEFF_STUN), /* Deku stick */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), /* Slingshot */ DMG_ENTRY(1, ENZF_DMGEFF_PROJECTILE), /* Explosive */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), /* Boomerang */ DMG_ENTRY(0, ENZF_DMGEFF_STUN), /* Normal arrow */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), /* Hammer swing */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), /* Hookshot */ DMG_ENTRY(0, ENZF_DMGEFF_STUN), /* Kokiri sword */ DMG_ENTRY(1, ENZF_DMGEFF_NONE), /* Master sword */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), /* Giant's Knife */ DMG_ENTRY(4, ENZF_DMGEFF_NONE), /* Fire arrow */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), /* Ice arrow */ DMG_ENTRY(4, ENZF_DMGEFF_ICE), /* Light arrow */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), /* Unk arrow 1 */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), /* Unk arrow 2 */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), /* Unk arrow 3 */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), /* Fire magic */ DMG_ENTRY(0, ENZF_DMGEFF_IMMUNE), /* Ice magic */ DMG_ENTRY(3, ENZF_DMGEFF_ICE), /* Light magic */ DMG_ENTRY(0, ENZF_DMGEFF_IMMUNE), /* Shield */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), /* Mirror Ray */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), /* Kokiri spin */ DMG_ENTRY(1, ENZF_DMGEFF_NONE), /* Giant spin */ DMG_ENTRY(4, ENZF_DMGEFF_NONE), /* Master spin */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), /* Kokiri jump */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), /* Giant jump */ DMG_ENTRY(8, ENZF_DMGEFF_NONE), /* Master jump */ DMG_ENTRY(4, ENZF_DMGEFF_NONE), /* Unknown 1 */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), /* Unblockable */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), /* Hammer jump */ DMG_ENTRY(4, ENZF_DMGEFF_NONE), /* Unknown 2 */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), }; static InitChainEntry sInitChain[] = { ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), ICHAIN_VEC3F_DIV1000(scale, 15, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(gravity, -3500, ICHAIN_STOP), }; static AnimationHeader* sHoppingAnims[] = { &gZfHopCrouchingAnim, &gZfHopLeapingAnim, &gZfHopLandingAnim }; static s32 D_80B4AB30; // Set to 0 and incremented in EnZf_HopAway, but not actually used void EnZf_SetupAction(EnZf* this, EnZfActionFunc actionFunc) { this->actionFunc = actionFunc; } /** * Tests if it will still be on a floor after moving forwards a distance determined by dist, in the shape forward * direction. If `dist` is 0, it defaults to a dist depending on speed direction, and params. */ s32 EnZf_PrimaryFloorCheck(EnZf* this, GlobalContext* globalCtx, f32 dist) { s16 ret; s16 curBgCheckFlags; f32 sin; f32 cos; Vec3f curPos; if (dist == 0.0f) { dist = ((this->actor.speedXZ >= 0.0f) ? 1.0f : -1.0f); dist = ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) ? dist * 45.0f : dist * 30.0f); } // Save currents to restore later curPos = this->actor.world.pos; curBgCheckFlags = this->actor.bgCheckFlags; sin = Math_SinS(this->actor.world.rot.y) * dist; cos = Math_CosS(this->actor.world.rot.y) * dist; this->actor.world.pos.x += sin; this->actor.world.pos.z += cos; Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 0x1C); this->actor.world.pos = curPos; ret = !(this->actor.bgCheckFlags & 1); this->actor.bgCheckFlags = curBgCheckFlags; return ret; } /** * Supplementary floor test. */ s16 EnZf_SecondaryFloorCheck(EnZf* this, GlobalContext* globalCtx, f32 dist) { s16 ret; s16 curBgCheckFlags; f32 sin; f32 cos; Vec3f curPos; if ((this->actor.speedXZ != 0.0f) && EnZf_PrimaryFloorCheck(this, globalCtx, this->actor.speedXZ)) { return true; } // Save currents to restore later curPos = this->actor.world.pos; curBgCheckFlags = this->actor.bgCheckFlags; sin = Math_SinS(this->actor.shape.rot.y) * dist; cos = Math_CosS(this->actor.shape.rot.y) * dist; this->actor.world.pos.x += sin; this->actor.world.pos.z += cos; Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 0x1C); this->actor.world.pos = curPos; ret = !(this->actor.bgCheckFlags & 1); this->actor.bgCheckFlags = curBgCheckFlags; return ret; } void EnZf_Init(Actor* thisx, GlobalContext* globalCtx) { s32 pad; EnZf* this = (EnZf*)thisx; Player* player = GET_PLAYER(globalCtx); EffectBlureInit1 blureInit; f32 posDiff; Actor_ProcessInitChain(thisx, sInitChain); thisx->targetMode = 3; this->clearFlag = (thisx->params & 0xFF00) >> 8; /* Strip the top byte of params */ thisx->params &= 0xFF; /* Return the params to their original value if they were originally negative, i.e. 0xFFFF or 0xFFFE */ if (thisx->params & 0x80) { thisx->params |= 0xFF00; } ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawFeet, 90.0f); this->unk_3E0 = 0; thisx->colChkInfo.mass = MASS_HEAVY; thisx->colChkInfo.damageTable = &sDamageTable; blureInit.p1StartColor[0] = blureInit.p1StartColor[1] = blureInit.p1StartColor[2] = blureInit.p1StartColor[3] = blureInit.p2StartColor[0] = blureInit.p2StartColor[1] = blureInit.p2StartColor[2] = blureInit.p1EndColor[0] = blureInit.p1EndColor[1] = blureInit.p1EndColor[2] = blureInit.p2EndColor[0] = blureInit.p2EndColor[1] = blureInit.p2EndColor[2] = 255; blureInit.p2StartColor[3] = 64; blureInit.p1EndColor[3] = blureInit.p2EndColor[3] = 0; blureInit.elemDuration = 8; blureInit.unkFlag = 0; blureInit.calcMode = 2; Effect_Add(globalCtx, &this->blureIndex, EFFECT_BLURE1, 0, 0, &blureInit); Actor_UpdateBgCheckInfo(globalCtx, thisx, 75.0f, 45.0f, 45.0f, 0x1D); this->alpha = 255; thisx->colChkInfo.cylRadius = 40; thisx->colChkInfo.cylHeight = 100; Collider_InitCylinder(globalCtx, &this->bodyCollider); Collider_SetCylinder(globalCtx, &this->bodyCollider, thisx, &sBodyCylinderInit); Collider_InitQuad(globalCtx, &this->swordCollider); Collider_SetQuad(globalCtx, &this->swordCollider, thisx, &sSwordQuadInit); if (thisx->params == ENZF_TYPE_DINOLFOS) { thisx->colChkInfo.health = 12; thisx->naviEnemyId = 0x10; SkelAnime_Init(globalCtx, &this->skelAnime, &gZfDinolfosSkel, &gZfCryingAnim, this->jointTable, this->morphTable, ENZF_LIMB_MAX); } else { // Lizalfos thisx->colChkInfo.health = 6; thisx->naviEnemyId = 0x0F; SkelAnime_Init(globalCtx, &this->skelAnime, &gZfLizalfosSkel, &gZfCryingAnim, this->jointTable, this->morphTable, ENZF_LIMB_MAX); } if (thisx->params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // Not minibosses this->homePlatform = this->curPlatform = -1; D_80B4A1B4 = -1; this->hopAnimIndex = 1; if (thisx->params == ENZF_TYPE_LIZALFOS_LONE) { EnZf_SetupDropIn(this); } else { // Dinolfos func_80B45384(this); } } else { // Minibosses posDiff = player->actor.world.pos.y - thisx->world.pos.y; if ((ABS(posDiff) <= 100.0f) && !Flags_GetSwitch(globalCtx, this->clearFlag)) { this->homePlatform = this->curPlatform = EnZf_FindPlatform(&thisx->world.pos, 0); EnZf_SetupDropIn(this); D_80B4A1B4 = 1; } else { Actor_Kill(thisx); } } } void EnZf_Destroy(Actor* thisx, GlobalContext* globalCtx) { EnZf* this = (EnZf*)thisx; if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && (Actor_FindNearby(globalCtx, &this->actor, ACTOR_EN_ZF, ACTORCAT_ENEMY, 10000.0f) == NULL)) { func_800F5B58(); } Effect_Delete(globalCtx, this->blureIndex); Collider_DestroyCylinder(globalCtx, &this->bodyCollider); Collider_DestroyQuad(globalCtx, &this->swordCollider); } /** * Finds the index of the platform position in `sPlatformPositions` that is sufficiently close to `pos`. * Returns `preferedIndex` if the associated position is close enough. */ s16 EnZf_FindPlatform(Vec3f* pos, s16 preferredIndex) { f32 rangeXZ; s16 i; rangeXZ = 210.0f; // Upstairs has a smaller range if (pos->y >= 420.0f) { rangeXZ = 110.0f; } if (preferredIndex != -1) { i = preferredIndex; if (((sPlatformPositions[i].y - 150.0f) <= pos->y) && (pos->y <= (sPlatformPositions[i].y + 150.0f)) && ((sPlatformPositions[i].x - rangeXZ) <= pos->x) && (pos->x <= (sPlatformPositions[i].x + rangeXZ)) && ((sPlatformPositions[i].z - rangeXZ) <= pos->z) && (pos->z <= (sPlatformPositions[i].z + rangeXZ))) { return preferredIndex; } } for (i = ARRAY_COUNT(sPlatformPositions) - 1; i > -1; i--) { if (((sPlatformPositions[i].y - 150.0f) <= pos->y) && (pos->y <= (sPlatformPositions[i].y + 150.0f)) && ((sPlatformPositions[i].x - rangeXZ) <= pos->x) && (pos->x <= (sPlatformPositions[i].x + rangeXZ)) && ((sPlatformPositions[i].z - rangeXZ) <= pos->z) && (pos->z <= (sPlatformPositions[i].z + rangeXZ))) { break; } } return i; } s16 EnZf_FindNextPlatformAwayFromPlayer(Vec3f* pos, s16 curPlatform, s16 arg2, GlobalContext* globalCtx) { f32 distToCurLoopPlatform; f32 platformMinDist = 585.0f; Player* player = GET_PLAYER(globalCtx); s16 initialPlatform = curPlatform; f32 playerMaxDist = 400.0f; f32 smallMaxRange = 99998.0f; s16 curLoopPlatform = PLATFORM_INDEX_DOWNSTAIRS_INNER_MAX; // Will never retreat to the last two s16 minIndex = PLATFORM_INDEX_DOWNSTAIRS_MIN; f32 largeMaxRange = 99999.0f; s16 altNextPlatform = -1; s16 nextPlatform = -1; s16 playerPlatform = EnZf_FindPlatform(&player->actor.world.pos, initialPlatform); // Set up search constraints // Upstairs if (pos->y > 420.0f) { minIndex = PLATFORM_INDEX_UPSTAIRS_MIN; playerMaxDist = 50.0f; // Upstairs outer if (initialPlatform >= PLATFORM_INDEX_UPSTAIRS_INNER_MAX) { curLoopPlatform = ARRAY_COUNT(sPlatformPositions) - 1; platformMinDist = 400.0f; } else { // upstairs inner curLoopPlatform = PLATFORM_INDEX_UPSTAIRS_INNER_MAX - 1; platformMinDist = 380.0f; } } for (; curLoopPlatform >= minIndex; curLoopPlatform--) { if ((curLoopPlatform == initialPlatform) || (curLoopPlatform == playerPlatform)) { continue; } if ((playerPlatform == -1) && (Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[curLoopPlatform]) < playerMaxDist)) { continue; } distToCurLoopPlatform = Math_Vec3f_DistXYZ(pos, &sPlatformPositions[curLoopPlatform]); if (platformMinDist < distToCurLoopPlatform) { continue; } if (distToCurLoopPlatform < smallMaxRange) { largeMaxRange = smallMaxRange; altNextPlatform = nextPlatform; smallMaxRange = distToCurLoopPlatform; nextPlatform = curLoopPlatform; } else if (distToCurLoopPlatform < largeMaxRange) { largeMaxRange = distToCurLoopPlatform; altNextPlatform = curLoopPlatform; } } // These functions have no side effects, so these two calls do nothing Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[nextPlatform]); Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[altNextPlatform]); if (altNextPlatform > 0) { s16 nextPlatformToPlayerYaw = Math_Vec3f_Yaw(pos, &sPlatformPositions[nextPlatform]) - Math_Vec3f_Yaw(pos, &player->actor.world.pos); if (ABS(nextPlatformToPlayerYaw) < 0x36B0) { nextPlatform = altNextPlatform; } } if (nextPlatform < 0) { nextPlatform = arg2; } return nextPlatform; } s16 EnZf_FindNextPlatformTowardsPlayer(Vec3f* pos, s16 curPlatform, s16 arg2, GlobalContext* globalCtx) { s16 curLoopPlatform = PLATFORM_INDEX_DOWNSTAIRS_MAX; s16 minIndex = PLATFORM_INDEX_DOWNSTAIRS_MIN; Player* player = GET_PLAYER(globalCtx); s16 nextPlatform = EnZf_FindPlatform(&player->actor.world.pos, -1); f32 minRange = 500.0f; f32 smallMaxRange = 99998.0f; f32 largeMaxRange = 99999.0f; s16 phi_s2 = curPlatform; s16 phi_s3 = arg2; // Upstairs if (pos->y > 200.0f) { curLoopPlatform = ARRAY_COUNT(sPlatformPositions) - 1; minIndex = PLATFORM_INDEX_UPSTAIRS_MIN; minRange = 290.0f; } for (; curLoopPlatform >= minIndex; curLoopPlatform--) { if (minRange < Math_Vec3f_DistXYZ(pos, &sPlatformPositions[curLoopPlatform])) { continue; } if (curLoopPlatform != nextPlatform) { f32 curPlatformDistToPlayer = Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[curLoopPlatform]); if (curPlatformDistToPlayer < smallMaxRange) { largeMaxRange = smallMaxRange; phi_s3 = phi_s2; smallMaxRange = curPlatformDistToPlayer; phi_s2 = curLoopPlatform; } else if (curPlatformDistToPlayer < largeMaxRange) { largeMaxRange = curPlatformDistToPlayer; phi_s3 = curLoopPlatform; } } else { phi_s2 = nextPlatform; break; } } if (phi_s3 != nextPlatform) { nextPlatform = phi_s2; } else { nextPlatform = phi_s3; } return nextPlatform; } // Player not targeting this or another EnZf? s32 EnZf_CanAttack(GlobalContext* globalCtx, EnZf* this) { Actor* targetedActor; Player* player = GET_PLAYER(globalCtx); if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss if (player->stateFlags1 & 0x6000) { // Hanging or climbing return false; } else { return true; } } else { if (!Actor_OtherIsTargeted(globalCtx, &this->actor)) { return true; } if (this->actor.params == ENZF_TYPE_DINOLFOS) { targetedActor = player->unk_664; if (targetedActor == NULL) { return false; } else { if (targetedActor->category != ACTORCAT_ENEMY) { return true; } if (targetedActor->id != ACTOR_EN_ZF) { return false; } else if (targetedActor->colorFilterTimer != 0) { return true; } } } } return false; } void func_80B44DC4(EnZf* this, GlobalContext* globalCtx) { s16 angleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; if (angleDiff < 0) { angleDiff = -angleDiff; } if (angleDiff >= 0x1B58) { func_80B483E4(this, globalCtx); } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 8) != 0) && EnZf_CanAttack(globalCtx, this)) { EnZf_SetupSlash(this); } else { func_80B45384(this); } } s32 EnZf_ChooseAction(GlobalContext* globalCtx, EnZf* this) { s16 angleToWall; Actor* explosive; angleToWall = this->actor.wallYaw - this->actor.shape.rot.y; angleToWall = ABS(angleToWall); if (func_800354B4(globalCtx, &this->actor, 100.0f, 0x5DC0, 0x2AA8, this->actor.shape.rot.y)) { this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; if ((this->actor.bgCheckFlags & 8) && (ABS(angleToWall) < 0x2EE0) && (this->actor.xzDistToPlayer < 80.0f)) { EnZf_SetupJumpUp(this); return true; } else if ((this->actor.xzDistToPlayer < 90.0f) && ((globalCtx->gameplayFrames % 2) != 0)) { EnZf_SetupJumpUp(this); return true; } else { EnZf_SetupJumpBack(this); return true; } } explosive = Actor_FindNearby(globalCtx, &this->actor, -1, ACTORCAT_EXPLOSIVE, 80.0f); if (explosive != NULL) { this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; if (((this->actor.bgCheckFlags & 8) && (angleToWall < 0x2EE0)) || (explosive->id == ACTOR_EN_BOM_CHU)) { if ((explosive->id == ACTOR_EN_BOM_CHU) && (Actor_WorldDistXYZToActor(&this->actor, explosive) < 80.0f) && ((s16)((this->actor.shape.rot.y - explosive->world.rot.y) + 0x8000) < 0x3E80)) { EnZf_SetupJumpUp(this); return true; } else { EnZf_SetupCircleAroundPlayer(this, 4.0f); return true; } } else { EnZf_SetupJumpBack(this); return true; } } return false; } // Setup functions and action functions /** * Set position 300 units above ground and invisible, fade in and drop to ground, fully solid when on ground */ void EnZf_SetupDropIn(EnZf* this) { Animation_Change(&this->skelAnime, &gZfJumpingAnim, 0.0f, 9.0f, Animation_GetLastFrame(&gZfJumpingAnim), ANIMMODE_LOOP, 0.0f); this->actor.world.pos.y = this->actor.floorHeight + 300.0f; this->alpha = this->actor.shape.shadowAlpha = 0; this->unk_3F0 = 10; this->hopAnimIndex = 1; this->action = ENZF_ACTION_DROP_IN; this->actor.bgCheckFlags &= ~2; this->actor.flags &= ~ACTOR_FLAG_0; this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; EnZf_SetupAction(this, EnZf_DropIn); } void EnZf_DropIn(EnZf* this, GlobalContext* globalCtx) { if (this->unk_3F0 == 1) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); this->actor.flags |= ACTOR_FLAG_0; if (this->actor.params == ENZF_TYPE_LIZALFOS_MINIBOSS_A) { func_800F5ACC(NA_BGM_MINI_BOSS); } } if (this->unk_3F0 != 0) { if (this->actor.params != ENZF_TYPE_LIZALFOS_LONE) { this->unk_3F0--; } else if (this->actor.xzDistToPlayer <= 160.0f) { this->unk_3F0 = 0; this->actor.flags |= ACTOR_FLAG_0; Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } this->actor.world.pos.y = this->actor.floorHeight + 300.0f; } else if (this->alpha < 255) { this->alpha += 255 / 5; } if ((this->actor.bgCheckFlags & 3) && (this->hopAnimIndex != 0)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_ONGND); Animation_Change(&this->skelAnime, &gZfLandingAnim, 1.0f, 0.0f, 17.0f, ANIMMODE_ONCE, 0.0f); this->hopAnimIndex = 0; this->actor.bgCheckFlags &= ~2; this->actor.world.pos.y = this->actor.floorHeight; this->actor.velocity.y = 0.0f; Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftFootPos, 3.0f, 2, 2.0f, 0, 0, 0); Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightFootPos, 3.0f, 2, 2.0f, 0, 0, 0); } if (SkelAnime_Update(&this->skelAnime)) { this->alpha = 255; if (this->actor.params > ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // Only miniboss B EnZf_SetupSheatheSword(this, globalCtx); } else { func_80B45384(this); } } this->actor.shape.shadowAlpha = this->alpha; } // stop? and choose an action void func_80B45384(EnZf* this) { Animation_Change(&this->skelAnime, &gZfCryingAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gZfCryingAnim), ANIMMODE_LOOP_INTERP, -4.0f); this->action = ENZF_ACTION_3; this->unk_3F0 = Rand_ZeroOne() * 10.0f + 5.0f; this->actor.speedXZ = 0.0f; this->actor.world.rot.y = this->actor.shape.rot.y; EnZf_SetupAction(this, func_80B4543C); } void func_80B4543C(EnZf* this, GlobalContext* globalCtx) { Player* player = GET_PLAYER(globalCtx); s32 pad; s16 angleToPlayer = (this->actor.yawTowardsPlayer - this->headRot) - this->actor.shape.rot.y; angleToPlayer = ABS(angleToPlayer); SkelAnime_Update(&this->skelAnime); if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { if (this->actor.params == ENZF_TYPE_DINOLFOS) { if (this->unk_3F4 != 0) { this->unk_3F4--; if (angleToPlayer >= 0x1FFE) { return; } this->unk_3F4 = 0; } else if (EnZf_ChooseAction(globalCtx, this)) { return; } } angleToPlayer = player->actor.shape.rot.y - this->actor.shape.rot.y; angleToPlayer = ABS(angleToPlayer); if ((this->actor.xzDistToPlayer < 100.0f) && (player->swordState != 0) && (angleToPlayer >= 0x1F40)) { this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; func_80B483E4(this, globalCtx); } else if (this->unk_3F0 != 0) { this->unk_3F0--; } else { if (Actor_IsFacingPlayer(&this->actor, 30 * 0x10000 / 360)) { if ((this->actor.xzDistToPlayer < 200.0f) && (this->actor.xzDistToPlayer > 100.0f) && (Rand_ZeroOne() < 0.3f)) { if (this->actor.params == ENZF_TYPE_DINOLFOS) { this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; EnZf_SetupJumpForward(this); } else { func_80B483E4(this, globalCtx); } } else if (Rand_ZeroOne() > 0.3f) { EnZf_SetupApproachPlayer(this, globalCtx); } else { func_80B483E4(this, globalCtx); } } else { func_80B4604C(this); } if ((globalCtx->gameplayFrames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } } } } void EnZf_SetupApproachPlayer(EnZf* this, GlobalContext* globalCtx) { Animation_MorphToLoop(&this->skelAnime, &gZfWalkingAnim, -4.0f); this->action = ENZF_ACTION_APPROACH_PLAYER; if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); this->nextPlatform = EnZf_FindNextPlatformTowardsPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); this->hopAnimIndex = 0; } this->actor.speedXZ = 0.0f; EnZf_SetupAction(this, EnZf_ApproachPlayer); } void EnZf_ApproachPlayer(EnZf* this, GlobalContext* globalCtx) { s32 sp54; s32 sp50; s32 temp; s16 temp_v1; s16 sp48 = -1; f32 sp44 = 350.0f; f32 sp40 = 0.0f; Player* player = GET_PLAYER(globalCtx); s32 sp30; if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss sp48 = EnZf_FindPlatform(&player->actor.world.pos, sp48); this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, sp48); if (this->actor.world.pos.y >= 420.0f) { sp44 = 270.0f; } } if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { if (sp48 != this->curPlatform) { this->nextPlatform = EnZf_FindNextPlatformTowardsPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); if ((sp48 < 0) && (this->nextPlatform == this->curPlatform)) { sp48 = this->curPlatform; this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; } else { this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer = Actor_WorldYawTowardPoint(&this->actor, &sPlatformPositions[this->nextPlatform]); temp_v1 = this->actor.wallYaw - this->actor.shape.rot.y; temp_v1 = ABS(temp_v1); if ((this->unk_3F8 && (this->actor.speedXZ > 0.0f)) || ((this->actor.bgCheckFlags & 8) && (temp_v1 >= 0x5C19))) { if ((Actor_WorldDistXZToPoint(&this->actor, &sPlatformPositions[this->nextPlatform]) < sp44) && !EnZf_PrimaryFloorCheck(this, globalCtx, 191.9956f)) { EnZf_SetupJumpForward(this); if (this->actor.bgCheckFlags & 8) { this->actor.velocity.y = 20.0f; } return; } else { this->actor.world.rot.y = Actor_WorldYawTowardPoint(&this->actor, &sPlatformPositions[this->curPlatform]); } } else { this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; Math_SmoothStepToF(&this->actor.speedXZ, 8.0f, 1.0f, 1.5f, 0.0f); } } } if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { sp40 = 100.0f; } if (this->actor.xzDistToPlayer <= (70.0f + sp40)) { Math_SmoothStepToF(&this->actor.speedXZ, -8.0f, 1.0f, 0.5f, 0.0f); } else { Math_SmoothStepToF(&this->actor.speedXZ, 8.0f, 1.0f, 0.5f, 0.0f); } this->skelAnime.playSpeed = this->actor.speedXZ * 1.2f; temp_v1 = player->actor.shape.rot.y - this->actor.shape.rot.y; temp_v1 = ABS(temp_v1); if ((sp48 == this->curPlatform) && (this->actor.xzDistToPlayer < 150.0f) && (player->swordState != 0) && (temp_v1 >= 0x1F40)) { this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; if (Rand_ZeroOne() > 0.7f) { func_80B483E4(this, globalCtx); return; } } sp54 = this->skelAnime.curFrame; SkelAnime_Update(&this->skelAnime); sp50 = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); sp30 = (f32)ABS(this->skelAnime.playSpeed); if (sp48 == this->curPlatform) { if (!Actor_IsFacingPlayer(&this->actor, 0x11C7)) { if (Rand_ZeroOne() > 0.5f) { func_80B462E4(this, globalCtx); } else { func_80B45384(this); } } else if (this->actor.xzDistToPlayer < 100.0f) { if ((Rand_ZeroOne() > 0.05f) && EnZf_CanAttack(globalCtx, this)) { EnZf_SetupSlash(this); } else if (Rand_ZeroOne() > 0.5f) { func_80B483E4(this, globalCtx); } else { func_80B45384(this); } } else { if (this->unk_3F8) { func_80B462E4(this, globalCtx); } else if (Rand_ZeroOne() < 0.1f) { func_80B45384(this); } } } if (this->actor.params == ENZF_TYPE_DINOLFOS) { if (EnZf_ChooseAction(globalCtx, this)) { return; } if ((this->actor.xzDistToPlayer < 180.0f) && (this->actor.xzDistToPlayer > 160.0f) && Actor_IsFacingPlayer(&this->actor, 0x71C)) { if (Actor_IsTargeted(globalCtx, &this->actor)) { if (Rand_ZeroOne() < 0.1f) { this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; EnZf_SetupJumpForward(this); return; } } else { func_80B483E4(this, globalCtx); return; } } } if ((globalCtx->gameplayFrames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } if (sp54 != (s32)this->skelAnime.curFrame) { temp = sp30 + sp54; if (((sp50 < 2) && (temp >= 4)) || ((sp50 < 32) && (temp >= 34))) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); } } } } void EnZf_SetupJumpForward(EnZf* this) { Animation_Change(&this->skelAnime, &gZfJumpingAnim, 1.0f, 0.0f, 3.0f, ANIMMODE_ONCE, -3.0f); this->unk_3F0 = 0; this->hopAnimIndex = 1; this->actor.velocity.y = 15.0f; if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss this->actor.speedXZ = 16.0f; } else { this->actor.speedXZ = 10.0f; } this->action = ENZF_ACTION_JUMP_FORWARD; Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); EnZf_SetupAction(this, EnZf_JumpForward); } void EnZf_JumpForward(EnZf* this, GlobalContext* globalCtx) { if ((this->unk_3F0 != 0) && (this->actor.world.pos.y <= this->actor.floorHeight)) { this->actor.world.pos.y = this->actor.floorHeight; this->hopAnimIndex = 0; this->actor.velocity.y = 0.0f; this->actor.speedXZ = 0.0f; } if (SkelAnime_Update(&this->skelAnime)) { if (this->unk_3F0 == 0) { Animation_Change(&this->skelAnime, &gZfLandingAnim, 3.0f, 0.0f, 17.0f, ANIMMODE_ONCE, -3.0f); this->unk_3F0 = 10; Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); } else { this->actor.speedXZ = 0.0f; this->hopAnimIndex = 0; EnZf_SetupApproachPlayer(this, globalCtx); } } if ((globalCtx->gameplayFrames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } if ((this->actor.params == ENZF_TYPE_DINOLFOS) && (this->actor.bgCheckFlags & 3)) { if (EnZf_CanAttack(globalCtx, this)) { EnZf_SetupSlash(this); } else { func_80B483E4(this, globalCtx); } } } void func_80B4604C(EnZf* this) { Animation_MorphToLoop(&this->skelAnime, &gZfWalkingAnim, -4.0f); this->action = ENZF_ACTION_6; EnZf_SetupAction(this, func_80B46098); } void func_80B46098(EnZf* this, GlobalContext* globalCtx) { s32 pad; f32 phi_f2; Player* player = GET_PLAYER(globalCtx); s16 temp_v0; s16 phi_v1; if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { if ((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this)) { temp_v0 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; if (temp_v0 > 0) { phi_v1 = temp_v0 * 0.25f + 2000.0f; } else { phi_v1 = temp_v0 * 0.25f - 2000.0f; } this->actor.shape.rot.y += phi_v1; this->actor.world.rot.y = this->actor.shape.rot.y; if (temp_v0 > 0) { phi_f2 = phi_v1 * 1.5f; if (phi_f2 > 2.0f) { phi_f2 = 2.0f; } } else { phi_f2 = phi_v1 * 1.5f; if (phi_f2 < -2.0f) { phi_f2 = -2.0f; } } this->skelAnime.playSpeed = -phi_f2; SkelAnime_Update(&this->skelAnime); if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); if (this->curPlatform != EnZf_FindPlatform(&player->actor.world.pos, -1)) { EnZf_SetupApproachPlayer(this, globalCtx); return; } } if (Actor_IsFacingPlayer(&this->actor, 30 * 0x10000 / 360)) { if (Rand_ZeroOne() > 0.8f) { func_80B462E4(this, globalCtx); } else { EnZf_SetupApproachPlayer(this, globalCtx); } } if ((globalCtx->gameplayFrames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } } } } // Conditional setup function void func_80B462E4(EnZf* this, GlobalContext* globalCtx) { if ((this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ || Actor_TestFloorInDirection(&this->actor, globalCtx, 40.0f, (s16)(this->actor.shape.rot.y + 0x3FFF)) || Actor_TestFloorInDirection(&this->actor, globalCtx, -40.0f, (s16)(this->actor.shape.rot.y + 0x3FFF))) { Animation_PlayLoop(&this->skelAnime, &gZfSidesteppingAnim); this->actor.speedXZ = Rand_CenteredFloat(12.0f); this->actor.world.rot.y = this->actor.shape.rot.y; this->unk_3F0 = Rand_ZeroOne() * 10.0f + 20.0f; this->hopAnimIndex = 0; this->action = ENZF_ACTION_7; this->unk_408 = 0.0f; EnZf_SetupAction(this, func_80B463E4); } else { EnZf_SetupApproachPlayer(this, globalCtx); } } void func_80B463E4(EnZf* this, GlobalContext* globalCtx) { s16 angleBehindPlayer; s16 phi_v0_3; s32 pad; s32 curKeyFrame; s32 prevKeyFrame; s32 playSpeed; Player* player = GET_PLAYER(globalCtx); f32 baseRange = 0.0f; Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 1); if (!EnZf_DodgeRangedEngaging(globalCtx, this) && ((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this))) { this->actor.world.rot.y = this->actor.shape.rot.y + 0x3A98; angleBehindPlayer = player->actor.shape.rot.y + 0x8000; if (Math_SinS(angleBehindPlayer - this->actor.shape.rot.y) >= 0.0f) { this->actor.speedXZ -= 0.25f; if (this->actor.speedXZ < -8.0f) { this->actor.speedXZ = -8.0f; } } else if (Math_SinS(angleBehindPlayer - this->actor.shape.rot.y) < 0.0f) { // Superfluous check this->actor.speedXZ += 0.25f; if (this->actor.speedXZ > 8.0f) { this->actor.speedXZ = 8.0f; } } if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss if (this->unk_3F8) { this->actor.speedXZ = -this->actor.speedXZ; } } else if ((this->actor.bgCheckFlags & 8) || !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y + 0x3FFF)) { if (this->actor.bgCheckFlags & 8) { if (this->actor.speedXZ >= 0.0f) { phi_v0_3 = this->actor.shape.rot.y + 0x3FFF; } else { phi_v0_3 = this->actor.shape.rot.y - 0x3FFF; } phi_v0_3 = this->actor.wallYaw - phi_v0_3; } else { this->actor.speedXZ *= -0.8f; phi_v0_3 = 0; } if (ABS(phi_v0_3) > 0x4000) { this->actor.speedXZ *= -0.8f; if (this->actor.speedXZ < 0.0f) { this->actor.speedXZ -= 0.5f; } else { this->actor.speedXZ += 0.5f; } } } if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { baseRange = 100.0f; } if (this->actor.xzDistToPlayer <= (70.0f + baseRange)) { Math_SmoothStepToF(&this->unk_408, -4.0f, 1.0f, 1.5f, 0.0f); } else if ((90.0f + baseRange) < this->actor.xzDistToPlayer) { Math_SmoothStepToF(&this->unk_408, 4.0f, 1.0f, 1.5f, 0.0f); } else { Math_SmoothStepToF(&this->unk_408, 0.0f, 1.0f, 5.65f, 0.0f); } if ((this->unk_408 != 0.0f) && !EnZf_SecondaryFloorCheck(this, globalCtx, this->unk_408)) { this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->unk_408; this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->unk_408; } if (ABS(this->actor.speedXZ) >= ABS(this->unk_408)) { this->skelAnime.playSpeed = this->actor.speedXZ * 0.75f; } else if (this->skelAnime.playSpeed < 0.0f) { this->skelAnime.playSpeed = this->unk_408 * -0.75f; } else { this->skelAnime.playSpeed = this->unk_408 * 0.75f; } curKeyFrame = this->skelAnime.curFrame; SkelAnime_Update(&this->skelAnime); prevKeyFrame = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); playSpeed = (f32)ABS(this->skelAnime.playSpeed); if (curKeyFrame != (s32)this->skelAnime.curFrame) { s32 nextKeyFrame = playSpeed + curKeyFrame; if (((prevKeyFrame < 14) && (nextKeyFrame > 15)) || ((prevKeyFrame < 27) && (nextKeyFrame > 28))) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); } } if ((globalCtx->gameplayFrames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } if ((Math_CosS(angleBehindPlayer - this->actor.shape.rot.y) < -0.85f) || (this->unk_3F0 == 0)) { this->actor.world.rot.y = this->actor.shape.rot.y; if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0) && EnZf_CanAttack(globalCtx, this)) { EnZf_SetupSlash(this); } else { func_80B45384(this); } } else if (this->unk_3F0 != 0) { this->unk_3F0--; } } } void EnZf_SetupSlash(EnZf* this) { Animation_Change(&this->skelAnime, &gZfSlashAnim, 1.25f, 0.0f, Animation_GetLastFrame(&gZfSlashAnim), ANIMMODE_ONCE, -4.0f); if (this->actor.params == ENZF_TYPE_DINOLFOS) { this->skelAnime.playSpeed = 1.75f; } this->swordCollider.base.atFlags &= ~AT_BOUNCED; this->action = ENZF_ACTION_SLASH; Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); this->actor.speedXZ = 0.0f; EnZf_SetupAction(this, EnZf_Slash); } void EnZf_Slash(EnZf* this, GlobalContext* globalCtx) { Player* player = GET_PLAYER(globalCtx); s16 rotDiff; s16 yawDiff; this->actor.speedXZ = 0.0f; if ((s32)this->skelAnime.curFrame == 10) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_ATTACK); } if (SkelAnime_Update(&this->skelAnime)) { EffectBlure_AddSpace(Effect_GetByIndex(this->blureIndex)); if ((this->actor.params == ENZF_TYPE_DINOLFOS) && !Actor_IsFacingPlayer(&this->actor, 5460)) { func_80B45384(this); this->unk_3F0 = Rand_ZeroOne() * 5.0f + 5.0f; this->unk_3F4 = Rand_ZeroOne() * 20.0f + 100.0f; } else if ((Rand_ZeroOne() > 0.7f) || (this->actor.xzDistToPlayer >= 120.0f)) { func_80B45384(this); this->unk_3F0 = Rand_ZeroOne() * 5.0f + 5.0f; } else { this->actor.world.rot.y = this->actor.yawTowardsPlayer; if (Rand_ZeroOne() > 0.7f) { func_80B483E4(this, globalCtx); } else { rotDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; rotDiff = ABS(rotDiff); if (rotDiff <= 10000) { yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; yawDiff = ABS(yawDiff); if (yawDiff > 16000) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; func_80B483E4(this, globalCtx); } else if (player->stateFlags1 & 0x6010) { if (this->actor.isTargeted) { EnZf_SetupSlash(this); } else { func_80B483E4(this, globalCtx); } } else { EnZf_SetupSlash(this); } } else { func_80B483E4(this, globalCtx); } } } } } void EnZf_SetupRecoilFromBlockedSlash(EnZf* this) { f32 frame = this->skelAnime.curFrame - 3.0f; Animation_Change(&this->skelAnime, &gZfSlashAnim, -1.0f, frame, 0.0f, ANIMMODE_ONCE, 0.0f); this->action = ENZF_ACTION_RECOIL_FROM_BLOCKED_SLASH; EnZf_SetupAction(this, EnZf_RecoilFromBlockedSlash); } void EnZf_RecoilFromBlockedSlash(EnZf* this, GlobalContext* globalCtx) { if (SkelAnime_Update(&this->skelAnime)) { if (Rand_ZeroOne() > 0.7f) { func_80B45384(this); } else if ((Rand_ZeroOne() > 0.2f) && EnZf_CanAttack(globalCtx, this)) { EnZf_SetupSlash(this); } else { func_80B483E4(this, globalCtx); } } } void EnZf_SetupJumpBack(EnZf* this) { Animation_Change(&this->skelAnime, &gZfJumpingAnim, -1.0f, 3.0f, 0.0f, ANIMMODE_ONCE, -3.0f); this->unk_3F0 = 0; this->hopAnimIndex = 1; this->action = ENZF_ACTION_JUMP_BACK; this->actor.velocity.y = 15.0f; this->actor.speedXZ = -15.0f; Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); EnZf_SetupAction(this, EnZf_JumpBack); } void EnZf_JumpBack(EnZf* this, GlobalContext* globalCtx) { if ((this->unk_3F0 != 0) && (this->actor.world.pos.y <= this->actor.floorHeight)) { this->actor.world.pos.y = this->actor.floorHeight; this->hopAnimIndex = 0; this->actor.velocity.y = 0.0f; this->actor.speedXZ = 0.0f; } if (SkelAnime_Update(&this->skelAnime)) { if (this->unk_3F0 == 0) { Animation_Change(&this->skelAnime, &gZfLandingAnim, 3.0f, 0.0f, 17.0f, ANIMMODE_ONCE, -3.0f); this->unk_3F0 = 10; Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); } else if ((globalCtx->gameplayFrames % 2) != 0) { func_80B483E4(this, globalCtx); } else { func_80B45384(this); } } if ((globalCtx->state.frames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } } void EnZf_SetupStunned(EnZf* this) { if ((this->actor.bgCheckFlags & 1) && ((this->actor.velocity.y == 0.0f) || (this->actor.velocity.y == -4.0f))) { this->actor.speedXZ = 0.0f; this->hopAnimIndex = 0; } else { this->hopAnimIndex = 1; } if (this->damageEffect == ENZF_DMGEFF_ICE) { this->iceTimer = 36; } else { Animation_PlayOnceSetSpeed(&this->skelAnime, &gZfKnockedBackAnim, 0.0f); } Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); this->action = ENZF_ACTION_STUNNED; EnZf_SetupAction(this, EnZf_Stunned); } void EnZf_Stunned(EnZf* this, GlobalContext* globalCtx) { s16 angleToWall; if (this->actor.bgCheckFlags & 2) { this->actor.speedXZ = 0.0f; } if (this->actor.bgCheckFlags & 1) { if (this->actor.speedXZ < 0.0f) { this->actor.speedXZ += 0.05f; } this->hopAnimIndex = 0; } if ((this->actor.colorFilterTimer == 0) && (this->actor.bgCheckFlags & 1)) { if (this->actor.colChkInfo.health == 0) { EnZf_SetupDie(this); } else if ((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this)) { if (D_80B4A1B4 != -1) { func_80B44DC4(this, globalCtx); } else { angleToWall = this->actor.wallYaw - this->actor.shape.rot.y; angleToWall = ABS(angleToWall); if ((this->actor.params == ENZF_TYPE_DINOLFOS) && (this->actor.bgCheckFlags & 8) && (ABS(angleToWall) < 0x2EE0) && (this->actor.xzDistToPlayer < 90.0f)) { this->actor.world.rot.y = this->actor.shape.rot.y; EnZf_SetupJumpUp(this); } else if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { if (this->actor.params != ENZF_TYPE_DINOLFOS) { func_80B44DC4(this, globalCtx); } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) != 0) && EnZf_CanAttack(globalCtx, this)) { EnZf_SetupSlash(this); } else { func_80B44DC4(this, globalCtx); } } } } } } void EnZf_SetupSheatheSword(EnZf* this, GlobalContext* globalCtx) { f32 morphFrames = 0.0f; f32 lastFrame = Animation_GetLastFrame(&gZfSheathingSwordAnim); if (this->action <= ENZF_ACTION_DAMAGED) { morphFrames = -4.0f; } Animation_Change(&this->skelAnime, &gZfSheathingSwordAnim, 2.0f, 0.0f, lastFrame, ANIMMODE_ONCE, morphFrames); this->action = ENZF_ACTION_SHEATHE_SWORD; this->actor.speedXZ = 0.0f; this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); this->nextPlatform = EnZf_FindNextPlatformAwayFromPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); this->actor.world.rot.y = this->actor.shape.rot.y; EnZf_SetupAction(this, EnZf_SheatheSword); } void EnZf_SheatheSword(EnZf* this, GlobalContext* globalCtx) { s16 yaw = Actor_WorldYawTowardPoint(&this->actor, &sPlatformPositions[this->nextPlatform]) + 0x8000; Math_SmoothStepToS(&this->actor.world.rot.y, yaw, 1, 1000, 0); this->actor.shape.rot.y = this->actor.world.rot.y; if (SkelAnime_Update(&this->skelAnime)) { this->actor.world.rot.y = yaw - 0x8000; EnZf_SetupHopAway(this, globalCtx); this->swordSheathed = true; } } void EnZf_SetupHopAndTaunt(EnZf* this) { this->hopAnimIndex = 0; Animation_MorphToPlayOnce(&this->skelAnime, sHoppingAnims[0], -4.0f); this->action = ENZF_ACTION_HOP_AND_TAUNT; this->actor.speedXZ = 0.0f; this->unk_40C = 0.0f; this->unk_408 = 0.0f; EnZf_SetupAction(this, EnZf_HopAndTaunt); } void EnZf_HopAndTaunt(EnZf* this, GlobalContext* globalCtx) { f32 lastFrame; f32 maxDist = 400.0f; Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + 0x8000, 1, 4000, 0); // Upstairs if (this->actor.world.pos.y >= 420.0f) { maxDist = 250.0f; } // If player gets too close, run away if ((this->actor.xzDistToPlayer < maxDist) && (this->hopAnimIndex != 1)) { this->actor.shape.rot.y = this->actor.world.rot.y; EnZf_SetupSheatheSword(this, globalCtx); } else { if (this->hopAnimIndex != 1) { EnZf_DodgeRangedWaiting(globalCtx, this); } if (SkelAnime_Update(&this->skelAnime)) { this->hopAnimIndex++; // move on to next animation // Loop back to beginning if (this->hopAnimIndex >= ARRAY_COUNT(sHoppingAnims)) { this->hopAnimIndex = 0; } if ((this->unk_408 != 0.0f) || (this->unk_40C != 0.0f)) { this->hopAnimIndex = 1; } lastFrame = Animation_GetLastFrame(sHoppingAnims[this->hopAnimIndex]); switch (this->hopAnimIndex) { case 0: this->actor.velocity.y = 0.0f; this->actor.world.pos.y = this->actor.floorHeight; break; case 1: this->actor.velocity.y = this->unk_40C + 10.0f; this->actor.speedXZ = this->unk_408; this->unk_408 = 0.0f; this->unk_40C = 0.0f; break; case 2: this->actor.world.pos.y = this->actor.floorHeight; lastFrame = 3.0f; break; default: break; } Animation_Change(&this->skelAnime, sHoppingAnims[this->hopAnimIndex], 1.5f, 0.0f, lastFrame, ANIMMODE_ONCE, 0.0f); } if ((globalCtx->gameplayFrames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } } } void EnZf_SetupHopAway(EnZf* this, GlobalContext* globalCtx) { this->hopAnimIndex = 0; Animation_PlayOnce(&this->skelAnime, sHoppingAnims[0]); this->action = ENZF_ACTION_HOP_AWAY; this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); this->nextPlatform = EnZf_FindNextPlatformAwayFromPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); EnZf_SetupAction(this, EnZf_HopAway); } void EnZf_HopAway(EnZf* this, GlobalContext* globalCtx) { f32 sp74; f32 sp70 = 1.0f; f32 phi_f20 = 550.0f; s32 pad; f32 phi_f20_2; f32 phi_f0; s32 pad2; s16 sp5A; s32 sp54; s32 temp_v1_2; s32 phi_v1; sp74 = Actor_WorldDistXZToPoint(&this->actor, &sPlatformPositions[this->nextPlatform]); sp54 = this->hopAnimIndex; if ((globalCtx->gameplayFrames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } // Upstairs if (this->actor.world.pos.y >= 420.0f) { phi_f20 = 280.0f; } sp5A = Actor_WorldYawTowardPoint(&this->actor, &sPlatformPositions[this->nextPlatform]); switch (this->hopAnimIndex) { case 0: this->actor.world.rot.y = sp5A; this->actor.shape.rot.y = sp5A + 0x8000; D_80B4AB30 = 0; this->homePlatform = this->curPlatform; temp_v1_2 = !EnZf_PrimaryFloorCheck(this, globalCtx, 107.0f); temp_v1_2 |= !EnZf_PrimaryFloorCheck(this, globalCtx, 220.0f) << 1; this->hopAnimIndex++; switch (temp_v1_2) { case 1: case 1 | 2: this->actor.velocity.y = 12.0f; if (this->actor.bgCheckFlags & 8) { this->actor.velocity.y += 8.0f; } this->actor.speedXZ = 8.0f; break; case 2: this->actor.velocity.y = 15.0f; this->actor.speedXZ = 20.0f; break; default: // 0 phi_f20_2 = 107.0f; phi_f20_2 += 10.0f; phi_f0 = 8.0f; phi_f0 += 1.2f; for (phi_v1 = 20; phi_v1 >= 0; phi_v1--, phi_f20_2 += 10.0f, phi_f0 += 1.2f) { if (!EnZf_PrimaryFloorCheck(this, globalCtx, phi_f20_2)) { this->actor.speedXZ = phi_f0; this->actor.velocity.y = 12.0f; break; } } if (this->actor.speedXZ == 0.0f) { EnZf_SetupHopAndTaunt(this); } } break; case 1: if ((this->actor.bgCheckFlags & 2) || (this->actor.bgCheckFlags & 1)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_ONGND); this->actor.velocity.y = 0.0f; this->actor.world.pos.y = this->actor.floorHeight; this->actor.speedXZ = 0.0f; Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftFootPos, 3.0f, 2, 2.0f, 0, 0, 0); Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightFootPos, 3.0f, 2, 2.0f, 0, 0, 0); if (phi_f20 <= this->actor.xzDistToPlayer) { EnZf_SetupHopAndTaunt(this); } else if (sp74 < 80.0f) { this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); this->nextPlatform = EnZf_FindNextPlatformAwayFromPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); } this->hopAnimIndex = 0; sp70 = 2.0f; } else { Math_SmoothStepToS(&this->actor.world.rot.y, sp5A, 1, 0xFA0, 0); this->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; D_80B4AB30++; } break; case 2: if (this->skelAnime.curFrame == this->skelAnime.endFrame) { this->hopAnimIndex = 0; } break; } if (sp54 != this->hopAnimIndex) { Animation_PlayOnceSetSpeed(&this->skelAnime, sHoppingAnims[this->hopAnimIndex], sp70); } SkelAnime_Update(&this->skelAnime); } void EnZf_SetupDrawSword(EnZf* this, GlobalContext* globalCtx) { Animation_PlayOnce(&this->skelAnime, &gZfDrawingSwordAnim); this->actor.world.rot.y += 0x8000; this->action = ENZF_ACTION_DRAW_SWORD; this->actor.speedXZ = 0.0f; this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); this->nextPlatform = EnZf_FindNextPlatformAwayFromPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); EnZf_SetupAction(this, EnZf_DrawSword); } void EnZf_DrawSword(EnZf* this, GlobalContext* globalCtx) { s16 yawTowardsPlayer = this->actor.yawTowardsPlayer; if (this->skelAnime.curFrame >= 26.0f) { Math_SmoothStepToS(&this->actor.shape.rot.y, yawTowardsPlayer, 1, 6000, 0); } if (SkelAnime_Update(&this->skelAnime)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); this->actor.world.rot.y = yawTowardsPlayer; this->hopAnimIndex = -1; func_80B45384(this); } if (this->skelAnime.curFrame == 22.0f) { this->swordSheathed = false; } } void EnZf_SetupDamaged(EnZf* this) { Animation_Change(&this->skelAnime, &gZfKnockedBackAnim, 1.5f, 0.0f, Animation_GetLastFrame(&gZfKnockedBackAnim), ANIMMODE_ONCE, -4.0f); if ((this->actor.bgCheckFlags & 1) && ((this->actor.velocity.y == 0.0f) || (this->actor.velocity.y == -4.0f))) { this->actor.speedXZ = -4.0f; this->hopAnimIndex = 0; } else { this->hopAnimIndex = 1; } if (this->actor.params == ENZF_TYPE_DINOLFOS) { this->skelAnime.playSpeed = 4.5f; } if (this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // not miniboss this->actor.world.rot.y = this->actor.yawTowardsPlayer; } Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DAMAGE); this->action = ENZF_ACTION_DAMAGED; EnZf_SetupAction(this, EnZf_Damaged); } void EnZf_Damaged(EnZf* this, GlobalContext* globalCtx) { s16 wallYawDiff; if (this->actor.bgCheckFlags & 2) { this->actor.speedXZ = 0.0f; } if (this->actor.bgCheckFlags & 1) { if (this->actor.speedXZ < 0.0f) { this->actor.speedXZ += 0.05f; } this->hopAnimIndex = 0; } Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4500, 0); if (((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this)) && SkelAnime_Update(&this->skelAnime) && (this->actor.bgCheckFlags & 1)) { if (D_80B4A1B4 != -1) { if (this->damageEffect == ENZF_DMGEFF_PROJECTILE) { D_80B4A1B0++; } else { this->actor.world.rot.y = this->actor.shape.rot.y; if (!EnZf_PrimaryFloorCheck(this, globalCtx, 135.0f) && (this->actor.xzDistToPlayer < 90.0f)) { EnZf_SetupJumpUp(this); } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0)) { EnZf_SetupSlash(this); } else { func_80B44DC4(this, globalCtx); } } } else { wallYawDiff = this->actor.wallYaw - this->actor.shape.rot.y; wallYawDiff = ABS(wallYawDiff); if ((this->actor.params == ENZF_TYPE_DINOLFOS) && (this->actor.bgCheckFlags & 8) && (ABS(wallYawDiff) < 12000) && (this->actor.xzDistToPlayer < 90.0f)) { EnZf_SetupJumpUp(this); } else if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { if (this->actor.params != ENZF_TYPE_DINOLFOS) { this->actor.world.rot.y = this->actor.shape.rot.y; if (!EnZf_PrimaryFloorCheck(this, globalCtx, 135.0f) && (this->actor.xzDistToPlayer < 90.0f)) { EnZf_SetupJumpUp(this); } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0)) { EnZf_SetupSlash(this); } else { func_80B44DC4(this, globalCtx); } } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0) && EnZf_CanAttack(globalCtx, this)) { EnZf_SetupSlash(this); } else { func_80B44DC4(this, globalCtx); } } } } } void EnZf_SetupJumpUp(EnZf* this) { Animation_Change(&this->skelAnime, &gZfJumpingAnim, 1.0f, 0.0f, 3.0f, ANIMMODE_ONCE, 0.0f); this->unk_3F0 = 0; this->hopAnimIndex = 1; this->action = ENZF_ACTION_JUMP_UP; this->actor.velocity.y = 22.0f; this->actor.speedXZ = 7.5f; Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); this->actor.world.rot.y = this->actor.shape.rot.y; EnZf_SetupAction(this, EnZf_JumpUp); } void EnZf_JumpUp(EnZf* this, GlobalContext* globalCtx) { Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 1); if (this->actor.velocity.y >= 5.0f) { func_800355B8(globalCtx, &this->leftFootPos); func_800355B8(globalCtx, &this->rightFootPos); } if (SkelAnime_Update(&this->skelAnime)) { if (this->unk_3F0 == 0) { Animation_Change(&this->skelAnime, &gZfSlashAnim, 3.0f, 0.0f, 13.0f, ANIMMODE_ONCE, -4.0f); this->unk_3F0 = 10; } else if (this->actor.bgCheckFlags & 3) { this->actor.velocity.y = 0.0f; this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; this->actor.speedXZ = 0.0f; this->actor.world.pos.y = this->actor.floorHeight; EnZf_SetupSlash(this); Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_ATTACK); this->skelAnime.curFrame = 13.0f; } } } // Conditional setup function void func_80B483E4(EnZf* this, GlobalContext* globalCtx) { s16 playerRotY; Player* player; if ((this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* not miniboss */ || Actor_TestFloorInDirection(&this->actor, globalCtx, 40.0f, (s16)(this->actor.shape.rot.y + 0x3FFF)) || Actor_TestFloorInDirection(&this->actor, globalCtx, -40.0f, (s16)(this->actor.shape.rot.y + 0x3FFF))) { Animation_PlayLoop(&this->skelAnime, &gZfSidesteppingAnim); player = GET_PLAYER(globalCtx); Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 1); playerRotY = player->actor.shape.rot.y; if (Math_SinS(playerRotY - this->actor.shape.rot.y) >= 0.0f) { this->actor.speedXZ = -6.0f; } else if (Math_SinS(playerRotY - this->actor.shape.rot.y) < 0.0f) { // Superfluous check this->actor.speedXZ = 6.0f; } this->unk_408 = 0.0f; this->hopAnimIndex = 0; this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; this->unk_3F0 = Rand_ZeroOne() * 10.0f + 5.0f; this->action = ENZF_ACTION_CIRCLE_AROUND_PLAYER; EnZf_SetupAction(this, EnZf_CircleAroundPlayer); } else { EnZf_SetupApproachPlayer(this, globalCtx); } } void EnZf_CircleAroundPlayer(EnZf* this, GlobalContext* globalCtx) { s16 playerRot; s16 phi_v0_4; Player* player = GET_PLAYER(globalCtx); s32 curKeyFrame; s32 prevKeyFrame; s32 playSpeed; f32 baseRange = 0.0f; Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xBB8, 1); playerRot = player->actor.shape.rot.y; if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss if (this->unk_3F8) { this->actor.speedXZ = -this->actor.speedXZ; } } else if ((this->actor.bgCheckFlags & 8) || !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y + 0x3FFF)) { if (this->actor.bgCheckFlags & 8) { if (this->actor.speedXZ >= 0.0f) { phi_v0_4 = this->actor.shape.rot.y + 0x3FFF; } else { phi_v0_4 = this->actor.shape.rot.y - 0x3FFF; } phi_v0_4 = this->actor.wallYaw - phi_v0_4; } else { this->actor.speedXZ *= -0.8f; phi_v0_4 = 0; } if (ABS(phi_v0_4) > 0x4000) { this->actor.speedXZ *= -0.8f; if (this->actor.speedXZ < 0.0f) { this->actor.speedXZ -= 0.5f; } else { this->actor.speedXZ += 0.5f; } } } if (Math_SinS(playerRot - this->actor.shape.rot.y) >= 0.0f) { this->actor.speedXZ += 0.125f; } else { this->actor.speedXZ -= 0.125f; } this->actor.world.rot.y = this->actor.shape.rot.y + 0x4000; if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { baseRange = 100.0f; } if (this->actor.xzDistToPlayer <= (70.0f + baseRange)) { Math_SmoothStepToF(&this->unk_408, -4.0f, 1.0f, 1.5f, 0.0f); } else if ((90.0f + baseRange) < this->actor.xzDistToPlayer) { Math_SmoothStepToF(&this->unk_408, 4.0f, 1.0f, 1.5f, 0.0f); } else { Math_SmoothStepToF(&this->unk_408, 0.0f, 1.0f, 5.65f, 0.0f); } if ((this->unk_408 != 0.0f) && !EnZf_SecondaryFloorCheck(this, globalCtx, this->unk_408)) { this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->unk_408; this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->unk_408; } if (ABS(this->actor.speedXZ) >= ABS(this->unk_408)) { this->skelAnime.playSpeed = -this->actor.speedXZ * 0.75f; } else if (this->skelAnime.playSpeed < 0.0f) { this->skelAnime.playSpeed = this->unk_408 * -0.75f; } else { this->skelAnime.playSpeed = this->unk_408 * 0.75f; } curKeyFrame = this->skelAnime.curFrame; SkelAnime_Update(&this->skelAnime); prevKeyFrame = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); playSpeed = (f32)ABS(this->skelAnime.playSpeed); this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); if (EnZf_FindPlatform(&player->actor.world.pos, -1) != this->curPlatform) { this->actor.speedXZ = 0.0f; if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && (D_80B4A1B4 == this->actor.params)) { EnZf_SetupHopAndTaunt(this); } else { EnZf_SetupApproachPlayer(this, globalCtx); } } else if ((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this)) { if (this->unk_3F0 == 0) { phi_v0_4 = player->actor.shape.rot.y - this->actor.shape.rot.y; phi_v0_4 = ABS(phi_v0_4); if (phi_v0_4 >= 0x3A98) { if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) && (D_80B4A1B4 == this->actor.params)) { EnZf_SetupHopAndTaunt(this); } else { func_80B45384(this); this->unk_3F0 = Rand_ZeroOne() * 5.0f + 1.0f; } } else if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) && (D_80B4A1B4 == this->actor.params)) { EnZf_SetupHopAndTaunt(this); } else { this->actor.world.rot.y = this->actor.shape.rot.y; if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0) && EnZf_CanAttack(globalCtx, this)) { EnZf_SetupSlash(this); } else if ((this->actor.xzDistToPlayer < 280.0f) && (this->actor.xzDistToPlayer > 240.0f) && !EnZf_PrimaryFloorCheck(this, globalCtx, 191.9956f) && ((globalCtx->gameplayFrames % 2) == 0)) { EnZf_SetupJumpForward(this); } else { EnZf_SetupApproachPlayer(this, globalCtx); } } } else { this->unk_3F0--; } if (curKeyFrame != (s32)this->skelAnime.curFrame) { s32 nextKeyFrame = playSpeed + curKeyFrame; if (((prevKeyFrame < 14) && (nextKeyFrame >= 16)) || ((prevKeyFrame < 27) && (nextKeyFrame >= 29))) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); } } if ((globalCtx->gameplayFrames & 0x5F) == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); } } } void EnZf_SetupDie(EnZf* this) { Animation_Change(&this->skelAnime, &gZfDyingAnim, 1.5f, 0.0f, Animation_GetLastFrame(&gZfDyingAnim), ANIMMODE_ONCE, -4.0f); if ((this->actor.bgCheckFlags & 1) && ((this->actor.velocity.y == 0.0f) || (this->actor.velocity.y == -4.0f))) { this->actor.speedXZ = 0.0f; this->hopAnimIndex = 0; } else { this->hopAnimIndex = 1; } this->action = ENZF_ACTION_DIE; this->actor.flags &= ~ACTOR_FLAG_0; if (D_80B4A1B4 != -1) { if (this->actor.prev != NULL) { ((EnZf*)this->actor.prev)->unk_3F4 = 90; if (this->actor.prev->colChkInfo.health < 3) { this->actor.prev->colChkInfo.health = 3; } } else { ((EnZf*)this->actor.next)->unk_3F4 = 90; if (this->actor.next->colChkInfo.health < 3) { this->actor.next->colChkInfo.health = 3; } } } D_80B4A1B0 = 0; Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DEAD); EnZf_SetupAction(this, EnZf_Die); } void EnZf_Die(EnZf* this, GlobalContext* globalCtx) { if (this->actor.bgCheckFlags & 2) { this->actor.speedXZ = 0.0f; } if (this->actor.bgCheckFlags & 1) { Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.15f, 0.0f); this->hopAnimIndex = 0; } if (SkelAnime_Update(&this->skelAnime)) { if (this->actor.category != ACTORCAT_PROP) { if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && (D_80B4A1B4 == -1)) { Flags_SetSwitch(globalCtx, this->clearFlag); func_800F5B58(); } else { D_80B4A1B4 = -1; } Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); } if (this->alpha != 0) { this->actor.shape.shadowAlpha = this->alpha -= 5; } else { Actor_Kill(&this->actor); } } else { s32 curFrame = this->skelAnime.curFrame; if ((curFrame == 10) || (curFrame == 18)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); } } } void EnZf_UpdateHeadRotation(EnZf* this, GlobalContext* globalCtx) { s16 angleTemp; if ((this->actor.params == ENZF_TYPE_DINOLFOS) && (this->action == ENZF_ACTION_3) && (this->unk_3F4 != 0)) { this->headRot = Math_SinS(this->unk_3F4 * 1400) * 0x2AA8; } else { angleTemp = this->actor.yawTowardsPlayer; angleTemp -= (s16)(this->headRot + this->actor.shape.rot.y); this->headRotTemp = CLAMP(angleTemp, -0x7D0, 0x7D0); this->headRot += this->headRotTemp; this->headRot = CLAMP(this->headRot, -0x1CD7, 0x1CD7); } } void EnZf_UpdateDamage(EnZf* this, GlobalContext* globalCtx) { s32 pad; s16 dropParams; if ((this->bodyCollider.base.acFlags & AC_HIT) && (this->action <= ENZF_ACTION_STUNNED)) { this->bodyCollider.base.acFlags &= ~AC_HIT; if (((this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* not miniboss */ || (D_80B4A1B4 != this->actor.params)) && (this->actor.colChkInfo.damageEffect != ENZF_DMGEFF_IMMUNE)) { this->damageEffect = this->actor.colChkInfo.damageEffect; Actor_SetDropFlag(&this->actor, &this->bodyCollider.info, 0); if ((this->actor.colChkInfo.damageEffect == ENZF_DMGEFF_STUN) || (this->actor.colChkInfo.damageEffect == ENZF_DMGEFF_ICE)) { if (this->action != ENZF_ACTION_STUNNED) { Actor_SetColorFilter(&this->actor, 0, 120, 0, 80); Actor_ApplyDamage(&this->actor); EnZf_SetupStunned(this); } } else { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 8); if (Actor_ApplyDamage(&this->actor) == 0) { dropParams = 0x40; EnZf_SetupDie(this); if (this->actor.params == ENZF_TYPE_DINOLFOS) { dropParams = 0xE0; } Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, dropParams); Enemy_StartFinishingBlow(globalCtx, &this->actor); } else { if ((D_80B4A1B4 != -1) && ((this->actor.colChkInfo.health + this->actor.colChkInfo.damage) >= 4) && (this->actor.colChkInfo.health < 4)) { this->damageEffect = ENZF_DMGEFF_PROJECTILE; } EnZf_SetupDamaged(this); } } } } } void EnZf_Update(Actor* thisx, GlobalContext* globalCtx) { s32 pad; EnZf* this = (EnZf*)thisx; s32 pad2; EnZf_UpdateDamage(this, globalCtx); if (this->actor.colChkInfo.damageEffect != ENZF_DMGEFF_IMMUNE) { this->unk_3F8 = false; if ((this->hopAnimIndex != 1) && (this->action != ENZF_ACTION_HOP_AWAY)) { if (this->actor.speedXZ != 0.0f) { this->unk_3F8 = EnZf_PrimaryFloorCheck(this, globalCtx, this->actor.speedXZ * 1.5f); } if (!this->unk_3F8) { this->unk_3F8 = EnZf_PrimaryFloorCheck(this, globalCtx, 0.0f); } } if (!this->unk_3F8) { Actor_MoveForward(&this->actor); } Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 25.0f, 30.0f, 60.0f, 0x1D); if (!(this->actor.bgCheckFlags & 1)) { this->hopAnimIndex = 1; } this->actionFunc(this, globalCtx); } if (this->actor.colChkInfo.health > 0) { if ((this->action != ENZF_ACTION_SLASH) && (this->action != ENZF_ACTION_STUNNED)) { EnZf_UpdateHeadRotation(this, globalCtx); } if ((D_80B4A1B0 != 0) && (D_80B4A1B4 != this->actor.params)) { EnZf_SetupSheatheSword(this, globalCtx); D_80B4A1B4 = this->actor.params; D_80B4A1B0 = 0; if (this->actor.prev != NULL) { ((EnZf*)this->actor.prev)->unk_3F4 = 90; } else { ((EnZf*)this->actor.next)->unk_3F4 = 90; } } } if (this->action >= ENZF_ACTION_DIE) { Math_SmoothStepToS(&this->headRot, 0, 1, 2000, 0); if (this->action <= ENZF_ACTION_HOP_AND_TAUNT) { if ((this->unk_3F4 == 1) && (this->actor.bgCheckFlags & 1)) { if (this->actor.colChkInfo.health > 0) { EnZf_SetupDrawSword(this, globalCtx); } this->unk_3F4--; } } if (this->unk_3F4 >= 2) { this->unk_3F4--; } } this->actor.focus.pos = this->actor.world.pos; this->actor.focus.pos.y += 40.0f; if ((this->actor.colChkInfo.health > 0) && (this->alpha == 255)) { Collider_UpdateCylinder(&this->actor, &this->bodyCollider); if ((this->actor.world.pos.y == this->actor.floorHeight) && (this->action <= ENZF_ACTION_DAMAGED)) { CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); } if ((this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* not miniboss */ || (D_80B4A1B4 != this->actor.params)) { if ((this->actor.colorFilterTimer == 0) || !(this->actor.colorFilterParams & 0x4000)) { CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); } } } if ((this->action == ENZF_ACTION_SLASH) && (this->skelAnime.curFrame >= 14.0f) && (this->skelAnime.curFrame <= 20.0f)) { if (!(this->swordCollider.base.atFlags & AT_BOUNCED) && !(this->swordCollider.base.acFlags & AC_HIT)) { CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->swordCollider.base); } else { this->swordCollider.base.atFlags &= ~AT_BOUNCED; this->swordCollider.base.acFlags &= ~AC_HIT; EnZf_SetupRecoilFromBlockedSlash(this); } } } s32 EnZf_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, Gfx** gfx) { EnZf* this = (EnZf*)thisx; switch (limbIndex) { case ENZF_LIMB_HEAD_ROOT: rot->y -= this->headRot; break; case ENZF_LIMB_SWORD: if (this->swordSheathed) { *dList = gZfEmptyHandDL; } break; case ENZF_LIMB_SCABBARD: if (this->swordSheathed) { *dList = gZfSheathedSwordDL; } break; default: break; } return false; } void EnZf_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { static Vec3f sUnused = { 1100.0f, -700.0f, 0.0f }; static Vec3f footOffset = { 300.0f, 0.0f, 0.0f }; static Vec3f D_80B4A2A4 = { 300.0f, -1700.0f, 0.0f }; // Sword tip? static Vec3f D_80B4A2B0 = { -600.0f, 300.0f, 0.0f }; // Sword hilt? static Vec3f swordQuadOffset1 = { 0.0f, 1500.0f, 0.0f }; static Vec3f swordQuadOffset0 = { -600.0f, -3000.0f, 1000.0f }; static Vec3f swordQuadOffset3 = { -600.0f, -3000.0f, -1000.0f }; static Vec3f swordQuadOffset2 = { 1500.0f, -3000.0f, 0.0f }; static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; Vec3f sp54; Vec3f sp48; EnZf* this = (EnZf*)thisx; s32 bodyPart = -1; if (limbIndex == ENZF_LIMB_SWORD) { Matrix_MultVec3f(&swordQuadOffset1, &this->swordCollider.dim.quad[1]); Matrix_MultVec3f(&swordQuadOffset0, &this->swordCollider.dim.quad[0]); Matrix_MultVec3f(&swordQuadOffset3, &this->swordCollider.dim.quad[3]); Matrix_MultVec3f(&swordQuadOffset2, &this->swordCollider.dim.quad[2]); Collider_SetQuadVertices(&this->swordCollider, &this->swordCollider.dim.quad[0], &this->swordCollider.dim.quad[1], &this->swordCollider.dim.quad[2], &this->swordCollider.dim.quad[3]); Matrix_MultVec3f(&D_80B4A2A4, &sp54); Matrix_MultVec3f(&D_80B4A2B0, &sp48); if (this->action == ENZF_ACTION_SLASH) { if (this->skelAnime.curFrame < 14.0f) { EffectBlure_AddSpace(Effect_GetByIndex(this->blureIndex)); } else if (this->skelAnime.curFrame < 20.0f) { EffectBlure_AddVertex(Effect_GetByIndex(this->blureIndex), &sp54, &sp48); } } } else { Actor_SetFeetPos(&this->actor, limbIndex, ENZF_LIMB_LEFT_FOOT, &footOffset, ENZF_LIMB_RIGHT_FOOT, &footOffset); } switch (limbIndex) { case ENZF_LIMB_LEFT_FOOT: Matrix_MultVec3f(&footOffset, &this->leftFootPos); break; case ENZF_LIMB_RIGHT_FOOT: Matrix_MultVec3f(&footOffset, &this->rightFootPos); break; } if (this->iceTimer != 0) { switch (limbIndex) { case ENZF_LIMB_HEAD: bodyPart = 0; break; case ENZF_LIMB_NECK: bodyPart = 1; break; case ENZF_LIMB_CHEST_ARMOR: bodyPart = 2; break; case ENZF_LIMB_RIGHT_FOREARM: bodyPart = 3; break; case ENZF_LIMB_LEFT_FOREARM: bodyPart = 4; break; case ENZF_LIMB_TRUNK: bodyPart = 5; break; case ENZF_LIMB_SWORD_ROOT: bodyPart = 6; break; case ENZF_LIMB_RIGHT_SHIN: bodyPart = 7; break; case ENZF_LIMB_LEFT_SHIN_ROOT: bodyPart = 8; break; default: break; } if (bodyPart >= 0) { Matrix_MultVec3f(&zeroVec, &this->bodyPartsPos[bodyPart]); } } } static Gfx D_80B4A2F8[] = { gsSPTexture(0x0A00, 0x0A00, 0, G_TX_RENDERTILE, G_ON), gsSPEndDisplayList(), }; void EnZf_Draw(Actor* thisx, GlobalContext* globalCtx) { s32 pad; EnZf* this = (EnZf*)thisx; OPEN_DISPS(globalCtx->state.gfxCtx); func_8002EBCC(thisx, globalCtx, 1); gSPTexture(D_80B4A2F8, IREG(0), IREG(1), 0, G_TX_RENDERTILE, G_ON); gSPSegment(POLY_OPA_DISP++, 0x08, D_80B4A2F8); if (this->alpha == 255) { func_80093D18(globalCtx->state.gfxCtx); gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, this->alpha); gSPSegment(POLY_OPA_DISP++, 0x09, &D_80116280[2]); POLY_OPA_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnZf_OverrideLimbDraw, EnZf_PostLimbDraw, this, POLY_OPA_DISP); if (this->iceTimer != 0) { thisx->colorFilterTimer++; this->iceTimer--; if ((this->iceTimer % 4) == 0) { s32 icePosIndex = this->iceTimer >> 2; EffectSsEnIce_SpawnFlyingVec3f(globalCtx, thisx, &this->bodyPartsPos[icePosIndex], 150, 150, 150, 250, 235, 245, 255, 1.4f); } } } else { // fades out when dead func_80093D84(globalCtx->state.gfxCtx); gDPPipeSync(POLY_XLU_DISP++); gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); gSPSegment(POLY_XLU_DISP++, 0x09, &D_80116280[0]); POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnZf_OverrideLimbDraw, EnZf_PostLimbDraw, this, POLY_XLU_DISP); } CLOSE_DISPS(globalCtx->state.gfxCtx); } void EnZf_SetupCircleAroundPlayer(EnZf* this, f32 speed) { Animation_MorphToLoop(&this->skelAnime, &gZfSidesteppingAnim, -1.0f); this->unk_3F0 = Rand_ZeroOne() * 10.0f + 8.0f; if (this->actor.params == ENZF_TYPE_DINOLFOS) { this->actor.speedXZ = 2.0f * speed; this->unk_3F0 /= 2; } else { this->actor.speedXZ = speed; } this->hopAnimIndex = 0; this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; this->action = ENZF_ACTION_CIRCLE_AROUND_PLAYER; EnZf_SetupAction(this, EnZf_CircleAroundPlayer); } s32 EnZf_DodgeRangedEngaging(GlobalContext* globalCtx, EnZf* this) { Actor* projectileActor; s16 yawToProjectile; s16 phi_t0; s16 phi_v1; projectileActor = Actor_GetProjectileActor(globalCtx, &this->actor, 600.0f); if (projectileActor != NULL) { yawToProjectile = Actor_WorldYawTowardActor(&this->actor, projectileActor) - (s16)(u16)(this->actor.shape.rot.y); this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; phi_t0 = 0; if (EnZf_PrimaryFloorCheck(this, globalCtx, -8.0f)) { phi_t0 = 1; } if (EnZf_PrimaryFloorCheck(this, globalCtx, 8.0f)) { phi_t0 |= 2; } this->actor.world.rot.y = this->actor.shape.rot.y; if ((((this->actor.xzDistToPlayer < 90.0f) || (phi_t0 == 3)) && !EnZf_PrimaryFloorCheck(this, globalCtx, 135.0f)) || (projectileActor->id == ACTOR_ARMS_HOOK)) { EnZf_SetupJumpUp(this); return true; } this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; if (phi_t0 == 0) { phi_v1 = globalCtx->gameplayFrames % 2; } else { phi_v1 = phi_t0; } if ((ABS(yawToProjectile) < 0x2000) || (ABS(yawToProjectile) >= 0x6000)) { if (phi_v1 & 1) { EnZf_SetupCircleAroundPlayer(this, 8.0f); return true; } EnZf_SetupCircleAroundPlayer(this, -8.0f); return true; } if (ABS(yawToProjectile) < 0x5FFF) { if (phi_v1 & 1) { EnZf_SetupCircleAroundPlayer(this, 4.0f); return true; } EnZf_SetupCircleAroundPlayer(this, -4.0f); } return true; } return false; } s32 EnZf_DodgeRangedWaiting(GlobalContext* globalCtx, EnZf* this) { Actor* projectileActor; s16 yawToProjectile; s16 phi_t0; s16 sp1E; s16 sp1C = 0; projectileActor = Actor_GetProjectileActor(globalCtx, &this->actor, 600.0f); if (projectileActor != NULL) { yawToProjectile = Actor_WorldYawTowardActor(&this->actor, projectileActor) - (s16)(u16)this->actor.shape.rot.y; this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; // Set to move sideways phi_t0 = 0; if (EnZf_PrimaryFloorCheck(this, globalCtx, -70.0f)) { phi_t0 = 1; } if (EnZf_PrimaryFloorCheck(this, globalCtx, 70.0f)) { phi_t0 |= 2; } this->actor.speedXZ = 0.0f; if ((ABS(yawToProjectile) < 0x2000) || (ABS(yawToProjectile) >= 0x6000)) { if (phi_t0 == 0) { if ((globalCtx->gameplayFrames % 2) != 0) { sp1E = 6; } else { sp1E = -6; } } else { switch (phi_t0) { case 1: sp1E = 6; break; case 2: sp1E = -6; break; case 1 | 2: sp1C = 5; sp1E = 0; break; } } } else if (ABS(yawToProjectile) < 0x5FFF) { if (phi_t0 == 0) { if ((globalCtx->gameplayFrames % 2) != 0) { sp1E = 6; } else { sp1E = -6; } } else { switch (phi_t0) { case 1: sp1E = 6; break; case 2: sp1E = -6; break; case 1 | 2: sp1C = 10; sp1E = 0; break; } } } this->unk_408 = sp1E; this->unk_40C = sp1C; return true; } return false; } void EnZf_Reset(void) { D_80B4A1B0 = 0; D_80B4A1B4 = 1; }