Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ def MatchingFor(*versions):
Object(NonMatching, "ty/source/props/LavaBurner.cpp"),
Object(NonMatching, "ty/source/props/LilyPad.cpp"),
Object(NonMatching, "ty/source/props/Log.cpp"),
Object(NonMatching, "ty/source/props/MobilePlatform.cpp"),
Object(Matching, "ty/source/props/MobilePlatform.cpp"),
Object(NonMatching, "ty/source/props/MorayEel.cpp"),
Object(NonMatching, "ty/source/props/MusicalIcicle.cpp"),
Object(NonMatching, "ty/source/props/OpalCollector.cpp"),
Expand Down
1 change: 1 addition & 0 deletions include/common/MKSceneManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// these are the values used in GameObject::Message
#define MKMSG_Reset -1
#define MKMSG_Deinit -2
#define MKMSG_UNK_3 -3
struct MKMessage {
int unk0;
};
Expand Down
64 changes: 64 additions & 0 deletions include/ty/props/MobilePlatform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifndef MOBILEPLATFORM_H
#define MOBILEPLATFORM_H

#include "ty/props/Platform.h"
#include "ty/CommonGameObjectFlags.h"
#include "ty/Spline.h"
#include "ty/tools.h"

void MobilePlatform_LoadResources(KromeIni* pIni);

struct MobilePlatformDesc : PlatformDesc {
float bobSpeed; // Unofficial: unkF0. Number of bobs per second
float bobHeight; // Unofficial: unkF4

virtual void Init(ModuleInfoBase* pMod, char* pMdlName, char* pDescrName, int _searchMask, int _flags);
};

// Temporary info used while loading a MobilePlatform
struct MobilePlatformLoadInfo {
Tools_WayPoints wayPoints; // Unofficial: unk0
float speed;
float startTime; // Normalised starting position on the path [0, 1]
bool bNonCircular;
};

struct MobilePlatform : Platform {
float turnSpeed;
int bReverse; // Unofficial: unkD8. Set when moving backwards along a non-circular path
int pathFrames; // Unofficial: unkDC. Number of frames to travel the path once
int startFrame; // Unofficial: unkE0
int defaultStartFrame; // Unofficial: unkE4
int frameOffset; // Unofficial: unkE8
int bobFrameOffset; // Unofficial: unkEC
UniformSpline spline; // Unofficial: unkF0
bool bCircular;
bool bMoveOne; // Deactivates once the end of the path is reached
CommonGameObjFlagsComponent gameObjFlags; // Unofficial: unk106

static MobilePlatformLoadInfo mpfLoadInfo; // Unofficial

virtual bool LoadLine(KromeIniLine* pLine);
virtual void LoadDone(void);
virtual void Reset(void);
virtual void Update(void);
virtual void Message(MKMessage* pMsg);
virtual void Init(GameObjDesc* pDesc);
virtual void Deinit(void);
void UpdateMove(void);
void UpdateBob(void);
float GetTime(void);
void SetYaw(float time, float maxTurn); // Unofficial: time, maxTurn

MobilePlatformDesc* GetDesc(void) {
return descr_cast<MobilePlatformDesc*>(pDescriptor);
}

// Returns the number of frames of a full cycle
// Non-circular paths travel forwards then backwards
uint GetCycleFrames(void) {
return pathFrames * (!bCircular + 1);
}
};

#endif // MOBILEPLATFORM_H
2 changes: 2 additions & 0 deletions src/ty/source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "ty/Mist.h"

#include "ty/props/AnimatingProp.h"
#include "ty/props/MobilePlatform.h"
#include "ty/props/Shatterable.h"
#include "ty/props/WeatherProp.h"

Expand Down Expand Up @@ -118,6 +119,7 @@ void Main_LoadStaticResources(void) {
Shatterable_LoadResources(&ini);

Platform_LoadResources(&ini);
MobilePlatform_LoadResources(&ini);
AnimatingProp_LoadResources(&ini);

TyAttributes_LoadResources(&ini);
Expand Down
223 changes: 223 additions & 0 deletions src/ty/source/props/MobilePlatform.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#include "ty/props/MobilePlatform.h"
#include "ty/GameObjectManager.h"
#include "ty/global.h"
#include "common/StdMath.h"
#include "common/System_GC.h"

extern "C" double atan2(double, double);
void GameCamera_AddDynamicCollisionItem(Model* pModel, int subObjectIndex);

static ModuleInfo<MobilePlatform> mobilePlatformModuleInfo; // Unofficial

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable name should not have been guessed as it is a variable in the symbols. So it is not "Unofficial"

MobilePlatformLoadInfo MobilePlatform::mpfLoadInfo;

void MobilePlatformDesc::Init(ModuleInfoBase* pMod, char* pMdlName, char* pDescrName, int _searchMask, int _flags) {
PlatformDesc::Init(pMod, pMdlName, pDescrName, _searchMask, _flags | 0x80);
bobSpeed = 0.0f;
bobHeight = 0.0f;
}

void MobilePlatform_LoadResources(KromeIni* pIni) { // Unofficial: pIni
MobilePlatformDesc defaultMobilePlatformDesc; // Unofficial
defaultMobilePlatformDesc.Init(&mobilePlatformModuleInfo, "", "", GOID_Platform, 1);
LoadDescriptors<MobilePlatformDesc>(pIni, "MobilePlatforms", &defaultMobilePlatformDesc);
}

void MobilePlatform::Init(GameObjDesc* pDesc) {
Platform::Init(pDesc);
mpfLoadInfo.wayPoints.Init();
mpfLoadInfo.bNonCircular = false;
mpfLoadInfo.speed = 7.0f;
mpfLoadInfo.startTime = 0.0f;
bReverse = 0;
bCircular = false;
bMoveOne = false;
pathFrames = 0;
turnSpeed = 0.0f;
startFrame = 0;
bobFrameOffset = 0;
gameObjFlags.Init(GameObjFlags_All);
}

void MobilePlatform::Deinit(void) {
spline.Deinit();
Platform::Deinit();
}

void MobilePlatform::Reset(void) {
Platform::Reset();
gameObjFlags.Reset();
startFrame = defaultStartFrame;
UpdateMove();
}

bool MobilePlatform::LoadLine(KromeIniLine* pLine) {
return gameObjFlags.LoadLine(pLine) ||
LoadLevel_LoadBool(pLine, "bCircular", &bCircular) ||
LoadLevel_LoadFloat(pLine, "turnSpeed", &turnSpeed) ||
LoadLevel_LoadFloat(pLine, "speed", &mpfLoadInfo.speed) ||
LoadLevel_LoadFloat(pLine, "startTime", &mpfLoadInfo.startTime) ||
LoadLevel_LoadBool(pLine, "bMoveOne", &bMoveOne) ||
LoadLevel_LoadBool(pLine, "bNonCircular", &mpfLoadInfo.bNonCircular) ||
mpfLoadInfo.wayPoints.LoadLine(pLine, Tools_WayPoints::LOAD_MODE_1) ||
Platform::LoadLine(pLine);
}

void MobilePlatform::LoadDone(void) {
gameObjFlags.SetDefaultFlags();

GetPos()->Copy(&mpfLoadInfo.wayPoints.vecs[0]);

if (mpfLoadInfo.wayPoints.unk104 > 1) {
if (mpfLoadInfo.wayPoints.unk104 > 2 && !mpfLoadInfo.bNonCircular) {
bCircular = true;
}

// Face the platform towards the second waypoint
float dx = mpfLoadInfo.wayPoints.vecs[1].x - mpfLoadInfo.wayPoints.vecs[0].x; // Unofficial
float dz = mpfLoadInfo.wayPoints.vecs[1].z - mpfLoadInfo.wayPoints.vecs[0].z; // Unofficial
if (Sqr<float>(dx) + Sqr<float>(dz) > Sqr<float>(0.01f) && turnSpeed > 0.0f) {
StaticProp::loadInfo.defaultRot.y = PI2 + (float)atan2(dz, dx);
}

spline.Init(mpfLoadInfo.wayPoints.unk104 + bCircular, true);

for (int i = 0; i < mpfLoadInfo.wayPoints.unk104; i++) { // Unofficial: i
spline.AddNode(&mpfLoadInfo.wayPoints.vecs[i]);
}

if (bCircular) {
spline.AddNode(&mpfLoadInfo.wayPoints.vecs[0]);
spline.MergeEnds();
} else {
spline.mpPoints[0].unk10.SetZero();
spline.mpPoints[spline.mNumPoints - 1].unk10.SetZero();
}

spline.RegulateSpeed();

pathFrames = gDisplay.fps * (spline.unk8 / mpfLoadInfo.speed);
} else {
spline.Init(mpfLoadInfo.wayPoints.unk104, false);
spline.AddNode(&mpfLoadInfo.wayPoints.vecs[0]);
}

startFrame = pathFrames * Clamp<float>(0.0f, mpfLoadInfo.startTime, 1.0f);
defaultStartFrame = startFrame;
frameOffset = startFrame - gb.logicGameCount;

if (GetDesc()->bobSpeed) {
bobFrameOffset = (gDisplay.fps * RandomFR(&gb.mRandSeed, 0.0f, 1.0f)) / GetDesc()->bobSpeed;
}

GameCamera_AddDynamicCollisionItem(pModel, -1);
Platform::LoadDone();
}

void MobilePlatform::Update(void) {
BeginUpdate();
UpdateTilt();
UpdateMove();
UpdateBob();
EndUpdate();
}

void MobilePlatform::Message(MKMessage* pMsg) {
switch (pMsg->unk0) {
case MSG_Activate:
if (!gameObjFlags.CheckFlags(GameObjFlags_Active)) {
frameOffset = startFrame - gb.logicGameCount;
}
break;
case MSG_Deactivate:
if (gameObjFlags.CheckFlags(GameObjFlags_Active)) {
startFrame = (frameOffset + gb.logicGameCount) % GetCycleFrames();
}
break;
case MSG_Resolve:
rider.Resolve();
rider.Attach(this);
for (int i = 0; i < spline.mNumPoints; i++) { // Unofficial: i
rider.ToLocal(&spline.mpPoints[i].mPos);
rider.ToLocalDir(&spline.mpPoints[i].unk10);
}
break;
case MKMSG_UNK_3:
if (turnSpeed > 0.0f && spline.mNumPoints > 1) {
BeginUpdate();
SetYaw(GetTime(), 2.0f * PI);
EndUpdate();
}
break;
}

gameObjFlags.Message(pMsg);
Platform::Message(pMsg);
}

void MobilePlatform::UpdateMove(void) {
if (spline.mNumPoints > 1) {
float time = GetTime(); // Unofficial
*GetPos() = spline.GetPosition(time);
GetPos()->w = 1.0f;
rider.ToWorld(GetPos());

if (turnSpeed > 0.0f) {
SetYaw(time, turnSpeed);
}

if (bMoveOne && gameObjFlags.CheckFlags(GameObjFlags_Active) && time > 0.99f && bReverse == 0) {
MKMessage msg = {MSG_Deactivate}; // Unofficial
Message(&msg);
}
} else {
*GetPos() = spline.mpPoints[0].mPos;
GetPos()->w = 1.0f;
rider.ToWorld(GetPos());
}
}

void MobilePlatform::UpdateBob(void) {
if (GetDesc()->bobHeight > 0.0f && GetDesc()->bobSpeed > 0.0f) {
int bobFrames = gDisplay.fps / GetDesc()->bobSpeed; // Unofficial
float t = (float)((gb.logicGameCount + bobFrameOffset) % bobFrames) / (float)bobFrames; // Unofficial: t
GetPos()->y += _table_sinf((2.0f * PI) * t) * GetDesc()->bobHeight;
}
}

float MobilePlatform::GetTime(void) {
if (!gameObjFlags.CheckFlags(GameObjFlags_Active)) {
frameOffset = startFrame - gb.logicGameCount;
}

float time = (float)((gb.logicGameCount + frameOffset) % GetCycleFrames()) / (float)pathFrames; // Unofficial: time

if (time > 1.0f) {
bReverse = 1;
time = 2.0f - time;
} else {
bReverse = 0;
}

return time;
}

void MobilePlatform::SetYaw(float time, float maxTurn) { // Unofficial: time, maxTurn
if (spline.mNumPoints > 0) {
Vector dir = spline.GetVelocity(time); // Unofficial
rider.ToWorldDir(&dir);
dir.y = 0.0f;

if (dir.MagSquared() > Sqr<float>(0.01f)) {
float yaw = atan2(dir.z, dir.x); // Unofficial
float direction; // Unofficial
if (bReverse == 0) {
direction = 1.0f;
} else {
direction = -1.0f;
}

float angle = GetSmallestAngle(mCurrRot.y, (2.0f * PI) * direction + (PI2 + yaw)); // Unofficial: angle
mCurrRot.y += Clamp<float>(-Abs<float>(maxTurn), angle, Abs<float>(maxTurn));
}
}
}