mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-08-13 17:03:47 -04:00
a5df9dddf0
* First batch some overlay
* Almost all overlay
* effect & gamestate
* kaleido stuffs
* more overlay
* more left over from code folder
* remaining hardcoded line and file
* Open & Close _DISP __FILE__ & __LINE__ clean up
* Some if (1) {} remove
* LOG_xxxx __FILE__ , __LINE__ cleaned
* ASSERT macro __FILE__ __LINE__
* mtx without line/file in functions
* " if (1) {} " & "if (0) {}" and tab/white place
* LogUtils as macro
* GameState_, GameAlloc_, SystemArena_ & ZeldaArena_
* Revert "GameState_, GameAlloc_, SystemArena_ & ZeldaArena_"
This reverts commit 0d85caaf7e
.
* Like last commit but as macro
* Fix matrix not using macros
* use function not macro
* DebugArena_* functions
GameAlloc_MallocDebug
BgCheck_PosErrorCheck as macros
removed issues with ; in macro file
1492 lines
54 KiB
C
1492 lines
54 KiB
C
/*
|
|
* File: z_en_wf.c
|
|
* Overlay: ovl_En_Wf
|
|
* Description: Wolfos (Normal and White)
|
|
*/
|
|
|
|
#include "z_en_wf.h"
|
|
#include "vt.h"
|
|
#include "overlays/actors/ovl_En_Encount1/z_en_encount1.h"
|
|
#include "objects/object_wf/object_wf.h"
|
|
|
|
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4)
|
|
|
|
void EnWf_Init(Actor* thisx, GlobalContext* globalCtx);
|
|
void EnWf_Destroy(Actor* thisx, GlobalContext* globalCtx);
|
|
void EnWf_Update(Actor* thisx, GlobalContext* globalCtx);
|
|
void EnWf_Draw(Actor* thisx, GlobalContext* globalCtx);
|
|
|
|
void EnWf_SetupWaitToAppear(EnWf* this);
|
|
void EnWf_WaitToAppear(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupWait(EnWf* this);
|
|
void EnWf_Wait(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupRunAtPlayer(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_RunAtPlayer(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupSearchForPlayer(EnWf* this);
|
|
void EnWf_SearchForPlayer(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupRunAroundPlayer(EnWf* this);
|
|
void EnWf_RunAroundPlayer(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupSlash(EnWf* this);
|
|
void EnWf_Slash(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_RecoilFromBlockedSlash(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupBackflipAway(EnWf* this);
|
|
void EnWf_BackflipAway(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_Stunned(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_Damaged(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupSomersaultAndAttack(EnWf* this);
|
|
void EnWf_SomersaultAndAttack(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupBlocking(EnWf* this);
|
|
void EnWf_Blocking(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupSidestep(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_Sidestep(EnWf* this, GlobalContext* globalCtx);
|
|
void EnWf_SetupDie(EnWf* this);
|
|
void EnWf_Die(EnWf* this, GlobalContext* globalCtx);
|
|
s32 EnWf_DodgeRanged(GlobalContext* globalCtx, EnWf* this);
|
|
|
|
static ColliderJntSphElementInit sJntSphItemsInit[4] = {
|
|
{
|
|
{
|
|
ELEMTYPE_UNK0,
|
|
{ 0xFFCFFFFF, 0x00, 0x04 },
|
|
{ 0x00000000, 0x00, 0x00 },
|
|
TOUCH_ON | TOUCH_SFX_NORMAL,
|
|
BUMP_NONE,
|
|
OCELEM_NONE,
|
|
},
|
|
{ WOLFOS_LIMB_FRONT_RIGHT_CLAW, { { 0, 0, 0 }, 15 }, 100 },
|
|
},
|
|
{
|
|
{
|
|
ELEMTYPE_UNK0,
|
|
{ 0xFFCFFFFF, 0x00, 0x04 },
|
|
{ 0x00000000, 0x00, 0x00 },
|
|
TOUCH_ON | TOUCH_SFX_NORMAL,
|
|
BUMP_NONE,
|
|
OCELEM_NONE,
|
|
},
|
|
{ WOLFOS_LIMB_FRONT_LEFT_CLAW, { { 0, 0, 0 }, 15 }, 100 },
|
|
},
|
|
{
|
|
{
|
|
ELEMTYPE_UNK1,
|
|
{ 0x00000000, 0x00, 0x00 },
|
|
{ 0xFFC1FFFF, 0x00, 0x00 },
|
|
TOUCH_NONE,
|
|
BUMP_ON | BUMP_HOOKABLE,
|
|
OCELEM_ON,
|
|
},
|
|
{ WOLFOS_LIMB_HEAD, { { 800, 0, 0 }, 25 }, 100 },
|
|
},
|
|
{
|
|
{
|
|
ELEMTYPE_UNK1,
|
|
{ 0x00000000, 0x00, 0x00 },
|
|
{ 0xFFC1FFFF, 0x00, 0x00 },
|
|
TOUCH_NONE,
|
|
BUMP_ON | BUMP_HOOKABLE,
|
|
OCELEM_ON,
|
|
},
|
|
{ WOLFOS_LIMB_THORAX, { { 0, 0, 0 }, 30 }, 100 },
|
|
},
|
|
};
|
|
|
|
static ColliderJntSphInit sJntSphInit = {
|
|
{
|
|
COLTYPE_METAL,
|
|
AT_ON | AT_TYPE_ENEMY,
|
|
AC_ON | AC_HARD | AC_TYPE_PLAYER,
|
|
OC1_ON | OC1_TYPE_ALL,
|
|
OC2_TYPE_1,
|
|
COLSHAPE_JNTSPH,
|
|
},
|
|
ARRAY_COUNT(sJntSphItemsInit),
|
|
sJntSphItemsInit,
|
|
};
|
|
|
|
static ColliderCylinderInit sBodyCylinderInit = {
|
|
{
|
|
COLTYPE_HIT5,
|
|
AT_NONE,
|
|
AC_ON | AC_TYPE_PLAYER,
|
|
OC1_NONE,
|
|
OC2_NONE,
|
|
COLSHAPE_CYLINDER,
|
|
},
|
|
{
|
|
ELEMTYPE_UNK1,
|
|
{ 0x00000000, 0x00, 0x00 },
|
|
{ 0xFFCFFFFF, 0x00, 0x00 },
|
|
TOUCH_NONE,
|
|
BUMP_ON,
|
|
OCELEM_NONE,
|
|
},
|
|
{ 20, 50, 0, { 0, 0, 0 } },
|
|
};
|
|
|
|
static ColliderCylinderInit sTailCylinderInit = {
|
|
{
|
|
COLTYPE_HIT5,
|
|
AT_NONE,
|
|
AC_ON | AC_TYPE_PLAYER,
|
|
OC1_NONE,
|
|
OC2_NONE,
|
|
COLSHAPE_CYLINDER,
|
|
},
|
|
{
|
|
ELEMTYPE_UNK1,
|
|
{ 0x00000000, 0x00, 0x00 },
|
|
{ 0xFFCFFFFF, 0x00, 0x00 },
|
|
TOUCH_NONE,
|
|
BUMP_ON,
|
|
OCELEM_NONE,
|
|
},
|
|
{ 15, 20, -15, { 0, 0, 0 } },
|
|
};
|
|
|
|
typedef enum {
|
|
/* 0 */ ENWF_DMGEFF_NONE,
|
|
/* 1 */ ENWF_DMGEFF_STUN,
|
|
/* 6 */ ENWF_DMGEFF_ICE_MAGIC = 6,
|
|
/* 13 */ ENWF_DMGEFF_LIGHT_MAGIC = 13,
|
|
/* 14 */ ENWF_DMGEFF_FIRE,
|
|
/* 15 */ ENWF_DMGEFF_UNDEF // used like STUN in the code, but not in the table
|
|
} EnWfDamageEffect;
|
|
|
|
static DamageTable sDamageTable = {
|
|
/* Deku nut */ DMG_ENTRY(0, ENWF_DMGEFF_STUN),
|
|
/* Deku stick */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Slingshot */ DMG_ENTRY(1, ENWF_DMGEFF_NONE),
|
|
/* Explosive */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Boomerang */ DMG_ENTRY(0, ENWF_DMGEFF_STUN),
|
|
/* Normal arrow */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Hammer swing */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Hookshot */ DMG_ENTRY(0, ENWF_DMGEFF_STUN),
|
|
/* Kokiri sword */ DMG_ENTRY(1, ENWF_DMGEFF_NONE),
|
|
/* Master sword */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Giant's Knife */ DMG_ENTRY(4, ENWF_DMGEFF_NONE),
|
|
/* Fire arrow */ DMG_ENTRY(4, ENWF_DMGEFF_FIRE),
|
|
/* Ice arrow */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Light arrow */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Unk arrow 1 */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Unk arrow 2 */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Unk arrow 3 */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Fire magic */ DMG_ENTRY(4, ENWF_DMGEFF_FIRE),
|
|
/* Ice magic */ DMG_ENTRY(0, ENWF_DMGEFF_ICE_MAGIC),
|
|
/* Light magic */ DMG_ENTRY(3, ENWF_DMGEFF_LIGHT_MAGIC),
|
|
/* Shield */ DMG_ENTRY(0, ENWF_DMGEFF_NONE),
|
|
/* Mirror Ray */ DMG_ENTRY(0, ENWF_DMGEFF_NONE),
|
|
/* Kokiri spin */ DMG_ENTRY(1, ENWF_DMGEFF_NONE),
|
|
/* Giant spin */ DMG_ENTRY(4, ENWF_DMGEFF_NONE),
|
|
/* Master spin */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Kokiri jump */ DMG_ENTRY(2, ENWF_DMGEFF_NONE),
|
|
/* Giant jump */ DMG_ENTRY(8, ENWF_DMGEFF_NONE),
|
|
/* Master jump */ DMG_ENTRY(4, ENWF_DMGEFF_NONE),
|
|
/* Unknown 1 */ DMG_ENTRY(0, ENWF_DMGEFF_NONE),
|
|
/* Unblockable */ DMG_ENTRY(0, ENWF_DMGEFF_NONE),
|
|
/* Hammer jump */ DMG_ENTRY(4, ENWF_DMGEFF_NONE),
|
|
/* Unknown 2 */ DMG_ENTRY(0, ENWF_DMGEFF_NONE),
|
|
};
|
|
|
|
const ActorInit En_Wf_InitVars = {
|
|
ACTOR_EN_WF,
|
|
ACTORCAT_ENEMY,
|
|
FLAGS,
|
|
OBJECT_WF,
|
|
sizeof(EnWf),
|
|
(ActorFunc)EnWf_Init,
|
|
(ActorFunc)EnWf_Destroy,
|
|
(ActorFunc)EnWf_Update,
|
|
(ActorFunc)EnWf_Draw,
|
|
NULL,
|
|
};
|
|
|
|
static InitChainEntry sInitChain[] = {
|
|
ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE),
|
|
ICHAIN_F32_DIV1000(gravity, -3000, ICHAIN_STOP),
|
|
};
|
|
|
|
void EnWf_SetupAction(EnWf* this, EnWfActionFunc actionFunc) {
|
|
this->actionFunc = actionFunc;
|
|
}
|
|
|
|
void EnWf_Init(Actor* thisx, GlobalContext* globalCtx) {
|
|
s32 pad;
|
|
EnWf* this = (EnWf*)thisx;
|
|
|
|
Actor_ProcessInitChain(thisx, sInitChain);
|
|
thisx->colChkInfo.damageTable = &sDamageTable;
|
|
ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 0.0f);
|
|
thisx->focus.pos = thisx->world.pos;
|
|
thisx->colChkInfo.mass = MASS_HEAVY;
|
|
thisx->colChkInfo.health = 8;
|
|
thisx->colChkInfo.cylRadius = 50;
|
|
thisx->colChkInfo.cylHeight = 100;
|
|
this->switchFlag = (thisx->params >> 8) & 0xFF;
|
|
thisx->params &= 0xFF;
|
|
this->eyeIndex = 0;
|
|
this->unk_2F4 = 10.0f; // Set and not used
|
|
|
|
Collider_InitJntSph(globalCtx, &this->colliderSpheres);
|
|
Collider_SetJntSph(globalCtx, &this->colliderSpheres, thisx, &sJntSphInit, this->colliderSpheresElements);
|
|
Collider_InitCylinder(globalCtx, &this->colliderCylinderBody);
|
|
Collider_SetCylinder(globalCtx, &this->colliderCylinderBody, thisx, &sBodyCylinderInit);
|
|
Collider_InitCylinder(globalCtx, &this->colliderCylinderTail);
|
|
Collider_SetCylinder(globalCtx, &this->colliderCylinderTail, thisx, &sTailCylinderInit);
|
|
|
|
if (thisx->params == WOLFOS_NORMAL) {
|
|
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gWolfosNormalSkel, &gWolfosWaitingAnim, this->jointTable,
|
|
this->morphTable, WOLFOS_LIMB_MAX);
|
|
Actor_SetScale(thisx, 0.0075f);
|
|
thisx->naviEnemyId = 0x4C; // Wolfos
|
|
} else { // WOLFOS_WHITE
|
|
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gWolfosWhiteSkel, &gWolfosWaitingAnim, this->jointTable,
|
|
this->morphTable, WOLFOS_LIMB_MAX);
|
|
Actor_SetScale(thisx, 0.01f);
|
|
this->colliderSpheres.elements[0].info.toucher.damage = this->colliderSpheres.elements[1].info.toucher.damage =
|
|
8;
|
|
thisx->naviEnemyId = 0x57; // White Wolfos
|
|
}
|
|
|
|
EnWf_SetupWaitToAppear(this);
|
|
|
|
if ((this->switchFlag != 0xFF) && Flags_GetSwitch(globalCtx, this->switchFlag)) {
|
|
Actor_Kill(thisx);
|
|
}
|
|
}
|
|
|
|
void EnWf_Destroy(Actor* thisx, GlobalContext* globalCtx) {
|
|
EnWf* this = (EnWf*)thisx;
|
|
|
|
Collider_DestroyJntSph(globalCtx, &this->colliderSpheres);
|
|
Collider_DestroyCylinder(globalCtx, &this->colliderCylinderBody);
|
|
Collider_DestroyCylinder(globalCtx, &this->colliderCylinderTail);
|
|
|
|
if ((this->actor.params != WOLFOS_NORMAL) && (this->switchFlag != 0xFF)) {
|
|
func_800F5B58();
|
|
}
|
|
|
|
if (this->actor.parent != NULL) {
|
|
EnEncount1* parent = (EnEncount1*)this->actor.parent;
|
|
|
|
if (parent->actor.update != NULL) {
|
|
if (parent->curNumSpawn > 0) {
|
|
parent->curNumSpawn--;
|
|
}
|
|
|
|
osSyncPrintf("\n\n");
|
|
// "☆☆☆☆☆ Number of concurrent events ☆☆☆☆☆"
|
|
osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 同時発生数 ☆☆☆☆☆%d\n" VT_RST, parent->curNumSpawn);
|
|
osSyncPrintf("\n\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 EnWf_ChangeAction(GlobalContext* globalCtx, EnWf* this, s16 mustChoose) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
s32 pad;
|
|
s16 wallYawDiff;
|
|
s16 playerYawDiff;
|
|
Actor* explosive;
|
|
|
|
wallYawDiff = this->actor.wallYaw - this->actor.shape.rot.y;
|
|
wallYawDiff = ABS(wallYawDiff);
|
|
playerYawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
|
|
playerYawDiff = ABS(playerYawDiff);
|
|
|
|
if (func_800354B4(globalCtx, &this->actor, 100.0f, 0x2710, 0x2EE0, this->actor.shape.rot.y)) {
|
|
if (player->swordAnimation == 0x11) {
|
|
EnWf_SetupBlocking(this);
|
|
return true;
|
|
}
|
|
|
|
if ((globalCtx->gameplayFrames % 2) != 0) {
|
|
EnWf_SetupBlocking(this);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
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(wallYawDiff) < 0x2EE0) && (this->actor.xzDistToPlayer < 120.0f)) {
|
|
EnWf_SetupSomersaultAndAttack(this);
|
|
return true;
|
|
} else if (player->swordAnimation == 0x11) {
|
|
EnWf_SetupBlocking(this);
|
|
return true;
|
|
} else if ((this->actor.xzDistToPlayer < 80.0f) && (globalCtx->gameplayFrames % 2) != 0) {
|
|
EnWf_SetupBlocking(this);
|
|
return true;
|
|
} else {
|
|
EnWf_SetupBackflipAway(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) && (wallYawDiff < 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) {
|
|
EnWf_SetupSomersaultAndAttack(this);
|
|
return true;
|
|
} else {
|
|
EnWf_SetupSidestep(this, globalCtx);
|
|
return true;
|
|
}
|
|
} else {
|
|
EnWf_SetupBackflipAway(this);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (mustChoose) {
|
|
s16 playerFacingAngleDiff;
|
|
|
|
if (playerYawDiff >= 0x1B58) {
|
|
EnWf_SetupSidestep(this, globalCtx);
|
|
return true;
|
|
}
|
|
|
|
playerFacingAngleDiff = player->actor.shape.rot.y - this->actor.shape.rot.y;
|
|
|
|
if ((this->actor.xzDistToPlayer <= 80.0f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) &&
|
|
(((globalCtx->gameplayFrames % 8) != 0) || (ABS(playerFacingAngleDiff) < 0x38E0))) {
|
|
EnWf_SetupSlash(this);
|
|
return true;
|
|
}
|
|
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void EnWf_SetupWaitToAppear(EnWf* this) {
|
|
Animation_Change(&this->skelAnime, &gWolfosRearingUpFallingOverAnim, 0.5f, 0.0f, 7.0f, ANIMMODE_ONCE_INTERP, 0.0f);
|
|
this->actor.world.pos.y = this->actor.home.pos.y - 5.0f;
|
|
this->actionTimer = 20;
|
|
this->unk_300 = false;
|
|
this->action = WOLFOS_ACTION_WAIT_TO_APPEAR;
|
|
this->actor.flags &= ~ACTOR_FLAG_0;
|
|
this->actor.scale.y = 0.0f;
|
|
this->actor.gravity = 0.0f;
|
|
EnWf_SetupAction(this, EnWf_WaitToAppear);
|
|
}
|
|
|
|
void EnWf_WaitToAppear(EnWf* this, GlobalContext* globalCtx) {
|
|
if (this->actionTimer >= 6) {
|
|
this->actor.world.pos.y = this->actor.home.pos.y - 5.0f;
|
|
|
|
if (this->actor.xzDistToPlayer < 240.0f) {
|
|
this->actionTimer = 5;
|
|
this->actor.flags |= ACTOR_FLAG_0;
|
|
|
|
if ((this->actor.params != WOLFOS_NORMAL) && (this->switchFlag != 0xFF)) {
|
|
func_800F5ACC(NA_BGM_MINI_BOSS);
|
|
}
|
|
}
|
|
} else if (this->actionTimer != 0) {
|
|
this->actor.scale.y += this->actor.scale.x * 0.2f;
|
|
this->actor.world.pos.y += 0.5f;
|
|
Math_SmoothStepToF(&this->actor.shape.shadowScale, 70.0f, 1.0f, 14.0f, 0.0f);
|
|
this->actionTimer--;
|
|
|
|
if (this->actionTimer == 0) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_APPEAR);
|
|
}
|
|
} else { // actionTimer == 0
|
|
if (SkelAnime_Update(&this->skelAnime)) {
|
|
this->actor.scale.y = this->actor.scale.x;
|
|
this->actor.gravity = -2.0f;
|
|
EnWf_SetupWait(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupWait(EnWf* this) {
|
|
Animation_MorphToLoop(&this->skelAnime, &gWolfosWaitingAnim, -4.0f);
|
|
this->action = WOLFOS_ACTION_WAIT;
|
|
this->actionTimer = (Rand_ZeroOne() * 10.0f) + 2.0f;
|
|
this->actor.speedXZ = 0.0f;
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
EnWf_SetupAction(this, EnWf_Wait);
|
|
}
|
|
|
|
void EnWf_Wait(EnWf* this, GlobalContext* globalCtx) {
|
|
Player* player;
|
|
s32 pad;
|
|
s16 angle;
|
|
|
|
player = GET_PLAYER(globalCtx);
|
|
SkelAnime_Update(&this->skelAnime);
|
|
|
|
if (this->unk_2E2 != 0) {
|
|
angle = (this->actor.yawTowardsPlayer - this->actor.shape.rot.y) - this->unk_4D4.y;
|
|
|
|
if (ABS(angle) > 0x2000) {
|
|
this->unk_2E2--;
|
|
return;
|
|
}
|
|
|
|
this->unk_2E2 = 0;
|
|
}
|
|
|
|
angle = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
|
|
angle = ABS(angle);
|
|
|
|
if (!EnWf_DodgeRanged(globalCtx, this)) {
|
|
// Only use of unk_2E0: never not zero, so this if block never runs
|
|
if (this->unk_2E0 != 0) {
|
|
this->unk_2E0--;
|
|
|
|
if (angle >= 0x1FFE) {
|
|
return;
|
|
}
|
|
this->unk_2E0 = 0;
|
|
} else {
|
|
if (EnWf_ChangeAction(globalCtx, this, false)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
angle = player->actor.shape.rot.y - this->actor.shape.rot.y;
|
|
angle = ABS(angle);
|
|
|
|
if ((this->actor.xzDistToPlayer < 80.0f) && (player->swordState != 0) && (angle >= 0x1F40)) {
|
|
this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
} else {
|
|
this->actionTimer--;
|
|
|
|
if (this->actionTimer == 0) {
|
|
if (Actor_IsFacingPlayer(&this->actor, 0x1555)) {
|
|
if (Rand_ZeroOne() > 0.3f) {
|
|
EnWf_SetupRunAtPlayer(this, globalCtx);
|
|
} else {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
}
|
|
} else {
|
|
EnWf_SetupSearchForPlayer(this);
|
|
}
|
|
if ((globalCtx->gameplayFrames & 95) == 0) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupRunAtPlayer(EnWf* this, GlobalContext* globalCtx) {
|
|
f32 lastFrame = Animation_GetLastFrame(&gWolfosRunningAnim);
|
|
|
|
Animation_Change(&this->skelAnime, &gWolfosRunningAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, -4.0f);
|
|
this->action = WOLFOS_ACTION_RUN_AT_PLAYER;
|
|
EnWf_SetupAction(this, EnWf_RunAtPlayer);
|
|
}
|
|
|
|
void EnWf_RunAtPlayer(EnWf* this, GlobalContext* globalCtx) {
|
|
s32 animPrevFrame;
|
|
s32 sp58;
|
|
s32 pad;
|
|
f32 baseRange = 0.0f;
|
|
s16 playerFacingAngleDiff;
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
s32 playSpeed;
|
|
|
|
if (!EnWf_DodgeRanged(globalCtx, this)) {
|
|
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0x2EE, 0);
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
|
|
if (Actor_OtherIsTargeted(globalCtx, &this->actor)) {
|
|
baseRange = 150.0f;
|
|
}
|
|
|
|
if (this->actor.xzDistToPlayer <= (50.0f + baseRange)) {
|
|
Math_SmoothStepToF(&this->actor.speedXZ, -8.0f, 1.0f, 1.5f, 0.0f);
|
|
} else if ((65.0f + baseRange) < this->actor.xzDistToPlayer) {
|
|
Math_SmoothStepToF(&this->actor.speedXZ, 8.0f, 1.0f, 1.5f, 0.0f);
|
|
} else {
|
|
Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 6.65f, 0.0f);
|
|
}
|
|
|
|
this->skelAnime.playSpeed = this->actor.speedXZ * 0.175f;
|
|
playerFacingAngleDiff = player->actor.shape.rot.y - this->actor.shape.rot.y;
|
|
playerFacingAngleDiff = ABS(playerFacingAngleDiff);
|
|
|
|
if ((this->actor.xzDistToPlayer < (150.0f + baseRange)) && (player->swordState != 0) &&
|
|
(playerFacingAngleDiff >= 8000)) {
|
|
this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
|
|
if (Rand_ZeroOne() > 0.7f) {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
return;
|
|
}
|
|
}
|
|
|
|
animPrevFrame = this->skelAnime.curFrame;
|
|
SkelAnime_Update(&this->skelAnime);
|
|
sp58 = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed);
|
|
playSpeed = (f32)ABS(this->skelAnime.playSpeed);
|
|
|
|
if (!Actor_IsFacingPlayer(&this->actor, 0x11C7)) {
|
|
if (Rand_ZeroOne() > 0.5f) {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
} else {
|
|
EnWf_SetupWait(this);
|
|
}
|
|
} else if (this->actor.xzDistToPlayer < (90.0f + baseRange)) {
|
|
s16 temp_v1 = player->actor.shape.rot.y - this->actor.shape.rot.y;
|
|
|
|
if (!Actor_OtherIsTargeted(globalCtx, &this->actor) &&
|
|
((Rand_ZeroOne() > 0.03f) || ((this->actor.xzDistToPlayer <= 80.0f) && (ABS(temp_v1) < 0x38E0)))) {
|
|
EnWf_SetupSlash(this);
|
|
} else if (Actor_OtherIsTargeted(globalCtx, &this->actor) && (Rand_ZeroOne() > 0.5f)) {
|
|
EnWf_SetupBackflipAway(this);
|
|
} else {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
}
|
|
}
|
|
|
|
if (!EnWf_ChangeAction(globalCtx, this, false)) {
|
|
if ((globalCtx->gameplayFrames & 95) == 0) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY);
|
|
}
|
|
if ((animPrevFrame != (s32)this->skelAnime.curFrame) && (sp58 <= 0) && ((playSpeed + animPrevFrame) > 0)) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_WALK);
|
|
Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 20.0f, 3, 3.0f, 50, 50, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupSearchForPlayer(EnWf* this) {
|
|
Animation_MorphToLoop(&this->skelAnime, &gWolfosSidesteppingAnim, -4.0f);
|
|
this->action = WOLFOS_ACTION_SEARCH_FOR_PLAYER;
|
|
EnWf_SetupAction(this, EnWf_SearchForPlayer);
|
|
}
|
|
|
|
void EnWf_SearchForPlayer(EnWf* this, GlobalContext* globalCtx) {
|
|
s16 yawDiff;
|
|
s16 phi_v1;
|
|
f32 phi_f2;
|
|
|
|
if (!EnWf_DodgeRanged(globalCtx, this) && !EnWf_ChangeAction(globalCtx, this, false)) {
|
|
yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
|
|
phi_v1 = (yawDiff > 0) ? (yawDiff * 0.25f) + 2000.0f : (yawDiff * 0.25f) - 2000.0f;
|
|
this->actor.shape.rot.y += phi_v1;
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
|
|
if (yawDiff > 0) {
|
|
phi_f2 = phi_v1 * 0.5f;
|
|
phi_f2 = CLAMP_MAX(phi_f2, 1.0f);
|
|
} else {
|
|
phi_f2 = phi_v1 * 0.5f;
|
|
phi_f2 = CLAMP_MIN(phi_f2, -1.0f);
|
|
}
|
|
|
|
this->skelAnime.playSpeed = -phi_f2;
|
|
SkelAnime_Update(&this->skelAnime);
|
|
|
|
if (Actor_IsFacingPlayer(&this->actor, 0x1555)) {
|
|
if (Rand_ZeroOne() > 0.8f) {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
} else {
|
|
EnWf_SetupRunAtPlayer(this, globalCtx);
|
|
}
|
|
}
|
|
|
|
if ((globalCtx->gameplayFrames & 95) == 0) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupRunAroundPlayer(EnWf* this) {
|
|
f32 lastFrame = Animation_GetLastFrame(&gWolfosRunningAnim);
|
|
|
|
Animation_Change(&this->skelAnime, &gWolfosRunningAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, -4.0f);
|
|
|
|
if (Rand_ZeroOne() > 0.5f) {
|
|
this->runAngle = 16000;
|
|
} else {
|
|
this->runAngle = -16000;
|
|
}
|
|
|
|
this->skelAnime.playSpeed = this->actor.speedXZ = 6.0f;
|
|
this->skelAnime.playSpeed *= 0.175f;
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
this->actionTimer = (Rand_ZeroOne() * 30.0f) + 30.0f;
|
|
this->action = WOLFOS_ACTION_RUN_AROUND_PLAYER;
|
|
this->runSpeed = 0.0f;
|
|
|
|
EnWf_SetupAction(this, EnWf_RunAroundPlayer);
|
|
}
|
|
|
|
void EnWf_RunAroundPlayer(EnWf* this, GlobalContext* globalCtx) {
|
|
s16 angle1;
|
|
s16 angle2;
|
|
s32 pad;
|
|
f32 baseRange = 0.0f;
|
|
s32 animPrevFrame;
|
|
s32 animFrameSpeedDiff;
|
|
s32 animSpeed;
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
|
|
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + this->runAngle, 1, 4000, 1);
|
|
|
|
if (!EnWf_DodgeRanged(globalCtx, this) && !EnWf_ChangeAction(globalCtx, this, false)) {
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
angle1 = player->actor.shape.rot.y + this->runAngle + 0x8000;
|
|
|
|
// Actor_TestFloorInDirection is useless here (see comment below)
|
|
if ((this->actor.bgCheckFlags & 8) ||
|
|
!Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y)) {
|
|
angle2 = (this->actor.bgCheckFlags & 8)
|
|
? (this->actor.wallYaw - this->actor.yawTowardsPlayer) - this->runAngle
|
|
: 0;
|
|
|
|
// This is probably meant to reverse direction if the edge of a floor is encountered, but does nothing
|
|
// unless bgCheckFlags & 8 anyway, since angle2 = 0 otherwise
|
|
if (ABS(angle2) > 0x2EE0) {
|
|
this->runAngle = -this->runAngle;
|
|
}
|
|
}
|
|
|
|
if (Actor_OtherIsTargeted(globalCtx, &this->actor)) {
|
|
baseRange = 150.0f;
|
|
}
|
|
|
|
if (this->actor.xzDistToPlayer <= (60.0f + baseRange)) {
|
|
Math_SmoothStepToF(&this->runSpeed, -4.0f, 1.0f, 1.5f, 0.0f);
|
|
} else if ((80.0f + baseRange) < this->actor.xzDistToPlayer) {
|
|
Math_SmoothStepToF(&this->runSpeed, 4.0f, 1.0f, 1.5f, 0.0f);
|
|
} else {
|
|
Math_SmoothStepToF(&this->runSpeed, 0.0f, 1.0f, 6.65f, 0.0f);
|
|
}
|
|
|
|
if (this->runSpeed != 0.0f) {
|
|
this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->runSpeed;
|
|
this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->runSpeed;
|
|
}
|
|
|
|
if (ABS(this->runSpeed) < ABS(this->actor.speedXZ)) {
|
|
this->skelAnime.playSpeed = this->actor.speedXZ * 0.175f;
|
|
} else {
|
|
this->skelAnime.playSpeed = this->runSpeed * 0.175f;
|
|
}
|
|
|
|
this->skelAnime.playSpeed = CLAMP(this->skelAnime.playSpeed, -3.0f, 3.0f);
|
|
animPrevFrame = this->skelAnime.curFrame;
|
|
SkelAnime_Update(&this->skelAnime);
|
|
animFrameSpeedDiff = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed);
|
|
animSpeed = (f32)ABS(this->skelAnime.playSpeed);
|
|
|
|
if ((animPrevFrame != (s32)this->skelAnime.curFrame) && (animFrameSpeedDiff <= 0) &&
|
|
(animSpeed + animPrevFrame > 0)) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_WALK);
|
|
Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 20.0f, 3, 3.0f, 50, 50, 1);
|
|
}
|
|
|
|
if ((globalCtx->gameplayFrames & 95) == 0) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY);
|
|
}
|
|
|
|
if ((Math_CosS(angle1 - this->actor.shape.rot.y) < -0.85f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) &&
|
|
(this->actor.xzDistToPlayer <= 80.0f)) {
|
|
EnWf_SetupSlash(this);
|
|
} else {
|
|
this->actionTimer--;
|
|
|
|
if (this->actionTimer == 0) {
|
|
if (Actor_OtherIsTargeted(globalCtx, &this->actor) && (Rand_ZeroOne() > 0.5f)) {
|
|
EnWf_SetupBackflipAway(this);
|
|
} else {
|
|
EnWf_SetupWait(this);
|
|
this->actionTimer = (Rand_ZeroOne() * 3.0f) + 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupSlash(EnWf* this) {
|
|
Animation_PlayOnce(&this->skelAnime, &gWolfosSlashingAnim);
|
|
this->colliderSpheres.base.atFlags &= ~AT_BOUNCED;
|
|
this->actor.shape.rot.y = this->actor.yawTowardsPlayer;
|
|
this->action = WOLFOS_ACTION_SLASH;
|
|
this->unk_2FA = 0; // Set and not used
|
|
this->actionTimer = 7;
|
|
this->skelAnime.endFrame = 20.0f;
|
|
this->actor.speedXZ = 0.0f;
|
|
|
|
EnWf_SetupAction(this, EnWf_Slash);
|
|
}
|
|
|
|
void EnWf_Slash(EnWf* this, GlobalContext* globalCtx) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
s16 shapeAngleDiff = player->actor.shape.rot.y - this->actor.shape.rot.y;
|
|
s16 yawAngleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
|
|
s32 curFrame = this->skelAnime.curFrame;
|
|
|
|
shapeAngleDiff = ABS(shapeAngleDiff);
|
|
yawAngleDiff = ABS(yawAngleDiff);
|
|
this->actor.speedXZ = 0.0f;
|
|
|
|
if (((curFrame >= 9) && (curFrame <= 12)) || ((curFrame >= 17) && (curFrame <= 19))) {
|
|
if (this->slashStatus == 0) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_ATTACK);
|
|
}
|
|
|
|
this->slashStatus = 1;
|
|
} else {
|
|
this->slashStatus = 0;
|
|
}
|
|
|
|
if (((curFrame == 15) && !Actor_IsTargeted(globalCtx, &this->actor) &&
|
|
(!Actor_IsFacingPlayer(&this->actor, 0x2000) || (this->actor.xzDistToPlayer >= 100.0f))) ||
|
|
SkelAnime_Update(&this->skelAnime)) {
|
|
if ((curFrame != 15) && (this->actionTimer != 0)) {
|
|
this->actor.shape.rot.y += (s16)(3276.0f * (1.5f + (this->actionTimer - 4) * 0.4f));
|
|
Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 15.0f, 1, 2.0f, 50, 50, 1);
|
|
this->actionTimer--;
|
|
} else if (!Actor_IsFacingPlayer(&this->actor, 0x1554) && (curFrame != 15)) {
|
|
EnWf_SetupWait(this);
|
|
this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f;
|
|
|
|
if (yawAngleDiff > 13000) {
|
|
this->unk_2E2 = 7;
|
|
}
|
|
} else if ((Rand_ZeroOne() > 0.7f) || (this->actor.xzDistToPlayer >= 120.0f)) {
|
|
EnWf_SetupWait(this);
|
|
this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f;
|
|
} else {
|
|
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
|
|
if (Rand_ZeroOne() > 0.7f) {
|
|
EnWf_SetupSidestep(this, globalCtx);
|
|
} else if (shapeAngleDiff <= 10000) {
|
|
if (yawAngleDiff > 16000) {
|
|
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
} else {
|
|
EnWf_ChangeAction(globalCtx, this, true);
|
|
}
|
|
} else {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupRecoilFromBlockedSlash(EnWf* this) {
|
|
f32 endFrame = 1.0f;
|
|
|
|
if ((s32)this->skelAnime.curFrame >= 16) {
|
|
endFrame = 15.0f;
|
|
}
|
|
|
|
Animation_Change(&this->skelAnime, &gWolfosSlashingAnim, -0.5f, this->skelAnime.curFrame - 1.0f, endFrame,
|
|
ANIMMODE_ONCE_INTERP, 0.0f);
|
|
this->action = WOLFOS_ACTION_RECOIL_FROM_BLOCKED_SLASH;
|
|
this->slashStatus = 0;
|
|
EnWf_SetupAction(this, EnWf_RecoilFromBlockedSlash);
|
|
}
|
|
|
|
void EnWf_RecoilFromBlockedSlash(EnWf* this, GlobalContext* globalCtx) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
s16 angle1 = player->actor.shape.rot.y - this->actor.shape.rot.y;
|
|
s16 angle2 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
|
|
|
|
angle1 = ABS(angle1);
|
|
angle2 = ABS(angle2);
|
|
|
|
if (SkelAnime_Update(&this->skelAnime)) {
|
|
if (!Actor_IsFacingPlayer(&this->actor, 0x1554)) {
|
|
EnWf_SetupWait(this);
|
|
this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f;
|
|
|
|
if (angle2 > 0x32C8) {
|
|
this->unk_2E2 = 30;
|
|
}
|
|
} else {
|
|
if ((Rand_ZeroOne() > 0.7f) || (this->actor.xzDistToPlayer >= 120.0f)) {
|
|
EnWf_SetupWait(this);
|
|
this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f;
|
|
} else {
|
|
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
|
|
if (Rand_ZeroOne() > 0.7f) {
|
|
EnWf_SetupSidestep(this, globalCtx);
|
|
} else if (angle1 <= 0x2710) {
|
|
if (angle2 > 0x3E80) {
|
|
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
} else {
|
|
EnWf_ChangeAction(globalCtx, this, true);
|
|
}
|
|
} else {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupBackflipAway(EnWf* this) {
|
|
Animation_MorphToPlayOnce(&this->skelAnime, &gWolfosBackflippingAnim, -3.0f);
|
|
this->actor.speedXZ = -6.0f;
|
|
this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
this->actionTimer = 0;
|
|
this->unk_300 = true;
|
|
this->action = WOLFOS_ACTION_BACKFLIP_AWAY;
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP);
|
|
EnWf_SetupAction(this, EnWf_BackflipAway);
|
|
}
|
|
|
|
void EnWf_BackflipAway(EnWf* this, GlobalContext* globalCtx) {
|
|
if (SkelAnime_Update(&this->skelAnime)) {
|
|
if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && (this->actor.xzDistToPlayer < 170.0f) &&
|
|
(this->actor.xzDistToPlayer > 140.0f) && (Rand_ZeroOne() < 0.2f)) {
|
|
EnWf_SetupRunAtPlayer(this, globalCtx);
|
|
} else if ((globalCtx->gameplayFrames % 2) != 0) {
|
|
EnWf_SetupSidestep(this, globalCtx);
|
|
} else {
|
|
EnWf_SetupWait(this);
|
|
}
|
|
}
|
|
if ((globalCtx->state.frames & 95) == 0) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY);
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupStunned(EnWf* this) {
|
|
if (this->actor.bgCheckFlags & 1) {
|
|
this->actor.speedXZ = 0.0f;
|
|
}
|
|
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE);
|
|
Animation_PlayOnceSetSpeed(&this->skelAnime, &gWolfosDamagedAnim, 0.0f);
|
|
this->action = WOLFOS_ACTION_STUNNED;
|
|
EnWf_SetupAction(this, EnWf_Stunned);
|
|
}
|
|
|
|
void EnWf_Stunned(EnWf* this, GlobalContext* globalCtx) {
|
|
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->unk_300 = false;
|
|
}
|
|
|
|
if ((this->actor.colorFilterTimer == 0) && (this->actor.bgCheckFlags & 1)) {
|
|
if (this->actor.colChkInfo.health == 0) {
|
|
EnWf_SetupDie(this);
|
|
} else {
|
|
EnWf_ChangeAction(globalCtx, this, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupDamaged(EnWf* this) {
|
|
Animation_MorphToPlayOnce(&this->skelAnime, &gWolfosDamagedAnim, -4.0f);
|
|
|
|
if (this->actor.bgCheckFlags & 1) {
|
|
this->unk_300 = false;
|
|
this->actor.speedXZ = -4.0f;
|
|
} else {
|
|
this->unk_300 = true;
|
|
}
|
|
|
|
this->unk_2E2 = 0;
|
|
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_DAMAGE);
|
|
this->action = WOLFOS_ACTION_DAMAGED;
|
|
EnWf_SetupAction(this, EnWf_Damaged);
|
|
}
|
|
|
|
void EnWf_Damaged(EnWf* 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->unk_300 = false;
|
|
}
|
|
|
|
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4500, 0);
|
|
|
|
if (!EnWf_ChangeAction(globalCtx, this, false) && SkelAnime_Update(&this->skelAnime)) {
|
|
if (this->actor.bgCheckFlags & 1) {
|
|
angleToWall = this->actor.wallYaw - this->actor.shape.rot.y;
|
|
angleToWall = ABS(angleToWall);
|
|
|
|
if ((this->actor.bgCheckFlags & 8) && (ABS(angleToWall) < 12000) && (this->actor.xzDistToPlayer < 120.0f)) {
|
|
EnWf_SetupSomersaultAndAttack(this);
|
|
} else if (!EnWf_DodgeRanged(globalCtx, this)) {
|
|
if ((this->actor.xzDistToPlayer <= 80.0f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) &&
|
|
((globalCtx->gameplayFrames % 8) != 0)) {
|
|
EnWf_SetupSlash(this);
|
|
} else if (Rand_ZeroOne() > 0.5f) {
|
|
EnWf_SetupWait(this);
|
|
this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f;
|
|
this->unk_2E2 = 30;
|
|
} else {
|
|
EnWf_SetupBackflipAway(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupSomersaultAndAttack(EnWf* this) {
|
|
f32 lastFrame = Animation_GetLastFrame(&gWolfosBackflippingAnim);
|
|
|
|
Animation_Change(&this->skelAnime, &gWolfosBackflippingAnim, -1.0f, lastFrame, 0.0f, ANIMMODE_ONCE, -3.0f);
|
|
this->actionTimer = 0;
|
|
this->unk_300 = false;
|
|
this->action = WOLFOS_ACTION_TURN_TOWARDS_PLAYER;
|
|
this->actor.speedXZ = 6.5f;
|
|
this->actor.velocity.y = 15.0f;
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP);
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
EnWf_SetupAction(this, EnWf_SomersaultAndAttack);
|
|
}
|
|
|
|
void EnWf_SomersaultAndAttack(EnWf* this, GlobalContext* globalCtx) {
|
|
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 1);
|
|
|
|
if (this->actor.velocity.y >= 5.0f) {
|
|
//! @bug unk_4C8 and unk_4BC are used but not set (presumably intended to be feet positions like other actors)
|
|
func_800355B8(globalCtx, &this->unk_4C8);
|
|
func_800355B8(globalCtx, &this->unk_4BC);
|
|
}
|
|
|
|
if (SkelAnime_Update(&this->skelAnime) && (this->actor.bgCheckFlags & (1 | 2))) {
|
|
this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer;
|
|
this->actor.shape.rot.x = 0;
|
|
this->actor.speedXZ = this->actor.velocity.y = 0.0f;
|
|
this->actor.world.pos.y = this->actor.floorHeight;
|
|
|
|
if (!Actor_OtherIsTargeted(globalCtx, &this->actor)) {
|
|
EnWf_SetupSlash(this);
|
|
} else {
|
|
EnWf_SetupWait(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupBlocking(EnWf* this) {
|
|
f32 lastFrame = Animation_GetLastFrame(&gWolfosBlockingAnim);
|
|
|
|
if (this->slashStatus != 0) {
|
|
this->slashStatus = -1;
|
|
}
|
|
|
|
this->actor.speedXZ = 0.0f;
|
|
this->action = WOLFOS_ACTION_BLOCKING;
|
|
this->actionTimer = 10;
|
|
|
|
Animation_Change(&this->skelAnime, &gWolfosBlockingAnim, 0.0f, 0.0f, lastFrame, ANIMMODE_ONCE_INTERP, -4.0f);
|
|
EnWf_SetupAction(this, EnWf_Blocking);
|
|
}
|
|
|
|
void EnWf_Blocking(EnWf* this, GlobalContext* globalCtx) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
s32 pad;
|
|
|
|
if (this->actionTimer != 0) {
|
|
this->actionTimer--;
|
|
} else {
|
|
this->skelAnime.playSpeed = 1.0f;
|
|
}
|
|
|
|
if (SkelAnime_Update(&this->skelAnime)) {
|
|
s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
|
|
|
|
if ((ABS(yawDiff) <= 0x4000) && (this->actor.xzDistToPlayer < 60.0f) &&
|
|
(ABS(this->actor.yDistToPlayer) < 50.0f)) {
|
|
if (func_800354B4(globalCtx, &this->actor, 100.0f, 10000, 0x4000, this->actor.shape.rot.y)) {
|
|
if (player->swordAnimation == 0x11) {
|
|
EnWf_SetupBlocking(this);
|
|
} else if ((globalCtx->gameplayFrames % 2) != 0) {
|
|
EnWf_SetupBlocking(this);
|
|
} else {
|
|
EnWf_SetupBackflipAway(this);
|
|
}
|
|
|
|
} else {
|
|
s16 angleFacingLink = player->actor.shape.rot.y - this->actor.shape.rot.y;
|
|
|
|
if (!Actor_OtherIsTargeted(globalCtx, &this->actor) &&
|
|
(((globalCtx->gameplayFrames % 2) != 0) || (ABS(angleFacingLink) < 0x38E0))) {
|
|
EnWf_SetupSlash(this);
|
|
} else {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
}
|
|
}
|
|
} else {
|
|
EnWf_SetupRunAroundPlayer(this);
|
|
}
|
|
} else if (this->actionTimer == 0) {
|
|
if (func_800354B4(globalCtx, &this->actor, 100.0f, 10000, 0x4000, this->actor.shape.rot.y)) {
|
|
if (player->swordAnimation == 0x11) {
|
|
EnWf_SetupBlocking(this);
|
|
} else if ((globalCtx->gameplayFrames % 2) != 0) {
|
|
EnWf_SetupBlocking(this);
|
|
} else {
|
|
EnWf_SetupBackflipAway(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupSidestep(EnWf* this, GlobalContext* globalCtx) {
|
|
s16 angle;
|
|
Player* player;
|
|
f32 lastFrame = Animation_GetLastFrame(&gWolfosRunningAnim);
|
|
|
|
Animation_Change(&this->skelAnime, &gWolfosRunningAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, -4.0f);
|
|
|
|
player = GET_PLAYER(globalCtx);
|
|
angle = player->actor.shape.rot.y + this->runAngle;
|
|
|
|
if (Math_SinS(angle - this->actor.yawTowardsPlayer) > 0.0f) {
|
|
this->runAngle = 16000;
|
|
} else if (Math_SinS(angle - this->actor.yawTowardsPlayer) < 0.0f) {
|
|
this->runAngle = -16000;
|
|
} else if (Rand_ZeroOne() > 0.5f) {
|
|
this->runAngle = 16000;
|
|
} else {
|
|
this->runAngle = -16000;
|
|
}
|
|
|
|
this->skelAnime.playSpeed = this->actor.speedXZ = 6.0f;
|
|
this->skelAnime.playSpeed *= 0.175f;
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
this->runSpeed = 0.0f;
|
|
this->actionTimer = (Rand_ZeroOne() * 10.0f) + 5.0f;
|
|
this->action = WOLFOS_ACTION_SIDESTEP;
|
|
|
|
EnWf_SetupAction(this, EnWf_Sidestep);
|
|
}
|
|
|
|
void EnWf_Sidestep(EnWf* this, GlobalContext* globalCtx) {
|
|
s16 angleDiff1;
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
s32 animPrevFrame;
|
|
s32 animFrameSpeedDiff;
|
|
s32 animSpeed;
|
|
f32 baseRange = 0.0f;
|
|
|
|
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + this->runAngle, 1, 3000, 1);
|
|
|
|
// Actor_TestFloorInDirection is useless here (see comment below)
|
|
if ((this->actor.bgCheckFlags & 8) ||
|
|
!Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y)) {
|
|
s16 angle =
|
|
(this->actor.bgCheckFlags & 8) ? (this->actor.wallYaw - this->actor.yawTowardsPlayer) - this->runAngle : 0;
|
|
|
|
// This is probably meant to reverse direction if the edge of a floor is encountered, but does nothing
|
|
// unless bgCheckFlags & 8 anyway, since angle = 0 otherwise
|
|
if (ABS(angle) > 0x2EE0) {
|
|
this->runAngle = -this->runAngle;
|
|
}
|
|
}
|
|
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
|
|
if (Actor_OtherIsTargeted(globalCtx, &this->actor)) {
|
|
baseRange = 150.0f;
|
|
}
|
|
|
|
if (this->actor.xzDistToPlayer <= (60.0f + baseRange)) {
|
|
Math_SmoothStepToF(&this->runSpeed, -4.0f, 1.0f, 1.5f, 0.0f);
|
|
} else if ((80.0f + baseRange) < this->actor.xzDistToPlayer) {
|
|
Math_SmoothStepToF(&this->runSpeed, 4.0f, 1.0f, 1.5f, 0.0f);
|
|
} else {
|
|
Math_SmoothStepToF(&this->runSpeed, 0.0f, 1.0f, 6.65f, 0.0f);
|
|
}
|
|
|
|
if (this->runSpeed != 0.0f) {
|
|
this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->runSpeed;
|
|
this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->runSpeed;
|
|
}
|
|
|
|
if (ABS(this->runSpeed) < ABS(this->actor.speedXZ)) {
|
|
this->skelAnime.playSpeed = this->actor.speedXZ * 0.175f;
|
|
} else {
|
|
this->skelAnime.playSpeed = this->runSpeed * 0.175f;
|
|
}
|
|
|
|
this->skelAnime.playSpeed = CLAMP(this->skelAnime.playSpeed, -3.0f, 3.0f);
|
|
|
|
animPrevFrame = this->skelAnime.curFrame;
|
|
SkelAnime_Update(&this->skelAnime);
|
|
animFrameSpeedDiff = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed);
|
|
animSpeed = (f32)ABS(this->skelAnime.playSpeed);
|
|
|
|
if (!EnWf_ChangeAction(globalCtx, this, false)) {
|
|
this->actionTimer--;
|
|
|
|
if (this->actionTimer == 0) {
|
|
angleDiff1 = player->actor.shape.rot.y - this->actor.yawTowardsPlayer;
|
|
angleDiff1 = ABS(angleDiff1);
|
|
|
|
if (angleDiff1 >= 0x3A98) {
|
|
EnWf_SetupWait(this);
|
|
this->actionTimer = (Rand_ZeroOne() * 3.0f) + 1.0f;
|
|
} else {
|
|
Player* player2 = GET_PLAYER(globalCtx);
|
|
s16 angleDiff2 = player2->actor.shape.rot.y - this->actor.yawTowardsPlayer;
|
|
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
|
|
if ((this->actor.xzDistToPlayer <= 80.0f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) &&
|
|
(((globalCtx->gameplayFrames % 4) == 0) || (ABS(angleDiff2) < 0x38E0))) {
|
|
EnWf_SetupSlash(this);
|
|
} else {
|
|
EnWf_SetupRunAtPlayer(this, globalCtx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((animPrevFrame != (s32)this->skelAnime.curFrame) && (animFrameSpeedDiff <= 0) &&
|
|
((animSpeed + animPrevFrame) > 0)) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_WALK);
|
|
Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 20.0f, 3, 3.0f, 50, 50, 1);
|
|
}
|
|
|
|
if ((globalCtx->gameplayFrames & 95) == 0) {
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_SetupDie(EnWf* this) {
|
|
Animation_MorphToPlayOnce(&this->skelAnime, &gWolfosRearingUpFallingOverAnim, -4.0f);
|
|
this->actor.world.rot.y = this->actor.yawTowardsPlayer;
|
|
|
|
if (this->actor.bgCheckFlags & 1) {
|
|
this->unk_300 = false;
|
|
this->actor.speedXZ = -6.0f;
|
|
} else {
|
|
this->unk_300 = true;
|
|
}
|
|
|
|
this->action = WOLFOS_ACTION_DIE;
|
|
this->actor.flags &= ~ACTOR_FLAG_0;
|
|
this->actionTimer = this->skelAnime.animLength;
|
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_DEAD);
|
|
EnWf_SetupAction(this, EnWf_Die);
|
|
}
|
|
|
|
void EnWf_Die(EnWf* 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.5f, 0.0f);
|
|
this->unk_300 = false;
|
|
}
|
|
|
|
if (SkelAnime_Update(&this->skelAnime)) {
|
|
Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xD0);
|
|
|
|
if (this->switchFlag != 0xFF) {
|
|
Flags_SetSwitch(globalCtx, this->switchFlag);
|
|
}
|
|
|
|
Actor_Kill(&this->actor);
|
|
} else {
|
|
s32 i;
|
|
Vec3f pos;
|
|
Vec3f velAndAccel = { 0.0f, 0.5f, 0.0f };
|
|
|
|
this->actionTimer--;
|
|
|
|
for (i = ((s32)this->skelAnime.animLength - this->actionTimer) >> 1; i >= 0; i--) {
|
|
pos.x = Rand_CenteredFloat(60.0f) + this->actor.world.pos.x;
|
|
pos.z = Rand_CenteredFloat(60.0f) + this->actor.world.pos.z;
|
|
pos.y = Rand_CenteredFloat(50.0f) + (this->actor.world.pos.y + 20.0f);
|
|
EffectSsDeadDb_Spawn(globalCtx, &pos, &velAndAccel, &velAndAccel, 100, 0, 255, 255, 255, 255, 0, 0, 255, 1,
|
|
9, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void func_80B36F40(EnWf* this, GlobalContext* globalCtx) {
|
|
if ((this->action == WOLFOS_ACTION_WAIT) && (this->unk_2E2 != 0)) {
|
|
this->unk_4D4.y = Math_SinS(this->unk_2E2 * 4200) * 8920.0f;
|
|
} else if (this->action != WOLFOS_ACTION_STUNNED) {
|
|
if (this->action != WOLFOS_ACTION_SLASH) {
|
|
Math_SmoothStepToS(&this->unk_4D4.y, this->actor.yawTowardsPlayer - this->actor.shape.rot.y, 1, 1500, 0);
|
|
this->unk_4D4.y = CLAMP(this->unk_4D4.y, -0x3127, 0x3127);
|
|
} else {
|
|
this->unk_4D4.y = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_UpdateDamage(EnWf* this, GlobalContext* globalCtx) {
|
|
if (this->colliderSpheres.base.acFlags & AC_BOUNCED) {
|
|
this->colliderSpheres.base.acFlags &= ~(AC_HIT | AC_BOUNCED);
|
|
this->colliderCylinderBody.base.acFlags &= ~AC_HIT;
|
|
this->colliderCylinderTail.base.acFlags &= ~AC_HIT;
|
|
} else if ((this->colliderCylinderBody.base.acFlags & AC_HIT) ||
|
|
(this->colliderCylinderTail.base.acFlags & AC_HIT)) {
|
|
if (this->action >= WOLFOS_ACTION_WAIT) {
|
|
s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
|
|
|
|
if ((!(this->colliderCylinderBody.base.acFlags & AC_HIT) &&
|
|
(this->colliderCylinderTail.base.acFlags & AC_HIT)) ||
|
|
(ABS(yawDiff) > 19000)) {
|
|
this->actor.colChkInfo.damage *= 4;
|
|
}
|
|
|
|
this->colliderCylinderBody.base.acFlags &= ~AC_HIT;
|
|
this->colliderCylinderTail.base.acFlags &= ~AC_HIT;
|
|
|
|
if (this->actor.colChkInfo.damageEffect != ENWF_DMGEFF_ICE_MAGIC) {
|
|
this->damageEffect = this->actor.colChkInfo.damageEffect;
|
|
Actor_SetDropFlag(&this->actor, &this->colliderCylinderBody.info, 1);
|
|
this->slashStatus = 0;
|
|
|
|
if ((this->actor.colChkInfo.damageEffect == ENWF_DMGEFF_STUN) ||
|
|
(this->actor.colChkInfo.damageEffect == ENWF_DMGEFF_UNDEF)) {
|
|
if (this->action != WOLFOS_ACTION_STUNNED) {
|
|
Actor_SetColorFilter(&this->actor, 0, 120, 0, 80);
|
|
Actor_ApplyDamage(&this->actor);
|
|
EnWf_SetupStunned(this);
|
|
}
|
|
} else { // LIGHT_MAGIC, FIRE, NONE
|
|
Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 8);
|
|
|
|
if (this->damageEffect == ENWF_DMGEFF_FIRE) {
|
|
this->fireTimer = 40;
|
|
}
|
|
|
|
if (Actor_ApplyDamage(&this->actor) == 0) {
|
|
EnWf_SetupDie(this);
|
|
Enemy_StartFinishingBlow(globalCtx, &this->actor);
|
|
} else {
|
|
EnWf_SetupDamaged(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnWf_Update(Actor* thisx, GlobalContext* globalCtx) {
|
|
s32 pad;
|
|
EnWf* this = (EnWf*)thisx;
|
|
|
|
EnWf_UpdateDamage(this, globalCtx);
|
|
|
|
if (this->actor.colChkInfo.damageEffect != ENWF_DMGEFF_ICE_MAGIC) {
|
|
Actor_MoveForward(&this->actor);
|
|
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 32.0f, 30.0f, 60.0f, 0x1D);
|
|
this->actionFunc(this, globalCtx);
|
|
func_80B36F40(this, globalCtx);
|
|
}
|
|
|
|
if (this->actor.bgCheckFlags & (1 | 2)) {
|
|
func_800359B8(&this->actor, this->actor.shape.rot.y, &this->actor.shape.rot);
|
|
} else {
|
|
Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 1000, 0);
|
|
Math_SmoothStepToS(&this->actor.shape.rot.z, 0, 1, 1000, 0);
|
|
}
|
|
|
|
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderSpheres.base);
|
|
|
|
if (this->action >= WOLFOS_ACTION_WAIT) {
|
|
if ((this->actor.colorFilterTimer == 0) || !(this->actor.colorFilterParams & 0x4000)) {
|
|
Collider_UpdateCylinder(&this->actor, &this->colliderCylinderBody);
|
|
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinderTail.base);
|
|
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinderBody.base);
|
|
}
|
|
}
|
|
|
|
if (this->action == WOLFOS_ACTION_BLOCKING) {
|
|
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSpheres.base);
|
|
}
|
|
|
|
if (this->slashStatus > 0) {
|
|
if (!(this->colliderSpheres.base.atFlags & AT_BOUNCED)) {
|
|
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderSpheres.base);
|
|
} else {
|
|
EnWf_SetupRecoilFromBlockedSlash(this);
|
|
}
|
|
}
|
|
|
|
this->actor.focus.pos = this->actor.world.pos;
|
|
this->actor.focus.pos.y += 25.0f;
|
|
|
|
if (this->eyeIndex == 0) {
|
|
if ((Rand_ZeroOne() < 0.2f) && ((globalCtx->gameplayFrames % 4) == 0) && (this->actor.colorFilterTimer == 0)) {
|
|
this->eyeIndex++;
|
|
}
|
|
} else {
|
|
this->eyeIndex = (this->eyeIndex + 1) & 3;
|
|
}
|
|
}
|
|
|
|
s32 EnWf_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) {
|
|
EnWf* this = (EnWf*)thisx;
|
|
|
|
if ((limbIndex == WOLFOS_LIMB_HEAD) || (limbIndex == WOLFOS_LIMB_EYES)) {
|
|
rot->y -= this->unk_4D4.y;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void EnWf_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
|
|
static Vec3f colliderVec = { 1200.0f, 0.0f, 0.0f };
|
|
static Vec3f bodyPartVec = { 0.0f, 0.0f, 0.0f };
|
|
EnWf* this = (EnWf*)thisx;
|
|
s32 bodyPartIndex = -1;
|
|
|
|
Collider_UpdateSpheres(limbIndex, &this->colliderSpheres);
|
|
|
|
if (limbIndex == WOLFOS_LIMB_TAIL) {
|
|
Vec3f colliderPos;
|
|
|
|
bodyPartIndex = -1;
|
|
Matrix_MultVec3f(&colliderVec, &colliderPos);
|
|
this->colliderCylinderTail.dim.pos.x = colliderPos.x;
|
|
this->colliderCylinderTail.dim.pos.y = colliderPos.y;
|
|
this->colliderCylinderTail.dim.pos.z = colliderPos.z;
|
|
}
|
|
|
|
if ((this->fireTimer != 0) || ((this->actor.colorFilterTimer != 0) && (this->actor.colorFilterParams & 0x4000))) {
|
|
switch (limbIndex) {
|
|
case WOLFOS_LIMB_EYES:
|
|
bodyPartIndex = 0;
|
|
break;
|
|
case WOLFOS_LIMB_FRONT_RIGHT_LOWER_LEG:
|
|
bodyPartIndex = 1;
|
|
break;
|
|
case WOLFOS_LIMB_FRONT_LEFT_LOWER_LEG:
|
|
bodyPartIndex = 2;
|
|
break;
|
|
case WOLFOS_LIMB_THORAX:
|
|
bodyPartIndex = 3;
|
|
break;
|
|
case WOLFOS_LIMB_ABDOMEN:
|
|
bodyPartIndex = 4;
|
|
break;
|
|
case WOLFOS_LIMB_TAIL:
|
|
bodyPartIndex = 5;
|
|
break;
|
|
case WOLFOS_LIMB_BACK_RIGHT_SHIN:
|
|
bodyPartIndex = 6;
|
|
break;
|
|
case 37:
|
|
//! @bug There is no limb with index this large, so bodyPartsPos[7] is uninitialised. Thus a flame will
|
|
//! be drawn at 0,0,0 when the Wolfos is on fire.
|
|
bodyPartIndex = 7;
|
|
break;
|
|
case WOLFOS_LIMB_BACK_RIGHT_PASTERN:
|
|
bodyPartIndex = 8;
|
|
break;
|
|
case WOLFOS_LIMB_BACK_LEFT_PAW:
|
|
bodyPartIndex = 9;
|
|
break;
|
|
}
|
|
|
|
if (bodyPartIndex >= 0) {
|
|
Vec3f bodyPartPos;
|
|
|
|
Matrix_MultVec3f(&bodyPartVec, &bodyPartPos);
|
|
this->bodyPartsPos[bodyPartIndex].x = bodyPartPos.x;
|
|
this->bodyPartsPos[bodyPartIndex].y = bodyPartPos.y;
|
|
this->bodyPartsPos[bodyPartIndex].z = bodyPartPos.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void* sWolfosNormalEyeTextures[] = { gWolfosNormalEyeOpenTex, gWolfosNormalEyeHalfTex, gWolfosNormalEyeNarrowTex,
|
|
gWolfosNormalEyeHalfTex };
|
|
static void* sWolfosWhiteEyeTextures[] = { gWolfosWhiteEyeOpenTex, gWolfosWhiteEyeHalfTex, gWolfosWhiteEyeNarrowTex,
|
|
gWolfosWhiteEyeHalfTex };
|
|
|
|
void EnWf_Draw(Actor* thisx, GlobalContext* globalCtx) {
|
|
EnWf* this = (EnWf*)thisx;
|
|
|
|
OPEN_DISPS(globalCtx->state.gfxCtx);
|
|
|
|
// This conditional will always evaluate to true, since unk_300 is false whenever action is
|
|
// WOLFOS_ACTION_WAIT_TO_APPEAR.
|
|
if ((this->action != WOLFOS_ACTION_WAIT_TO_APPEAR) || !this->unk_300) {
|
|
func_80093D18(globalCtx->state.gfxCtx);
|
|
|
|
if (this->actor.params == WOLFOS_NORMAL) {
|
|
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sWolfosNormalEyeTextures[this->eyeIndex]));
|
|
} else {
|
|
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sWolfosWhiteEyeTextures[this->eyeIndex]));
|
|
}
|
|
|
|
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
|
this->skelAnime.dListCount, EnWf_OverrideLimbDraw, EnWf_PostLimbDraw, &this->actor);
|
|
|
|
if (this->fireTimer != 0) {
|
|
this->actor.colorFilterTimer++;
|
|
this->fireTimer--;
|
|
|
|
if ((this->fireTimer % 4) == 0) {
|
|
s32 fireIndex = this->fireTimer >> 2;
|
|
|
|
EffectSsEnFire_SpawnVec3s(globalCtx, &this->actor, &this->bodyPartsPos[fireIndex], 75, 0, 0, fireIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
CLOSE_DISPS(globalCtx->state.gfxCtx);
|
|
}
|
|
|
|
s32 EnWf_DodgeRanged(GlobalContext* globalCtx, EnWf* this) {
|
|
Actor* actor = Actor_GetProjectileActor(globalCtx, &this->actor, 600.0f);
|
|
|
|
if (actor != NULL) {
|
|
s16 angleToFacing;
|
|
s16 pad;
|
|
f32 dist;
|
|
|
|
angleToFacing = Actor_WorldYawTowardActor(&this->actor, actor) - this->actor.shape.rot.y;
|
|
this->actor.world.rot.y = (u16)this->actor.shape.rot.y & 0xFFFF;
|
|
dist = Actor_WorldDistXYZToPoint(&this->actor, &actor->world.pos);
|
|
|
|
if ((ABS(angleToFacing) < 0x2EE0) && (sqrt(dist) < 400.0)) {
|
|
EnWf_SetupBlocking(this);
|
|
} else {
|
|
this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF;
|
|
if ((ABS(angleToFacing) < 0x2000) || (ABS(angleToFacing) > 0x5FFF)) {
|
|
EnWf_SetupSidestep(this, globalCtx);
|
|
this->actor.speedXZ *= 2.0f;
|
|
} else if (ABS(angleToFacing) < 0x5FFF) {
|
|
EnWf_SetupBackflipAway(this);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|