summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpetrie911 <69443847+petrie911@users.noreply.github.com>2024-04-06 12:40:41 -0500
committerGitHub <noreply@github.com>2024-04-06 10:40:41 -0700
commit45ae63ccc550ea29f475669a87f7f8ac681a6b29 (patch)
tree19e43bc0ba2e51853534cc42e3bf452fe677d8fe
parente3ce14c932b80e5ed4c7680cb9dd9f638d739541 (diff)
Obj_Mine matched (#1225)
* Obj_Mine * Obj_Mine * Fixups * Nonmatchings + cleanup * Small cleanup * externs * structs complete * wew * some docs. still baffling * documented physics stuff * docs. physics be wack * forgot some things * format * more cleanup * merge and such * format * more format and cleanup * asm stuff * Update src/overlays/actors/ovl_Obj_Mine/z_obj_mine.c Co-authored-by: Anghelo Carvajal <angheloalf95@gmail.com> * now with decomp.me * removed some extraneous macros I didn't end up using * fixes and formats * merge * my god, it matches * format * removed macros * more fixes * format * format --------- Co-authored-by: Maide <eeeedddccc@hotmail.co.uk> Co-authored-by: Derek Hensley <hensley.derek58@gmail.com> Co-authored-by: petrie911 <petrie911@users.noreply.github.com> Co-authored-by: petrie911 <pmontag@Monday.localdomain> Co-authored-by: Anghelo Carvajal <angheloalf95@gmail.com>
-rw-r--r--spec4
-rw-r--r--src/overlays/actors/ovl_Obj_Mine/z_obj_mine.c1217
-rw-r--r--src/overlays/actors/ovl_Obj_Mine/z_obj_mine.h82
-rw-r--r--tools/disasm/functions.txt90
-rw-r--r--tools/disasm/variables.txt2
5 files changed, 1276 insertions, 119 deletions
diff --git a/spec b/spec
index 795a32387..c7bd96323 100644
--- a/spec
+++ b/spec
@@ -2905,9 +2905,7 @@ beginseg
name "ovl_Obj_Mine"
compress
include "build/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.o"
- include "build/data/ovl_Obj_Mine/ovl_Obj_Mine.data.o"
- include "build/data/ovl_Obj_Mine/ovl_Obj_Mine.bss.o"
- include "build/data/ovl_Obj_Mine/ovl_Obj_Mine.reloc.o"
+ include "build/src/overlays/actors/ovl_Obj_Mine/ovl_Obj_Mine_reloc.o"
endseg
beginseg
diff --git a/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.c b/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.c
index 36537cfe3..e5b17469c 100644
--- a/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.c
+++ b/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.c
@@ -1,21 +1,50 @@
/*
* File: z_obj_mine.c
* Overlay: ovl_Obj_Mine
- * Description: Spike metal Mine
+ * Description: Spike metal mine
*/
#include "z_obj_mine.h"
+#include "overlays/actors/ovl_En_Bom/z_en_bom.h"
+#include "objects/object_ny/object_ny.h"
#define FLAGS 0x00000000
#define THIS ((ObjMine*)thisx)
+#define LINK_SIZE 12.0f
+#define ATTACH_OFFSET 10.0f
+#define PATH_RADIUS 32.0f
+#define AIR_RADIUS 20.0f
+#define WATER_RADIUS 36.0f
+#define BOMB_SPAWN_OFFSET 15.0f
+#define WATER_KNOCKBACK 7.0f
+#define AIR_KNOCKBACK 0.04f
+
void ObjMine_Init(Actor* thisx, PlayState* play);
void ObjMine_Destroy(Actor* thisx, PlayState* play);
-void ObjMine_Update(Actor* thisx, PlayState* play);
-void ObjMine_Draw(Actor* thisx, PlayState* play);
+void ObjMine_Path_Update(Actor* thisx, PlayState* play);
+void ObjMine_Path_Draw(Actor* thisx, PlayState* play);
+
+void ObjMine_Path_SetupStationary(ObjMine* this);
+void ObjMine_Path_Stationary(ObjMine* this, PlayState* play);
+void ObjMine_Path_SetupMove(ObjMine* this);
+void ObjMine_Path_Move(ObjMine* this, PlayState* play);
+void ObjMine_Explode(ObjMine* this, PlayState* play);
+void ObjMine_Air_SetupChained(ObjMine* this);
+void ObjMine_Air_Chained(ObjMine* this, PlayState* play);
+void ObjMine_Air_SetupStationary(ObjMine* this);
+void ObjMine_Air_Stationary(ObjMine* this, PlayState* play);
+void ObjMine_Water_SetupChained(ObjMine* this);
+void ObjMine_Water_Chained(ObjMine* this, PlayState* play);
+void ObjMine_Water_SetupStationary(ObjMine* this);
+void ObjMine_Water_Stationary(ObjMine* this, PlayState* play);
+
+void ObjMine_AirWater_Update(Actor* thisx, PlayState* play);
+void ObjMine_DrawExplosion(Actor* thisx, PlayState* play);
+void ObjMine_Air_Draw(Actor* thisx, PlayState* play);
+void ObjMine_Water_Draw(Actor* thisx, PlayState* play);
-#if 0
ActorInit Obj_Mine_InitVars = {
/**/ ACTOR_OBJ_MINE,
/**/ ACTORCAT_PROP,
@@ -24,131 +53,1183 @@ ActorInit Obj_Mine_InitVars = {
/**/ sizeof(ObjMine),
/**/ ObjMine_Init,
/**/ ObjMine_Destroy,
- /**/ ObjMine_Update,
- /**/ ObjMine_Draw,
+ /**/ ObjMine_Path_Update,
+ /**/ ObjMine_Path_Draw,
};
-// static ColliderJntSphElementInit sJntSphElementsInit[1] = {
-static ColliderJntSphElementInit D_80A84570[1] = {
+static ColliderJntSphElementInit sJntSphElementsInit[1] = {
{
- { ELEMTYPE_UNK2, { 0x00000000, 0x00, 0x00 }, { 0x01CBFFBE, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
+ {
+ ELEMTYPE_UNK2,
+ { 0x00000000, 0x00, 0x00 },
+ { 0x01CBFFBE, 0x00, 0x00 },
+ TOUCH_NONE | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
{ 0, { { 0, 0, 0 }, 30 }, 100 },
},
};
-// static ColliderJntSphInit sJntSphInit = {
-static ColliderJntSphInit D_80A84594 = {
- { COLTYPE_METAL, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER | OC1_TYPE_1, OC2_TYPE_1, COLSHAPE_JNTSPH, },
- ARRAY_COUNT(sJntSphElementsInit), D_80A84570, // sJntSphElementsInit,
+static ColliderJntSphInit sJntSphInit = {
+ {
+ COLTYPE_METAL,
+ AT_NONE,
+ AC_ON | AC_TYPE_PLAYER,
+ OC1_ON | OC1_TYPE_PLAYER | OC1_TYPE_1,
+ OC2_TYPE_1,
+ COLSHAPE_JNTSPH,
+ },
+ ARRAY_COUNT(sJntSphElementsInit),
+ sJntSphElementsInit,
};
-// static InitChainEntry sInitChain[] = {
-static InitChainEntry D_80A845E8[] = {
+static f32 sPathSpeeds[] = {
+ 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f,
+};
+
+static ObjMineMtxF3 sStandardBasis = {
+ { 1.0f, 0.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 0.0f, 1.0f },
+};
+
+static InitChainEntry sInitChain[] = {
ICHAIN_F32(uncullZoneForward, 1300, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneScale, 150, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneDownward, 100, ICHAIN_CONTINUE),
ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_STOP),
};
-#endif
+void ObjMine_Path_MoveToWaypoint(ObjMine* this, s32 index) {
+ Math_Vec3s_ToVec3f(&this->actor.world.pos, &this->waypoints[index]);
+}
+
+s32 ObjMine_GetUnitVec3f(Vec3f* src, Vec3f* dst) {
+ f32 magnitude = Math3D_Vec3fMagnitude(src);
+
+ if (magnitude < 0.001f) {
+ return false;
+ }
+
+ dst->x = src->x * (1.0f / magnitude);
+ dst->y = src->y * (1.0f / magnitude);
+ dst->z = src->z * (1.0f / magnitude);
+ return true;
+}
+
+s32 ObjMine_GetUnitVec3fNorm(Vec3f* src, Vec3f* dst, f32* norm, f32* invNorm) {
+ f32 magnitude = Math3D_Vec3fMagnitude(src);
+
+ if (magnitude < 0.001f) {
+ return false;
+ }
+
+ dst->x = src->x * (1.0f / magnitude);
+ dst->y = src->y * (1.0f / magnitude);
+ dst->z = src->z * (1.0f / magnitude);
+ *norm = magnitude;
+ *invNorm = 1.0f / magnitude;
+
+ return true;
+}
+
+void ObjMine_Path_SpawnBomb(ObjMine* this, PlayState* play) {
+ EnBom* bomb = (EnBom*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, this->actor.world.pos.x,
+ this->actor.world.pos.y - BOMB_SPAWN_OFFSET, this->actor.world.pos.z, 0, 0, 0,
+ BOMB_TYPE_BODY);
+
+ if (bomb != NULL) {
+ bomb->timer = 0;
+ }
+}
+
+void ObjMine_AirWater_SpawnBomb(ObjMine* this, PlayState* play) {
+ f32 x = this->collider.elements[0].dim.worldSphere.center.x;
+ f32 y = this->collider.elements[0].dim.worldSphere.center.y - BOMB_SPAWN_OFFSET;
+ f32 z = this->collider.elements[0].dim.worldSphere.center.z;
+ EnBom* bomb = (EnBom*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, x, y, z, 0, 0, 0, BOMB_TYPE_BODY);
+
+ if (bomb != NULL) {
+ bomb->timer = 0;
+ }
+}
+
+s32 ObjMine_AirWater_CheckOC(ObjMine* this) {
+ if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) {
+ return true;
+ }
+
+ if (this->collider.base.ocFlags1 & OC1_HIT) {
+ Actor* hitActor = this->collider.base.oc;
+
+ if ((hitActor->id == ACTOR_OBJ_MINE) && (hitActor->room == this->actor.room)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ObjMine_Air_CheckAC(ObjMine* this, s16* hitAngle, s16* torqueAngle) {
+ s32 pad[2];
+ Vec3s* centerPos3s = &this->collider.elements[0].dim.worldSphere.center;
+ Vec3f centerPos;
+ s16 yawToAttack;
+ Actor* attackActor = this->collider.base.ac;
+
+ Math_Vec3s_ToVec3f(&centerPos, centerPos3s);
+ yawToAttack = Math_Vec3f_Yaw(&attackActor->world.pos, &centerPos);
+
+ // dmgFlag check is (DMG_DEKU_BUBBLE | DMG_FIRE_ARROW | DMG_ICE_ARROW | DMG_FIRE_ARROW | DMG_NORMAL_ARROW)
+ if (this->collider.elements[0].info.acHitInfo->toucher.dmgFlags & 0x13820) {
+ *hitAngle = attackActor->shape.rot.y;
+ *torqueAngle = attackActor->shape.rot.y - yawToAttack;
+ } else {
+ Vec3f hitPos;
+ Vec3s* hitPos3s = &this->collider.elements[0].info.bumper.hitPos;
+
+ Math_Vec3s_ToVec3f(&hitPos, hitPos3s);
+ *hitAngle = Actor_WorldYawTowardActor(attackActor, &this->actor);
+ *torqueAngle = Math_Vec3f_Yaw(&attackActor->world.pos, &hitPos) - yawToAttack;
+ }
+}
+
+void ObjMine_Water_CheckAC(ObjMine* this, Vec3f* knockbackDir) {
+ Actor* attackActor = this->collider.base.ac;
+
+ // dmgFlag check is (DMG_DEKU_BUBBLE | DMG_LIGHT_ARROW | DMG_ICE_ARROW | DMG_FIRE_ARROW | DMG_NORMAL_ARROW)
+ if (this->collider.elements[0].info.acHitInfo->toucher.dmgFlags & 0x13820) {
+ Matrix_Push();
+ Matrix_RotateYS(attackActor->shape.rot.y, MTXMODE_NEW);
+ Matrix_RotateXS(attackActor->shape.rot.x, MTXMODE_APPLY);
+ Matrix_MultVecZ(1.0f, knockbackDir);
+ Matrix_Pop();
+ } else {
+ Vec3f posDiff;
+ Sphere16* sphere = &this->collider.elements[0].dim.worldSphere;
+
+ posDiff.x = sphere->center.x - attackActor->world.pos.x;
+ posDiff.y = sphere->center.y - attackActor->world.pos.y;
+ posDiff.z = sphere->center.z - attackActor->world.pos.z;
+
+ if (!ObjMine_GetUnitVec3f(&posDiff, knockbackDir)) {
+ Math_Vec3f_Copy(knockbackDir, &sStandardBasis.y);
+ }
+ }
+}
+
+void ObjMine_AirWater_Noop(ObjMine* this) {
+}
+
+void ObjMine_ReplaceTranslation(Vec3f* translation) {
+ MtxF* matrix = Matrix_GetCurrent();
+
+ matrix->xw = translation->x;
+ matrix->yw = translation->y;
+ matrix->zw = translation->z;
+}
+
+void ObjMine_SetRotation(ObjMineMtxF3* basis) {
+ MtxF* matrix = Matrix_GetCurrent();
+
+ matrix->xx = basis->x.x;
+ matrix->yx = basis->x.y;
+ matrix->zx = basis->x.z;
+ matrix->wx = 0.0f;
+
+ matrix->xy = basis->y.x;
+ matrix->yy = basis->y.y;
+ matrix->zy = basis->y.z;
+ matrix->wy = 0.0f;
+
+ matrix->xz = basis->z.x;
+ matrix->yz = basis->z.y;
+ matrix->zz = basis->z.z;
+ matrix->wz = 0.0f;
+
+ matrix->xw = 0.0f;
+ matrix->yw = 0.0f;
+ matrix->zw = 0.0f;
+ matrix->ww = 1.0f;
+}
+
+s32 ObjMine_StepUntilParallel(Vec3f* value, Vec3f* target, f32 angleStep) {
+ Vec3f perpVec;
+ Vec3f prevValue;
+ Vec3f perpNormal;
+ f32 cosAngle = Math3D_Parallel(value, target);
+
+ if (Math_CosF(angleStep) <= cosAngle) {
+ Math_Vec3f_Copy(value, target);
+ return true;
+ }
+
+ Matrix_Push();
+ Math_Vec3f_Copy(&prevValue, value);
+ Math3D_CrossProduct(value, target, &perpVec);
+ if (ObjMine_GetUnitVec3f(&perpVec, &perpNormal)) {
+ Matrix_RotateAxisS(RAD_TO_BINANG(angleStep), &perpNormal, MTXMODE_NEW);
+ Matrix_MultVec3f(&prevValue, value);
+ } else {
+ Matrix_RotateXFNew(angleStep);
+ Matrix_MultVec3f(&prevValue, value);
+ }
+
+ Matrix_Pop();
+ return false;
+}
+
+void ObjMine_UpdateCollider(ObjMine* this) {
+ this->collider.elements[0].dim.worldSphere.center.x = this->actor.world.pos.x;
+ this->collider.elements[0].dim.worldSphere.center.y = this->actor.world.pos.y;
+ this->collider.elements[0].dim.worldSphere.center.z = this->actor.world.pos.z;
+}
+
+void ObjMine_Air_InitChain(ObjMine* this, s32 linkCount) {
+ f32 linkCountF = linkCount;
+ ObjMineAirChain* airChain = &this->chain.air;
+ ObjMineAirLink* airLink;
+ s32 i;
+ f32 wallCheckRadius = this->actor.home.rot.z * 5.0f;
+
+ airChain->basis.x.x = 1.0f;
+ airChain->basis.y.y = 1.0f;
+ airChain->basis.z.z = 1.0f;
+
+ airChain->translation.y = 1.0f;
+
+ // Sets restoring force and drag. Longer chains have lower frequency and less drag.
+ if (linkCount > 0) {
+ airChain->restore = -sqrtf((0.124992f / OBJMINE_CHAIN_MAX) / linkCountF); // constant is close to 1/8
+ airChain->drag = 0.95f + ((0.02000004f / OBJMINE_CHAIN_MAX) * linkCountF); // constant is close to 1/50
+ }
+
+ airChain->swayMax = 0.0002f;
+
+ // Consecutive chain links are offset 90 degrees.
+ for (i = 0, airLink = airChain->links; i < linkCount; i++, airLink++) {
+ airLink->twist = 0x4000;
+ }
+
+ if (wallCheckRadius < 0.0f) {
+ airChain->wallCheckDistSq = -1.0f; // Negative value means skip wall collision checks
+ } else if (wallCheckRadius <= AIR_RADIUS + 1.0f) {
+ airChain->wallCheckDistSq = 0.0f;
+ } else {
+ airChain->wallCheckDistSq = SQ(wallCheckRadius - AIR_RADIUS);
+ }
+}
+
+void ObjMine_Air_InitCollider(ObjMine* this, s32 linkCount) {
+ Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW);
+ Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
+ Collider_UpdateSpheres(0, &this->collider);
+}
+
+void ObjMine_Air_SetBasis(ObjMine* this) {
+ ObjMineAirChain* airChain = &this->chain.air;
+ Vec3f tempVec;
+
+ tempVec.x = -airChain->displacement.x;
+ tempVec.y = 1.0f;
+ tempVec.z = -airChain->displacement.z;
+
+ ObjMine_GetUnitVec3f(&tempVec, &airChain->basis.y);
+
+ Math3D_CrossProduct(&sStandardBasis.x, &airChain->basis.y, &tempVec);
+ ObjMine_GetUnitVec3f(&tempVec, &airChain->basis.z);
+
+ Math3D_CrossProduct(&airChain->basis.y, &airChain->basis.z, &tempVec);
+ ObjMine_GetUnitVec3f(&tempVec, &airChain->basis.x);
+}
+
+void ObjMine_Air_SetWorld(ObjMine* this) {
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+ f32 chainLength = ATTACH_OFFSET + (linkCount * LINK_SIZE);
+
+ this->actor.world.pos.x = (this->chain.air.basis.y.x * -chainLength) + this->actor.home.pos.x;
+ this->actor.world.pos.y = (this->chain.air.basis.y.y * -chainLength) + this->actor.home.pos.y;
+ this->actor.world.pos.z = (this->chain.air.basis.y.z * -chainLength) + this->actor.home.pos.z;
+}
+
+void ObjMine_Air_SetChainXZ(ObjMine* this) {
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+ f32 invLength = 1.0f / (ATTACH_OFFSET + (linkCount * LINK_SIZE));
+ f32 dx = this->actor.world.pos.x - this->actor.home.pos.x;
+ f32 dz = this->actor.world.pos.z - this->actor.home.pos.z;
+
+ this->chain.air.displacement.x = dx * invLength;
+ this->chain.air.displacement.z = dz * invLength;
+}
+
+void ObjMine_Water_InitChain(ObjMine* this, s32 linkCount) {
+ ObjMineWaterChain* waterChain = &this->chain.water;
+ ObjMineWaterLink* waterLink;
+ s32 i;
+ f32 wallCheckRadius = this->actor.home.rot.z * 5.0f;
+ f32 linkY;
+ f32 swayVel;
+
+ waterChain->drag = 0.9f;
+ waterChain->swayMax = 0.003f;
+
+ waterChain->swayPhaseVel = Rand_Next() >> (0x20 - 13);
+ waterChain->restoreXZ = -0.0002f;
+ waterChain->restoreY = -0.0002f;
+
+ swayVel = waterChain->swayMax * (LINK_SIZE / 2.0f);
+ linkY = this->actor.home.pos.y;
+
+ for (i = 0, waterLink = waterChain->links; i < linkCount; i++, waterLink++) {
+ linkY += LINK_SIZE;
+ waterLink->basis.x.x = 1.0f;
+ waterLink->basis.y.y = 1.0f;
+ waterLink->basis.z.z = 1.0f;
+ waterLink->pos.x = this->actor.home.pos.x;
+ waterLink->pos.y = linkY;
+ waterLink->pos.z = this->actor.home.pos.z;
+ waterLink->velocity.x = (Rand_ZeroOne() - 0.5f) * swayVel;
+ waterLink->velocity.y = (Rand_ZeroOne() - 0.5f) * swayVel;
+ waterLink->velocity.z = (Rand_ZeroOne() - 0.5f) * swayVel;
+ }
+ waterChain->maxY = linkY;
+
+ // Rest position is slightly below max chain length to give slack.
+ waterChain->restY = waterChain->maxY * 0.97f;
+ linkY -= LINK_SIZE * 1.5f;
+ if (waterChain->restY < linkY) { // This should only happen if linkCount > 50
+ waterChain->restY = linkY;
+ }
+
+ if (wallCheckRadius < 0.0f) {
+ waterChain->wallCheckDistSq = -1.0f; // Negative value means skip wall collision checks
+ } else if (wallCheckRadius <= WATER_RADIUS + 1.0f) {
+ waterChain->wallCheckDistSq = 0.0f;
+ } else {
+ waterChain->wallCheckDistSq = SQ(wallCheckRadius - WATER_RADIUS);
+ }
+}
+
+void ObjMine_Water_InitCollider(ObjMine* this, s32 linkCount) {
+ Matrix_Translate(this->actor.home.pos.x, this->actor.home.pos.y + (linkCount * LINK_SIZE) + ATTACH_OFFSET,
+ this->actor.home.pos.z, MTXMODE_NEW);
+ Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
+ Collider_UpdateSpheres(0, &this->collider);
+}
+
+void ObjMine_Water_SetWorld(ObjMine* this) {
+ s32 pad; // Could be recast to thisx as in ObjMine_Path_Move
+ ObjMineWaterChain* waterChain = &this->chain.water;
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+
+ if (linkCount == 0) {
+ this->actor.world.pos.y = this->actor.home.pos.y + LINK_SIZE + ATTACH_OFFSET;
+ } else {
+ ObjMineWaterLink* lastLink = &waterChain->links[linkCount - 1];
+ Vec3f mineOffset;
+
+ Math_Vec3f_ScaleAndStore(&lastLink->basis.y, ATTACH_OFFSET, &mineOffset);
+ Math_Vec3f_Sum(&lastLink->pos, &mineOffset, &this->actor.world.pos);
+ }
+}
+
+void ObjMine_Water_WallCheck(ObjMine* this, PlayState* play) {
+ s32 pad; // Could be recast to thisx as in ObjMine_Path_Move
+ ObjMineWaterChain* waterChain = &this->chain.water;
+
+ waterChain->touchWall = false;
+ if (waterChain->wallCheckDistSq > -1e-6f) {
+ // Checks for walls if mine is sufficiently far from home. If found, sets ejection force towards home.
+ if (waterChain->wallCheckDistSq <= Math3D_XZDistanceSquared(this->actor.home.pos.x, this->actor.home.pos.z,
+ this->actor.world.pos.x, this->actor.world.pos.z)) {
+ Vec3f centerPos;
+ Vec3f offsetPos;
+ Vec3f result; // not used
+ Vec3f xzFromHome;
+ Vec3f xzDirFromHome;
+ CollisionPoly* poly; // not used
+ s32 bgId; // not used
+ f32 norm; // not used
+ f32 invNorm; // not used
+
+ xzFromHome.x = this->actor.world.pos.x - this->actor.home.pos.x;
+ xzFromHome.y = 0.0f;
+ xzFromHome.z = this->actor.world.pos.z - this->actor.home.pos.z;
+
+ if (ObjMine_GetUnitVec3fNorm(&xzFromHome, &xzDirFromHome, &norm, &invNorm)) {
+
+ offsetPos.x = this->actor.world.pos.x + (xzDirFromHome.x * WATER_RADIUS);
+ offsetPos.y = this->actor.world.pos.y;
+ offsetPos.z = this->actor.world.pos.z + (xzDirFromHome.z * WATER_RADIUS);
+
+ centerPos.x = this->actor.home.pos.x;
+ centerPos.y = this->actor.world.pos.y;
+ centerPos.z = this->actor.home.pos.z;
+
+ if (BgCheck_EntityLineTest1(&play->colCtx, &centerPos, &offsetPos, &result, &poly, true, false, false,
+ true, &bgId)) {
+ waterChain->touchWall = true;
+ waterChain->wallEject.x = xzDirFromHome.x * -0.2f;
+ waterChain->wallEject.z = xzDirFromHome.z * -0.2f;
+ }
+ }
+ }
+ }
+}
+
+Vec3f sLastLinkAccel[OBJMINE_CHAIN_MAX + 1];
+
+void ObjMine_Water_ApplyForces(ObjMine* this) {
+ ObjMineWaterChain* waterChain = &this->chain.water;
+ ObjMineWaterLink* waterLink;
+ s32 i;
+ s16 swayPhase = 0;
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+ f32 inverseCount = 1.0f / linkCount;
+ f32 restoreY = (waterChain->links[linkCount - 1].pos.y - waterChain->restY) * waterChain->restoreY;
+ Vec3f tension;
+ f32 scaledKnockback;
+ s32 index;
+ s32 pad;
+ Vec3f* lastLinkAccel;
+
+ // Applies the buoyant force and sway from the water.
+ for (i = 0, waterLink = waterChain->links, lastLinkAccel = sLastLinkAccel; i < linkCount;
+ i++, waterLink++, swayPhase += waterChain->swayPhaseVel, lastLinkAccel++) {
+ Math_Vec3f_Copy(lastLinkAccel, &waterLink->accel);
+
+ if (waterLink->pos.y <= this->actor.home.pos.y) {
+ waterLink->accel.y = waterChain->restoreY * -96.0f;
+ } else if (restoreY > 0.0f) {
+ waterLink->accel.y = (i + 1) * inverseCount * (restoreY / 0.3f);
+ } else {
+ waterLink->accel.y = restoreY;
+ }
+
+ waterLink->accel.x = (waterLink->pos.x - this->actor.home.pos.x) * waterChain->restoreXZ;
+ waterLink->accel.z = (waterLink->pos.z - this->actor.home.pos.z) * waterChain->restoreXZ;
+ waterLink->accel.x += waterChain->swayXZ * Math_SinS(swayPhase);
+ waterLink->accel.y += waterChain->swayY;
+ waterLink->accel.z += waterChain->swayXZ * Math_CosS(swayPhase);
+ }
+
+ // Applies knockback force. Knockback on the links is scaled quadratically, possibly to account for link rotations
+ // being cumulative
+ if (waterChain->knockback > 0.0001f) {
+ for (i = 0, waterLink = waterChain->links; i < linkCount; i++, waterLink++) {
+ scaledKnockback = SQ((linkCount - i) * inverseCount);
+
+ waterLink->accel.x += waterChain->knockbackDir.x * scaledKnockback;
+ waterLink->accel.y += waterChain->knockbackDir.y * scaledKnockback;
+ waterLink->accel.z += waterChain->knockbackDir.z * scaledKnockback;
+ }
+ }
+
+ // Moves chain away from wall if mine is intersecting one
+ if (waterChain->touchWall) {
+ for (i = 0, waterLink = waterChain->links; i < linkCount; i++, waterLink++) {
+ waterLink->accel.x += waterChain->wallEject.x;
+ waterLink->accel.z += waterChain->wallEject.z;
+ }
+ }
+
+ // Forces on the links from other links. This is simulated by a triangle filter on the forces from the previous
+ // frame.
+ for (i = 0, waterLink = waterChain->links; i < linkCount; i++, waterLink++) {
+ Math_Vec3f_Copy(&tension, &gZeroVec3f);
+
+ if (i >= 2) {
+ tension.x += sLastLinkAccel[i - 2].x * 0.075f;
+ tension.y += sLastLinkAccel[i - 2].y * 0.075f;
+ tension.z += sLastLinkAccel[i - 2].z * 0.075f;
+ }
+
+ if (i >= 1) {
+ tension.x += sLastLinkAccel[i - 1].x * 0.15f;
+ tension.y += sLastLinkAccel[i - 1].y * 0.15f;
+ tension.z += sLastLinkAccel[i - 1].z * 0.15f;
+ }
+
+ tension.x += sLastLinkAccel[i].x * 0.3f;
+ tension.y += sLastLinkAccel[i].y * 0.3f;
+ tension.z += sLastLinkAccel[i].z * 0.3f;
+
+ index = i + 1;
+ if (index < linkCount) {
+ tension.x += sLastLinkAccel[index].x * 0.15f;
+ tension.y += sLastLinkAccel[index].y * 0.15f;
+ tension.z += sLastLinkAccel[index].z * 0.15f;
+ }
+
+ index = i + 2;
+ if (index < linkCount) {
+ tension.x += sLastLinkAccel[index].x * 0.075f;
+ tension.y += sLastLinkAccel[index].y * 0.075f;
+ tension.z += sLastLinkAccel[index].z * 0.075f;
+ }
+ waterLink->accel.x += tension.x;
+ waterLink->accel.y += tension.y;
+ waterLink->accel.z += tension.z;
+ }
+}
+
+void ObjMine_Water_UpdateLinks(ObjMine* this) {
+ s32 pad; // Could be recast to thisx as in ObjMine_Path_Move
+ ObjMineMtxF3 newBasis;
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+ ObjMineWaterChain* waterChain = &this->chain.water;
+ ObjMineWaterLink* waterLink;
+ s32 i;
+ Vec3f* prevBasisX;
+ Vec3f tempVec;
+ Vec3f diffDir;
+ Vec3f unusedLinkPos;
+ Vec3f jointPos;
+ s32 changeBasis;
+ f32 diffNorm;
+ f32 invNorm; // not used
+ Vec3f* tempBasisX;
+
+ // The joint between the current link and next link is half the link's length from its center. The first link is
+ // assumed to be vertical and centered on home.
+ jointPos.x = this->actor.home.pos.x;
+ jointPos.y = this->actor.home.pos.y + (LINK_SIZE / 2.0f);
+ jointPos.z = this->actor.home.pos.z;
+
+ for (i = 0, prevBasisX = NULL, waterLink = waterChain->links; i < linkCount;
+ i++, prevBasisX = &waterLink->basis.x, waterLink++) {
+ changeBasis = false;
+
+ waterLink->velocity.x += waterLink->accel.x;
+ waterLink->velocity.y += waterLink->accel.y;
+ waterLink->velocity.z += waterLink->accel.z;
+
+ Math_Vec3f_Scale(&waterLink->velocity, waterChain->drag);
+ Math_Vec3f_Copy(&unusedLinkPos, &waterLink->pos);
+
+ waterLink->pos.x += waterLink->velocity.x;
+ waterLink->pos.y += waterLink->velocity.y;
+ waterLink->pos.z += waterLink->velocity.z;
+
+ // Heavily reduce velocity if link is below home and moving downward.
+ if ((waterLink->pos.y <= this->actor.home.pos.y) && (waterLink->velocity.y < 0.0f)) {
+ waterLink->velocity.y *= 0.1f;
+ }
+
+ // If the calculated position is less than 1/3 of the link's length from the joint, the chain is considered
+ // slack and the link keeps the same basis. Otherwise the basis is updated with the new position relative to the
+ // joint. The StepUntilParallel causes the chain to straighten over time.
+ Math_Vec3f_Diff(&waterLink->pos, &jointPos, &tempVec);
+
+ if (ObjMine_GetUnitVec3fNorm(&tempVec, &diffDir, &diffNorm, &invNorm) && (diffNorm > LINK_SIZE / 3.0f)) {
+ Math_Vec3f_Copy(&newBasis.y, &waterLink->basis.y);
+ ObjMine_StepUntilParallel(&newBasis.y, &diffDir, M_PI / 30);
+
+ tempBasisX = (prevBasisX == NULL) ? &sStandardBasis.x : prevBasisX;
+
+ Math3D_CrossProduct(tempBasisX, &newBasis.y, &tempVec);
+
+ // Skips change of basis if any of the basis vectors would be zero.
+ if (ObjMine_GetUnitVec3f(&tempVec, &newBasis.z)) {
+ Math3D_CrossProduct(&newBasis.y, &newBasis.z, &tempVec);
+ if (ObjMine_GetUnitVec3f(&tempVec, &newBasis.x)) {
+ changeBasis = true;
+ }
+ }
+ }
+
+ if (changeBasis) {
+ Math_Vec3f_Copy(&waterLink->basis.x, &newBasis.x);
+ Math_Vec3f_Copy(&waterLink->basis.y, &newBasis.y);
+ Math_Vec3f_Copy(&waterLink->basis.z, &newBasis.z);
+ } else {
+ diffNorm = LINK_SIZE / 3.0f;
+ }
+
+ // Sets new link position from calculated rotation basis. The check ensures displacement from the previous link
+ // doesn't cause the links to separate
+ if (diffNorm >= (LINK_SIZE / 2.0f)) {
+ waterLink->pos.x = jointPos.x + (waterLink->basis.y.x * (LINK_SIZE / 2.0f));
+ waterLink->pos.y = jointPos.y + (waterLink->basis.y.y * (LINK_SIZE / 2.0f));
+ waterLink->pos.z = jointPos.z + (waterLink->basis.y.z * (LINK_SIZE / 2.0f));
+ } else {
+ waterLink->pos.x = jointPos.x + (waterLink->basis.y.x * diffNorm);
+ waterLink->pos.y = jointPos.y + (waterLink->basis.y.y * diffNorm);
+ waterLink->pos.z = jointPos.z + (waterLink->basis.y.z * diffNorm);
+ }
+
+ // Calculate the position of the joint with the next link.
+ jointPos.x = waterLink->pos.x + (waterLink->basis.y.x * (LINK_SIZE / 2.0f));
+ jointPos.y = waterLink->pos.y + (waterLink->basis.y.y * (LINK_SIZE / 2.0f));
+ jointPos.z = waterLink->pos.z + (waterLink->basis.y.z * (LINK_SIZE / 2.0f));
+ }
+}
+
+void ObjMine_Water_UpdateChain(ObjMine* this, PlayState* play) {
+ ObjMine_Water_WallCheck(this, play);
+ ObjMine_Water_ApplyForces(this);
+ ObjMine_Water_UpdateLinks(this);
+}
+
+void ObjMine_Init(Actor* thisx, PlayState* play) {
+ s32 pad; // Can be playstate recast. Must be gamestate recast.
+ ObjMine* this = THIS;
+ s32 pathIndex = OBJMINE_GET_PATH_INDEX(&this->actor);
+ Path* path;
+ s32 bgId; // not used
+ s32 type = OBJMINE_GET_TYPE(&this->actor);
+
+ Actor_ProcessInitChain(&this->actor, sInitChain);
+
+ this->actor.shape.rot.z = 0;
+ this->actor.world.rot.z = 0;
+
+ Collider_InitJntSph(play, &this->collider);
+ Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->colliderElements);
+
+ if (type == OBJMINE_TYPE_PATH) {
+ ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 45.0f);
+ this->actor.shape.shadowAlpha = 140;
+ this->pathSpeed = sPathSpeeds[OBJMINE_GET_PATH_SPEED(&this->actor)]; // sPathSpeeds[i] = i + 1.0f
+ if (pathIndex == 0xFF) {
+ ObjMine_Path_SetupStationary(this);
+ } else {
+ path = &play->setupPathList[pathIndex];
+
+ this->waypointIndex = 0;
+ this->waypointCount = path->count - 1;
+ this->waypoints = (Vec3s*)Lib_SegmentedToVirtual(path->points);
+
+ ObjMine_Path_MoveToWaypoint(this, this->waypointIndex);
+ ObjMine_Path_SetupMove(this);
+ }
+ Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z,
+ &this->actor.shape.rot);
+ Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
+ Collider_UpdateSpheres(0, &this->collider);
+ this->actor.floorHeight = BgCheck_EntityRaycastFloor5(&play->colCtx, &this->actor.floorPoly, &bgId,
+ &this->actor, &this->actor.world.pos);
+ } else {
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+
+ this->actor.update = ObjMine_AirWater_Update;
+ this->actor.uncullZoneScale = 150.0f + (linkCount * (LINK_SIZE * 1.75f));
+ this->actor.uncullZoneDownward = 150.0f + (linkCount * (LINK_SIZE * 1.75f));
+ ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 45.0f);
+ this->actor.shape.shadowAlpha = 140;
+
+ if (type == OBJMINE_TYPE_AIR) {
+ this->actor.draw = ObjMine_Air_Draw;
+ ObjMine_Air_InitChain(this, linkCount);
+ this->actor.world.pos.y = -ATTACH_OFFSET - (linkCount * LINK_SIZE) + this->actor.home.pos.y;
+ ObjMine_Air_InitCollider(this, linkCount);
+ func_800B4AEC(play, &this->actor, 0.0f);
+ if (linkCount == 0) {
+ ObjMine_Air_SetupStationary(this);
+ } else {
+ ObjMine_Air_SetupChained(this);
+ }
+ } else {
+ this->actor.draw = ObjMine_Water_Draw;
+ ObjMine_Water_InitChain(this, linkCount);
+ this->actor.world.pos.y = ATTACH_OFFSET + (linkCount * LINK_SIZE) + this->actor.home.pos.y;
+ ObjMine_Water_InitCollider(this, linkCount);
+ if (linkCount == 0) {
+ ObjMine_Water_SetupStationary(this);
+ } else {
+ ObjMine_Water_SetupChained(this);
+ }
+ }
+ }
+}
+
+void ObjMine_Destroy(Actor* thisx, PlayState* play) {
+ ObjMine* this = THIS;
+
+ Collider_DestroyJntSph(play, &this->collider);
+}
+
+void ObjMine_Path_SetupStationary(ObjMine* this) {
+ this->actionFunc = ObjMine_Path_Stationary;
+}
+
+void ObjMine_Path_Stationary(ObjMine* this, PlayState* play) {
+}
+
+void ObjMine_Path_SetupMove(ObjMine* this) {
+ this->actor.flags |= ACTOR_FLAG_10;
+ this->actionFunc = ObjMine_Path_Move;
+}
+
+void ObjMine_Path_Move(ObjMine* this, PlayState* play) {
+ Actor* thisx = &this->actor;
+ Vec3f nextWaypoint;
+ f32 distToWaypoint;
+ f32 step;
+ f32 target;
+ s32 bgId; // not used
+
+ // thisx->velocity is temporarily set to the vector difference to the next waypoint.
+ Math_Vec3s_ToVec3f(&nextWaypoint, &this->waypoints[this->waypointIndex + 1]);
+ Math_Vec3f_Diff(&nextWaypoint, &thisx->world.pos, &thisx->velocity);
+ distToWaypoint = Math3D_Vec3fMagnitude(&thisx->velocity);
+
+ // Mine slows to 2.0f speed when 8 frames away from the next waypoint
+ if ((distToWaypoint < (this->pathSpeed * 8.0f)) && (this->pathSpeed > 2.0f)) {
+ target = 2.0f + ((this->pathSpeed - 2.0f) * 0.1f);
+ step = this->pathSpeed * 0.03f;
+ } else {
+ target = this->pathSpeed;
+ step = this->pathSpeed * 0.16f;
+ }
+ Math_StepToF(&thisx->speed, target, step);
+
+ // Checks if mine will reach the waypoint next frame
+ if ((thisx->speed + 0.05f) < distToWaypoint) {
+ // Rescales thisx->velocity to be equal in magnitude to speed
+ Math_Vec3f_Scale(&thisx->velocity, thisx->speed / distToWaypoint);
+ thisx->world.pos.x += thisx->velocity.x;
+ thisx->world.pos.y += thisx->velocity.y;
+ thisx->world.pos.z += thisx->velocity.z;
+ } else {
+ thisx->speed *= 0.4f;
+ this->waypointIndex++;
+ if (this->waypointIndex >= this->waypointCount) {
+ this->waypointIndex = 0;
+ }
+ ObjMine_Path_MoveToWaypoint(this, this->waypointIndex);
+ }
+ thisx->floorHeight = BgCheck_EntityRaycastFloor5(&play->colCtx, &thisx->floorPoly, &bgId, thisx, &thisx->world.pos);
+ if (thisx->flags & ACTOR_FLAG_40) {
+ Vec3f rotAxis;
+ Vec3f yhatCrossV;
+ MtxF rotMtxF;
+
+ // Makes mines appear to roll while traversing the path
+ Math3D_CrossProduct(&sStandardBasis.y, &thisx->velocity, &yhatCrossV);
+ if (ObjMine_GetUnitVec3f(&yhatCrossV, &rotAxis)) {
+ Matrix_RotateAxisF(thisx->speed / PATH_RADIUS, &rotAxis, MTXMODE_NEW);
+ Matrix_RotateYS(thisx->shape.rot.y, MTXMODE_APPLY);
+ Matrix_RotateXS(thisx->shape.rot.x, MTXMODE_APPLY);
+ Matrix_RotateZS(thisx->shape.rot.z, MTXMODE_APPLY);
+ Matrix_Get(&rotMtxF);
+ Matrix_MtxFToYXZRot(&rotMtxF, &thisx->shape.rot, false);
+ }
+ }
+}
+
+void ObjMine_SetupExplode(ObjMine* this) {
+ this->actor.flags |= ACTOR_FLAG_10;
+ this->actor.draw = ObjMine_DrawExplosion;
+ this->actor.shape.shadowDraw = NULL;
+ this->actor.scale.x = 0.02f;
+ this->actor.scale.y = 0.02f;
+ this->actor.scale.z = 0.02f;
+ this->actionFunc = ObjMine_Explode;
+}
+
+void ObjMine_Explode(ObjMine* this, PlayState* play) {
+ this->actor.scale.x *= 1.8f;
+ if (this->actor.scale.x > (0.02f * 1.5f * 5.832f)) { // 5.832 = 1.8^3
+ Actor_Kill(&this->actor);
+ return;
+ }
+
+ this->actor.scale.y = this->actor.scale.x;
+ this->actor.scale.z = this->actor.scale.x;
+}
+
+void ObjMine_Air_SetupChained(ObjMine* this) {
+ this->actionFunc = ObjMine_Air_Chained;
+}
+
+void ObjMine_Air_Chained(ObjMine* this, PlayState* play) {
+ s32 pad; // Could be recast to thisx as in ObjMine_Path_Move
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+ ObjMineAirChain* airChain = &this->chain.air;
+ ObjMineAirLink* airLink;
+ s32 i;
+ f32 xAccel;
+ f32 zAccel;
+ f32 spin;
+ s16 twistDiff;
+
+ Math_Vec3f_Copy(&airChain->translation, &airChain->basis.y);
+
+ // Explodes if collision with Player or another mine is detected.
+ if (ObjMine_AirWater_CheckOC(this)) {
+ ObjMine_AirWater_SpawnBomb(this, play);
+ ObjMine_AirWater_Noop(this);
+ ObjMine_SetupExplode(this);
+ return;
+ }
+
+ // Calculates initial knockback and torque from hit scaled to chain length
+ if (this->collider.base.acFlags & AC_HIT) {
+ s16 torqueAngle;
+ f32 torque;
+ f32 torqueDiff;
+
+ this->collider.base.acFlags &= ~AC_HIT;
+ airChain->knockback = AIR_KNOCKBACK;
+ ObjMine_Air_CheckAC(this, &airChain->knockbackAngle, &torqueAngle);
+ torque = Math_SinS(torqueAngle) * 0x96;
+ torqueDiff = -(torque / linkCount);
+
+ for (i = 0, airLink = airChain->links; i < linkCount; i++, airLink++) {
+ airLink->spin += (s16)torque;
+ airLink->spin = CLAMP(airLink->spin, -0x320, 0x320);
+ torque -= torqueDiff;
+ }
+ }
+
+ // Applies knockback force. Knockback applies over two frames, with the second at half strength
+ if (airChain->knockback > 0.0001f) {
+ xAccel = Math_SinS(airChain->knockbackAngle) * airChain->knockback;
+ zAccel = Math_CosS(airChain->knockbackAngle) * airChain->knockback;
+ airChain->velocity.x += xAccel;
+ airChain->velocity.z += zAccel;
+ Math_StepToF(&airChain->knockback, 0.0f, AIR_KNOCKBACK * 0.5f);
+ }
+
+ // Applies chain sway, choosing a new sway randomly about every 32 frames
+ if ((Rand_Next() >> (0x20 - 5)) == 0) {
+ airChain->swaySize = Rand_ZeroOne() * airChain->swayMax;
+ airChain->swayPhase = Rand_Next() >> (0x20 - 16);
+ }
+ xAccel = Math_SinS(airChain->swayPhase) * airChain->swaySize;
+ zAccel = Math_CosS(airChain->swayPhase) * airChain->swaySize;
+ airChain->velocity.x += xAccel;
+ airChain->velocity.z += zAccel;
+
+ // Applies the restoring force of gravity and chain tension using small-angle approximation
+ airChain->velocity.x += airChain->displacement.x * airChain->restore;
+ airChain->velocity.z += airChain->displacement.z * airChain->restore;
+
+ // Applies linear drag
+ airChain->velocity.x *= airChain->drag;
+ airChain->velocity.z *= airChain->drag;
+
+ // Updates scaled position and does a safety clamp to 5.0f.
+ airChain->displacement.x += airChain->velocity.x;
+ airChain->displacement.z += airChain->velocity.z;
+
+ airChain->displacement.x = CLAMP(airChain->displacement.x, -5.0f, 5.0f);
+ airChain->displacement.z = CLAMP(airChain->displacement.z, -5.0f, 5.0f);
+
+ ObjMine_Air_SetBasis(this);
+ ObjMine_Air_SetWorld(this);
+
+ // Checks for wall collisions if sufficiently far from home. If collision detected, bounce off the wall at half
+ // speed. If speed is close to zero when hitting wall, weakly eject it instead.
+ if (airChain->wallCheckDistSq > -1e-6f) {
+ if (airChain->wallCheckDistSq <= Math3D_XZDistanceSquared(this->actor.world.pos.x, this->actor.world.pos.z,
+ this->actor.home.pos.x, this->actor.home.pos.z)) {
+
+ Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, AIR_RADIUS, 0.0f, UPDBGCHECKINFO_FLAG_1);
+
+ if ((this->actor.bgCheckFlags & BGCHECKFLAG_WALL) && (this->actor.wallPoly != NULL)) {
+ Vec3f xzDir;
+ Vec3f reflectedDir;
+ Vec3f wallNormal;
+ Vec3f xzVel;
+ f32 xzSpeed;
+ f32 invNorm; // not used
+
+ xzVel.x = airChain->velocity.x;
+ xzVel.y = 0.0f;
+ xzVel.z = airChain->velocity.z;
+
+ if (ObjMine_GetUnitVec3fNorm(&xzVel, &xzDir, &xzSpeed, &invNorm)) {
+ wallNormal.x = COLPOLY_GET_NORMAL(this->actor.wallPoly->normal.x);
+ wallNormal.y = COLPOLY_GET_NORMAL(this->actor.wallPoly->normal.y);
+ wallNormal.z = COLPOLY_GET_NORMAL(this->actor.wallPoly->normal.z);
+
+ func_80179F64(&xzDir, &wallNormal, &reflectedDir);
+
+ xzSpeed /= 2.0f;
+ airChain->velocity.x = reflectedDir.x * xzSpeed;
+ airChain->velocity.z = reflectedDir.z * xzSpeed;
+ } else {
+ airChain->velocity.x *= -0.1f;
+ airChain->velocity.z *= -0.1f;
+ }
+ ObjMine_Air_SetChainXZ(this);
+ ObjMine_Air_SetBasis(this);
+ ObjMine_Air_SetWorld(this);
+ }
+ }
+ }
+
+ // Applies three torques to individual chain links:
+ // - restoring torque towards the default twist of 0x4000
+ // - random torque for variance
+ // - linear drag torque
+ for (i = 0, airLink = airChain->links; i < linkCount; i++, airLink++) {
+ twistDiff = airLink->twist - 0x4000;
+ spin = airLink->spin + (twistDiff * -0.05f) + (30.0f * Rand_ZeroOne() - 15.0f);
+ spin *= 0.995f;
+ airLink->spin = spin;
+ airLink->twist += airLink->spin;
+ }
+
+ func_800B4AEC(play, &this->actor, 0.0f);
+ ObjMine_UpdateCollider(this);
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base);
+}
+
+void ObjMine_Air_SetupStationary(ObjMine* this) {
+ this->actionFunc = ObjMine_Air_Stationary;
+}
+
+void ObjMine_Air_Stationary(ObjMine* this, PlayState* play) {
+ // Explodes if collision with Player or another mine is detected.
+ if (ObjMine_AirWater_CheckOC(this)) {
+ ObjMine_AirWater_SpawnBomb(this, play);
+ ObjMine_AirWater_Noop(this);
+ ObjMine_SetupExplode(this);
+ return;
+ }
+
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base);
+}
+
+void ObjMine_Water_SetupChained(ObjMine* this) {
+ this->actionFunc = ObjMine_Water_Chained;
+}
+
+void ObjMine_Water_Chained(ObjMine* this, PlayState* play) {
+ ObjMineWaterChain* waterChain = &this->chain.water;
+
+ // Explodes if collision with Player or another mine is detected.
+ if (ObjMine_AirWater_CheckOC(this)) {
+ ObjMine_AirWater_SpawnBomb(this, play);
+ ObjMine_AirWater_Noop(this);
+ ObjMine_SetupExplode(this);
+ return;
+ }
-extern ColliderJntSphElementInit D_80A84570[1];
-extern ColliderJntSphInit D_80A84594;
-extern InitChainEntry D_80A845E8[];
+ // Calculates knockback from AC hits
+ if (this->collider.base.acFlags & AC_HIT) {
+ this->collider.base.acFlags &= ~AC_HIT;
+ ObjMine_Water_CheckAC(this, &waterChain->knockbackDir);
+ waterChain->knockback = WATER_KNOCKBACK;
+ Math_Vec3f_Scale(&waterChain->knockbackDir, WATER_KNOCKBACK);
+ }
-extern UNK_TYPE D_06000030;
-extern UNK_TYPE D_06002068;
+ // Reduces knockback each frame. Effectively makes knockback 60% on the first frame and 20% on the second.
+ if (waterChain->knockback > 0.0001f) {
+ waterChain->knockback = Math_Vec3f_StepTo(&waterChain->knockbackDir, &gZeroVec3f, WATER_KNOCKBACK * 0.4f);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A811D0.s")
+ waterChain->drag = 0.9f;
+ waterChain->restoreXZ = -0.0002f;
+ waterChain->restoreY = -0.0002f;
+ waterChain->swayMax = 0.003f;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A8120C.s")
+ // Chooses a new sway randomly about every 32 frames.
+ if (Rand_Next() >> (0x20 - 5) == 0) {
+ s16 randAngle = Rand_Next() >> (0x20 - 16);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81288.s")
+ waterChain->swayXZ = Math_SinS(randAngle) * 1.8f * waterChain->swayMax;
+ waterChain->swayY = Math_CosS(randAngle) * 0.2f * waterChain->swayMax;
+ waterChain->swayPhaseVel = Rand_Next() >> (0x20 - 13);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A8131C.s")
+ ObjMine_Water_UpdateChain(this, play);
+ ObjMine_Water_SetWorld(this);
+ ObjMine_UpdateCollider(this);
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base);
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81384.s")
+void ObjMine_Water_SetupStationary(ObjMine* this) {
+ this->actionFunc = ObjMine_Water_Stationary;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A8140C.s")
+void ObjMine_Water_Stationary(ObjMine* this, PlayState* play) {
+ // Explodes if collision with Player or another mine is detected.
+ if (ObjMine_AirWater_CheckOC(this)) {
+ ObjMine_AirWater_SpawnBomb(this, play);
+ ObjMine_AirWater_Noop(this);
+ ObjMine_SetupExplode(this);
+ return;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A8146C.s")
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base);
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81544.s")
+void ObjMine_Path_Update(Actor* thisx, PlayState* play) {
+ s32 pad; // Can be playstate recast.
+ ObjMine* this = THIS;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81640.s")
+ if ((this->collider.base.ocFlags2 & OC2_HIT_PLAYER) || (this->collider.base.acFlags & AC_HIT)) {
+ ObjMine_Path_SpawnBomb(this, play);
+ ObjMine_SetupExplode(this);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A8164C.s")
+ this->actionFunc(this, play);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81684.s")
+ if (this->actor.update != NULL) {
+ this->collider.base.ocFlags1 &= ~OC1_HIT;
+ this->collider.base.acFlags &= ~AC_HIT;
+ this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER;
+ if ((this->actor.flags & ACTOR_FLAG_40) && (this->actionFunc != ObjMine_Explode)) {
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base);
+ }
+ }
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81714.s")
+void ObjMine_AirWater_Update(Actor* thisx, PlayState* play) {
+ ObjMine* this = THIS;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81818.s")
+ this->actionFunc(this, play);
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81868.s")
+void ObjMine_Path_Draw(Actor* thisx, PlayState* play) {
+ s32 pad; // Can be playstate recast
+ ObjMine* this = THIS;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A819A4.s")
+ OPEN_DISPS(play->state.gfxCtx);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81A00.s")
+ if (this->actionFunc == ObjMine_Path_Move) {
+ Collider_UpdateSpheres(0, &this->collider);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81AA4.s")
+ func_800B8050(&this->actor, play, true);
+ Gfx_SetupDL25_Opa(play->state.gfxCtx);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81B14.s")
+ gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gDPSetRenderMode(POLY_OPA_DISP++, G_RM_PASS, G_RM_AA_ZB_OPA_SURF2);
+ gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255);
+ gSPDisplayList(POLY_OPA_DISP++, object_ny_DL_002068);
+ gSPDisplayList(POLY_OPA_DISP++, object_ny_DL_002188);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81B7C.s")
+ CLOSE_DISPS(play->state.gfxCtx);
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81D70.s")
+void ObjMine_DrawExplosion(Actor* thisx, PlayState* play) {
+ s32 pad; // This could be thisx recast or playstate recast. Not enough room on stack for both.
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81DEC.s")
+ OPEN_DISPS(play->state.gfxCtx);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81E7C.s")
+ func_800B8118(thisx, play, true);
+ Gfx_SetupDL25_Xlu(play->state.gfxCtx);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A81FFC.s")
+ gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_AA_ZB_XLU_SURF2);
+ gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 75);
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, object_ny_DL_002068);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A828A8.s")
+ CLOSE_DISPS(play->state.gfxCtx);
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A82C28.s")
+void ObjMine_Air_Draw(Actor* thisx, PlayState* play) {
+ s32 pad; // Can be playstate recast
+ ObjMine* this = THIS;
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+ ObjMineAirChain* airChain = &this->chain.air;
+ ObjMineAirLink* airLink;
+ s32 i;
+ Vec3f linkPos;
+ Vec3f linkOffset;
+ Gfx* gfx;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/ObjMine_Init.s")
+ func_800B8050(&this->actor, play, true);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/ObjMine_Destroy.s")
+ OPEN_DISPS(play->state.gfxCtx);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A82F84.s")
+ gfx = POLY_OPA_DISP;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A82F98.s")
+ gSPDisplayList(gfx++, &gSetupDLs[SETUPDL_25]);
+ gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(gfx++, object_ny_DL_000030);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A82FA8.s")
+ ObjMine_SetRotation(&airChain->basis);
+ Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
+ if (linkCount != 0) {
+ // Sets pivot point to be half a chain link length below home
+ Math_Vec3f_ScaleAndStore(&airChain->basis.y, -(LINK_SIZE / 2.0f), &linkOffset);
+ linkPos.x = this->actor.home.pos.x - linkOffset.x;
+ linkPos.y = this->actor.home.pos.y - (LINK_SIZE / 2.0f) - linkOffset.y;
+ linkPos.z = this->actor.home.pos.z - linkOffset.z;
+ Math_Vec3f_ScaleAndStore(&airChain->basis.y, -LINK_SIZE, &linkOffset);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A82FC8.s")
+ for (i = 0, airLink = airChain->links; i < linkCount; i++, airLink++) {
+ Matrix_RotateYS(airLink->twist, MTXMODE_APPLY);
+ linkPos.x += linkOffset.x;
+ linkPos.y += linkOffset.y;
+ linkPos.z += linkOffset.z;
+ ObjMine_ReplaceTranslation(&linkPos);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83214.s")
+ gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(gfx++, object_ny_DL_000030);
+ }
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83258.s")
+ Matrix_RotateXS(0x2000, MTXMODE_APPLY);
+ ObjMine_ReplaceTranslation(&this->actor.world.pos);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A832BC.s")
+ gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gDPPipeSync(gfx++);
+ gDPSetRenderMode(gfx++, G_RM_PASS, G_RM_AA_ZB_OPA_SURF2);
+ gDPSetEnvColor(gfx++, 0, 0, 0, 255);
+ gSPDisplayList(gfx++, object_ny_DL_002068);
+ gSPDisplayList(gfx++, object_ny_DL_002188);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A832D0.s")
+ POLY_OPA_DISP = gfx;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83A74.s")
+ CLOSE_DISPS(play->state.gfxCtx);
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83A88.s")
+void ObjMine_Water_Draw(Actor* thisx, PlayState* play) {
+ s32 pad; // Can be playstate recast
+ ObjMine* this = THIS;
+ s32 linkCount = OBJMINE_GET_LINK_COUNT(&this->actor);
+ ObjMineWaterChain* waterChain = &this->chain.water;
+ ObjMineWaterLink* waterLink;
+ s32 i;
+ Gfx* gfx;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83B14.s")
+ func_800B8050(&this->actor, play, true);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83B28.s")
+ OPEN_DISPS(play->state.gfxCtx);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83CEC.s")
+ gfx = POLY_OPA_DISP;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83D00.s")
+ gSPDisplayList(gfx++, &gSetupDLs[SETUPDL_25]);
+ gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(gfx++, object_ny_DL_000030);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/ObjMine_Update.s")
+ for (i = 0, waterLink = waterChain->links; i < linkCount; i++, waterLink++) {
+ ObjMine_SetRotation(&waterLink->basis);
+ Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
+ // Consecutive chain links are offset 90 degrees.
+ if ((i % 2) == 0) {
+ Matrix_RotateYS(0x4000, MTXMODE_APPLY);
+ }
+ ObjMine_ReplaceTranslation(&waterLink->pos);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83E7C.s")
+ gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(gfx++, object_ny_DL_000030);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/ObjMine_Draw.s")
+ Matrix_RotateXS(0x2000, MTXMODE_APPLY);
+ ObjMine_ReplaceTranslation(&this->actor.world.pos);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A83FBC.s")
+ gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gDPPipeSync(gfx++);
+ gDPSetRenderMode(gfx++, G_RM_PASS, G_RM_AA_ZB_OPA_SURF2);
+ gDPSetEnvColor(gfx++, 0, 0, 0, 255);
+ gSPDisplayList(gfx++, object_ny_DL_002068);
+ gSPDisplayList(gfx++, object_ny_DL_002188);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A84088.s")
+ POLY_OPA_DISP = gfx;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Mine/func_80A84338.s")
+ CLOSE_DISPS(play->state.gfxCtx);
+}
diff --git a/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.h b/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.h
index 257a6ff3a..e831f91de 100644
--- a/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.h
+++ b/src/overlays/actors/ovl_Obj_Mine/z_obj_mine.h
@@ -7,11 +7,89 @@ struct ObjMine;
typedef void (*ObjMineActionFunc)(struct ObjMine*, PlayState*);
+#define OBJMINE_GET_LINK_COUNT(thisx) ((thisx)->params & 0x3F)
+#define OBJMINE_GET_PATH_INDEX(thisx) ((thisx)->params & 0xFF)
+#define OBJMINE_GET_PATH_SPEED(thisx) (((thisx)->params >> 8) & 7)
+#define OBJMINE_GET_TYPE(thisx) (((thisx)->params >> 12) & 3)
+
+#define OBJMINE_PARAM(type, linkCount, pathIndex, pathSpeed) (((type) << 0xC) | ((type == OBJMINE_TYPE_PATH) ? ((pathIndex) | ((pathSpeed) << 8)) : (linkCount)))
+#define OBJMINE_PATH_PARAM(pathIndex, pathSpeed) OBJMINE_PARAM(OBJMINE_TYPE_PATH, 0, pathIndex, pathSpeed)
+#define OBJMINE_AIR_PARAM(linkCount) OBJMINE_PARAM(OBJMINE_TYPE_AIR, linkCount, 0, 0)
+#define OBJMINE_WATER_PARAM(linkCount) OBJMINE_PARAM(OBJMINE_TYPE_WATER, linkCount, 0, 0)
+
+#define OBJMINE_CHAIN_MAX 63
+
+typedef enum {
+ /* 0 */ OBJMINE_TYPE_PATH,
+ /* 1 */ OBJMINE_TYPE_AIR,
+ /* 2 */ OBJMINE_TYPE_WATER
+} ObjMineType;
+
+typedef struct {
+ /* 0x00 */ Vec3f x;
+ /* 0x0C */ Vec3f y;
+ /* 0x18 */ Vec3f z;
+} ObjMineMtxF3; // size = 0x24
+
+typedef struct {
+ /* 0x0 */ s16 twist;
+ /* 0x2 */ s16 spin;
+} ObjMineAirLink; // size = 0x4
+
+typedef struct {
+ /* 0x00 */ ObjMineMtxF3 basis;
+ /* 0x24 */ Vec3f translation; // unused
+ /* 0x30 */ Vec2f displacement;
+ /* 0x38 */ Vec2f velocity;
+ /* 0x40 */ f32 restore;
+ /* 0x44 */ f32 drag;
+ /* 0x48 */ f32 knockback;
+ /* 0x4C */ s16 knockbackAngle;
+ /* 0x50 */ f32 swaySize;
+ /* 0x54 */ s16 swayPhase;
+ /* 0x58 */ f32 swayMax;
+ /* 0x5C */ f32 wallCheckDistSq;
+ /* 0x60 */ ObjMineAirLink links[OBJMINE_CHAIN_MAX];
+} ObjMineAirChain; // size = 0x15C
+
+typedef struct {
+ /* 0x00 */ ObjMineMtxF3 basis;
+ /* 0x24 */ Vec3f pos;
+ /* 0x30 */ Vec3f velocity;
+ /* 0x3C */ Vec3f accel;
+} ObjMineWaterLink; // size = 0x48
+
+typedef struct {
+ /* 0x00 */ f32 drag;
+ /* 0x04 */ Vec3f knockbackDir;
+ /* 0x10 */ f32 knockback;
+ /* 0x14 */ f32 swayXZ;
+ /* 0x18 */ f32 swayY;
+ /* 0x1C */ f32 swayMax;
+ /* 0x20 */ s16 swayPhaseVel;
+ /* 0x24 */ f32 restoreXZ;
+ /* 0x28 */ f32 maxY;
+ /* 0x2C */ f32 restY;
+ /* 0x30 */ f32 restoreY;
+ /* 0x34 */ f32 wallCheckDistSq;
+ /* 0x38 */ Vec2f wallEject;
+ /* 0x40 */ s8 touchWall;
+ /* 0x44 */ ObjMineWaterLink links[OBJMINE_CHAIN_MAX];
+} ObjMineWaterChain; // size = 0x11FC
+
typedef struct ObjMine {
/* 0x0000 */ Actor actor;
- /* 0x0144 */ char unk_144[0x60];
+ /* 0x0144 */ ColliderJntSph collider;
+ /* 0x0164 */ ColliderJntSphElement colliderElements[1];
/* 0x01A4 */ ObjMineActionFunc actionFunc;
- /* 0x01A8 */ char unk_1A8[0x120C];
+ /* 0x01A8 */ f32 pathSpeed;
+ /* 0x01AC */ s32 waypointCount;
+ /* 0x01B0 */ s32 waypointIndex;
+ /* 0x01B4 */ Vec3s* waypoints;
+ /* 0x01B8 */ union {
+ ObjMineAirChain air;
+ ObjMineWaterChain water;
+ } chain;
} ObjMine; // size = 0x13B4
#endif // Z_OBJ_MINE_H
diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt
index 3b50554ba..0e9195dfb 100644
--- a/tools/disasm/functions.txt
+++ b/tools/disasm/functions.txt
@@ -11150,53 +11150,53 @@
0x80A80508:("EnEgol_SpawnEffect",),
0x80A80750:("EnEgol_UpdateEffects",),
0x80A80904:("EnEgol_DrawEffects",),
- 0x80A811D0:("func_80A811D0",),
- 0x80A8120C:("func_80A8120C",),
- 0x80A81288:("func_80A81288",),
- 0x80A8131C:("func_80A8131C",),
- 0x80A81384:("func_80A81384",),
- 0x80A8140C:("func_80A8140C",),
- 0x80A8146C:("func_80A8146C",),
- 0x80A81544:("func_80A81544",),
- 0x80A81640:("func_80A81640",),
- 0x80A8164C:("func_80A8164C",),
- 0x80A81684:("func_80A81684",),
- 0x80A81714:("func_80A81714",),
- 0x80A81818:("func_80A81818",),
- 0x80A81868:("func_80A81868",),
- 0x80A819A4:("func_80A819A4",),
- 0x80A81A00:("func_80A81A00",),
- 0x80A81AA4:("func_80A81AA4",),
- 0x80A81B14:("func_80A81B14",),
- 0x80A81B7C:("func_80A81B7C",),
- 0x80A81D70:("func_80A81D70",),
- 0x80A81DEC:("func_80A81DEC",),
- 0x80A81E7C:("func_80A81E7C",),
- 0x80A81FFC:("func_80A81FFC",),
- 0x80A828A8:("func_80A828A8",),
- 0x80A82C28:("func_80A82C28",),
+ 0x80A811D0:("ObjMine_Path_MoveToWaypoint",),
+ 0x80A8120C:("ObjMine_GetUnitVec3f",),
+ 0x80A81288:("ObjMine_GetUnitVec3fNorm",),
+ 0x80A8131C:("ObjMine_Path_SpawnBomb",),
+ 0x80A81384:("ObjMine_AirWater_SpawnBomb",),
+ 0x80A8140C:("ObjMine_AirWater_CheckOC",),
+ 0x80A8146C:("ObjMine_Air_CheckAC",),
+ 0x80A81544:("ObjMine_Water_CheckAC",),
+ 0x80A81640:("ObjMine_AirWater_Noop",),
+ 0x80A8164C:("ObjMine_ReplaceTranslation",),
+ 0x80A81684:("ObjMine_ReplaceRotation",),
+ 0x80A81714:("ObjMine_StepUntilParallel",),
+ 0x80A81818:("ObjMine_UpdateCollider",),
+ 0x80A81868:("ObjMine_Air_InitChain",),
+ 0x80A819A4:("ObjMine_Air_SetCollider",),
+ 0x80A81A00:("ObjMine_Air_SetBasis",),
+ 0x80A81AA4:("ObjMine_Air_SetWorld",),
+ 0x80A81B14:("ObjMine_Air_SetChainXZ",),
+ 0x80A81B7C:("ObjMine_Water_InitChain",),
+ 0x80A81D70:("ObjMine_Water_SetCollider",),
+ 0x80A81DEC:("ObjMine_Water_SetWorld",),
+ 0x80A81E7C:("ObjMine_Water_WallCheck",),
+ 0x80A81FFC:("ObjMine_Water_ApplyForces",),
+ 0x80A828A8:("ObjMine_Water_UpdateLinks",),
+ 0x80A82C28:("ObjMine_Water_UpdateChain",),
0x80A82C5C:("ObjMine_Init",),
0x80A82F58:("ObjMine_Destroy",),
- 0x80A82F84:("func_80A82F84",),
- 0x80A82F98:("func_80A82F98",),
- 0x80A82FA8:("func_80A82FA8",),
- 0x80A82FC8:("func_80A82FC8",),
- 0x80A83214:("func_80A83214",),
- 0x80A83258:("func_80A83258",),
- 0x80A832BC:("func_80A832BC",),
- 0x80A832D0:("func_80A832D0",),
- 0x80A83A74:("func_80A83A74",),
- 0x80A83A88:("func_80A83A88",),
- 0x80A83B14:("func_80A83B14",),
- 0x80A83B28:("func_80A83B28",),
- 0x80A83CEC:("func_80A83CEC",),
- 0x80A83D00:("func_80A83D00",),
- 0x80A83D8C:("ObjMine_Update",),
- 0x80A83E7C:("func_80A83E7C",),
- 0x80A83EA0:("ObjMine_Draw",),
- 0x80A83FBC:("func_80A83FBC",),
- 0x80A84088:("func_80A84088",),
- 0x80A84338:("func_80A84338",),
+ 0x80A82F84:("ObjMine_Path_SetupStationary",),
+ 0x80A82F98:("ObjMine_Path_Stationary",),
+ 0x80A82FA8:("ObjMine_Path_SetupMove",),
+ 0x80A82FC8:("ObjMine_Path_Move",),
+ 0x80A83214:("ObjMine_SetupExplode",),
+ 0x80A83258:("ObjMine_Explode",),
+ 0x80A832BC:("ObjMine_Air_SetupChained",),
+ 0x80A832D0:("ObjMine_Air_Chained",),
+ 0x80A83A74:("ObjMine_Air_SetupStationary",),
+ 0x80A83A88:("ObjMine_Air_Stationary",),
+ 0x80A83B14:("ObjMine_Water_SetupChained",),
+ 0x80A83B28:("ObjMine_Water_Chained",),
+ 0x80A83CEC:("ObjMine_Water_SetupStationary",),
+ 0x80A83D00:("ObjMine_Water_Stationary",),
+ 0x80A83D8C:("ObjMine_Path_Update",),
+ 0x80A83E7C:("ObjMine_AirWater_Update",),
+ 0x80A83EA0:("ObjMine_Path_Draw",),
+ 0x80A83FBC:("ObjMine_DrawExplosion",),
+ 0x80A84088:("ObjMine_Air_Draw",),
+ 0x80A84338:("ObjMine_Water_Draw",),
0x80A84CD0:("ObjPurify_SetSysMatrix",),
0x80A84CF8:("ObjPurify_IsPurified",),
0x80A84D68:("ObjPurify_Init",),
diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt
index 1f28ec606..91efbd8e2 100644
--- a/tools/disasm/variables.txt
+++ b/tools/disasm/variables.txt
@@ -12111,7 +12111,7 @@
0x80A84698:("D_80A84698","f32","",0x4),
0x80A8469C:("D_80A8469C","f32","",0x4),
0x80A846A0:("D_80A846A0","f32","",0x4),
- 0x80A849D0:("D_80A849D0","UNK_TYPE4","",0x4),
+ 0x80A849D0:("sLastLinkAccel","Vec3f[]","",0x4),
0x80A85460:("Obj_Purify_InitVars","UNK_TYPE1","",0x1),
0x80A85480:("ObjPurifyInfoList","UNK_TYPE1","",0x1),
0x80A854A0:("D_80A854A0","UNK_TYPE1","",0x1),