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::maxNumber(); + s32 best = 0; + mVec3_c c; + for (s32 i = 0; i < mPath.getNumPoints(); i++) { + const Vec *point = mPath.getPoint(i); + c = *reinterpret_cast(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); +}