diff --git a/assets/xml/objects/object_tanron4.xml b/assets/xml/objects/object_tanron4.xml index f1002fd229..2c988e74b3 100644 --- a/assets/xml/objects/object_tanron4.xml +++ b/assets/xml/objects/object_tanron4.xml @@ -1,14 +1,19 @@  + - - + + + + + + @@ -19,6 +24,7 @@ - + + diff --git a/include/functions.h b/include/functions.h index 3595508fc7..aa37f2e11d 100644 --- a/include/functions.h +++ b/include/functions.h @@ -1647,7 +1647,7 @@ void Actor_ProcessInitChain(Actor* actor, InitChainEntry* ichain); f32 Math_SmoothStepToF(f32* pValue, f32 target, f32 fraction, f32 step, f32 minStep); void Math_ApproachF(f32* pValue, f32 target, f32 scale, f32 maxStep); void Math_ApproachZeroF(f32* pValue, f32 scale, f32 maxStep); -s32 Math_SmoothStepToS(s16* pValue, s16 target, s16 scale, s16 step, s16 minStep); +s16 Math_SmoothStepToS(s16* pValue, s16 target, s16 scale, s16 step, s16 minStep); void Math_ApproachS(s16* pValue, s16 target, s16 scale, s16 maxStep); void Color_RGBA8_Copy(Color_RGBA8* dst, Color_RGBA8* src); void func_801000A4(u16 sfxId); diff --git a/spec b/spec index d67962a93a..32ee43eb97 100644 --- a/spec +++ b/spec @@ -4828,8 +4828,7 @@ beginseg name "ovl_En_Tanron4" compress include "build/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.o" - include "build/data/ovl_En_Tanron4/ovl_En_Tanron4.data.o" - include "build/data/ovl_En_Tanron4/ovl_En_Tanron4.reloc.o" + include "build/src/overlays/actors/ovl_En_Tanron4/ovl_En_Tanron4_reloc.o" endseg beginseg diff --git a/src/code/z_lib.c b/src/code/z_lib.c index d9300deda3..aaf4bc7d91 100644 --- a/src/code/z_lib.c +++ b/src/code/z_lib.c @@ -578,7 +578,7 @@ void Math_ApproachZeroF(f32* pValue, f32 scale, f32 maxStep) { *pValue = *pValue - f0; } -s32 Math_SmoothStepToS(s16* pValue, s16 target, s16 scale, s16 step, s16 minStep) { +s16 Math_SmoothStepToS(s16* pValue, s16 target, s16 scale, s16 step, s16 minStep) { s16 stepSize = 0; s16 diff = target - *pValue; diff --git a/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.c b/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.c index 9912baf601..86f4d5ea54 100644 --- a/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.c +++ b/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.c @@ -1,7 +1,7 @@ /* * File: z_en_tanron4.c * Overlay: ovl_En_Tanron4 - * Description: Flock of Seagulls + * Description: Seagull */ #include "z_en_tanron4.h" @@ -15,7 +15,22 @@ void EnTanron4_Destroy(Actor* thisx, GlobalContext* globalCtx); void EnTanron4_Update(Actor* thisx, GlobalContext* globalCtx); void EnTanron4_Draw(Actor* thisx, GlobalContext* globalCtx); -#if 0 +void EnTanron4_SetupFlyNearHome(EnTanron4* this); +void EnTanron4_SetupFlyNearActor(EnTanron4* this); + +void EnTanron4_FlyNearHome(EnTanron4* this, GlobalContext* globalCtx); +void EnTanron4_FlyNearActor(EnTanron4* this, GlobalContext* globalCtx); + +typedef enum { + /* 0 */ SEAGULL_FLY_FLAP, + /* 1 */ SEAGULL_FLY_GLIDE, +} SeagullFlyState; + +typedef enum { + /* 0 */ SEAGULL_TIMER_FLY_STATE, + /* 1 */ SEAGULL_TIMER_CHOOSE_TARGET, +} SeagullTimers; + const ActorInit En_Tanron4_InitVars = { ACTOR_EN_TANRON4, ACTORCAT_ITEMACTION, @@ -28,22 +43,241 @@ const ActorInit En_Tanron4_InitVars = { (ActorFunc)EnTanron4_Draw, }; -#endif +void EnTanron4_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnTanron4* this = THIS; -extern UNK_TYPE D_06000168; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSeagullSkel, &gSeagullFlapAnim, this->jointTable, + this->morphTable, OBJECT_TANRON4_LIMB_MAX); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron4/EnTanron4_Init.s") + thisx->flags &= ~ACTOR_FLAG_1; + thisx->speedXZ = 3.0f + KREG(48); + thisx->uncullZoneForward = 10000.0f + KREG(70); + this->randRollTimer = Rand_ZeroFloat(10.0f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron4/EnTanron4_Destroy.s") + if (thisx->params == SEAGULL_FOLLOW_ACTOR) { + EnTanron4_SetupFlyNearActor(this); + } else { + EnTanron4_SetupFlyNearHome(this); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron4/func_80BE3DC0.s") + // spawn number of seagulls specified by params + if (thisx->params >= 0) { + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron4/func_80BE3DFC.s") + for (i = 0; i < thisx->params; i++) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_TANRON4, + thisx->world.pos.x + randPlusMinusPoint5Scaled(500.0f), + thisx->world.pos.y + randPlusMinusPoint5Scaled(100.0f), + thisx->world.pos.z + randPlusMinusPoint5Scaled(500.0f), 0, Rand_ZeroFloat(65536.0f), 0, + SEAGULL_CLONE); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron4/func_80BE4268.s") + if ((gSaveContext.time > CLOCK_TIME(20, 0)) || (gSaveContext.time < CLOCK_TIME(4, 0))) { + this->timeInfluence = 1500.0f; + thisx->world.pos.y += 1500.0f; + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron4/func_80BE42A4.s") +void EnTanron4_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron4/EnTanron4_Update.s") +void EnTanron4_SetupFlyNearHome(EnTanron4* this) { + this->actionFunc = EnTanron4_FlyNearHome; + Animation_MorphToLoop(&this->skelAnime, &gSeagullFlapAnim, 0.0f); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron4/EnTanron4_Draw.s") +void EnTanron4_FlyNearHome(EnTanron4* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 distToTarget; + s16 xRot; + s16 yRot; + s16 zRot; + + // `timeInfluence` controls both the height of the seagulls and when they are visible. + // They fly higher in the sky as the night goes on, and they disapear as dawn approaches. + if ((gSaveContext.time > CLOCK_TIME(20, 0)) || (gSaveContext.time < CLOCK_TIME(4, 0))) { + Math_ApproachF(&this->timeInfluence, 1500.0f, 1.0f, 1.0f); + } else { + Math_ApproachZeroF(&this->timeInfluence, 1.0f, 1.0f); + } + + xDiff = this->targetPos.x - this->actor.world.pos.x; + yDiff = (this->targetPos.y + this->timeInfluence) - this->actor.world.pos.y; + zDiff = this->targetPos.z - this->actor.world.pos.z; + + distToTarget = sqrtf(SQ(xDiff) + SQ(zDiff)); + + if ((this->timers[SEAGULL_TIMER_CHOOSE_TARGET] == 0) || (distToTarget < 100.0f)) { + this->targetPos.x = this->actor.home.pos.x + randPlusMinusPoint5Scaled(500.0f); + this->targetPos.y = this->actor.home.pos.y + randPlusMinusPoint5Scaled(100.0f); + this->targetPos.z = this->actor.home.pos.z + randPlusMinusPoint5Scaled(500.0f); + + this->timers[SEAGULL_TIMER_CHOOSE_TARGET] = Rand_ZeroFloat(100.0f) + 60.0f; + this->step = 0; + } + + yRot = Math_Atan2S(xDiff, zDiff); + xRot = Math_Atan2S(yDiff, distToTarget); + zRot = Math_SmoothStepToS(&this->actor.world.rot.y, yRot, 0xA, this->step, 0); + + if (zRot > 0x1000) { + zRot = 0x1000; + } else if (zRot < -0x1000) { + zRot = -0x1000; + } + + Math_ApproachS(&this->actor.world.rot.x, xRot, 0xA, this->step); + Math_ApproachS(&this->actor.world.rot.z, -zRot, 0xA, this->step); + + Math_ApproachS(&this->step, 0x200, 1, 0x10); + + SkelAnime_Update(&this->skelAnime); + + switch (this->flyState) { + case SEAGULL_FLY_FLAP: + if ((this->timers[SEAGULL_TIMER_FLY_STATE] == 0) && + (Animation_OnFrame(&this->skelAnime, 2.0f + KREG(42)))) { + this->flyState = SEAGULL_FLY_GLIDE; + this->timers[SEAGULL_TIMER_FLY_STATE] = Rand_ZeroFloat(50.0f) + 50.0f; + Animation_MorphToLoop(&this->skelAnime, &gSeagullFlapAnim, -15.0f + KREG(43)); + this->skelAnime.curFrame = 2.0f + KREG(42); + this->skelAnime.playSpeed = 0.0f; + } + break; + + case SEAGULL_FLY_GLIDE: + if (((this->randRollTimer % 8) == 0) && (Rand_ZeroOne() < 0.5f)) { + this->rollTarget = randPlusMinusPoint5Scaled(3000.0f + KREG(44)); + } + + if (this->timers[SEAGULL_TIMER_FLY_STATE] == 0) { + this->flyState = SEAGULL_FLY_FLAP; + this->timers[SEAGULL_TIMER_FLY_STATE] = Rand_ZeroFloat(50.0f) + 70.0f; + Animation_MorphToLoop(&this->skelAnime, &gSeagullFlapAnim, -10.0f + KREG(43)); + this->skelAnime.curFrame = 2.0f + KREG(42); + } + break; + } + + this->actor.shape.rot.x = -this->actor.world.rot.x; + this->actor.shape.rot.y = this->actor.world.rot.y; + this->actor.shape.rot.z = this->actor.world.rot.z; + + Math_ApproachS(&this->roll, this->rollTarget, 3, 0x3E8 + KREG(45)); +} + +void EnTanron4_SetupFlyNearActor(EnTanron4* this) { + this->actionFunc = EnTanron4_FlyNearActor; + Animation_MorphToLoop(&this->skelAnime, &gSeagullFlapAnim, 0.0f); +} + +void EnTanron4_FlyNearActor(EnTanron4* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 distToTarget; + s16 xRot; + s16 yRot; + s16 zRot; + Actor* targetActor = this->actor.parent; + + xDiff = this->targetPos.x - this->actor.world.pos.x; + yDiff = this->targetPos.y - this->actor.world.pos.y; + zDiff = this->targetPos.z - this->actor.world.pos.z; + + distToTarget = sqrtf(SQ(xDiff) + SQ(zDiff)); + + if ((this->timers[SEAGULL_TIMER_CHOOSE_TARGET] == 0) || (distToTarget < 100.0f)) { + this->targetPos.x = targetActor->world.pos.x + randPlusMinusPoint5Scaled(200.0f + KREG(82)); + this->targetPos.y = targetActor->world.pos.y + KREG(80) + 100.0f + randPlusMinusPoint5Scaled(100.0f + KREG(81)); + this->targetPos.z = targetActor->world.pos.z + randPlusMinusPoint5Scaled(200.0f + KREG(82)); + + this->timers[SEAGULL_TIMER_CHOOSE_TARGET] = Rand_ZeroFloat(100.0f) + 60.0f; + this->step = 0; + } + + yRot = Math_Atan2S(xDiff, zDiff); + xRot = Math_Atan2S(yDiff, distToTarget); + zRot = Math_SmoothStepToS(&this->actor.world.rot.y, yRot, 0xA, this->step, 0); + + if (zRot > 0x1000) { + zRot = 0x1000; + } else if (zRot < -0x1000) { + zRot = -0x1000; + } + + Math_ApproachS(&this->actor.world.rot.x, xRot, 0xA, this->step); + Math_ApproachS(&this->actor.world.rot.z, -zRot, 0xA, this->step); + Math_ApproachS(&this->step, 0x200, 1, 0x10); + + SkelAnime_Update(&this->skelAnime); + + switch (this->flyState) { + case SEAGULL_FLY_FLAP: + if ((this->timers[SEAGULL_TIMER_FLY_STATE] == 0) && + (Animation_OnFrame(&this->skelAnime, 2.0f + KREG(42)))) { + this->flyState = SEAGULL_FLY_GLIDE; + this->timers[SEAGULL_TIMER_FLY_STATE] = Rand_ZeroFloat(50.0f) + 50.0f; + Animation_MorphToLoop(&this->skelAnime, &gSeagullFlapAnim, -15.0f + KREG(43)); + this->skelAnime.curFrame = 2.0f + KREG(42); + this->skelAnime.playSpeed = 0.0f; + } + break; + + case SEAGULL_FLY_GLIDE: + if (((this->randRollTimer % 8) == 0) && (Rand_ZeroOne() < 0.5f)) { + this->rollTarget = randPlusMinusPoint5Scaled(3000.0f + KREG(44)); + } + + if (this->timers[SEAGULL_TIMER_FLY_STATE] == 0) { + this->flyState = SEAGULL_FLY_FLAP; + this->timers[SEAGULL_TIMER_FLY_STATE] = Rand_ZeroFloat(50.0f) + 70.0f; + Animation_MorphToLoop(&this->skelAnime, &gSeagullFlapAnim, -10.0f + KREG(43)); + this->skelAnime.curFrame = 2.0f + KREG(42); + } + break; + } + + this->actor.shape.rot.x = -this->actor.world.rot.x; + this->actor.shape.rot.y = this->actor.world.rot.y; + this->actor.shape.rot.z = this->actor.world.rot.z; + + Math_ApproachS(&this->roll, this->rollTarget, 3, 0x3E8 + KREG(45)); +} + +void EnTanron4_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTanron4* this = THIS; + + Actor_SetScale(&this->actor, 0.001f * KREG(16) + 0.01f); + + if (KREG(63) == 0) { + s32 i; + + this->randRollTimer++; + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + this->actionFunc(this, globalCtx); + + Actor_UpdateVelocityWithoutGravity(&this->actor); + Actor_UpdatePos(&this->actor); + } +} + +void EnTanron4_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnTanron4* this = THIS; + + if (this->timeInfluence < 1400.0f) { + Matrix_InsertZRotation_s(this->roll, MTXMODE_APPLY); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, NULL, NULL, &this->actor); + } +} diff --git a/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.h b/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.h index abbcc66332..250a62cf12 100644 --- a/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.h +++ b/src/overlays/actors/ovl_En_Tanron4/z_en_tanron4.h @@ -2,17 +2,32 @@ #define Z_EN_TANRON4_H #include "global.h" +#include "assets/objects/object_tanron4/object_tanron4.h" struct EnTanron4; typedef void (*EnTanron4ActionFunc)(struct EnTanron4*, GlobalContext*); typedef struct EnTanron4 { - /* 0x0000 */ Actor actor; - /* 0x0144 */ char unk_144[0xE8]; + /* 0x000 */ Actor actor; + /* 0x144 */ s16 randRollTimer; + /* 0x146 */ s16 timers[2]; + /* 0x14A */ u8 flyState; + /* 0x14C */ Vec3f targetPos; + /* 0x158 */ s16 step; + /* 0x15A */ s16 roll; + /* 0x15C */ s16 rollTarget; + /* 0x160 */ f32 timeInfluence; + /* 0x164 */ SkelAnime skelAnime; + /* 0x1A8 */ Vec3s jointTable[OBJECT_TANRON4_LIMB_MAX]; + /* 0x1EA */ Vec3s morphTable[OBJECT_TANRON4_LIMB_MAX]; /* 0x022C */ EnTanron4ActionFunc actionFunc; } EnTanron4; // size = 0x230 +// other than these magic values, params is the number of desired seagulls in the flock +#define SEAGULL_CLONE -1 +#define SEAGULL_FOLLOW_ACTOR 100 + extern const ActorInit En_Tanron4_InitVars; #endif // Z_EN_TANRON4_H diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 7cdf982e6a..41ad2eccbe 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -16094,10 +16094,10 @@ 0x80BE393C:("EnRuppecrow_Draw",), 0x80BE3B80:("EnTanron4_Init",), 0x80BE3DB0:("EnTanron4_Destroy",), - 0x80BE3DC0:("func_80BE3DC0",), - 0x80BE3DFC:("func_80BE3DFC",), - 0x80BE4268:("func_80BE4268",), - 0x80BE42A4:("func_80BE42A4",), + 0x80BE3DC0:("EnTanron4_SetupFlyNearHome",), + 0x80BE3DFC:("EnTanron4_FlyNearHome",), + 0x80BE4268:("EnTanron4_SetupFlyNearActor",), + 0x80BE42A4:("EnTanron4_FlyNearActor",), 0x80BE4734:("EnTanron4_Update",), 0x80BE4804:("EnTanron4_Draw",), 0x80BE4930:("func_80BE4930",),