summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/SOUE01/splits.txt3
-rw-r--r--config/SOUE01/symbols.txt60
-rw-r--r--include/d/a/obj/d_a_obj_dungeon_ship.h2
-rw-r--r--include/d/a/obj/d_a_obj_ivy_rope.h2
-rw-r--r--include/d/d_bzs_types.h33
-rw-r--r--include/d/d_room.h36
-rw-r--r--include/d/t/d_t_sound_area.h4
-rw-r--r--include/egg/math/eggRotation.h2
-rw-r--r--include/m/m_vec.h4
-rw-r--r--include/rvl/MTX/vec.h1
-rw-r--r--include/toBeSorted/actor_on_rail.h85
-rw-r--r--include/toBeSorted/d_path.h182
-rw-r--r--src/REL/d/a/npc/d_a_npc_saltalk.cpp1
-rw-r--r--src/REL/d/a/obj/d_a_obj_dungeon_ship.cpp22
-rw-r--r--src/REL/d/t/d_t_sound_area.cpp8
-rw-r--r--src/toBeSorted/d_path.cpp614
16 files changed, 909 insertions, 150 deletions
diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt
index 5021099d..9e9f9347 100644
--- a/config/SOUE01/splits.txt
+++ b/config/SOUE01/splits.txt
@@ -423,9 +423,12 @@ toBeSorted/salvage_mgr.cpp:
toBeSorted/d_path.cpp:
.text start:0x800A6690 end:0x800A9B00 align:16
+ .data start:0x8050FFB8 end:0x8050FFC8
+ .sdata2 start:0x80579748 end:0x80579780
toBeSorted/d_area.cpp:
.text start:0x800A9B00 end:0x800A9D24 align:16
+ .sdata2 start:0x80579780 end:0x80579788
d/lyt/d2d.cpp:
.text start:0x800A9D30 end:0x800ACAB8 align:16
diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt
index 65a5b5c8..10fd1b5a 100644
--- a/config/SOUE01/symbols.txt
+++ b/config/SOUE01/symbols.txt
@@ -3673,30 +3673,30 @@ fn_800A61C0 = .text:0x800A61C0; // type:function size:0x9C
fn_800A6260 = .text:0x800A6260; // type:function size:0x9C
fn_800A6300 = .text:0x800A6300; // type:function size:0x368
fn_800A6670 = .text:0x800A6670; // type:function size:0x1C
-__ct__11ActorOnRailFv = .text:0x800A6690; // type:function size:0x3C
-__dt__11ActorOnRailFv = .text:0x800A66D0; // type:function size:0x40
-ActorOnRail__clear = .text:0x800A6710; // type:function size:0x18
-fn_800A6730 = .text:0x800A6730; // type:function size:0xBC
-fn_800A67F0 = .text:0x800A67F0; // type:function size:0xBC
-getPntPosForIndex__11ActorOnRailFi = .text:0x800A68B0; // type:function size:0x58
-ActorOnRail__getPosInPath = .text:0x800A6910; // type:function size:0x2B4
-fn_800A6BD0 = .text:0x800A6BD0; // type:function size:0x74
-fn_800A6C50 = .text:0x800A6C50; // type:function size:0x110
-ActorOnRail__set = .text:0x800A6D60; // type:function size:0x10
-dRoom__getPathCount = .text:0x800A6D70; // type:function size:0x18
-init__11ActorOnRailFiii = .text:0x800A6D90; // type:function size:0xC4
-fn_800A6E60 = .text:0x800A6E60; // type:function size:0x68
-fn_800A6ED0 = .text:0x800A6ED0; // type:function size:0xB4
-fn_800A6F90 = .text:0x800A6F90; // type:function size:0x184
-fn_800A7120 = .text:0x800A7120; // type:function size:0x464
-fn_800A7590 = .text:0x800A7590; // type:function size:0x3BC
-fn_800A7950 = .text:0x800A7950; // type:function size:0x324
-fn_800A7C80__11ActorOnRailFiR7mVec3_cf = .text:0x800A7C80; // type:function size:0x4CC
-fn_800A8150 = .text:0x800A8150; // type:function size:0x124
+__ct__7dPath_cFv = .text:0x800A6690; // type:function size:0x3C
+__dt__7dPath_cFv = .text:0x800A66D0; // type:function size:0x40
+clear__7dPath_cFv = .text:0x800A6710; // type:function size:0x18
+getPathPoint__7dPath_cCFl = .text:0x800A6730; // type:function size:0xBC
+getPathBpoint__7dPath_cCFl = .text:0x800A67F0; // type:function size:0xBC
+getPoint__7dPath_cCFl = .text:0x800A68B0; // type:function size:0x58
+getPoint__7dPath_cCFldR7mVec3_c = .text:0x800A6910; // type:function size:0x2B4
+getPointParam__7dPath_cCFll = .text:0x800A6BD0; // type:function size:0x74
+initWithPathId__7dPath_cFllb = .text:0x800A6C50; // type:function size:0x110
+set__7dPath_cFPC4PATHlb = .text:0x800A6D60; // type:function size:0x10
+getPathCount__FP7dRoom_cb = .text:0x800A6D70; // type:function size:0x18
+initWithPathIndex__7dPath_cFllb = .text:0x800A6D90; // type:function size:0xC4
+getMyPathIndex__7dPath_cCFl = .text:0x800A6E60; // type:function size:0x68
+getNextPath__7dPath_cCFP7dPath_c = .text:0x800A6ED0; // type:function size:0xB4
+isLinearSegment__7dPath_cCFl = .text:0x800A6F90; // type:function size:0x184
+getDistanceMovedOnSegment__7dPath_cCFllf = .text:0x800A7120; // type:function size:0x464
+getSpeed__7dPath_cCFlfPf = .text:0x800A7590; // type:function size:0x3BC
+getSegmentTime__7dPath_cFlffPf = .text:0x800A7950; // type:function size:0x324
+getDirection__7dPath_cCFlfR7mVec3_c = .text:0x800A7C80; // type:function size:0x4CC
+extractControlPoints__7dPath_cCFlR7mVec3_cR7mVec3_cR7mVec3_cR7mVec3_c = .text:0x800A8150; // type:function size:0x124
fn_800A8280 = .text:0x800A8280; // type:function size:0x138
fn_800A83C0 = .text:0x800A83C0; // type:function size:0x144
fn_800A8510 = .text:0x800A8510; // type:function size:0x1D0
-fn_800A86E0 = .text:0x800A86E0; // type:function size:0x318
+getVelocity__7dPath_cCFlfR7mVec3_c = .text:0x800A86E0; // type:function size:0x318
fn_800A8A00 = .text:0x800A8A00; // type:function size:0x28C
SpecialActorOnRail__init = .text:0x800A8C90; // type:function size:0x78
fn_800A8D10 = .text:0x800A8D10; // type:function size:0xF4
@@ -3712,15 +3712,15 @@ fn_800A93E0 = .text:0x800A93E0; // type:function size:0x90
fn_800A9470 = .text:0x800A9470; // type:function size:0x74
__ct__15ActorOnRail_ExtFv = .text:0x800A94F0; // type:function size:0x60
__dt__15ActorOnRail_ExtFv = .text:0x800A9550; // type:function size:0x58
-initExt__15ActorOnRail_ExtFiiiiifff = .text:0x800A95B0; // type:function size:0x9C
-fn_800A9650__15ActorOnRail_ExtFv = .text:0x800A9650; // type:function size:0x208
+init__15ActorOnRail_ExtFllUllbfff = .text:0x800A95B0; // type:function size:0x9C
+execute__15ActorOnRail_ExtFv = .text:0x800A9650; // type:function size:0x208
fn_800A9860 = .text:0x800A9860; // type:function size:0x50
-fn_800A98B0 = .text:0x800A98B0; // type:function size:0x58
-fn_800A9910 = .text:0x800A9910; // type:function size:0x48
-fn_800A9960 = .text:0x800A9960; // type:function size:0x8
+getRemainingDistanceOnSegment__15ActorOnRail_ExtCFv = .text:0x800A98B0; // type:function size:0x58
+getNextPointIndex__15ActorOnRail_ExtCFl = .text:0x800A9910; // type:function size:0x48
+getNextPointIndex__15ActorOnRail_ExtCFv = .text:0x800A9960; // type:function size:0x8
fn_800A9970 = .text:0x800A9970; // type:function size:0x58
-fn_800A99D0 = .text:0x800A99D0; // type:function size:0xE0
-setSegment__15ActorOnRail_ExtFUsf = .text:0x800A9AB0; // type:function size:0x50
+getClosestXZPoint__15ActorOnRail_ExtCFRC7mVec3_c = .text:0x800A99D0; // type:function size:0xE0
+setSegment__15ActorOnRail_ExtFlf = .text:0x800A9AB0; // type:function size:0x50
checkPosInAREA = .text:0x800A9B00; // type:function size:0xC4
getAreaForIndexInRoom__Fll = .text:0x800A9BD0; // type:function size:0x78
checkPosInArea__FllRC7mVec3_cPCP4AREA = .text:0x800A9C50; // type:function size:0xD4
@@ -13352,7 +13352,7 @@ fn_80248040 = .text:0x80248040; // type:function size:0x8
fn_80248050 = .text:0x80248050; // type:function size:0x8
fn_80248060 = .text:0x80248060; // type:function size:0x8
__ct__9dAcItem_cFv = .text:0x80248070; // type:function size:0x1FC
-fn_80248270 = .text:0x80248270; // type:function size:0x58
+__dt__13dAcItemBase_cFv = .text:0x80248270; // type:function size:0x58
__dt__21sFState_c<9dAcItem_c>Fv = .text:0x802482D0; // type:function size:0x58
__dt__24sFStateFct_c<9dAcItem_c>Fv = .text:0x80248330; // type:function size:0x6C
__dt__77sStateMgr_c<9dAcItem_c,20sStateMethodUsr_FI_c,12sFStateFct_c,13sStateIDChk_c>Fv = .text:0x802483A0; // type:function size:0xA0
@@ -31017,7 +31017,7 @@ lbl_8050FF78 = .data:0x8050FF78; // type:object size:0x10
lbl_8050FF88 = .data:0x8050FF88; // type:object size:0x10
lbl_8050FF98 = .data:0x8050FF98; // type:object size:0x10
lbl_8050FFA8 = .data:0x8050FFA8; // type:object size:0x10 data:string
-lbl_8050FFB8 = .data:0x8050FFB8; // type:object size:0x10
+__vt__7dPath_c = .data:0x8050FFB8; // type:object size:0x10
@11136 = .data:0x8050FFC8; // type:object size:0xC scope:local data:string
...data.0 = .data:0x8050FFC8; // type:label scope:local
@11137 = .data:0x8050FFD4; // type:object size:0xC scope:local data:string
diff --git a/include/d/a/obj/d_a_obj_dungeon_ship.h b/include/d/a/obj/d_a_obj_dungeon_ship.h
index d8106eb8..259b33d1 100644
--- a/include/d/a/obj/d_a_obj_dungeon_ship.h
+++ b/include/d/a/obj/d_a_obj_dungeon_ship.h
@@ -11,7 +11,7 @@
#include "nw4r/g3d/res/g3d_resfile.h"
#include "s/s_State.hpp"
#include "toBeSorted/actor_event.h"
-#include "toBeSorted/actor_on_rail.h"
+#include "toBeSorted/d_path.h"
#include "toBeSorted/dowsing_target.h"
#include "toBeSorted/d_emitter.h"
#include "toBeSorted/time_proc.h"
diff --git a/include/d/a/obj/d_a_obj_ivy_rope.h b/include/d/a/obj/d_a_obj_ivy_rope.h
index 1f342416..c85d3ff4 100644
--- a/include/d/a/obj/d_a_obj_ivy_rope.h
+++ b/include/d/a/obj/d_a_obj_ivy_rope.h
@@ -12,7 +12,7 @@
#include "m/m_vec.h"
#include "s/s_State.hpp"
#include "toBeSorted/actor_event.h"
-#include "toBeSorted/actor_on_rail.h"
+#include "toBeSorted/d_path.h"
#include "toBeSorted/attention.h"
class dAcOivyRope_c : public dAcObjBase_c {
diff --git a/include/d/d_bzs_types.h b/include/d/d_bzs_types.h
index cf5264b4..638d98e0 100644
--- a/include/d/d_bzs_types.h
+++ b/include/d/d_bzs_types.h
@@ -24,10 +24,10 @@ struct AREA {
// TODO: double check, copied from ss-tools
// Size 0x28
struct BPNT {
- /* 0x00 */ mVec3_c position1;
- /* 0x0C */ mVec3_c position2;
- /* 0x18 */ mVec3_c position3;
- /* 0x24 */ u8 _0x24[4];
+ /* 0x00 */ Vec position;
+ /* 0x0C */ Vec control1;
+ /* 0x18 */ Vec control2;
+ /* 0x24 */ u8 params[4];
};
// TODO: double check, copied from ss-tools
@@ -99,15 +99,24 @@ struct OBJN {
/* 0x00 */ u16 offset;
};
+#define PATH_FLAG_WRAP_AROUND 1
+// 0 = spnt, pnt. 1 = bpnt, sbpt
+#define PATH_FLAG_SPLINE 2
+
// Size 0xC
struct PATH {
/* 0x00 */ u8 field_0x00;
- /* 0x01 */ u8 field_0x01;
+ /* 0x01 */ u8 pathId;
/* 0x02 */ u16 pointStartIndex;
/* 0x04 */ u16 pointCount;
- /* 0x06 */ u8 _0x06[4];
- /* 0x0A */ u8 firstBitIsWrapAround;
+ /* 0x06 */ u16 nextPath;
+ /* 0x08 */ u8 _0x08[2];
+ /* 0x0A */ u8 flags;
/* 0x0B */ u8 _0x0B[1];
+
+ u8 getNextId() const {
+ return nextPath;
+ }
};
// Size 0x24
@@ -135,8 +144,8 @@ struct PLY {
// Size 0x10
struct PNT {
- /* 0x00 */ mVec3_c position;
- /* 0x0C */ u8 _0x0C[4];
+ /* 0x00 */ Vec position;
+ /* 0x0C */ u8 params[4];
};
// Size 0x4
@@ -170,14 +179,12 @@ struct SOBJ {
/* 0x28 */ char name[8];
};
-// Size 0x???
-struct SBPT {
- // ???
-};
// Parsed the same way in ss-tools
typedef PNT SPNT;
typedef PATH SPTH;
+// Similarly accessed
+typedef BPNT SBPT;
// Size 0x14
struct STIF {
diff --git a/include/d/d_room.h b/include/d/d_room.h
index 30696fc2..287ae7c1 100644
--- a/include/d/d_room.h
+++ b/include/d/d_room.h
@@ -149,16 +149,40 @@ public:
mpPath = path;
}
+ const PATH *getPath(s32 off) const {
+ return mpPath + off;
+ }
+
+ const u16 getPathCount() const {
+ return mPathCount;
+ }
+
+ const SPTH *getSpth(s32 off) const {
+ return mpSpth + off;
+ }
+
+ const u16 getSpthCount() const {
+ return mSpthCount;
+ }
+
void setPnt(const PNT *pnt, u16 count) {
mPntCount = count;
mpPnt = pnt;
}
+ const PNT *getPnt(s32 off) const {
+ return mpPnt + off;
+ }
+
void setBpnt(const BPNT *bpnt, u16 count) {
mBpntCount = count;
mpBpnt = bpnt;
}
+ const BPNT *getBpnt(s32 off) const {
+ return mpBpnt + off;
+ }
+
void setSpth(const SPTH *spth, u16 count) {
mSpthCount = count;
mpSpth = spth;
@@ -169,14 +193,22 @@ public:
mpSpnt = spnt;
}
+ const SPNT *getSpnt(s32 off) const {
+ return mpSpnt + off;
+ }
+
void setSbpt(const SBPT *sbpt, u16 count) {
mSbptCount = count;
mpSbpt = sbpt;
}
- void setArea(const AREA *path, u16 count) {
+ const SBPT *getSbpt(s32 off) const {
+ return mpSbpt + off;
+ }
+
+ void setArea(const AREA *area, u16 count) {
mAreaCount = count;
- mpArea = path;
+ mpArea = area;
}
void setPly(const PLY *ply, u16 count) {
diff --git a/include/d/t/d_t_sound_area.h b/include/d/t/d_t_sound_area.h
index 377adee4..0258f846 100644
--- a/include/d/t/d_t_sound_area.h
+++ b/include/d/t/d_t_sound_area.h
@@ -4,7 +4,7 @@
#include "d/t/d_tg.h"
#include "m/m_mtx.h"
#include "m/m_vec.h"
-#include "toBeSorted/actor_on_rail.h"
+#include "toBeSorted/d_path.h"
class dTgSndAr_c : public dTg_c {
public:
@@ -27,7 +27,7 @@ private:
bool checkAlg3(const mVec3_c &pos);
mMtx_c mtx;
- ActorOnRail mRail;
+ dPath_c mRail;
};
#endif
diff --git a/include/egg/math/eggRotation.h b/include/egg/math/eggRotation.h
index 97910e97..b1e1e075 100644
--- a/include/egg/math/eggRotation.h
+++ b/include/egg/math/eggRotation.h
@@ -13,7 +13,7 @@ struct Rotation {
mRot = other.mRot;
}
Rotation &operator=(const Rotation &other) {
- mRot = val;
+ mRot = other.mRot;
return *this;
}
Rotation &operator=(T val) {
diff --git a/include/m/m_vec.h b/include/m/m_vec.h
index 877da205..0fb52aea 100644
--- a/include/m/m_vec.h
+++ b/include/m/m_vec.h
@@ -196,6 +196,10 @@ public:
return mVec3_c(x * f, y * f, z * f);
}
+ friend mVec3_c operator*(f32 f, const mVec3_c &v) {
+ return mVec3_c(v.x * f, v.y * f, v.z * f);
+ }
+
/// @brief Scalar division operator.
mVec3_c operator/(f32 f) const {
f32 r = 1.0f / f;
diff --git a/include/rvl/MTX/vec.h b/include/rvl/MTX/vec.h
index a5b330dc..e220fa40 100644
--- a/include/rvl/MTX/vec.h
+++ b/include/rvl/MTX/vec.h
@@ -15,6 +15,7 @@ f32 PSVECMag(const Vec *);
f32 PSVECDotProduct(const Vec *, const Vec *);
void PSVECCrossProduct(const Vec *, const Vec *, Vec *);
f32 PSVECSquareDistance(const Vec *, const Vec *);
+f32 PSVECDistance(const Vec *, const Vec *);
#define VECAdd PSVECAdd
#define VECSubtract PSVECSubtract
diff --git a/include/toBeSorted/actor_on_rail.h b/include/toBeSorted/actor_on_rail.h
deleted file mode 100644
index b4e1924e..00000000
--- a/include/toBeSorted/actor_on_rail.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef ACTOR_ON_RAIL_H
-#define ACTOR_ON_RAIL_H
-
-#include "common.h"
-#include "m/m_vec.h"
-
-class ActorOnRail {
-private:
- void *mpPathPtr;
- int mRoomIndex;
- u8 mPathSubtype;
-
-public:
- /* 800A6690 */
- ActorOnRail();
- /* 800A66D0 */
- virtual ~ActorOnRail();
-
- /* 800A6D90 */
- bool init(int pathIndex, int roomId, int pathSubtype);
- /* 800A68B0 */
- mVec3_c *getPntPosForIndex(int index);
-
- void fn_800A7C80(int segmentIndex, mVec3_c &vec, f32 segmentFraction);
-};
-
-class ActorOnRail_Ext : public ActorOnRail {
-public:
- ActorOnRail_Ext();
- virtual ~ActorOnRail_Ext();
-
- void setSegment(u16 segmentIndex, f32 segmentFraction);
- bool initExt(int pathIndex, int roomId, int, int pathSegment, int pathSubtype, f32, f32, f32);
- void fn_800A9650();
-
- s32 getSegmentIndex() const {
- return mSegmentIndex;
- }
-
- f32 getSegmentFraction() const {
- return mSegmentFraction;
- }
-
- bool CheckFlag(u32 flag) const {
- return (field_0x1C & flag) != 0;
- }
-
- void ClearFlag(u32 flag) {
- field_0x1C &= ~flag;
- }
-
- void SetFlag(u32 flag) {
- field_0x1C |= flag;
- }
-
- const mVec3_c &getPosition() const {
- return mPosition;
- }
-
- void setSpeed(f32 speed) {
- mSpeed = speed;
- }
-
-
-
-private:
- s32 mSegmentIndex;
- f32 mSegmentFraction;
- f32 mSpeed;
- u32 field_0x1C;
- f32 field_0x20;
- UNKWORD field_0x24;
- mVec3_c mPosition;
-};
-
-class ActorOnRail3 {
- ActorOnRail mPath;
- u16 mSomePntIdx;
- u32 mFlags;
-
-public:
- ActorOnRail3() : mSomePntIdx(0), mFlags(0) {}
-};
-
-#endif
diff --git a/include/toBeSorted/d_path.h b/include/toBeSorted/d_path.h
new file mode 100644
index 00000000..38891596
--- /dev/null
+++ b/include/toBeSorted/d_path.h
@@ -0,0 +1,182 @@
+#ifndef D_PATH_H
+#define D_PATH_H
+
+#include "common.h"
+#include "d/d_bzs_types.h"
+#include "m/m_vec.h"
+
+/**
+ * A dPath_c describes a parametric 3D curve in a stage.
+ * This base path class provides read-only evaluation of the curve.
+ *
+ * A path consists of a number of points that this curve passes through.
+ * Paths can have two ends, or wrap around, connecting the end point with the start point again.
+ * The curve can either linearly connect the points, or it can be implemented as a spline
+ * (splines are used for certain actors where smooth movement is necessary, e.g. Sandship).
+ *
+ * A curve point (or the segment between that point and the next) is identified by an integer point index [0; n-1], and
+ * each segment itself is parametrized by a float parameter in [0.0; 1.0]. We call this parameter "time", though since
+ * no requirements are imposed on the distance between points, there is no guarantee of constant speed across the curve.
+ * ActorOnRail_Ext (todo rename) provides calculations that allow actors to move along the curve with constant speed.
+ */
+class dPath_c {
+private:
+ const PATH *mpPathPtr;
+ s32 mRoomIndex;
+ bool mPathSubtype;
+
+ /** Must only be called for spline paths. Extracts the relevant path points and control points for the given segment. */
+ bool extractControlPoints(
+ s32 pointIndex, mVec3_c &currPos, mVec3_c &currControl2, mVec3_c &nextControl1, mVec3_c &nextPos
+ ) const;
+
+ /**
+ * Check if the given path segment is a linear segment - even splines can have linear segments!
+ * This can speed up some calculations.
+ */
+ bool isLinearSegment(s32 pointIndex) const;
+
+ void set(const PATH *path, s32 roomIndex, bool subtype);
+
+ const PNT *getPathPoint(s32 pointIndex) const;
+ const BPNT *getPathBpoint(s32 pointIndex) const;
+
+public:
+ /* 800A6690 */
+ dPath_c();
+ /* 800A66D0 */
+ virtual ~dPath_c();
+
+ bool isWrapping() const {
+ return mpPathPtr->flags & PATH_FLAG_WRAP_AROUND;
+ }
+
+ bool isLinear() const {
+ return (mpPathPtr->flags & PATH_FLAG_SPLINE) == 0;
+ }
+
+ s32 getNumPoints() const {
+ return mpPathPtr->pointCount;
+ }
+
+ /* 800A6D90 */
+ bool initWithPathId(s32 pathId, s32 roomId, bool pathSubtype);
+ bool initWithPathIndex(s32 pathIndex, s32 roomId, bool pathSubtype);
+
+ void clear();
+
+
+ /** Get a given point that the curve passes through. */
+ const Vec *getPoint(s32 pointIndex) const;
+ /** Evaluates the curve position for the given segment and time. */
+ void getPoint(s32 pointIndex, f64 time, mVec3_c &out) const;
+ /** Extract point param - TODO what is this used for? */
+ u8 getPointParam(s32 pointIndex, s32 param) const;
+
+ /** Return the index of this PATH in the current room. */
+ u16 getMyPathIndex(s32 roomId) const;
+ /** Apparently paths can be linked somehow. */
+ bool getNextPath(dPath_c *next) const;
+
+ /**
+ * Approximate the time for the given distance along the segment.
+ * The return value is clamped to [0.0, 1.0]. If the distance exceeds
+ * the length of this segment, write the excess to the provided pointer.
+ */
+ f32 getSegmentTime(s32 pointIndex, f32 segmentDistance, f32 stepSize, f32 *excess);
+
+ /**
+ * Approximates the distance already traveled on the given segment for the given time.
+ * Calling this with time == 1.0f returns the segment length.
+ * Higher resolution -> higher accuracy.
+ */
+ f32 getDistanceMovedOnSegment(s32 pointIndex, s32 resolution, f32 time) const;
+ /** Calculates the normalized direction of an actor moving along the segment for the given time. */
+ bool getDirection(s32 segmentIndex, f32 time, mVec3_c &result) const;
+ /** Calculates the speed (scalar) of an actor moving along the segment at the given time. */
+ bool getSpeed(s32 segmentIndex, f32 time, f32 *result) const;
+ /** Calculates the velocity (vector) of an actor moving along the segment at the given time. */
+ bool getVelocity(s32 segmentIndex, f32 time, mVec3_c &result) const;
+
+};
+
+/** A stateful "path driver" that moves along a given path with a given speed. */
+class ActorOnRail_Ext {
+public:
+ ActorOnRail_Ext();
+ ~ActorOnRail_Ext();
+
+ f32 getRemainingDistanceOnSegment() const;
+ s32 getClosestXZPoint(const mVec3_c &) const;
+ void setSegment(s32 segmentIndex, f32 segmentTime);
+ bool init(s32 pathIndex, s32 roomId, u32 flags, s32 segmentIndex, bool pathSubtype, f32 segmentTime, f32 speed, f32 unk);
+ s32 execute();
+
+ s32 getSegmentIndex() const {
+ return mSegmentIndex;
+ }
+
+ f32 getSegmentTime() const {
+ return mSegmentTime;
+ }
+
+ bool CheckFlag(u32 flag) const {
+ return (mFlags & flag) != 0;
+ }
+
+ void ClearFlag(u32 flag) {
+ mFlags &= ~flag;
+ }
+
+ void SetFlag(u32 flag) {
+ mFlags |= flag;
+ }
+
+ const mVec3_c &getPosition() const {
+ return mPosition;
+ }
+
+ void setSpeed(f32 speed) {
+ mSpeed = speed;
+ }
+
+ void getDirection(mVec3_c &result) {
+ mPath.getDirection(mSegmentIndex, mSegmentTime, result);
+ }
+
+ bool checkFlag(u32 flags) const {
+ return (mFlags & flags) != 0;
+ }
+
+ void offFlag(u32 flags) {
+ mFlags &= ~flags;
+ }
+
+ void onFlag(u32 flags) {
+ mFlags |= flags;
+ }
+
+private:
+ s32 getNextPointIndex(s32 point) const;
+ s32 getNextPointIndex() const;
+
+ /* 0x00 */ dPath_c mPath;
+ /* 0x10 */ s32 mSegmentIndex;
+ /* 0x14 */ f32 mSegmentTime;
+ /* 0x18 */ f32 mSpeed;
+ /* 0x1C */ u32 mFlags;
+ /* 0x20 */ f32 field_0x20;
+ /* 0x24 */ f32 mSegmentDistance;
+ /* 0x28 */ mVec3_c mPosition;
+};
+
+class ActorOnRail3 {
+ /* 0x00 */ dPath_c mPath;
+ /* 0x10 */ u16 mSomePntIdx;
+ /* 0x14 */ u32 mFlags;
+
+public:
+ ActorOnRail3() : mSomePntIdx(0), mFlags(0) {}
+};
+
+#endif
diff --git a/src/REL/d/a/npc/d_a_npc_saltalk.cpp b/src/REL/d/a/npc/d_a_npc_saltalk.cpp
index 3c608b62..540f34aa 100644
--- a/src/REL/d/a/npc/d_a_npc_saltalk.cpp
+++ b/src/REL/d/a/npc/d_a_npc_saltalk.cpp
@@ -134,7 +134,6 @@ bool dAcNpcSltk_c::checkSomething(mVec3_c pos) const {
} else {
mVec3_c dist = pos - position;
if (dist.squareMagXZ() <= field_0x758 * field_0x758) {
- // TODO reload from position.y here
f32 fDist = field_0x75C + position.y;
f32 fDist2 = position.y;
if (fDist <= fDist2) {
diff --git a/src/REL/d/a/obj/d_a_obj_dungeon_ship.cpp b/src/REL/d/a/obj/d_a_obj_dungeon_ship.cpp
index cdd7d2ec..0a5cdb37 100644
--- a/src/REL/d/a/obj/d_a_obj_dungeon_ship.cpp
+++ b/src/REL/d/a/obj/d_a_obj_dungeon_ship.cpp
@@ -295,7 +295,7 @@ void dAcODungeonShip_c::executeState_Transparency() {
u16 idx = fn_485_1900();
if (idx != 0xFFFF) {
- mPath.setSegment(idx, 1.0f - mPath.getSegmentFraction());
+ mPath.setSegment(idx, 1.0f - mPath.getSegmentTime());
field_0x868 = 900;
}
}
@@ -424,11 +424,12 @@ void dAcODungeonShip_c::eventEnd() {
}
void dAcODungeonShip_c::fn_485_1660() {
- f32 arg = 0.0f;
- f32 arg2 = 0.0001f;
+ f32 time = 0.0f;
+ f32 speed = 0.0f;
+ f32 unk = 0.0001f;
field_0x856 = 1;
- if (mPath.initExt(mPathIdx, roomid, 0, 0, 0, arg, arg, arg2)) {
- mPath.setSegment(0, arg);
+ if (mPath.init(mPathIdx, roomid, 0, 0, false, time, speed, unk)) {
+ mPath.setSegment(0, time);
position = mPath.getPosition();
mOldPosition = mPath.getPosition();
}
@@ -439,12 +440,12 @@ static u32 rot_4000 = 0x4000;
void dAcODungeonShip_c::fn_485_1720() {
mPath.setSpeed(forwardSpeed);
- mPath.fn_800A9650();
+ mPath.execute();
// TODO
position = mPath.getPosition();
mVec3_c tmp;
- mPath.fn_800A7C80(mPath.getSegmentIndex(), tmp, mPath.getSegmentFraction());
+ mPath.getDirection(tmp);
rotation.y = cM::atan2s(tmp.x, tmp.z);
if (mPath.CheckFlag(0x40000000)) {
rotation.y += rot_7fff;
@@ -480,10 +481,9 @@ u32 dAcODungeonShip_c::fn_485_1960() {
mVec3_c dist = link->position - position;
dist.y = 0.0f;
dist.normalizeRS();
- s32 a1 = cLib::targetAngleY(mVec3_c::Zero, v);
- s32 a2 = cLib::targetAngleY(mVec3_c::Zero, dist);
- // okay
- return labs(mAng(mAng(a1) - mAng(a2)));
+ s16 a1 = cLib::targetAngleY(mVec3_c::Zero, v);
+ s16 a2 = cLib::targetAngleY(mVec3_c::Zero, dist);
+ return mAng::abs((s32)(a1 - a2));
}
f32 dAcODungeonShip_c::fn_485_1A50() {
diff --git a/src/REL/d/t/d_t_sound_area.cpp b/src/REL/d/t/d_t_sound_area.cpp
index b89886a7..ca60e02f 100644
--- a/src/REL/d/t/d_t_sound_area.cpp
+++ b/src/REL/d/t/d_t_sound_area.cpp
@@ -25,7 +25,7 @@ int dTgSndAr_c::create() {
mtx.YrotM(rotation.y);
PSMTXInverse(mtx.m, mtx.m);
break;
- case 3: mRail.init(params >> 8 & 0xFF, roomid, 0); break;
+ case 3: mRail.initWithPathIndex(params >> 8 & 0xFF, roomid, 0); break;
}
fBase_c *base = nullptr;
@@ -144,8 +144,10 @@ bool dTgSndAr_c::checkAlg3(const mVec3_c &pos) {
cM3dGCps unk;
// Line between b and c
- mVec3_c b = *mRail.getPntPosForIndex(0);
- mVec3_c c = *mRail.getPntPosForIndex(1);
+ mVec3_c b;
+ b.copyFrom(mRail.getPoint(0));
+ mVec3_c c;
+ c.copyFrom(mRail.getPoint(1));
unk.Set(b, c, mScale.x * 100.0f);
f32 d;
diff --git a/src/toBeSorted/d_path.cpp b/src/toBeSorted/d_path.cpp
new file mode 100644
index 00000000..43b2b896
--- /dev/null
+++ b/src/toBeSorted/d_path.cpp
@@ -0,0 +1,614 @@
+#include "toBeSorted/d_path.h"
+
+#include "common.h"
+#include "d/col/c/c_m3d.h"
+#include "d/d_bzs_types.h"
+#include "d/d_room.h"
+#include "d/d_stage.h"
+#include "egg/math/eggMath.h"
+#include "m/m_vec.h"
+#include "nw4r/math/math_arithmetic.h"
+#include "rvl/MTX/mtx.h"
+
+// BPNT = B-Spline Point?
+
+dPath_c::dPath_c() {
+ clear();
+}
+
+dPath_c::~dPath_c() {}
+
+void dPath_c::clear() {
+ mpPathPtr = nullptr;
+ mRoomIndex = -1;
+ mPathSubtype = 0;
+}
+
+const PNT *dPath_c::getPathPoint(s32 pointIndex) const {
+ if (mpPathPtr == nullptr || pointIndex < 0 || (pointIndex >= mpPathPtr->pointCount || !isLinear())) {
+ return nullptr;
+ }
+ dRoom_c *room = dStage_c::GetInstance()->getRoom(mRoomIndex);
+ if (mPathSubtype != 0) {
+ return &room->getSpnt(mpPathPtr->pointStartIndex)[pointIndex];
+ }
+ return &room->getPnt(mpPathPtr->pointStartIndex)[pointIndex];
+}
+
+const BPNT *dPath_c::getPathBpoint(s32 pointIndex) const {
+ if (mpPathPtr == nullptr || pointIndex < 0 || (pointIndex >= mpPathPtr->pointCount || isLinear())) {
+ return nullptr;
+ }
+ dRoom_c *room = dStage_c::GetInstance()->getRoom(mRoomIndex);
+ if (mPathSubtype != 0) {
+ return &room->getSbpt(mpPathPtr->pointStartIndex)[pointIndex];
+ }
+ return &room->getBpnt(mpPathPtr->pointStartIndex)[pointIndex];
+}
+
+const Vec *dPath_c::getPoint(s32 pointIndex) const {
+ if (isLinear()) {
+ const PNT *result = getPathPoint(pointIndex);
+ if (result != nullptr) {
+ return &result->position;
+ }
+ return nullptr;
+ } else {
+ const BPNT *result = getPathBpoint(pointIndex);
+ if (result != nullptr) {
+ return &result->position;
+ }
+ return nullptr;
+ }
+}
+
+void dPath_c::getPoint(s32 pointIndex, f64 time, mVec3_c &out) const {
+ if (isLinear()) {
+ // Linear path
+ const Vec *pnt = getPoint(pointIndex);
+ const Vec *next;
+ if (pointIndex == mpPathPtr->pointCount - 1) {
+ if (isWrapping()) {
+ // Last point but wrap around, next is 0 again
+ next = getPoint(0);
+ } else {
+ // We are at the end and no wrap - simply return the end position
+ out.x = pnt->x;
+ out.y = pnt->y;
+ out.z = pnt->z;
+ return;
+ }
+ } else {
+ // Regular next point
+ next = getPoint(pointIndex + 1);
+ }
+ // Linearly interpolate
+ mVec3_c tmp;
+ VECSubtract(next, pnt, tmp);
+ VECScale(tmp, tmp, time);
+ VECAdd(tmp, pnt, out);
+ } else {
+ // B-Spline?
+ const BPNT *pnt = getPathBpoint(pointIndex);
+ const Vec *nextPos;
+ const Vec *nextCtrl;
+ if (pointIndex == mpPathPtr->pointCount - 1) {
+ if (isWrapping()) {
+ // Last point but wrap around, next is 0 again
+ const BPNT *next = getPathBpoint(0);
+ nextPos = &next->position;
+ nextCtrl = &next->control1;
+ } else {
+ // We are at the end and no wrap - simply return the end position
+ out.x = pnt->position.x;
+ out.y = pnt->position.y;
+ out.z = pnt->position.z;
+ return;
+ }
+ } else {
+ // Regular next point
+ const BPNT *next = getPathBpoint(pointIndex + 1);
+ nextPos = &next->position;
+ nextCtrl = &next->control1;
+ }
+
+ if (pnt->position.x == pnt->control2.x && pnt->position.y == pnt->control2.y &&
+ pnt->position.z == pnt->control2.z && nextPos->x == nextCtrl->x && nextPos->y == nextCtrl->y &&
+ nextPos->z == nextCtrl->z) {
+ // Point is unaffected by control points, so linearly interpolate
+ mVec3_c tmp;
+ VECSubtract(nextPos, &pnt->position, tmp);
+ VECScale(tmp, tmp, time);
+ VECAdd(tmp, &pnt->position, out);
+ } else {
+ // Cubic B-Spline?
+ f32 f = time;
+ f32 f1 = 1.0f - f;
+ f32 fISq = f1 * f1;
+ f32 fSq = f * f;
+
+ f32 f3 = fISq * f1;
+ f32 f5 = 3.0f * fISq * f;
+ f32 f4 = 3.0f * f1 * fSq;
+ f32 f2 = fSq * f;
+
+ out.x = f3 * pnt->position.x + f5 * pnt->control2.x + f4 * nextCtrl->x + f2 * nextPos->x;
+ out.y = f3 * pnt->position.y + f5 * pnt->control2.y + f4 * nextCtrl->y + f2 * nextPos->y;
+ out.z = f3 * pnt->position.z + f5 * pnt->control2.z + f4 * nextCtrl->z + f2 * nextPos->z;
+ }
+ }
+}
+
+u8 dPath_c::getPointParam(s32 pointIndex, s32 param) const {
+ if (isLinear()) {
+ const PNT *result = getPathPoint(pointIndex);
+ if (result != nullptr) {
+ return result->params[param];
+ }
+ return 0;
+ } else {
+ const BPNT *result = getPathBpoint(pointIndex);
+ if (result != nullptr) {
+ return result->params[param];
+ }
+ return 0;
+ }
+}
+
+bool dPath_c::initWithPathId(s32 pathId, s32 roomId, bool pathSubtype) {
+ dRoom_c *room = dStage_c::GetInstance()->getRoom(roomId);
+ s32 pathIndex = -1;
+ if (!pathSubtype) {
+ for (s32 i = 0; i < room->getPathCount(); i++) {
+ if (room->getPath(i)->pathId == pathId) {
+ pathIndex = i;
+ break;
+ }
+ }
+ } else {
+ for (s32 i = 0; i < room->getSpthCount(); i++) {
+ if (room->getSpth(i)->pathId == pathId) {
+ pathIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (pathIndex == -1) {
+ return false;
+ }
+ return initWithPathIndex(pathIndex, roomId, pathSubtype);
+}
+
+void dPath_c::set(const PATH *path, s32 roomIndex, bool subtype) {
+ mpPathPtr = path;
+ mRoomIndex = roomIndex;
+ mPathSubtype = subtype;
+}
+
+static s32 getPathCount(dRoom_c *room, bool isSpth) {
+ s32 ret = room->getPathCount();
+ if (isSpth) {
+ ret = room->getSpthCount();
+ }
+ return ret;
+}
+
+bool dPath_c::initWithPathIndex(s32 pathIndex, s32 roomId, bool pathSubtype) {
+ dRoom_c *room = dStage_c::GetInstance()->getRoom(roomId);
+ s32 pathCount = getPathCount(room, pathSubtype);
+ // @bug ineffective null check
+ if (room == nullptr || pathIndex < 0 || pathIndex >= pathCount) {
+ clear();
+ return false;
+ } else {
+ if (pathSubtype) {
+ set(room->getSpth(pathIndex), roomId, pathSubtype);
+ } else {
+ set(room->getPath(pathIndex), roomId, pathSubtype);
+ }
+ return true;
+ }
+}
+
+u16 dPath_c::getMyPathIndex(s32 roomId) const {
+ dRoom_c *room = dStage_c::GetInstance()->getRoom(roomId);
+ const PATH *base = room->getPath(0);
+ if (mPathSubtype) {
+ base = room->getSpth(0);
+ }
+ return (mpPathPtr - base);
+}
+
+bool dPath_c::getNextPath(dPath_c *next) const {
+ if (mpPathPtr->getNextId() == 0xFF) {
+ next->clear();
+ return false;
+ }
+ dRoom_c *room = dStage_c::GetInstance()->getRoom(mRoomIndex);
+ u8 nextId = mpPathPtr->getNextId();
+ if (mPathSubtype) {
+ next->set(room->getSpth(nextId), mRoomIndex, mPathSubtype);
+ } else {
+ next->set(room->getPath(nextId), mRoomIndex, mPathSubtype);
+ }
+ return true;
+}
+
+bool dPath_c::isLinearSegment(s32 pointIndex) const {
+ if (isLinear()) {
+ // Non-spline paths are always linear
+ return true;
+ }
+ const BPNT *pnt = getPathBpoint(pointIndex);
+ mVec3_c pos;
+ mVec3_c control;
+ mVec3_c nextCtrl;
+ mVec3_c nextPos;
+ if (pointIndex == mpPathPtr->pointCount - 1) {
+ if (isWrapping()) {
+ // Last point but wrap around, next is 0 again
+ const BPNT *next = getPathBpoint(0);
+ nextPos.copyFrom(&next->position);
+ nextCtrl.copyFrom(&next->control1);
+ } else {
+ return false;
+ }
+ } else {
+ // Regular next point
+ const BPNT *next = getPathBpoint(pointIndex + 1);
+ nextPos.copyFrom(&next->position);
+ nextCtrl.copyFrom(&next->control1);
+ }
+
+ pos.copyFrom(&pnt->position);
+ control.copyFrom(&pnt->control2);
+
+ // Spline paths are trivially linear if the respective control points
+ // are at the same position
+ if (pos.x == control.x && pos.y == control.y && pos.z == control.z && nextCtrl.x == nextPos.x &&
+ nextCtrl.y == nextPos.y && nextCtrl.z == nextPos.z) {
+ return true;
+ }
+ return false;
+}
+
+f32 dPath_c::getDistanceMovedOnSegment(s32 pointIndex, s32 resolution, f32 time) const {
+ if (isLinearSegment(pointIndex)) {
+ // On linear segments, our current distance is simply the distance between
+ // the start point of this segment
+ mVec3_c point;
+ getPoint(pointIndex, time, point);
+ const Vec *start = getPoint(pointIndex);
+ return VECDistance(start, point);
+ }
+
+ s32 numIterations = resolution * 2;
+ mVec3_c currPos;
+ mVec3_c currControl;
+ mVec3_c nextControl;
+ mVec3_c nextPos;
+ if (!extractControlPoints(pointIndex, currPos, currControl, nextControl, nextPos)) {
+ // Extracting control points only fails if we have reached our end position
+ // and aren't wrapping, so we haven't moved off the last point.
+ return 0.0f;
+ }
+
+ mVec3_c v1, v2, v3;
+ v1 = -currPos + nextPos + 3.f * (currControl - nextControl);
+ v2 = 3.f * (currPos + nextControl) - 6.f * currControl;
+ v3 = 3.f * (currControl - currPos);
+
+ f32 v1Sq = v1.dot(v1);
+ f32 v1Dotv2 = v1.dot(v2);
+ f32 v1Dotv3 = v1.dot(v3);
+ f32 v2Sq = v2.dot(v2);
+ f32 v2Dotv3 = v2.dot(v3);
+ f32 v3Sq = v3.dot(v3);
+
+ f32 a = v1Sq * 9.f; // t4
+ f32 b = v1Dotv2 * 12.f; // t3
+ f32 c = v1Dotv3 * 6.0f + v2Sq * 4.f; // t2
+ f32 d = v2Dotv3 * 4.0f; // t1
+ f32 e = v3Sq; // t0
+
+ f32 t = 0.0f;
+ f32 f4 = 0.0f;
+ f32 dt = time / numIterations;
+ for (s32 i = 0; i < numIterations + 1; i++, t += dt) {
+ f32 factor;
+ if (i == 0 || i == numIterations) {
+ factor = 1.0f;
+ } else if ((i & 1) != 0) {
+ factor = 4.0f;
+ } else {
+ factor = 2.0f;
+ }
+
+ // TODO: Oh no
+ /*
+ fVar6 = fVar5 * fVar5 * fVar5;
+ fVar6 = z3 * z3 + x3 * x3 + y3 * y3 +
+ (z2 * z3 + x2 * x3 + y2 * y3) * 4.0 * fVar5 +
+ ((z1 * z3 + x1 * x3 + y1 * y3) * 6.0 + (z2 * z2 + x2 * x2 + y2 * y2) * 4.0) *
+ fVar5 * fVar5 +
+ (z1 * z1 + x1 * x1 + y1 * y1) * 9.0 * fVar6 * fVar5 +
+ (z1 * z2 + x1 * x2 + y1 * y2) * 12.0 * fVar6;
+ */
+ f32 f = (a * (t * t * t * t)) + (b * (t * t * t)) + (c * (t * t)) + (d * (t)) + e;
+ // f32 f = v3Sq + (v2Dotv3 * 4.0f * t) + ((v1Dotv3 * 6.0f + v2Sq * 4.0f) * f5Sq) + (v1Sq * 9.0f * f5Pow4) +
+ // (v1Dotv2 * 12.0f * f5Cube);
+ if (f > 0.0f) {
+ f4 += factor * nw4r::math::FSqrt(f);
+ }
+ }
+ f4 *= (dt / 3.0f);
+ return f4;
+}
+
+bool dPath_c::getSpeed(s32 segmentIndex, f32 time, f32 *result) const {
+ // TODO
+ return false;
+}
+
+f32 dPath_c::getSegmentTime(s32 pointIndex, f32 segmentDistance, f32 stepSize, f32 *excess) {
+ // TODO
+ return 0.0f;
+}
+
+bool dPath_c::getDirection(s32 pointIndex, f32 time, mVec3_c &out) const {
+ if (isLinear()) {
+ // Linear path
+ const Vec *pnt = getPoint(pointIndex);
+ const Vec *next;
+ if (pointIndex == mpPathPtr->pointCount - 1) {
+ if (!isWrapping()) {
+ // We are at the end and no wrap - simply return the end position
+ out.x = 0.0f;
+ out.y = 0.0f;
+ out.z = 0.0f;
+ return false;
+ } else {
+ // Last point but wrap around, next is 0 again
+ next = getPoint(0);
+ }
+ } else {
+ // Regular next point
+ next = getPoint(pointIndex + 1);
+ }
+ f32 distance = VECDistance(pnt, next);
+ if (cM3d_IsZero(distance)) {
+ out.x = 0.0f;
+ out.y = 0.0f;
+ out.z = 0.0f;
+ return false;
+ } else {
+ VECSubtract(next, pnt, out);
+ VECScale(out, out, 1.0f / distance);
+ return true;
+ }
+ } else {
+ mVec3_c currPos;
+ mVec3_c currControl;
+ mVec3_c nextControl;
+ mVec3_c nextPos;
+ if (!extractControlPoints(pointIndex, currPos, currControl, nextControl, nextPos)) {
+ out.x = 0.0f;
+ out.y = 0.0f;
+ out.z = 0.0f;
+ return false;
+ }
+
+ if (currPos.x == currControl.x && currPos.y == currControl.y && currPos.z == currControl.z) {
+ // TODO: why is the other point not checked?
+ out = nextPos - currPos;
+ } else {
+ // TODO: Oh no
+ mVec3_c a, b, c;
+ a = -currPos + nextPos + 3.f * (currControl - nextControl);
+ b = 3.f * (currPos + nextControl) - 6.f * currControl;
+ c = 3.f * (currControl - currPos);
+
+ out = a * (time * time * 3.f) + b * (time * 2.f) + c;
+ }
+
+ f32 mag = out.mag();
+ if (cM3d_IsZero(mag)) {
+ out.x = 0.0f;
+ out.y = 0.0f;
+ out.z = 0.0f;
+ return false;
+ } else {
+ out *= 1.0f / mag;
+ return true;
+ }
+ }
+}
+
+bool dPath_c::extractControlPoints(
+ s32 pointIndex, mVec3_c &currPos, mVec3_c &currControl2, mVec3_c &nextControl1, mVec3_c &nextPos
+) const {
+ const BPNT *pnt = getPathBpoint(pointIndex);
+ if (pointIndex == mpPathPtr->pointCount - 1) {
+ if (isWrapping()) {
+ // Last point but wrap around, next is 0 again
+ const BPNT *next = getPathBpoint(0);
+ nextPos.copyFrom(&next->position);
+ nextControl1.copyFrom(&next->control1);
+ } else {
+ // We are at the end and no wrap - cannot extract control points
+ return false;
+ }
+ } else {
+ const BPNT *next = getPathBpoint(pointIndex + 1);
+ nextPos.copyFrom(&next->position);
+ nextControl1.copyFrom(&next->control1);
+ }
+
+ currPos.copyFrom(&pnt->position);
+ currControl2.copyFrom(&pnt->control2);
+
+ return true;
+}
+
+bool dPath_c::getVelocity(s32 segmentIndex, f32 t, mVec3_c &result) const {
+ if (isLinear()) {
+ return false;
+ }
+ mVec3_c p0, p1, p2, p3;
+ if (!extractControlPoints(segmentIndex, p0, p1, p2, p3)) {
+ return false;
+ }
+
+ // First Derivative Cubic Bezier
+ // [3t^2 *(-p0 + p3 * 3(p1 - p2))] + [2t(3(p0 + p2) - 6p1)] + [3(p1 - p0)]
+ mVec3_c a, b, c;
+ a = (-p0 + p3) + 3.f * (p1 - p2);
+ b = 3.f * (p0 + p2) - 6.f * p1;
+ c = 3.f * (p1 - p0);
+ result = a * (t * t * 3.f) + b * (t * 2.f) + c;
+ return true;
+}
+
+ActorOnRail_Ext::ActorOnRail_Ext() {
+ mSegmentIndex = 0;
+ mSegmentTime = 0.0f;
+ mSpeed = 0.0f;
+ mFlags = 0;
+ field_0x20 = 0.01f;
+ mSegmentDistance = 0.0f;
+ mPosition.set(0.0f, 0.0f, 0.0f);
+}
+
+ActorOnRail_Ext::~ActorOnRail_Ext() {}
+
+bool ActorOnRail_Ext::init(
+ s32 pathIndex, s32 roomId, u32 flags, s32 segmentIndex, bool pathSubtype, f32 segmentTime, f32 speed, f32 unk
+) {
+ if (!mPath.initWithPathIndex(pathIndex, roomId, pathSubtype)) {
+ return false;
+ }
+ setSegment(segmentIndex, segmentTime);
+ mFlags = flags;
+ field_0x20 = unk;
+ mSpeed = speed;
+ return true;
+}
+
+s32 ActorOnRail_Ext::execute() {
+ s32 ret = 0;
+ // Move along the curve distance with the defined speed, then fix the curve segment and time
+ // until the curve parameters match the distance.
+ mSegmentDistance += mSpeed;
+ if (mSpeed < 0.0f) {
+ while (true) {
+ if (!(mSegmentDistance < 0.0f)) {
+ break;
+ }
+ ret = 1;
+ mSegmentIndex--;
+ if (mSegmentIndex < 0) {
+ if (mPath.isWrapping()) {
+ ret = 4;
+ mSegmentIndex = mPath.getNumPoints() - 1;
+ } else {
+ mSegmentIndex = 0;
+ ret = 2;
+ mSegmentDistance = 0.0f;
+ if (checkFlag(0x1)) {
+ ret = 3;
+ offFlag(0x40000000);
+ mSpeed *= -1.0f;
+ }
+ break;
+ }
+ // Add curve length
+ }
+ mSegmentDistance += mPath.getDistanceMovedOnSegment(mSegmentIndex, 8, 1.0f);
+ }
+ } else {
+ // While our target distance is longer than the current segment...
+ while (true) {
+ f32 segmentLength = mPath.getDistanceMovedOnSegment(mSegmentIndex, 8, 1.0f);
+ if (!(mSegmentDistance > segmentLength)) {
+ break;
+ }
+ ret = 1;
+ mSegmentIndex++;
+ if (mSegmentIndex >= mPath.getNumPoints()) {
+ if (mPath.isWrapping()) {
+ ret = 4;
+ mSegmentIndex = 0;
+ } else {
+ mSegmentIndex = mPath.getNumPoints() - 1;
+ ret = 2;
+ mSegmentDistance = segmentLength;
+ if (checkFlag(0x1)) {
+ ret = 3;
+ onFlag(0x40000000);
+ mSpeed *= -1.0f;
+ }
+ break;
+ }
+ }
+ mSegmentDistance -= segmentLength;
+ }
+ }
+
+ f32 dummy = 0.0f;
+ // Finally query the time
+ mSegmentTime = mPath.getSegmentTime(mSegmentIndex, mSegmentDistance, field_0x20, &dummy);
+ // and update the position
+ mPath.getPoint(mSegmentIndex, mSegmentTime, mPosition);
+ return ret;
+}
+
+f32 ActorOnRail_Ext::getRemainingDistanceOnSegment() const {
+ if (mSpeed < 0.0f) {
+ return mSegmentDistance;
+ } else {
+ return mPath.getDistanceMovedOnSegment(mSegmentIndex, 8, 1.0f) - mSegmentDistance;
+ }
+}
+
+s32 ActorOnRail_Ext::getNextPointIndex(s32 point) const {
+ if (mSpeed > 0.0f) {
+ if (point == mPath.getNumPoints() - 1) {
+ if (mPath.isWrapping()) {
+ return 0;
+ } else {
+ return point;
+ }
+ }
+ return point + 1;
+ }
+ return point;
+}
+
+s32 ActorOnRail_Ext::getNextPointIndex() const {
+ return getNextPointIndex(mSegmentIndex);
+}
+
+s32 ActorOnRail_Ext::getClosestXZPoint(const mVec3_c &pos) const {
+ f32 max = EGG::Math<f32>::maxNumber();
+ s32 best = 0;
+ mVec3_c c;
+ for (s32 i = 0; i < mPath.getNumPoints(); i++) {
+ const Vec *point = mPath.getPoint(i);
+ c = *reinterpret_cast<const mVec3_c *>(point) - pos;
+ f32 dist = c.squareMagXZ();
+ if (max > dist) {
+ best = i;
+ max = dist;
+ }
+ }
+
+ return best;
+}
+
+void ActorOnRail_Ext::setSegment(s32 segmentIndex, f32 segmentTime) {
+ mSegmentIndex = segmentIndex;
+ mSegmentTime = segmentTime;
+ mSegmentDistance = mPath.getDistanceMovedOnSegment(mSegmentIndex, 8, segmentTime);
+ mPath.getPoint(mSegmentIndex, mSegmentTime, mPosition);
+}