diff --git a/assets/xml/objects/gameplay_keep.xml b/assets/xml/objects/gameplay_keep.xml
index f3ed5c8984..134cbc0e39 100644
--- a/assets/xml/objects/gameplay_keep.xml
+++ b/assets/xml/objects/gameplay_keep.xml
@@ -766,59 +766,62 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/include/variables.h b/include/variables.h
index 9ee10419a4..d7ae1e7a3a 100644
--- a/include/variables.h
+++ b/include/variables.h
@@ -3866,14 +3866,6 @@ extern UNK_TYPE D_0400E3D8;
extern UNK_TYPE D_0400E408;
extern UNK_TYPE D_0400E410;
extern UNK_TYPE D_0400E418;
-extern CollisionHeader D_0400E710; // Pink Deku Flower collision
-extern AnimationHeader D_0400EB7C; // Deku Flower intense flutter animation
-extern Gfx D_0400ED80; // Pink Deku Flower display list
-extern SkeletonHeader D_04011518; // Pink Deku Flower skeleton
-extern AnimationHeader D_040117A8; // Deku Flower small flutter animation
-extern CollisionHeader D_040118D8; // Gold Deku Flower collision
-extern Gfx D_04011BD0; // Gold Deku Flower display list
-extern SkeletonHeader D_040127E8; // Gold Deku Flower skeleton
extern UNK_TYPE D_04012860;
extern UNK_TYPE D_040128BC;
extern u64 D_04014570[];
diff --git a/src/overlays/actors/ovl_Obj_Etcetera/z_obj_etcetera.c b/src/overlays/actors/ovl_Obj_Etcetera/z_obj_etcetera.c
index a4eb9ad9ac..56f9b9f7a4 100644
--- a/src/overlays/actors/ovl_Obj_Etcetera/z_obj_etcetera.c
+++ b/src/overlays/actors/ovl_Obj_Etcetera/z_obj_etcetera.c
@@ -5,6 +5,7 @@
*/
#include "z_obj_etcetera.h"
+#include "objects/gameplay_keep/gameplay_keep.h"
#define FLAGS 0x00000010
@@ -14,8 +15,8 @@ void ObjEtcetera_Init(Actor* thisx, GlobalContext* globalCtx);
void ObjEtcetera_Destroy(Actor* thisx, GlobalContext* globalCtx);
void ObjEtcetera_Update(Actor* thisx, GlobalContext* globalCtx);
-void ObjEtcetera_PlaySmallFlutterAnimation(ObjEtcetera* this, GlobalContext* globalCtx);
-void ObjEtcetera_DoIntenseOscillation(ObjEtcetera* this, GlobalContext* globalCtx);
+void ObjEtcetera_PlayRustleAnimation(ObjEtcetera* this, GlobalContext* globalCtx);
+void ObjEtcetera_DoBounceOscillation(ObjEtcetera* this, GlobalContext* globalCtx);
void ObjEtcetera_Setup(ObjEtcetera* this, GlobalContext* globalCtx);
void ObjEtcetera_DrawIdle(Actor* thisx, GlobalContext* globalCtx);
void ObjEtcetera_DrawAnimated(Actor* thisx, GlobalContext* globalCtx);
@@ -105,6 +106,11 @@ void ObjEtcetera_Destroy(Actor* thisx, GlobalContext* globalCtx) {
Collider_DestroyCylinder(globalCtx, &this->collider);
}
+/**
+ * This function will make the flower oscillate on the X and Z axes in most situations
+ * where something interacts with it. When the player launches out of the flower, the
+ * oscillation is handled by ObjEtcetera_DoBounceOscillation instead.
+ */
void ObjEtcetera_DoNormalOscillation(ObjEtcetera* this, GlobalContext* globalCtx) {
if (this->oscillationTimer > 0) {
s32 requiredScopeTemp;
@@ -119,10 +125,11 @@ void ObjEtcetera_DoNormalOscillation(ObjEtcetera* this, GlobalContext* globalCtx
}
}
-void ObjEtcetera_StartSmallFlutterAnimation(ObjEtcetera* this) {
- Animation_Change(&this->skelAnime, &D_040117A8, 1.0f, 0.0f, Animation_GetLastFrame(&D_040117A8), 2, 0.0f);
+void ObjEtcetera_StartRustleAnimation(ObjEtcetera* this) {
+ Animation_Change(&this->skelAnime, &gDekuFlowerRustleAnim, 1.0f, 0.0f,
+ Animation_GetLastFrame(&gDekuFlowerRustleAnim), 2, 0.0f);
this->dyna.actor.draw = ObjEtcetera_DrawAnimated;
- this->actionFunc = ObjEtcetera_PlaySmallFlutterAnimation;
+ this->actionFunc = ObjEtcetera_PlayRustleAnimation;
}
void ObjEtcetera_Idle(ObjEtcetera* this, GlobalContext* globalCtx) {
@@ -131,12 +138,13 @@ void ObjEtcetera_Idle(ObjEtcetera* this, GlobalContext* globalCtx) {
if ((player->stateFlags3 & 0x200) && (this->dyna.actor.xzDistToPlayer < 20.0f)) {
// Player is launching out of the Deku Flower
- Animation_Change(&this->skelAnime, &D_0400EB7C, 1.0f, 0.0f, Animation_GetLastFrame(&D_0400EB7C), 2, 0.0f);
+ Animation_Change(&this->skelAnime, &gDekuFlowerBounceAnim, 1.0f, 0.0f,
+ Animation_GetLastFrame(&gDekuFlowerBounceAnim), 2, 0.0f);
this->dyna.actor.draw = ObjEtcetera_DrawAnimated;
- this->actionFunc = ObjEtcetera_DoIntenseOscillation;
+ this->actionFunc = ObjEtcetera_DoBounceOscillation;
Actor_SetScale(&this->dyna.actor, 0.01f);
this->dyna.actor.scale.y = 0.02f;
- this->intenseOscillationScale = 0.003f;
+ this->bounceOscillationScale = 0.003f;
this->oscillationTimer = 30;
this->burrowFlag &= ~1;
} else if ((player->stateFlags3 & 0x2000) && (this->dyna.actor.xzDistToPlayer < 30.0f) &&
@@ -151,7 +159,7 @@ void ObjEtcetera_Idle(ObjEtcetera* this, GlobalContext* globalCtx) {
if (!(this->burrowFlag & 1)) {
// Player is walking onto the Deku Flower, or falling on it from a height
this->oscillationTimer = 10;
- ObjEtcetera_StartSmallFlutterAnimation(this);
+ ObjEtcetera_StartRustleAnimation(this);
} else if ((player->actor.speedXZ > 0.1f) || ((player->unk_ABC < 0.0f) && !(player->stateFlags3 & 0x100))) {
// Player is walking on top of the Deku Flower, is at the very start of burrowing, or is at the very
// start of launching
@@ -162,19 +170,19 @@ void ObjEtcetera_Idle(ObjEtcetera* this, GlobalContext* globalCtx) {
if (this->burrowFlag & 1) {
// Player is walking off the Deku Flower
this->oscillationTimer = 10;
- ObjEtcetera_StartSmallFlutterAnimation(this);
+ ObjEtcetera_StartRustleAnimation(this);
}
this->burrowFlag &= ~1;
}
}
if ((this->collider.base.acFlags & AC_HIT)) {
this->oscillationTimer = 10;
- ObjEtcetera_StartSmallFlutterAnimation(this);
+ ObjEtcetera_StartRustleAnimation(this);
}
ObjEtcetera_DoNormalOscillation(this, globalCtx);
}
-void ObjEtcetera_PlaySmallFlutterAnimation(ObjEtcetera* this, GlobalContext* globalCtx) {
+void ObjEtcetera_PlayRustleAnimation(ObjEtcetera* this, GlobalContext* globalCtx) {
if (DynaPolyActor_IsInRidingMovingState(&this->dyna)) {
this->burrowFlag |= 1;
} else {
@@ -187,7 +195,12 @@ void ObjEtcetera_PlaySmallFlutterAnimation(ObjEtcetera* this, GlobalContext* glo
ObjEtcetera_DoNormalOscillation(this, globalCtx);
}
-void ObjEtcetera_DoIntenseOscillation(ObjEtcetera* this, GlobalContext* globalCtx) {
+/**
+ * When the bounce animation plays (either because the player launched out of the flower,
+ * or because the flower spawned after killing a Mad Scrub), this function makes the
+ * flower oscillate stronger than it normally does, including an oscillation on the Y-axis.
+ */
+void ObjEtcetera_DoBounceOscillation(ObjEtcetera* this, GlobalContext* globalCtx) {
// In order to match, we are seemingly required to access scale.x at one point
// without using this. We can create a thisx or dyna pointer to achieve that, but
// it's more likely they used dyna given that DynaPolyActor_IsInRidingMovingState takes a DynaPolyActor.
@@ -208,12 +221,12 @@ void ObjEtcetera_DoIntenseOscillation(ObjEtcetera* this, GlobalContext* globalCt
Actor_SetScale(&this->dyna.actor, 0.01f);
this->dyna.actor.scale.y = 0.02f;
this->oscillationTimer = 0;
- this->intenseOscillationScale = 0.0f;
+ this->bounceOscillationScale = 0.0f;
return;
}
- this->intenseOscillationScale *= 0.8f;
- this->intenseOscillationScale -= (this->dyna.actor.scale.x - 0.01f) * 0.4f;
- scaleTemp = dyna->actor.scale.x + this->intenseOscillationScale;
+ this->bounceOscillationScale *= 0.8f;
+ this->bounceOscillationScale -= (this->dyna.actor.scale.x - 0.01f) * 0.4f;
+ scaleTemp = dyna->actor.scale.x + this->bounceOscillationScale;
Actor_SetScale(&this->dyna.actor, scaleTemp);
this->dyna.actor.scale.y = 2.0f * scaleTemp;
}
@@ -221,7 +234,12 @@ void ObjEtcetera_DoIntenseOscillation(ObjEtcetera* this, GlobalContext* globalCt
void ObjEtcetera_Setup(ObjEtcetera* this, GlobalContext* globalCtx) {
CollisionHeader* colHeader = NULL;
s32 type;
- CollisionHeader* collisionHeaders[] = { &D_0400E710, &D_0400E710, &D_040118D8, &D_040118D8 };
+ CollisionHeader* collisionHeaders[] = {
+ &gPinkDekuFlowerCol,
+ &gPinkDekuFlowerCol,
+ &gGoldDekuFlowerCol,
+ &gGoldDekuFlowerCol,
+ };
s32 pad;
CollisionHeader* thisCollisionHeader;
@@ -244,15 +262,15 @@ void ObjEtcetera_Setup(ObjEtcetera* this, GlobalContext* globalCtx) {
switch (type) {
case DEKU_FLOWER_TYPE_PINK:
case DEKU_FLOWER_TYPE_PINK_SPAWNED_FROM_MAD_SCRUB:
- SkelAnime_Init(globalCtx, &this->skelAnime, &D_04011518, &D_0400EB7C, this->jointTable,
- this->morphTable, 11);
- this->dList = &D_0400ED80;
+ SkelAnime_Init(globalCtx, &this->skelAnime, &gPinkDekuFlowerSkel, &gDekuFlowerBounceAnim,
+ this->jointTable, this->morphTable, DEKU_FLOWER_LIMB_MAX);
+ this->dList = gPinkDekuFlowerIdleDL;
break;
case DEKU_FLOWER_TYPE_GOLD:
case DEKU_FLOWER_TYPE_GOLD_SPAWNED_FROM_MAD_SCRUB:
- this->dList = &D_04011BD0;
- SkelAnime_Init(globalCtx, &this->skelAnime, &D_040127E8, &D_0400EB7C, this->jointTable,
- this->morphTable, 11);
+ this->dList = gGoldDekuFlowerIdleDL;
+ SkelAnime_Init(globalCtx, &this->skelAnime, &gGoldDekuFlowerSkel.sh, &gDekuFlowerBounceAnim,
+ this->jointTable, this->morphTable, DEKU_FLOWER_LIMB_MAX);
this->collider.dim.height = 20;
break;
}
@@ -270,13 +288,13 @@ void ObjEtcetera_Setup(ObjEtcetera* this, GlobalContext* globalCtx) {
break;
case DEKU_FLOWER_TYPE_PINK_SPAWNED_FROM_MAD_SCRUB:
case DEKU_FLOWER_TYPE_GOLD_SPAWNED_FROM_MAD_SCRUB:
- Animation_Change(&this->skelAnime, &D_0400EB7C, 1.0f, 0.0f, Animation_GetLastFrame(&D_0400EB7C), 2,
- 0.0f);
+ Animation_Change(&this->skelAnime, &gDekuFlowerBounceAnim, 1.0f, 0.0f,
+ Animation_GetLastFrame(&gDekuFlowerBounceAnim), 2, 0.0f);
this->dyna.actor.draw = ObjEtcetera_DrawAnimated;
- this->actionFunc = ObjEtcetera_DoIntenseOscillation;
+ this->actionFunc = ObjEtcetera_DoBounceOscillation;
Actor_SetScale(&this->dyna.actor, 0.0f);
this->oscillationTimer = 30;
- this->intenseOscillationScale = 0.0f;
+ this->bounceOscillationScale = 0.0f;
this->dyna.actor.focus.pos.y = this->dyna.actor.home.pos.y + 10.0f;
this->dyna.actor.targetMode = 3;
break;
@@ -299,6 +317,11 @@ void ObjEtcetera_Update(Actor* thisx, GlobalContext* globalCtx) {
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
+/**
+ * When the flower isn't animating, this function is used to draw it.
+ * It draws the flower as a single, non-moving display list that encompasses the whole flower.
+ * When an animation is finished, functions are expected to set the actor's draw function to this.
+ */
void ObjEtcetera_DrawIdle(Actor* thisx, GlobalContext* globalCtx) {
ObjEtcetera* this = THIS;
@@ -311,6 +334,11 @@ void ObjEtcetera_DrawIdle(Actor* thisx, GlobalContext* globalCtx) {
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
+/**
+ * When the flower is animating, this function is used to draw it.
+ * It draws the flower as an animated bunch of limbs using the SkelAnime system.
+ * When a function wants to play an animation, it is expected to set the actor's draw function to this.
+ */
void ObjEtcetera_DrawAnimated(Actor* thisx, GlobalContext* globalCtx) {
ObjEtcetera* this = THIS;
diff --git a/src/overlays/actors/ovl_Obj_Etcetera/z_obj_etcetera.h b/src/overlays/actors/ovl_Obj_Etcetera/z_obj_etcetera.h
index f5642ff282..a52f5ae2ef 100644
--- a/src/overlays/actors/ovl_Obj_Etcetera/z_obj_etcetera.h
+++ b/src/overlays/actors/ovl_Obj_Etcetera/z_obj_etcetera.h
@@ -13,6 +13,26 @@ typedef enum {
/* 4 */ DEKU_FLOWER_TYPE_MAX,
} DekuFlowerType;
+/**
+ * For the petal/leaf directions, "back" is negative and "front" is positive
+ * on the flower's local Z-axis. "Left" and "right" are relative to the front
+ * and back.
+ */
+typedef enum {
+ /* 0 */ DEKU_FLOWER_LIMB_NONE,
+ /* 1 */ DEKU_FLOWER_LIMB_BASE,
+ /* 2 */ DEKU_FLOWER_LIMB_CENTER,
+ /* 3 */ DEKU_FLOWER_LIMB_BACK_PETAL_OR_LEAF,
+ /* 4 */ DEKU_FLOWER_LIMB_FRONT_PETAL_OR_LEAF,
+ /* 5 */ DEKU_FLOWER_LIMB_FRONT_RIGHT_PETAL,
+ /* 6 */ DEKU_FLOWER_LIMB_BACK_RIGHT_PETAL,
+ /* 7 */ DEKU_FLOWER_LIMB_RIGHT_PETAL_OR_LEAF,
+ /* 8 */ DEKU_FLOWER_LIMB_FRONT_LEFT_PETAL,
+ /* 9 */ DEKU_FLOWER_LIMB_LEFT_PETAL_OR_LEAF,
+ /* 10 */ DEKU_FLOWER_LIMB_BACK_LEFT_PETAL,
+ /* 11 */ DEKU_FLOWER_LIMB_MAX,
+} DekuFlowerLimbs;
+
struct ObjEtcetera;
typedef void (*ObjEtceteraActionFunc)(struct ObjEtcetera*, GlobalContext*);
@@ -21,9 +41,9 @@ typedef struct ObjEtcetera {
/* 0x000 */ DynaPolyActor dyna;
/* 0x15C */ SkelAnime skelAnime;
/* 0x1A0 */ ColliderCylinder collider;
- /* 0x1EC */ Vec3s jointTable[11];
- /* 0x22E */ Vec3s morphTable[11];
- /* 0x270 */ f32 intenseOscillationScale;
+ /* 0x1EC */ Vec3s jointTable[DEKU_FLOWER_LIMB_MAX];
+ /* 0x22E */ Vec3s morphTable[DEKU_FLOWER_LIMB_MAX];
+ /* 0x270 */ f32 bounceOscillationScale;
/* 0x274 */ s16 oscillationTimer;
/* 0x276 */ u16 burrowFlag;
/* 0x278 */ s8 objIndex;
diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt
index ef8f7630c3..2911e3e105 100644
--- a/tools/disasm/functions.txt
+++ b/tools/disasm/functions.txt
@@ -11100,10 +11100,10 @@
0x80A7BC70:("ObjEtcetera_Init",),
0x80A7BD80:("ObjEtcetera_Destroy",),
0x80A7BDC8:("ObjEtcetera_DoNormalOscillation",),
- 0x80A7BE8C:("ObjEtcetera_StartSmallFlutterAnimation",),
+ 0x80A7BE8C:("ObjEtcetera_StartRustleAnimation",),
0x80A7BF08:("ObjEtcetera_Idle",),
- 0x80A7C168:("ObjEtcetera_PlaySmallFlutterAnimation",),
- 0x80A7C1F0:("ObjEtcetera_DoIntenseOscillation",),
+ 0x80A7C168:("ObjEtcetera_PlayRustleAnimation",),
+ 0x80A7C1F0:("ObjEtcetera_DoBounceOscillation",),
0x80A7C308:("ObjEtcetera_Setup",),
0x80A7C5EC:("ObjEtcetera_Update",),
0x80A7C690:("ObjEtcetera_DrawIdle",),