Compare commits

...

20 Commits

Author SHA1 Message Date
bb8d4f2eb7 impl mover and rotator loadconfigdata 2024-02-18 00:55:20 -06:00
7e42efa82e Merge branch 'main' into movingPlatformWork 2024-02-17 03:28:33 -06:00
d4ca011466 fix tests 2024-02-17 03:21:41 -06:00
52228ece16 Make the test have the capability of passing if the code is correct
Before the platform was not moving and the positions werent; getting updated
2024-02-17 02:41:17 -06:00
92696606e1 Expand tests to cover path types and edge cases 2024-02-17 02:14:36 -06:00
c35ab1f9e3 Add test logs, fix seg faults in tests 2024-02-12 10:43:58 -06:00
David Markowitz
1ae57dc6e8 Fix one failing test 2024-02-10 19:53:20 -08:00
David Markowitz
944d3e1bac branch back to working state 2024-02-10 19:23:35 -08:00
David Markowitz
790505ba6f Merge branch 'main' into movingPlatformWork 2024-02-10 19:10:55 -08:00
David Markowitz
af8bc2c458 refactor to class style and not c style 2023-08-05 01:21:59 -07:00
David Markowitz
42de987e25 Keep updating 2023-08-04 00:54:48 -07:00
David Markowitz
e6fe2edfae better tests for advancing waypoint 2023-08-03 21:37:08 -07:00
David Markowitz
e31b6ca54b Merge branch 'main' into movingPlatformWork 2023-08-03 20:32:26 -07:00
David Markowitz
2252088e1b TDD 2023-08-03 01:50:34 -07:00
David Markowitz
c293b7a9d7 moving platform work 2023-08-02 00:02:02 -07:00
David Markowitz
4336cb7f50 quick notes 2023-08-01 01:36:24 -07:00
David Markowitz
43657324e9 Further work on subcomponents
serialization is improved, next is re-implementing the actual functionality.
2023-08-01 01:19:07 -07:00
David Markowitz
ba409b6ee2 add serialization test 2023-07-31 18:38:26 -07:00
David Markowitz
a7eb28279e finish construction serialization test 2023-07-31 18:36:14 -07:00
David Markowitz
3cf460cc52 throw away the old impl 2023-07-31 02:13:19 -07:00
23 changed files with 1256 additions and 486 deletions

View File

@@ -2,6 +2,7 @@
#define __DLUASSERT__H__ #define __DLUASSERT__H__
#include <assert.h> #include <assert.h>
#define _DEBUG
#ifdef _DEBUG #ifdef _DEBUG
# define DluAssert(expression) assert(expression) # define DluAssert(expression) assert(expression)

View File

@@ -31,6 +31,7 @@ constexpr const char* GetFileNameFromAbsolutePath(const char* path) {
// is used in the instruction instead of the start of the absolute path. // is used in the instruction instead of the start of the absolute path.
#define LOG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->Log(str, message, ##__VA_ARGS__); } while(0) #define LOG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->Log(str, message, ##__VA_ARGS__); } while(0)
#define LOG_DEBUG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->LogDebug(str, message, ##__VA_ARGS__); } while(0) #define LOG_DEBUG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->LogDebug(str, message, ##__VA_ARGS__); } while(0)
#define LOG_TEST(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->LogDebug(str, message, ##__VA_ARGS__); Game::logger->Flush();} while(0)
// Writer class for writing data to files. // Writer class for writing data to files.
class Writer { class Writer {

View File

@@ -1,16 +0,0 @@
#ifndef __EMOVEMENTPLATFORMSTATE__H__
#define __EMOVEMENTPLATFORMSTATE__H__
#include <cstdint>
/**
* The different types of platform movement state, supposedly a bitmap
*/
enum class eMovementPlatformState : uint32_t
{
Moving = 0b00010,
Stationary = 0b11001,
Stopped = 0b01100
};
#endif //!__EMOVEMENTPLATFORMSTATE__H__

View File

@@ -40,6 +40,7 @@
#include "CDRailActivatorComponent.h" #include "CDRailActivatorComponent.h"
#include "CDRewardCodesTable.h" #include "CDRewardCodesTable.h"
#include "CDPetComponentTable.h" #include "CDPetComponentTable.h"
#include "CDMovingPlatformComponentTable.h"
#include <exception> #include <exception>
@@ -110,6 +111,7 @@ DEFINE_TABLE_STORAGE(CDScriptComponentTable);
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable); DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
DEFINE_TABLE_STORAGE(CDVendorComponentTable); DEFINE_TABLE_STORAGE(CDVendorComponentTable);
DEFINE_TABLE_STORAGE(CDZoneTableTable); DEFINE_TABLE_STORAGE(CDZoneTableTable);
DEFINE_TABLE_STORAGE(CDMovingPlatformComponentTable);
void CDClientManager::LoadValuesFromDatabase() { void CDClientManager::LoadValuesFromDatabase() {
if (!CDClientDatabase::isConnected) throw CDClientConnectionException(); if (!CDClientDatabase::isConnected) throw CDClientConnectionException();

View File

@@ -0,0 +1,48 @@
#include "CDMovingPlatformComponentTable.h"
CDMovingPlatformComponentTable::CDMovingPlatformComponentTable() {
auto& entries = GetEntriesMutable();
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MovingPlatforms");
while (!tableData.eof()) {
CDMovingPlatformTableEntry entry;
entry.platformIsSimpleMover = tableData.getIntField("platformIsSimpleMover", 0) == 1;
entry.platformStartAtEnd = tableData.getIntField("platformStartAtEnd", 0) == 1;
entry.platformMove.x = tableData.getFloatField("platformMoveX", 0.0f);
entry.platformMove.y = tableData.getFloatField("platformMoveY", 0.0f);
entry.platformMove.z = tableData.getFloatField("platformMoveZ", 0.0f);
entry.moveTime = tableData.getFloatField("platformMoveTime", -1.0f);
DluAssert(entries.insert(std::make_pair(tableData.getIntField("id", -1), entry)).second);
tableData.nextRow();
}
}
void CDMovingPlatformComponentTable::CachePlatformEntry(ComponentID id) {
auto& entries = GetEntriesMutable();
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM MovingPlatforms WHERE id = ?;");
query.bind(1, static_cast<int32_t>(id));
auto tableData = query.execQuery();
while (!tableData.eof()) {
CDMovingPlatformTableEntry entry;
entry.platformIsSimpleMover = tableData.getIntField("platformIsSimpleMover", 0) == 1;
entry.platformStartAtEnd = tableData.getIntField("platformStartAtEnd", 0) == 1;
entry.platformMove.x = tableData.getFloatField("platformMoveX", 0.0f);
entry.platformMove.y = tableData.getFloatField("platformMoveY", 0.0f);
entry.platformMove.z = tableData.getFloatField("platformMoveZ", 0.0f);
entry.moveTime = tableData.getFloatField("platformMoveTime", -1.0f);
DluAssert(entries.insert(std::make_pair(tableData.getIntField("id", -1), entry)).second);
tableData.nextRow();
}
}
const std::optional<CDMovingPlatformTableEntry> CDMovingPlatformComponentTable::GetPlatformEntry(ComponentID id) {
auto& entries = GetEntriesMutable();
auto itr = entries.find(id);
if (itr == entries.end()) {
CachePlatformEntry(id);
itr = entries.find(id);
}
return itr != entries.end() ? std::make_optional<CDMovingPlatformTableEntry>(itr->second) : std::nullopt;
}

View File

@@ -0,0 +1,25 @@
#ifndef __CDMOVINGPLATFORMCOMPONENTTABLE__H__
#define __CDMOVINGPLATFORMCOMPONENTTABLE__H__
#include "CDTable.h"
#include "NiPoint3.h"
#include <optional>
typedef uint32_t ComponentID;
struct CDMovingPlatformTableEntry {
NiPoint3 platformMove;
float moveTime;
bool platformIsSimpleMover;
bool platformStartAtEnd;
};
class CDMovingPlatformComponentTable : public CDTable<CDMovingPlatformComponentTable, std::map<ComponentID, CDMovingPlatformTableEntry>> {
public:
CDMovingPlatformComponentTable();
void CachePlatformEntry(ComponentID id);
const std::optional<CDMovingPlatformTableEntry> GetPlatformEntry(ComponentID id);
};
#endif //!__CDMOVINGPLATFORMCOMPONENTTABLE__H__

View File

@@ -16,6 +16,7 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
"CDLevelProgressionLookupTable.cpp" "CDLevelProgressionLookupTable.cpp"
"CDLootMatrixTable.cpp" "CDLootMatrixTable.cpp"
"CDLootTableTable.cpp" "CDLootTableTable.cpp"
"CDMovingPlatformComponentTable.cpp"
"CDMissionEmailTable.cpp" "CDMissionEmailTable.cpp"
"CDMissionNPCComponentTable.cpp" "CDMissionNPCComponentTable.cpp"
"CDMissionsTable.cpp" "CDMissionsTable.cpp"

View File

@@ -315,7 +315,7 @@ Entity* EntityManager::GetSpawnPointEntity(const std::string& spawnName) const {
// Check if the spawn point entity is valid just in case // Check if the spawn point entity is valid just in case
return spawnPoint == m_SpawnPoints.end() ? nullptr : GetEntity(spawnPoint->second); return spawnPoint == m_SpawnPoints.end() ? nullptr : GetEntity(spawnPoint->second);
} }
#include <thread>
const std::unordered_map<std::string, LWOOBJID>& EntityManager::GetSpawnPointEntities() const { const std::unordered_map<std::string, LWOOBJID>& EntityManager::GetSpawnPointEntities() const {
return m_SpawnPoints; return m_SpawnPoints;
} }

View File

@@ -12,109 +12,402 @@
#include "GameMessages.h" #include "GameMessages.h"
#include "CppScripts.h" #include "CppScripts.h"
#include "SimplePhysicsComponent.h" #include "SimplePhysicsComponent.h"
#include "CDClientManager.h"
#include "CDMovingPlatformComponentTable.h"
#include "Zone.h" #include "Zone.h"
#include "StringifiedEnum.h"
MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) { //------------- PlatformSubComponent begin --------------
mPosition = {};
mState = eMovementPlatformState::Stopped; PlatformSubComponent::PlatformSubComponent(MovingPlatformComponent* parentComponent) {
mDesiredWaypointIndex = 0; // -1; DluAssert(parentComponent != nullptr);
mInReverse = false; m_ParentComponent = parentComponent;
mShouldStopAtDesiredWaypoint = false; m_Position = parentComponent->GetParent()->GetPosition();
m_Rotation = parentComponent->GetParent()->GetRotation();
mPercentBetweenPoints = 0.0f; m_State = eMovementPlatformState::Stopped | eMovementPlatformState::ReachedDesiredWaypoint;
m_DesiredWaypointIndex = -1;
mCurrentWaypointIndex = 0; m_PercentUntilNextWaypoint = 0.0f;
mNextWaypointIndex = 0; //mCurrentWaypointIndex + 1; m_CurrentWaypointIndex = 0;
m_NextWaypointIndex = -1;
mIdleTimeElapsed = 0.0f; m_IdleTimeElapsed = 0.0f;
m_Speed = 0.0f;
m_WaitTime = 0.0f;
m_MoveTimeElapsed = 0.0f;
m_IsDirty = false;
m_InReverse = false;
m_ShouldStopAtDesiredWaypoint = false;
m_LinearVelocity = NiPoint3Constant::ZERO;
m_AngularVelocity = NiPoint3Constant::ZERO;
m_TimeBasedMovement = false;
m_Path = nullptr;
} }
MoverSubComponent::~MoverSubComponent() = default; void PlatformSubComponent::Update(float deltaTime) {
if (m_State == 0 || !m_Path) return;
if (m_State & eMovementPlatformState::Travelling) {
m_MoveTimeElapsed += deltaTime;
void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { // Only need to recalculate the linear velocity if the speed is changing between waypoints
outBitStream->Write<bool>(true); // Unfortunately for the poor client, they chose to, instead of change the speed once at the start of the waypoint,
// the speed is changed over the course of the waypoint. This means we have to recalculate the linear velocity every frame.
outBitStream->Write(mState); // yay.
outBitStream->Write<int32_t>(mDesiredWaypointIndex); if (m_Speed == 0.0f || (GetCurrentWaypoint().movingPlatform.speed != GetNextWaypoint().movingPlatform.speed)) {
outBitStream->Write(mShouldStopAtDesiredWaypoint); UpdateLinearVelocity();
outBitStream->Write(mInReverse); m_IsDirty = true;
}
outBitStream->Write<float_t>(mPercentBetweenPoints); m_Position += m_LinearVelocity * deltaTime;
if (CalculatePercentToNextWaypoint() > 0.99) {
outBitStream->Write<float_t>(mPosition.x); m_MoveTimeElapsed = 0;
outBitStream->Write<float_t>(mPosition.y); m_ParentComponent->GetParent()->SetPosition(m_Position);
outBitStream->Write<float_t>(mPosition.z); m_InReverse ? AdvanceToNextReverseWaypoint() : AdvanceToNextWaypoint();
m_IsDirty = true;
outBitStream->Write<uint32_t>(mCurrentWaypointIndex); Game::entityManager->SerializeEntity(m_ParentComponent->GetParent());
outBitStream->Write<uint32_t>(mNextWaypointIndex); }
outBitStream->Write<float_t>(mIdleTimeElapsed);
outBitStream->Write<float_t>(0.0f); // Move time elapsed
}
//------------- MovingPlatformComponent below --------------
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) {
m_MoverSubComponentType = eMoverSubComponentType::mover;
m_MoverSubComponent = new MoverSubComponent(m_Parent->GetDefaultPosition());
m_PathName = GeneralUtils::ASCIIToUTF16(pathName);
m_Path = Game::zoneManager->GetZone()->GetPath(pathName);
m_NoAutoStart = false;
if (m_Path == nullptr) {
LOG("Path not found: %s", pathName.c_str());
} }
} }
MovingPlatformComponent::~MovingPlatformComponent() { float PlatformSubComponent::CalculatePercentToNextWaypoint() {
delete static_cast<MoverSubComponent*>(m_MoverSubComponent); if (m_TimeBasedMovement) return 0;
float distanceToNextWaypoint = (GetNextWaypoint().position - GetCurrentWaypoint().position).Length();
if (distanceToNextWaypoint == 0.0f) return 0;
float distanceToCurrentWaypoint = (m_Position - GetCurrentWaypoint().position).Length();
return distanceToCurrentWaypoint / distanceToNextWaypoint;
}
void PlatformSubComponent::UpdateAngularVelocity() {
// Update the angular velocity
// This one is sure to be fun...
}
void PlatformSubComponent::UpdateLinearVelocity() {
m_LinearVelocity = CalculateLinearVelocity();
}
void PlatformSubComponent::AdvanceToNextWaypoint() {
m_CurrentWaypointIndex = m_NextWaypointIndex;
m_ParentComponent->GetParent()->SetPosition(GetCurrentWaypoint().position);
m_ParentComponent->GetParent()->SetRotation(GetCurrentWaypoint().rotation);
int32_t nextWaypointIndex = FindNextWaypointIndex();
m_NextWaypointIndex = nextWaypointIndex;
m_DesiredWaypointIndex = nextWaypointIndex;
UpdateLinearVelocity();
UpdateAngularVelocity();
m_IsDirty = true;
}
void PlatformSubComponent::AdvanceToNextReverseWaypoint() {
m_ParentComponent->GetParent()->SetPosition(GetCurrentWaypoint().position);
m_ParentComponent->GetParent()->SetRotation(GetCurrentWaypoint().rotation);
m_CurrentWaypointIndex = m_NextWaypointIndex;
int32_t nextWaypointIndex = FindNextReversedWaypointIndex();
m_NextWaypointIndex = nextWaypointIndex;
m_DesiredWaypointIndex = nextWaypointIndex;
UpdateLinearVelocity();
UpdateAngularVelocity();
m_IsDirty = true;
}
void PlatformSubComponent::SetupPath(const std::string& pathName, uint32_t startingWaypointIndex, bool startsInReverse) {
m_Path = Game::zoneManager->GetZone()->GetPath(pathName);
LOG("setting up path %s", pathName.c_str());
if (!m_Path) {
LOG("Failed to find path (%s)", pathName.c_str());
return;
}
m_InReverse = startsInReverse;
m_CurrentWaypointIndex = startingWaypointIndex;
m_TimeBasedMovement = m_Path->movingPlatform.timeBasedMovement;
m_NextWaypointIndex = m_InReverse ? FindNextReversedWaypointIndex() : FindNextWaypointIndex();
}
const PathWaypoint& PlatformSubComponent::GetNextWaypoint() const {
DluAssert(m_Path != nullptr);
return m_Path->pathWaypoints.at(m_NextWaypointIndex);
}
const int32_t PlatformSubComponent::FindNextWaypointIndex() {
DluAssert(m_Path != nullptr);
uint32_t numWaypoints = m_Path->pathWaypoints.size();
uint32_t nextWaypointIndex = m_CurrentWaypointIndex + 1;
if (numWaypoints <= nextWaypointIndex) {
PathBehavior behavior = m_Path->pathBehavior;
if (behavior == PathBehavior::Once) {
nextWaypointIndex = m_Path->pathWaypoints.size() - 1;
} else if (behavior == PathBehavior::Bounce) {
nextWaypointIndex = m_Path->pathWaypoints.size() - 2;
m_InReverse = true;
} else {
nextWaypointIndex = 0;
}
}
return nextWaypointIndex;
}
const int32_t PlatformSubComponent::FindNextReversedWaypointIndex() {
DluAssert(m_Path != nullptr);
uint32_t numWaypoints = m_Path->pathWaypoints.size();
int32_t nextWaypointIndex = m_CurrentWaypointIndex - 1;
if (nextWaypointIndex < 0) {
PathBehavior behavior = m_Path->pathBehavior;
if (behavior == PathBehavior::Once) {
nextWaypointIndex = 0;
} else if (behavior == PathBehavior::Bounce) {
nextWaypointIndex = 1;
m_InReverse = false;
} else {
nextWaypointIndex = m_Path->pathWaypoints.size() - 1;
}
}
return nextWaypointIndex;
}
const PathWaypoint& PlatformSubComponent::GetCurrentWaypoint() const {
DluAssert(m_Path != nullptr);
return m_Path->pathWaypoints.at(m_CurrentWaypointIndex);
}
float PlatformSubComponent::CalculateSpeed() const {
float speed;
if (m_TimeBasedMovement) {
float unitizedDirection = 1.0f / (GetNextWaypoint().position - GetCurrentWaypoint().position).Length();
speed = unitizedDirection / GetCurrentWaypoint().movingPlatform.speed;
} else {
LOG("%i %i", m_CurrentWaypointIndex, m_NextWaypointIndex);
Game::logger->Flush();
speed = (GetNextWaypoint().movingPlatform.speed - GetCurrentWaypoint().movingPlatform.speed) * m_PercentUntilNextWaypoint + GetCurrentWaypoint().movingPlatform.speed;
}
return speed;
}
NiPoint3 PlatformSubComponent::CalculateLinearVelocity() {
return (GetNextWaypoint().position - GetCurrentWaypoint().position).Unitize() * CalculateSpeed();
}
void PlatformSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(bIsInitialUpdate || m_IsDirty);
if (!(bIsInitialUpdate || m_IsDirty)) return;
outBitStream->Write(m_State);
outBitStream->Write(m_DesiredWaypointIndex);
outBitStream->Write(m_ShouldStopAtDesiredWaypoint);
outBitStream->Write(m_InReverse);
outBitStream->Write(m_PercentUntilNextWaypoint);
outBitStream->Write(m_Position.x);
outBitStream->Write(m_Position.y);
outBitStream->Write(m_Position.z);
outBitStream->Write(m_CurrentWaypointIndex);
outBitStream->Write(m_NextWaypointIndex);
outBitStream->Write(m_IdleTimeElapsed);
outBitStream->Write(m_MoveTimeElapsed);
if (!bIsInitialUpdate) m_IsDirty = false;
}
void PlatformSubComponent::StartPathing() {
m_State |= eMovementPlatformState::Travelling;
m_State &= ~eMovementPlatformState::Stopped;
m_State &= ~eMovementPlatformState::Waiting;
m_IsDirty = true;
m_CurrentWaypointIndex = m_ParentComponent->GetStartingWaypointIndex();
m_InReverse = m_ParentComponent->GetStartsIsInReverse();
m_NextWaypointIndex = m_InReverse ? m_CurrentWaypointIndex - 1 : m_CurrentWaypointIndex + 1;
Game::entityManager->SerializeEntity(m_ParentComponent->GetParent());
}
void PlatformSubComponent::ResumePathing() {
if (m_State & eMovementPlatformState::Stopped && (m_State & eMovementPlatformState::ReachedDesiredWaypoint) == 0) {
StartPathing();
}
if (m_State & eMovementPlatformState::Travelling == 0) {
m_State |= eMovementPlatformState::Waiting;
m_State &= ~eMovementPlatformState::Stopped;
m_State &= ~eMovementPlatformState::Travelling;
m_IsDirty = true;
} else {
m_State &= eMovementPlatformState::Waiting;
m_State &= eMovementPlatformState::Travelling;
m_State &= eMovementPlatformState::Stopped;
m_IsDirty = true;
UpdateLinearVelocity();
UpdateAngularVelocity();
}
}
void PlatformSubComponent::StopPathing() {
m_State |= eMovementPlatformState::Stopped;
m_State &= ~eMovementPlatformState::Travelling;
m_State &= ~eMovementPlatformState::Waiting;
m_LinearVelocity = NiPoint3Constant::ZERO;
m_AngularVelocity = NiPoint3Constant::ZERO;
}
//------------- PlatformSubComponent end --------------
//------------- MoverPlatformSubComponent begin --------------
MoverPlatformSubComponent::MoverPlatformSubComponent(MovingPlatformComponent* parentComponent) : PlatformSubComponent(parentComponent) {
}
void MoverPlatformSubComponent::LoadConfigData() {
m_AllowPositionSnapping = m_ParentComponent->GetParent()->GetVar<bool>(u"allowPosSnap");
if(m_ParentComponent->GetParent()->HasVar(u"maxLerpDist")){
m_MaxLerpDistnace = m_ParentComponent->GetParent()->GetVar<float>(u"maxLerpDist");
m_MaxLerpDistnace = m_MaxLerpDistnace * m_MaxLerpDistnace;
}
}
//------------- MoverPlatformSubComponent end --------------
//------------- RotatorPlatformSubComponent begin --------------
RotatorPlatformSubComponent::RotatorPlatformSubComponent(MovingPlatformComponent* parentComponent) : PlatformSubComponent(parentComponent) {
}
void RotatorPlatformSubComponent::LoadConfigData() {
if(m_ParentComponent->GetParent()->HasVar(u"rotX")){
m_Rotation.x = m_ParentComponent->GetParent()->GetVar<float>(u"rotX") * M_PI;
}
if(m_ParentComponent->GetParent()->HasVar(u"rotY")){
m_Rotation.y = m_ParentComponent->GetParent()->GetVar<float>(u"rotY") * M_PI;
}
if(m_ParentComponent->GetParent()->HasVar(u"rotZ")){
m_Rotation.z = m_ParentComponent->GetParent()->GetVar<float>(u"rotZ") * M_PI;
}
if(m_ParentComponent->GetParent()->HasVar(u"allowRotSnap")){
m_AllowRotationSnapping = m_ParentComponent->GetParent()->GetVar<bool>(u"allowRotSnap");
}
if(m_AllowRotationSnapping) {
if(m_ParentComponent->GetParent()->HasVar(u"maxLerpAngle")){
m_MaxLerpAngle = (m_ParentComponent->GetParent()->GetVar<float>(u"maxLerpAngle") * M_PI) / 180;
}
}
}
//------------- RotatorPlatformSubComponent end --------------
//------------- SimpleMoverPlatformSubComponent begin --------------
void SimpleMoverPlatformSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(bIsInitialUpdate || m_DirtyStartingPoint);
if (bIsInitialUpdate || m_DirtyStartingPoint) {
outBitStream->Write(m_HasStartingPoint);
if (m_HasStartingPoint) {
outBitStream->Write(m_StartingPoint.x);
outBitStream->Write(m_StartingPoint.y);
outBitStream->Write(m_StartingPoint.z);
outBitStream->Write(m_StartingRotation.w);
outBitStream->Write(m_StartingRotation.x);
outBitStream->Write(m_StartingRotation.y);
outBitStream->Write(m_StartingRotation.z);
}
if (!bIsInitialUpdate) m_DirtyStartingPoint = false;
}
outBitStream->Write(bIsInitialUpdate || m_IsDirty);
if (bIsInitialUpdate || m_IsDirty) {
outBitStream->Write(m_State);
outBitStream->Write(m_CurrentWaypointIndex);
outBitStream->Write(m_InReverse);
if (!bIsInitialUpdate) m_IsDirty = false;
}
}
void SimpleMoverPlatformSubComponent::LoadConfigData() {
if (m_ParentComponent->GetParent()->GetVar<bool>(u"dbonly")) return;
NiPoint3 platformMove(
m_ParentComponent->GetParent()->GetVar<float>(u"platformMoveX"),
m_ParentComponent->GetParent()->GetVar<float>(u"platformMoveY"),
m_ParentComponent->GetParent()->GetVar<float>(u"platformMoveZ")
);
m_PlatformMove = platformMove;
m_MoveTime = m_ParentComponent->GetParent()->GetVar<float>(u"platformMoveTime");
m_StartAtEnd = m_ParentComponent->GetParent()->GetVar<bool>(u"platformStartAtEnd");
}
void SimpleMoverPlatformSubComponent::LoadDataFromTemplate() {
if (!m_ParentComponent->GetParent()->GetVar<bool>(u"dbonly")) return;
auto* movingPlatformTable = CDClientManager::GetTable<CDMovingPlatformComponentTable>();
if (movingPlatformTable == nullptr) return;
const auto& platformEntry = movingPlatformTable->GetPlatformEntry(m_ParentComponent->GetComponentId());
if (!platformEntry || !platformEntry->platformIsSimpleMover) return;
NiPoint3 platformMove = platformEntry->platformMove;
float moveTime = platformEntry->moveTime;
m_PlatformMove = platformMove;
m_MoveTime = moveTime;
}
SimpleMoverPlatformSubComponent::SimpleMoverPlatformSubComponent(MovingPlatformComponent* parentComponent, const NiPoint3& platformMove, const bool startsInReverse) : PlatformSubComponent(parentComponent) {
m_PlatformMove = platformMove;
m_InReverse = startsInReverse;
m_HasStartingPoint = true;
m_DirtyStartingPoint = true;
m_IsDirty = true;
m_StartingPoint = m_ParentComponent->GetParent()->GetPosition();
m_StartingRotation = m_ParentComponent->GetParent()->GetRotation();
}
//------------- SimpleMoverPlatformSubComponent end --------------
//------------- MovingPlatformComponent begin --------------
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) {
m_PathName = GeneralUtils::ASCIIToUTF16(pathName);
}
void MovingPlatformComponent::LoadDataFromTemplate() {
std::for_each(m_Platforms.begin(), m_Platforms.end(), [](const std::unique_ptr<PlatformSubComponent>& platform) { platform->LoadDataFromTemplate(); });
}
void MovingPlatformComponent::LoadConfigData() {
if (m_Parent->GetVar<bool>(u"platformIsMover")) {
AddMovingPlatform<MoverPlatformSubComponent>();
}
if (m_Parent->GetVar<bool>(u"platformIsSimpleMover")) {
AddMovingPlatform<SimpleMoverPlatformSubComponent>(NiPoint3Constant::ZERO, false);
}
if (m_Parent->GetVar<bool>(u"platformIsRotater")) {
AddMovingPlatform<RotatorPlatformSubComponent>();
}
m_StartingWaypointIndex = m_Parent->GetVar<uint32_t>(u"attached_path_start");
m_StartsIsInReverse = false;
m_DirtyPathInfo = true;
m_StartOnload = m_Parent->GetVar<bool>(u"startPathingOnLoad");
}
void MovingPlatformComponent::Update(float deltaTime) {
std::for_each(m_Platforms.begin(), m_Platforms.end(), [deltaTime](const std::unique_ptr<PlatformSubComponent>& platform) { platform->Update(deltaTime); });
} }
void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
// Here we don't serialize the moving platform to let the client simulate the movement // For some reason we need to write this here instead of later on.
outBitStream->Write(!m_Platforms.empty());
if (!m_Serialize) { outBitStream->Write(bIsInitialUpdate || m_DirtyPathInfo);
outBitStream->Write<bool>(false); if (bIsInitialUpdate || m_DirtyPathInfo) {
outBitStream->Write<bool>(false); outBitStream->Write(!m_PathName.empty());
if (!m_PathName.empty()) {
return; outBitStream->Write(static_cast<uint16_t>(m_PathName.size()));
} for (const auto& c : m_PathName) {
outBitStream->Write(static_cast<uint16_t>(c));
outBitStream->Write<bool>(true); }
outBitStream->Write(m_StartingWaypointIndex);
auto hasPath = !m_PathingStopped && !m_PathName.empty(); outBitStream->Write(m_StartsIsInReverse);
outBitStream->Write(hasPath);
if (hasPath) {
// Is on rail
outBitStream->Write1();
outBitStream->Write<uint16_t>(m_PathName.size());
for (const auto& c : m_PathName) {
outBitStream->Write<uint16_t>(c);
} }
if (!bIsInitialUpdate) m_DirtyPathInfo = false;
// Starting point
outBitStream->Write<uint32_t>(0);
// Reverse
outBitStream->Write<bool>(false);
} }
if (m_Platforms.empty()) return;
const auto hasPlatform = m_MoverSubComponent != nullptr; for (const auto& platform : m_Platforms) {
outBitStream->Write<bool>(hasPlatform); outBitStream->Write1(); // Has platform to write
outBitStream->Write(platform->GetPlatformType());
if (hasPlatform) { platform->Serialize(outBitStream, bIsInitialUpdate);
auto* mover = static_cast<MoverSubComponent*>(m_MoverSubComponent);
outBitStream->Write(m_MoverSubComponentType);
if (m_MoverSubComponentType == eMoverSubComponentType::simpleMover) {
// TODO
} else {
mover->Serialize(outBitStream, bIsInitialUpdate);
}
} }
outBitStream->Write0(); // No more platforms to write
} }
void MovingPlatformComponent::OnQuickBuildInitilized() { void MovingPlatformComponent::OnQuickBuildInitilized() {
@@ -122,232 +415,45 @@ void MovingPlatformComponent::OnQuickBuildInitilized() {
} }
void MovingPlatformComponent::OnCompleteQuickBuild() { void MovingPlatformComponent::OnCompleteQuickBuild() {
if (m_NoAutoStart) if (m_NoAutoStart) return;
return;
StartPathing(); StartPathing();
} }
void MovingPlatformComponent::SetMovementState(eMovementPlatformState value) { void MovingPlatformComponent::SetMovementState(eMovementPlatformState value) {
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
subComponent->mState = value;
Game::entityManager->SerializeEntity(m_Parent);
} }
void MovingPlatformComponent::GotoWaypoint(uint32_t index, bool stopAtWaypoint) { void MovingPlatformComponent::GotoWaypoint(uint32_t index, bool stopAtWaypoint) {
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
subComponent->mDesiredWaypointIndex = index;
subComponent->mNextWaypointIndex = index;
subComponent->mShouldStopAtDesiredWaypoint = stopAtWaypoint;
StartPathing();
} }
void MovingPlatformComponent::StartPathing() { void MovingPlatformComponent::StartPathing() {
//GameMessages::SendStartPathing(m_Parent); std::for_each(m_Platforms.begin(), m_Platforms.end(), [](const std::unique_ptr<PlatformSubComponent>& platform) {
m_PathingStopped = false; platform->StartPathing();
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
subComponent->mShouldStopAtDesiredWaypoint = true;
subComponent->mState = eMovementPlatformState::Stationary;
NiPoint3 targetPosition;
if (m_Path != nullptr) {
const auto& currentWaypoint = m_Path->pathWaypoints[subComponent->mCurrentWaypointIndex];
const auto& nextWaypoint = m_Path->pathWaypoints[subComponent->mNextWaypointIndex];
subComponent->mPosition = currentWaypoint.position;
subComponent->mSpeed = currentWaypoint.movingPlatform.speed;
subComponent->mWaitTime = currentWaypoint.movingPlatform.wait;
targetPosition = nextWaypoint.position;
} else {
subComponent->mPosition = m_Parent->GetPosition();
subComponent->mSpeed = 1.0f;
subComponent->mWaitTime = 2.0f;
targetPosition = m_Parent->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f);
}
m_Parent->AddCallbackTimer(subComponent->mWaitTime, [this] {
SetMovementState(eMovementPlatformState::Moving);
}); });
const auto travelTime = Vector3::Distance(targetPosition, subComponent->mPosition) / subComponent->mSpeed + 1.5f;
const auto travelNext = subComponent->mWaitTime + travelTime;
m_Parent->AddCallbackTimer(travelTime, [subComponent, this] {
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) {
script->OnWaypointReached(m_Parent, subComponent->mNextWaypointIndex);
}
});
m_Parent->AddCallbackTimer(travelNext, [this] {
ContinuePathing();
});
//GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS);
Game::entityManager->SerializeEntity(m_Parent);
} }
void MovingPlatformComponent::ContinuePathing() { void MovingPlatformComponent::ContinuePathing() {
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
subComponent->mState = eMovementPlatformState::Stationary;
subComponent->mCurrentWaypointIndex = subComponent->mNextWaypointIndex;
NiPoint3 targetPosition;
uint32_t pathSize;
PathBehavior behavior;
if (m_Path != nullptr) {
const auto& currentWaypoint = m_Path->pathWaypoints[subComponent->mCurrentWaypointIndex];
const auto& nextWaypoint = m_Path->pathWaypoints[subComponent->mNextWaypointIndex];
subComponent->mPosition = currentWaypoint.position;
subComponent->mSpeed = currentWaypoint.movingPlatform.speed;
subComponent->mWaitTime = currentWaypoint.movingPlatform.wait; // + 2;
pathSize = m_Path->pathWaypoints.size() - 1;
behavior = static_cast<PathBehavior>(m_Path->pathBehavior);
targetPosition = nextWaypoint.position;
} else {
subComponent->mPosition = m_Parent->GetPosition();
subComponent->mSpeed = 1.0f;
subComponent->mWaitTime = 2.0f;
targetPosition = m_Parent->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f);
pathSize = 1;
behavior = PathBehavior::Loop;
}
if (m_Parent->GetLOT() == 9483) {
behavior = PathBehavior::Bounce;
} else {
return;
}
if (subComponent->mCurrentWaypointIndex >= pathSize) {
subComponent->mCurrentWaypointIndex = pathSize;
switch (behavior) {
case PathBehavior::Once:
Game::entityManager->SerializeEntity(m_Parent);
return;
case PathBehavior::Bounce:
subComponent->mInReverse = true;
break;
case PathBehavior::Loop:
subComponent->mNextWaypointIndex = 0;
break;
default:
break;
}
} else if (subComponent->mCurrentWaypointIndex == 0) {
subComponent->mInReverse = false;
}
if (subComponent->mInReverse) {
subComponent->mNextWaypointIndex = subComponent->mCurrentWaypointIndex - 1;
} else {
subComponent->mNextWaypointIndex = subComponent->mCurrentWaypointIndex + 1;
}
/*
subComponent->mNextWaypointIndex = 0;
subComponent->mCurrentWaypointIndex = 1;
*/
//GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS);
if (subComponent->mCurrentWaypointIndex == subComponent->mDesiredWaypointIndex) {
// TODO: Send event?
StopPathing();
return;
}
m_Parent->CancelCallbackTimers();
m_Parent->AddCallbackTimer(subComponent->mWaitTime, [this] {
SetMovementState(eMovementPlatformState::Moving);
});
auto travelTime = Vector3::Distance(targetPosition, subComponent->mPosition) / subComponent->mSpeed + 1.5;
if (m_Parent->GetLOT() == 9483) {
travelTime += 20;
}
const auto travelNext = subComponent->mWaitTime + travelTime;
m_Parent->AddCallbackTimer(travelTime, [subComponent, this] {
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) {
script->OnWaypointReached(m_Parent, subComponent->mNextWaypointIndex);
}
});
m_Parent->AddCallbackTimer(travelNext, [this] {
ContinuePathing();
});
Game::entityManager->SerializeEntity(m_Parent);
} }
void MovingPlatformComponent::StopPathing() { void MovingPlatformComponent::StopPathing() {
//m_Parent->CancelCallbackTimers();
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
m_PathingStopped = true;
subComponent->mState = eMovementPlatformState::Stopped;
subComponent->mDesiredWaypointIndex = -1;
subComponent->mShouldStopAtDesiredWaypoint = false;
Game::entityManager->SerializeEntity(m_Parent);
//GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS);
}
void MovingPlatformComponent::SetSerialized(bool value) {
m_Serialize = value;
} }
bool MovingPlatformComponent::GetNoAutoStart() const { bool MovingPlatformComponent::GetNoAutoStart() const {
return m_NoAutoStart; return false;
} }
void MovingPlatformComponent::SetNoAutoStart(const bool value) { void MovingPlatformComponent::SetNoAutoStart(const bool value) {
m_NoAutoStart = value;
} }
void MovingPlatformComponent::WarpToWaypoint(size_t index) { void MovingPlatformComponent::WarpToWaypoint(size_t index) {
const auto& waypoint = m_Path->pathWaypoints[index];
m_Parent->SetPosition(waypoint.position);
m_Parent->SetRotation(waypoint.rotation);
Game::entityManager->SerializeEntity(m_Parent);
} }
size_t MovingPlatformComponent::GetLastWaypointIndex() const { size_t MovingPlatformComponent::GetLastWaypointIndex() const {
return m_Path->pathWaypoints.size() - 1; return 0;
} }
MoverSubComponent* MovingPlatformComponent::GetMoverSubComponent() const { //------------- MovingPlatformComponent end --------------
return static_cast<MoverSubComponent*>(m_MoverSubComponent);
}

View File

@@ -6,96 +6,166 @@
#ifndef MOVINGPLATFORMCOMPONENT_H #ifndef MOVINGPLATFORMCOMPONENT_H
#define MOVINGPLATFORMCOMPONENT_H #define MOVINGPLATFORMCOMPONENT_H
#include "RakNetTypes.h"
#include "NiPoint3.h" #include "NiPoint3.h"
#include <memory>
#include <string> #include <string>
#include <vector>
#include "dCommonVars.h" #include "dCommonVars.h"
#include "EntityManager.h"
#include "Component.h" #include "Component.h"
#include "eMovementPlatformState.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
class PathWaypoint;
class Path; class Path;
/** /**
* Different types of available platforms * The different types of platform movement state
*/ */
enum class eMoverSubComponentType : uint32_t { enum eMovementPlatformState : uint32_t
mover = 4, {
Waiting = 1 << 0U,
/** Travelling = 1 << 1U,
* Used in NJ Stopped = 1 << 2U,
*/ ReachedDesiredWaypoint = 1 << 3U,
simpleMover = 5, ReachedFinalWaypoint = 1 << 4U,
}; };
/** /**
* Sub component for moving platforms that determine the actual current movement state * Different types of available platforms
*/ */
class MoverSubComponent { enum class eMoverSubComponentType : uint32_t {
public: None = 0,
MoverSubComponent(const NiPoint3& startPos); Mover = 4,
~MoverSubComponent(); SimpleMover = 5,
Rotator = 6
};
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate); class MovingPlatformComponent;
// In the context of a platform that is TimeBasedMovement,
// the speed member from the Path is used as the time to go between waypoints.
class PlatformSubComponent {
public:
PlatformSubComponent(MovingPlatformComponent* parentComponent);
virtual ~PlatformSubComponent() = default;
virtual void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate);
virtual eMoverSubComponentType GetPlatformType() { return eMoverSubComponentType::None; };
bool GetIsDirty() const { return m_IsDirty; }
virtual void LoadDataFromTemplate() {};
virtual void LoadConfigData() {};
virtual void StartPathing();
virtual void ResumePathing();
virtual void StopPathing();
virtual void Update(float deltaTime);
float CalculateSpeed() const;
const PathWaypoint& GetNextWaypoint() const;
const PathWaypoint& GetCurrentWaypoint() const;
const int32_t FindNextWaypointIndex();
const int32_t FindNextReversedWaypointIndex();
void SetupPath(const std::string& pathName, uint32_t startingWaypointIndex, bool startsInReverse);
void AdvanceToNextWaypoint();
void AdvanceToNextReverseWaypoint();
NiPoint3 CalculateLinearVelocity();
void UpdateLinearVelocity();
void UpdateAngularVelocity();
float CalculatePercentToNextWaypoint();
// Write all the getters for the below members
bool GetTimeBasedMovement() const { return m_TimeBasedMovement; }
const Path* GetPath() const { return m_Path; }
float GetSpeed() const { return m_Speed; }
float GetWaitTime() const { return m_WaitTime; }
float GetMoveTimeElapsed() const { return m_MoveTimeElapsed; }
float GetPercentUntilNextWaypoint() const { return m_PercentUntilNextWaypoint; }
int32_t GetCurrentWaypointIndex() const { return m_CurrentWaypointIndex; }
int32_t GetNextWaypointIndex() const { return m_NextWaypointIndex; }
bool GetInReverse() const { return m_InReverse; }
bool GetShouldStopAtDesiredWaypoint() const { return m_ShouldStopAtDesiredWaypoint; }
int32_t GetDesiredWaypointIndex() const { return m_DesiredWaypointIndex; }
uint32_t GetState() const { return m_State; }
const NiPoint3& GetPosition() const { return m_Position; }
const NiQuaternion& GetRotation() const { return m_Rotation; }
const NiPoint3& GetLinearVelocity() const { return m_LinearVelocity; }
const NiPoint3& GetAngularVelocity() const { return m_AngularVelocity; }
const MovingPlatformComponent* GetParentComponent() const { return m_ParentComponent; }
const float GetIdleTimeElapsed() const { return m_IdleTimeElapsed; }
protected:
MovingPlatformComponent* m_ParentComponent;
/** /**
* The state the platform is currently in * The state the platform is currently in
*/ */
eMovementPlatformState mState = eMovementPlatformState::Stationary; uint32_t m_State;
int32_t m_DesiredWaypointIndex;
/** float m_PercentUntilNextWaypoint;
* The waypoint this platform currently wants to traverse to NiPoint3 m_Position;
*/ int32_t m_CurrentWaypointIndex;
int32_t mDesiredWaypointIndex = 0; int32_t m_NextWaypointIndex;
float m_IdleTimeElapsed;
/** float m_Speed;
* Whether the platform is currently reversing away from the desired waypoint float m_WaitTime;
*/ float m_MoveTimeElapsed;
bool mInReverse = false; bool m_IsDirty;
bool m_InReverse;
/** bool m_ShouldStopAtDesiredWaypoint;
* Whether the platform should stop moving when reaching the desired waypoint NiPoint3 m_LinearVelocity;
*/ NiPoint3 m_AngularVelocity;
bool mShouldStopAtDesiredWaypoint = false; bool m_TimeBasedMovement;
const Path* m_Path;
/** NiQuaternion m_Rotation;
* The percentage of the way between the last point and the desired point
*/
float mPercentBetweenPoints = 0;
/**
* The current position of the platofrm
*/
NiPoint3 mPosition{};
/**
* The waypoint the platform is (was) at
*/
uint32_t mCurrentWaypointIndex;
/**
* The waypoint the platform is attempting to go to
*/
uint32_t mNextWaypointIndex;
/**
* The timer that handles the time before stopping idling and continue platform movement
*/
float mIdleTimeElapsed = 0;
/**
* The speed the platform is currently moving at
*/
float mSpeed = 0;
/**
* The time to wait before continuing movement
*/
float mWaitTime = 0;
}; };
class MoverPlatformSubComponent : public PlatformSubComponent {
public:
inline static const eMoverSubComponentType SubComponentType = eMoverSubComponentType::Mover;
MoverPlatformSubComponent(MovingPlatformComponent* parentComponent);
~MoverPlatformSubComponent() override = default;
eMoverSubComponentType GetPlatformType() override { return eMoverSubComponentType::Mover; }
void LoadConfigData() override;
private:
bool m_AllowPositionSnapping = true;
float m_MaxLerpDistnace = 16.0f;
};
class RotatorPlatformSubComponent : public PlatformSubComponent {
public:
inline static const eMoverSubComponentType SubComponentType = eMoverSubComponentType::Rotator;
RotatorPlatformSubComponent(MovingPlatformComponent* parentComponent);
~RotatorPlatformSubComponent() override = default;
eMoverSubComponentType GetPlatformType() override { return eMoverSubComponentType::Rotator; }
void LoadConfigData() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override { PlatformSubComponent::Serialize(outBitStream, bIsInitialUpdate); };
private:
NiPoint3 m_Rotation = NiPoint3Constant::ZERO;
float m_Rate = 0.0f;
NiPoint3 m_AngularVelocity = NiPoint3Constant::ZERO;
bool m_AllowRotationSnapping = true;
float m_MaxLerpAngle = 0.1396263;
bool m_DirtyAngularVelocity = false;
float m_UnknownFloat = 0.0f;
};
// Only moves. Has NO path. This moving platform gets its initial position and rotation from the server on serialization.
class SimpleMoverPlatformSubComponent : public PlatformSubComponent {
public:
inline static const eMoverSubComponentType SubComponentType = eMoverSubComponentType::SimpleMover;
SimpleMoverPlatformSubComponent(MovingPlatformComponent* parentComponent, const NiPoint3& platformMove, const bool startAtEnd);
~SimpleMoverPlatformSubComponent() override = default;
eMoverSubComponentType GetPlatformType() override { return eMoverSubComponentType::SimpleMover; }
void LoadConfigData() override;
void LoadDataFromTemplate() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
bool m_HasStartingPoint = false;
bool m_DirtyStartingPoint = false;
NiPoint3 m_StartingPoint;
NiQuaternion m_StartingRotation;
NiPoint3 m_PlatformMove;
float m_MoveTime;
bool m_StartAtEnd;
};
/** /**
* Represents entities that may be moving platforms, indicating how they should move through the world. * Represents entities that may be moving platforms, indicating how they should move through the world.
@@ -109,7 +179,10 @@ public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM; static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM;
MovingPlatformComponent(Entity* parent, const std::string& pathName); MovingPlatformComponent(Entity* parent, const std::string& pathName);
~MovingPlatformComponent() override;
void LoadDataFromTemplate();
void LoadConfigData();
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
@@ -155,7 +228,7 @@ public:
* Determines if the entity should be serialized on the next update * Determines if the entity should be serialized on the next update
* @param value whether to serialize the entity or not * @param value whether to serialize the entity or not
*/ */
void SetSerialized(bool value); void SetSerialized(bool value) {};
/** /**
* Returns if this platform will start automatically after spawn * Returns if this platform will start automatically after spawn
@@ -181,19 +254,44 @@ public:
*/ */
size_t GetLastWaypointIndex() const; size_t GetLastWaypointIndex() const;
/** template<typename MovingPlatform, typename ...ConstructorValues>
* Returns the sub component that actually defines how the platform moves around (speeds, etc). void AddMovingPlatform(ConstructorValues... arguments) {
* @return the sub component that actually defines how the platform moves around static_assert(std::is_base_of<PlatformSubComponent, MovingPlatform>::value, "MovingPlatform must derive from PlatformSubComponent");
*/ auto hasPlatform = std::find_if(m_Platforms.begin(), m_Platforms.end(), [](const std::unique_ptr<PlatformSubComponent>& platform) {
MoverSubComponent* GetMoverSubComponent() const; return platform->GetPlatformType() == MovingPlatform::SubComponentType;
}) != m_Platforms.end();
if (!hasPlatform) {
m_Platforms.push_back(std::make_unique<MovingPlatform>(this, std::forward<ConstructorValues>(arguments)...));
}
}
bool HasPlatform() { return !m_Platforms.empty(); }
const PlatformSubComponent& GetPlatform() const {
return *m_Platforms.at(0);
}
int32_t GetComponentId() const { return componentId; }
// Make
const std::u16string& GetPathName() const { return m_PathName; }
void SetPathName(const std::u16string& pathName) { m_PathName = pathName; }
bool GetPathingStopped() const { return m_PathingStopped; }
void SetPathingStopped(bool value) { m_PathingStopped = value; }
uint32_t GetStartingWaypointIndex() const { return m_StartingWaypointIndex; }
void SetStartingWaypointIndex(uint32_t value) { m_StartingWaypointIndex = value; }
bool GetStartsIsInReverse() const { return m_StartsIsInReverse; }
void SetStartsIsInReverse(bool value) { m_StartsIsInReverse = value; }
bool GetStartOnload() const { return m_StartOnload; }
void SetStartOnload(bool value) { m_StartOnload = value; }
bool GetDirtyPathInfo() const { return m_DirtyPathInfo; }
private: private:
/**
* The path this platform is currently on
*/
const Path* m_Path = nullptr;
/** /**
* The name of the path this platform is currently on * The name of the path this platform is currently on
*/ */
@@ -204,25 +302,24 @@ private:
*/ */
bool m_PathingStopped = false; bool m_PathingStopped = false;
/** uint32_t m_StartingWaypointIndex = 0;
* The type of the subcomponent
*/ bool m_StartsIsInReverse = false;
eMoverSubComponentType m_MoverSubComponentType;
int32_t componentId = -1;
/** /**
* The mover sub component that belongs to this platform * The mover sub component that belongs to this platform
*/ */
void* m_MoverSubComponent; std::vector<std::unique_ptr<PlatformSubComponent>> m_Platforms;
/** /**
* Whether the platform shouldn't auto start * Whether the platform shouldn't auto start
*/ */
bool m_NoAutoStart; bool m_NoAutoStart;
/** bool m_DirtyPathInfo = false;
* Whether to serialize the entity on the next update bool m_StartOnload = false;
*/
bool m_Serialize = false;
}; };
#endif // MOVINGPLATFORMCOMPONENT_H #endif // MOVINGPLATFORMCOMPONENT_H

View File

@@ -25,7 +25,6 @@
#include "ChatPackets.h" #include "ChatPackets.h"
#include "MultiZoneEntranceComponent.h" #include "MultiZoneEntranceComponent.h"
#include "eUnequippableActiveType.h" #include "eUnequippableActiveType.h"
#include "eMovementPlatformState.h"
#include "LeaderboardManager.h" #include "LeaderboardManager.h"
#include "Amf3.h" #include "Amf3.h"
#include "Loot.h" #include "Loot.h"
@@ -359,55 +358,43 @@ void GameMessages::SendResetMissions(Entity* entity, const SystemAddress& sysAdd
SEND_PACKET; SEND_PACKET;
} }
void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAddr, bool bStopAtDesiredWaypoint, void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAddr,
int iIndex, int iDesiredWaypointIndex, int nextIndex, eMovementPlatformState movementState, bool bStopAtDesiredWaypoint,
eMovementPlatformState movementState) { int iIndex, int iDesiredWaypointIndex, int nextIndex) {
CBITSTREAM; CBITSTREAM;
CMSGHEADER; CMSGHEADER;
const auto lot = entity->GetLOT(); const auto lot = entity->GetLOT();
if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449) {
iDesiredWaypointIndex = 0;
iIndex = 0;
nextIndex = 0;
bStopAtDesiredWaypoint = true;
movementState = eMovementPlatformState::Stationary;
}
bitStream.Write(entity->GetObjectID()); bitStream.Write(entity->GetObjectID());
bitStream.Write(eGameMessageType::PLATFORM_RESYNC); bitStream.Write(eGameMessageType::PLATFORM_RESYNC);
bool bReverse = false; auto* movingPlatformComponent = entity->GetComponent<MovingPlatformComponent>();
int eCommand = 0; if (!movingPlatformComponent) return;
int eUnexpectedCommand = 0; if (!movingPlatformComponent->HasPlatform()) return;
float fIdleTimeElapsed = 0.0f; auto& subComponent = movingPlatformComponent->GetPlatform();
float fMoveTimeElapsed = 0.0f;
float fPercentBetweenPoints = 0.0f;
NiPoint3 ptUnexpectedLocation = NiPoint3Constant::ZERO;
NiQuaternion qUnexpectedRotation = NiQuaternionConstant::IDENTITY;
bitStream.Write(bReverse); bitStream.Write(subComponent.GetInReverse());
bitStream.Write(bStopAtDesiredWaypoint); bitStream.Write(subComponent.GetShouldStopAtDesiredWaypoint());
bitStream.Write(eCommand); bitStream.Write<int32_t>(0);
bitStream.Write(static_cast<int32_t>(movementState)); bitStream.Write(subComponent.GetState());
bitStream.Write(eUnexpectedCommand); bitStream.Write<int32_t>(0);
bitStream.Write(fIdleTimeElapsed); bitStream.Write(subComponent.GetIdleTimeElapsed());
bitStream.Write(fMoveTimeElapsed); bitStream.Write(subComponent.GetMoveTimeElapsed());
bitStream.Write(fPercentBetweenPoints); bitStream.Write(subComponent.GetPercentUntilNextWaypoint());
bitStream.Write(iDesiredWaypointIndex); bitStream.Write(subComponent.GetDesiredWaypointIndex());
bitStream.Write(iIndex); bitStream.Write(subComponent.GetCurrentWaypointIndex());
bitStream.Write(nextIndex); bitStream.Write(subComponent.GetNextWaypointIndex());
bitStream.Write(ptUnexpectedLocation.x); bitStream.Write(subComponent.GetPosition().x);
bitStream.Write(ptUnexpectedLocation.y); bitStream.Write(subComponent.GetPosition().y);
bitStream.Write(ptUnexpectedLocation.z); bitStream.Write(subComponent.GetPosition().z);
bitStream.Write(qUnexpectedRotation != NiQuaternionConstant::IDENTITY); bitStream.Write(subComponent.GetRotation() != NiQuaternionConstant::IDENTITY);
if (qUnexpectedRotation != NiQuaternionConstant::IDENTITY) { if (subComponent.GetRotation() != NiQuaternionConstant::IDENTITY) {
bitStream.Write(qUnexpectedRotation.x); bitStream.Write(subComponent.GetRotation().x);
bitStream.Write(qUnexpectedRotation.y); bitStream.Write(subComponent.GetRotation().y);
bitStream.Write(qUnexpectedRotation.z); bitStream.Write(subComponent.GetRotation().z);
bitStream.Write(qUnexpectedRotation.w); bitStream.Write(subComponent.GetRotation().w);
} }
SEND_PACKET_BROADCAST; SEND_PACKET_BROADCAST;
@@ -4962,8 +4949,7 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream* inStream, Entity
} }
void GameMessages::HandleRequestPlatformResync(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { void GameMessages::HandleRequestPlatformResync(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
if (entity->GetLOT() == 6267 || entity->GetLOT() == 16141) return; GameMessages::SendPlatformResync(entity, sysAddr, eMovementPlatformState::Travelling);
GameMessages::SendPlatformResync(entity, sysAddr);
} }
void GameMessages::HandleQuickBuildCancel(RakNet::BitStream* inStream, Entity* entity) { void GameMessages::HandleQuickBuildCancel(RakNet::BitStream* inStream, Entity* entity) {

View File

@@ -5,7 +5,6 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include "eMovementPlatformState.h"
#include "NiPoint3.h" #include "NiPoint3.h"
#include "eEndBehavior.h" #include "eEndBehavior.h"
#include "eCyclingMode.h" #include "eCyclingMode.h"
@@ -21,6 +20,7 @@ class Leaderboard;
class PropertySelectQueryProperty; class PropertySelectQueryProperty;
class TradeItem; class TradeItem;
enum eMovementPlatformState : uint32_t;
enum class eAnimationFlags : uint32_t; enum class eAnimationFlags : uint32_t;
enum class eUnequippableActiveType; enum class eUnequippableActiveType;
@@ -71,9 +71,9 @@ namespace GameMessages {
void SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& sysAddr, std::string audioGUID); void SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& sysAddr, std::string audioGUID);
void SendStartPathing(Entity* entity); void SendStartPathing(Entity* entity);
void SendPlatformResync(Entity* entity, const SystemAddress& sysAddr, bool bStopAtDesiredWaypoint = false, void SendPlatformResync(Entity* entity, const SystemAddress& sysAddr,
int iIndex = 0, int iDesiredWaypointIndex = 1, int nextIndex = 1, eMovementPlatformState movementState, bool bStopAtDesiredWaypoint = false,
eMovementPlatformState movementState = eMovementPlatformState::Moving); int iIndex = 0, int iDesiredWaypointIndex = 1, int nextIndex = 1);
void SendResetMissions(Entity* entity, const SystemAddress& sysAddr, const int32_t missionid = -1); void SendResetMissions(Entity* entity, const SystemAddress& sysAddr, const int32_t missionid = -1);
void SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr); void SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr);

View File

@@ -4,24 +4,16 @@
#include "MovingPlatformComponent.h" #include "MovingPlatformComponent.h"
void PropertyPlatform::OnQuickBuildComplete(Entity* self, Entity* target) { void PropertyPlatform::OnQuickBuildComplete(Entity* self, Entity* target) {
// auto* movingPlatform = self->GetComponent<MovingPlatformComponent>(); GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS
// if (movingPlatform != nullptr) { , static_cast<eMovementPlatformState>(eMovementPlatformState::Waiting | eMovementPlatformState::ReachedDesiredWaypoint | eMovementPlatformState::ReachedFinalWaypoint),
// movingPlatform->StopPathing(); true, 0, 0, 0);
// movingPlatform->SetNoAutoStart(true);
// }
GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0,
0, 0, eMovementPlatformState::Stationary);
} }
void PropertyPlatform::OnUse(Entity* self, Entity* user) { void PropertyPlatform::OnUse(Entity* self, Entity* user) {
auto* quickBuildComponent = self->GetComponent<QuickBuildComponent>(); auto* rebuildComponent = self->GetComponent<QuickBuildComponent>();
if (quickBuildComponent != nullptr && quickBuildComponent->GetState() == eQuickBuildState::COMPLETED) { if (rebuildComponent != nullptr && rebuildComponent->GetState() == eQuickBuildState::COMPLETED) {
// auto* movingPlatform = self->GetComponent<MovingPlatformComponent>(); GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, eMovementPlatformState::Travelling, true, 0,
// if (movingPlatform != nullptr) { 1, 1);
// movingPlatform->GotoWaypoint(1);
// }
GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0,
1, 1, eMovementPlatformState::Moving);
self->AddCallbackTimer(movementDelay + effectDelay, [self, this]() { self->AddCallbackTimer(movementDelay + effectDelay, [self, this]() {
self->SetNetworkVar<float_t>(u"startEffect", dieDelay); self->SetNetworkVar<float_t>(u"startEffect", dieDelay);

View File

@@ -5,6 +5,7 @@
#include "Character.h" #include "Character.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "RenderComponent.h" #include "RenderComponent.h"
#include "MovingPlatformComponent.h"
void CavePrisonCage::OnStartup(Entity* self) { void CavePrisonCage::OnStartup(Entity* self) {
const auto& myNum = self->GetVar<std::u16string>(u"myNumber"); const auto& myNum = self->GetVar<std::u16string>(u"myNumber");
@@ -83,8 +84,8 @@ void CavePrisonCage::SpawnCounterweight(Entity* self, Spawner* spawner) {
return; return;
} }
// Move the counterweight down 2 units auto* mpc = counterweight->GetComponent<MovingPlatformComponent>();
counterweight->SetPosition(counterweight->GetPosition() + NiPoint3(0, -2, 0)); if (mpc) mpc->StartPathing();
// Serialize the counterweight // Serialize the counterweight
Game::entityManager->SerializeEntity(counterweight); Game::entityManager->SerializeEntity(counterweight);

View File

@@ -2,6 +2,7 @@
#include "Entity.h" #include "Entity.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "ProximityMonitorComponent.h" #include "ProximityMonitorComponent.h"
#include "MovingPlatformComponent.h"
void AgBusDoor::OnStartup(Entity* self) { void AgBusDoor::OnStartup(Entity* self) {
m_Counter = 0; m_Counter = 0;
@@ -48,9 +49,9 @@ void AgBusDoor::OnProximityUpdate(Entity* self, Entity* entering, std::string na
void AgBusDoor::MoveDoor(Entity* self, bool bOpen) { void AgBusDoor::MoveDoor(Entity* self, bool bOpen) {
if (bOpen) { if (bOpen) {
GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 1, 0); GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, eMovementPlatformState::Travelling, true, 1, 0);
} else { } else {
GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, 1); GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, eMovementPlatformState::Travelling, true, 0, 1);
self->AddTimer("dustTimer", 2.0f); self->AddTimer("dustTimer", 2.0f);
} }

View File

@@ -1,6 +1,7 @@
#include "AgQbElevator.h" #include "AgQbElevator.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "MovingPlatformComponent.h"
void AgQbElevator::OnStartup(Entity* self) { void AgQbElevator::OnStartup(Entity* self) {
@@ -14,8 +15,9 @@ void AgQbElevator::OnQuickBuildComplete(Entity* self, Entity* target) {
float delayTime = killTime - endTime; float delayTime = killTime - endTime;
if (delayTime < 1) delayTime = 1; if (delayTime < 1) delayTime = 1;
GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS,
0, 0, eMovementPlatformState::Stationary); static_cast<eMovementPlatformState>(eMovementPlatformState::Waiting | eMovementPlatformState::ReachedDesiredWaypoint | eMovementPlatformState::ReachedFinalWaypoint)
, true, 0, 0, 0);
//add a timer that will kill the QB if no players get on in the killTime //add a timer that will kill the QB if no players get on in the killTime
self->AddTimer("startKillTimer", killTime); self->AddTimer("startKillTimer", killTime);
@@ -32,8 +34,8 @@ void AgQbElevator::OnProximityUpdate(Entity* self, Entity* entering, std::string
self->SetBoolean(u"qbPlayerRdy", true); self->SetBoolean(u"qbPlayerRdy", true);
self->CancelTimer("StartElevator"); self->CancelTimer("StartElevator");
GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, eMovementPlatformState::Travelling, true, 0,
1, 1, eMovementPlatformState::Moving); 1, 1);
} else if (!self->GetBoolean(u"StartTimer")) { } else if (!self->GetBoolean(u"StartTimer")) {
self->SetBoolean(u"StartTimer", true); self->SetBoolean(u"StartTimer", true);
self->AddTimer("StartElevator", startTime); self->AddTimer("StartElevator", startTime);
@@ -44,8 +46,8 @@ void AgQbElevator::OnProximityUpdate(Entity* self, Entity* entering, std::string
void AgQbElevator::OnTimerDone(Entity* self, std::string timerName) { void AgQbElevator::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "StartElevator") { if (timerName == "StartElevator") {
GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, eMovementPlatformState::Travelling, true, 0,
1, 1, eMovementPlatformState::Moving); 1, 1);
} else if (timerName == "startKillTimer") { } else if (timerName == "startKillTimer") {
killTimerStartup(self); killTimerStartup(self);
} else if (timerName == "KillTimer") { } else if (timerName == "KillTimer") {

View File

@@ -16,6 +16,7 @@
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "RenderComponent.h" #include "RenderComponent.h"
#include "eGameActivity.h" #include "eGameActivity.h"
#include "MovingPlatformComponent.h"
void SGCannon::OnStartup(Entity* self) { void SGCannon::OnStartup(Entity* self) {
LOG("OnStartup"); LOG("OnStartup");
@@ -326,11 +327,13 @@ void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
// Save the enemy and tell it to start pathing // Save the enemy and tell it to start pathing
if (enemy != nullptr) { if (enemy != nullptr) {
const_cast<std::vector<LWOOBJID>&>(self->GetVar<std::vector<LWOOBJID>>(SpawnedObjects)).push_back(enemy->GetObjectID()); const_cast<std::vector<LWOOBJID>&>(self->GetVar<std::vector<LWOOBJID>>(SpawnedObjects)).push_back(enemy->GetObjectID());
GameMessages::SendPlatformResync(enemy, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendPlatformResync(enemy, UNASSIGNED_SYSTEM_ADDRESS, eMovementPlatformState::Travelling);
} }
} }
} }
#pragma warning("TODO: FIX THE ABOVE GM CALL")
void SGCannon::EndGameBufferTimerFunc(Entity* self) { void SGCannon::EndGameBufferTimerFunc(Entity* self) {
RecordPlayerScore(self); RecordPlayerScore(self);
StopGame(self, false); StopGame(self, false);
@@ -346,7 +349,68 @@ void SGCannon::OnActivityTimerDone(Entity* self, const std::string& name) {
} else if (name == GameOverTimer) { } else if (name == GameOverTimer) {
GameOverTimerFunc(self); GameOverTimerFunc(self);
} else if (name.rfind(DoSpawnTimer, 0) == 0) { } else if (name.rfind(DoSpawnTimer, 0) == 0) {
DoSpawnTimerFunc(self, name); if (self->GetVar<bool>(GameStartedVariable)) {
const auto spawnNumber = (uint32_t)std::stoi(name.substr(7));
const auto& activeSpawns = self->GetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable);
if (activeSpawns.size() < spawnNumber) {
LOG("Trying to spawn %i when spawns size is only %i", spawnNumber, activeSpawns.size());
return;
}
const auto& toSpawn = activeSpawns.at(spawnNumber);
const auto pathIndex = GeneralUtils::GenerateRandomNumber<float_t>(0, toSpawn.spawnPaths.size() - 1);
const auto* path = Game::zoneManager->GetZone()->GetPath(toSpawn.spawnPaths.at(pathIndex));
if (!path) {
LOG("Path %s at index %i is null", toSpawn.spawnPaths.at(pathIndex).c_str(), pathIndex);
return;
}
auto info = EntityInfo{};
info.lot = toSpawn.lot;
info.spawnerID = self->GetObjectID();
info.pos = path->pathWaypoints.at(0).position;
info.settings = {
new LDFData<SGEnemy>(u"SpawnData", toSpawn),
new LDFData<std::string>(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"),
new LDFData<std::string>(u"custom_script_client", "scripts/client/ai/SG_TARGET_CLIENT.lua"),
new LDFData<std::string>(u"attached_path", path->pathName),
new LDFData<uint32_t>(u"attached_path_start", 0),
new LDFData<std::u16string>(u"groupID", u"SGEnemy")
};
LOG("Spawning enemy %i on path %s", toSpawn.lot, path->pathName.c_str());
auto* enemy = Game::entityManager->CreateEntity(info, nullptr, self);
Game::entityManager->ConstructEntity(enemy);
auto* movementAI = enemy->AddComponent<MovementAIComponent, MovementAIInfo>({});
movementAI->SetMaxSpeed(toSpawn.initialSpeed);
movementAI->SetCurrentSpeed(toSpawn.initialSpeed);
movementAI->SetHaltDistance(0.0f);
std::vector<NiPoint3> pathWaypoints;
for (const auto& waypoint : path->pathWaypoints) {
pathWaypoints.push_back(waypoint.position);
}
if (GeneralUtils::GenerateRandomNumber<float_t>(0, 1) < 0.5f) {
std::reverse(pathWaypoints.begin(), pathWaypoints.end());
}
movementAI->SetPath(pathWaypoints);
enemy->AddDieCallback([this, self, enemy, name]() {
RegisterHit(self, enemy, name);
});
// Save the enemy and tell it to start pathing
if (enemy != nullptr) {
const_cast<std::vector<LWOOBJID>&>(self->GetVar<std::vector<LWOOBJID>>(SpawnedObjects)).push_back(enemy->GetObjectID());
GameMessages::SendPlatformResync(enemy, UNASSIGNED_SYSTEM_ADDRESS, eMovementPlatformState::Travelling);
}
}
} else if (name == EndGameBufferTimer) { } else if (name == EndGameBufferTimer) {
EndGameBufferTimerFunc(self); EndGameBufferTimerFunc(self);
} }

View File

@@ -348,7 +348,7 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::BinaryRead(file, path.pathVersion); BinaryIO::BinaryRead(file, path.pathVersion);
BinaryIO::ReadString<uint8_t>(file, path.pathName, BinaryIO::ReadType::WideString); BinaryIO::ReadString<uint8_t>(file, path.pathName, BinaryIO::ReadType::WideString);
LOG("pathname: %s", path.pathName.c_str());
BinaryIO::BinaryRead(file, path.pathType); BinaryIO::BinaryRead(file, path.pathType);
BinaryIO::BinaryRead(file, path.flags); BinaryIO::BinaryRead(file, path.flags);
BinaryIO::BinaryRead(file, path.pathBehavior); BinaryIO::BinaryRead(file, path.pathBehavior);
@@ -420,6 +420,7 @@ void Zone::LoadPath(std::istream& file) {
if (path.pathType == PathType::MovingPlatform) { if (path.pathType == PathType::MovingPlatform) {
BinaryIO::BinaryRead(file, waypoint.movingPlatform.lockPlayer); BinaryIO::BinaryRead(file, waypoint.movingPlatform.lockPlayer);
BinaryIO::BinaryRead(file, waypoint.movingPlatform.speed); BinaryIO::BinaryRead(file, waypoint.movingPlatform.speed);
LOG("speed: %f", waypoint.movingPlatform.speed);
BinaryIO::BinaryRead(file, waypoint.movingPlatform.wait); BinaryIO::BinaryRead(file, waypoint.movingPlatform.wait);
if (path.pathVersion >= 13) { if (path.pathVersion >= 13) {
BinaryIO::ReadString<uint8_t>(file, waypoint.movingPlatform.departSound, BinaryIO::ReadType::WideString); BinaryIO::ReadString<uint8_t>(file, waypoint.movingPlatform.departSound, BinaryIO::ReadType::WideString);

View File

@@ -212,6 +212,7 @@ public:
const uint32_t GetChecksum() const { return m_CheckSum; } const uint32_t GetChecksum() const { return m_CheckSum; }
LUTriggers::Trigger* GetTrigger(uint32_t sceneID, uint32_t triggerID); LUTriggers::Trigger* GetTrigger(uint32_t sceneID, uint32_t triggerID);
const Path* GetPath(std::string name) const; const Path* GetPath(std::string name) const;
void AddPath(const Path& path) { m_Paths.push_back(path); };
uint32_t GetWorldID() const { return m_WorldID; } uint32_t GetWorldID() const { return m_WorldID; }
[[nodiscard]] std::string GetZoneName() const { return m_ZoneName; } [[nodiscard]] std::string GetZoneName() const { return m_ZoneName; }

View File

@@ -13,3 +13,28 @@ namespace Game {
EntityManager* entityManager = nullptr; EntityManager* entityManager = nullptr;
std::string projectVersion; std::string projectVersion;
} }
void GameDependenciesTest::SetUpDependencies() {
info.pos = NiPoint3Constant::ZERO;
info.rot = NiQuaternionConstant::IDENTITY;
info.scale = 1.0f;
info.spawner = nullptr;
info.lot = 999;
Game::logger = new Logger("./testing.log", true, true);
Game::server = new dServerMock();
Game::config = new dConfig("worldconfig.ini");
Game::entityManager = new EntityManager();
Game::zoneManager = new dZoneManager();
Game::zoneManager->LoadZone(LWOZONEID(0, 0, 0));
}
void GameDependenciesTest::TearDownDependencies() {
if (Game::server) delete Game::server;
if (Game::entityManager) delete Game::entityManager;
if (Game::config) delete Game::config;
if (Game::zoneManager) delete Game::zoneManager;
if (Game::logger) {
Game::logger->Flush();
delete Game::logger;
}
}

View File

@@ -7,6 +7,7 @@
#include "CDClientManager.h" #include "CDClientManager.h"
#include "EntityInfo.h" #include "EntityInfo.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "dZoneManager.h"
#include "dConfig.h" #include "dConfig.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
@@ -24,32 +25,11 @@ public:
class GameDependenciesTest : public ::testing::Test { class GameDependenciesTest : public ::testing::Test {
protected: protected:
void SetUpDependencies() { void SetUpDependencies();
info.pos = NiPoint3Constant::ZERO;
info.rot = NiQuaternionConstant::IDENTITY;
info.scale = 1.0f;
info.spawner = nullptr;
info.lot = 999;
Game::logger = new Logger("./testing.log", true, true);
Game::server = new dServerMock();
Game::config = new dConfig("worldconfig.ini");
Game::entityManager = new EntityManager();
// Create a CDClientManager instance and load from defaults void TearDownDependencies();
CDClientManager::LoadValuesFromDefaults();
}
void TearDownDependencies() { EntityInfo info;
if (Game::server) delete Game::server;
if (Game::entityManager) delete Game::entityManager;
if (Game::logger) {
Game::logger->Flush();
delete Game::logger;
}
if (Game::config) delete Game::config;
}
EntityInfo info{};
}; };
#endif //!__GAMEDEPENDENCIES__H__ #endif //!__GAMEDEPENDENCIES__H__

View File

@@ -1,5 +1,6 @@
set(DCOMPONENTS_TESTS set(DCOMPONENTS_TESTS
"DestroyableComponentTests.cpp" "DestroyableComponentTests.cpp"
"MovingPlatformComponentTests.cpp"
"PetComponentTests.cpp" "PetComponentTests.cpp"
"SimplePhysicsComponentTests.cpp" "SimplePhysicsComponentTests.cpp"
) )

View File

@@ -0,0 +1,451 @@
#include "GameDependencies.h"
#include <gtest/gtest.h>
#include "BitStream.h"
#include "Entity.h"
#define _MOVING_PLATFORM_TEST
#include "MovingPlatformComponent.h"
#undef _MOVING_PLATFORM_TEST
#include "Zone.h"
#include "SimplePhysicsComponent.h"
#include "eReplicaComponentType.h"
class MovingPlatformComponentTests : public GameDependenciesTest {
protected:
std::unique_ptr<Entity> baseEntity;
CBITSTREAM;
uint32_t flags = 0;
Path pathOnce;
Path pathBounce;
Path pathLoop;
void SetUp() override {
SetUpDependencies();
PathWaypoint waypointStart;
waypointStart.position = NiPoint3(1, 2, 3);
waypointStart.rotation = NiQuaternion(4, 5, 6, 7);
waypointStart.movingPlatform.speed = 16.0f;
PathWaypoint waypointMiddle;
waypointMiddle.position = NiPoint3(4, 5, 6);
waypointMiddle.rotation = NiQuaternion(7, 8, 9, 10);
waypointStart.movingPlatform.speed = 16.0f;
PathWaypoint waypointEnd;
waypointEnd.position = NiPoint3(4, 5, 7);
waypointEnd.rotation = NiQuaternion(7, 8, 9, 10);
waypointStart.movingPlatform.speed = 16.0f;
{
pathOnce.movingPlatform.timeBasedMovement = false;
pathOnce.pathBehavior = PathBehavior::Once;
pathOnce.pathName = "ExampleOncePath";
pathOnce.pathWaypoints.push_back(waypointStart);
pathOnce.pathWaypoints.push_back(waypointMiddle);
pathOnce.pathWaypoints.push_back(waypointEnd);
Game::zoneManager->GetZone()->AddPath(pathOnce);
}
{
pathBounce.movingPlatform.timeBasedMovement = false;
pathBounce.pathBehavior = PathBehavior::Bounce;
pathBounce.pathName = "ExampleBouncePath";
pathBounce.pathWaypoints.push_back(waypointStart);
pathBounce.pathWaypoints.push_back(waypointMiddle);
pathBounce.pathWaypoints.push_back(waypointEnd);
Game::zoneManager->GetZone()->AddPath(pathBounce);
}
{
pathLoop.movingPlatform.timeBasedMovement = false;
pathLoop.pathBehavior = PathBehavior::Loop;
pathLoop.pathName = "ExampleLoopPath";
pathLoop.pathWaypoints.push_back(waypointStart);
pathLoop.pathWaypoints.push_back(waypointMiddle);
pathLoop.pathWaypoints.push_back(waypointEnd);
Game::zoneManager->GetZone()->AddPath(pathLoop);
}
// Set our starting position
info.pos = NiPoint3(25, 26, 27);
info.rot = NiQuaternion(28, 29, 30, 31);
// Simple mover data
info.settings.push_back(new LDFData<float>(u"platformMoveX", 23));
info.settings.push_back(new LDFData<float>(u"platformMoveY", 453));
info.settings.push_back(new LDFData<float>(u"platformMoveZ", 523));
info.settings.push_back(new LDFData<float>(u"platformMoveTime", 5724));
info.settings.push_back(new LDFData<bool>(u"platformStartAtEnd", true));
info.settings.push_back(new LDFData<bool>(u"dbonly", false));
info.settings.push_back(new LDFData<bool>(u"platformIsMover", true));
info.settings.push_back(new LDFData<bool>(u"platformIsSimpleMover", true));
info.settings.push_back(new LDFData<bool>(u"platformIsRotater", true));
baseEntity = std::make_unique<Entity>(15, GameDependenciesTest::info);
auto* simplePhysicsComponent = baseEntity->AddComponent<SimplePhysicsComponent>(1);
auto* movingPlatformComponent = baseEntity->AddComponent<MovingPlatformComponent>("ExampleOncePath");
movingPlatformComponent->LoadConfigData();
movingPlatformComponent->LoadDataFromTemplate();
}
void TearDown() override {
TearDownDependencies();
}
void DeserializeSimpleMoverPlatformSubComponent() {
bool dirtyStartingPoint = false;
bitStream.Read(dirtyStartingPoint);
ASSERT_TRUE(dirtyStartingPoint);
bool hasStartingPoint = false;
bitStream.Read(hasStartingPoint);
ASSERT_TRUE(hasStartingPoint);
NiPoint3 startingPoint;
bitStream.Read(startingPoint.x);
bitStream.Read(startingPoint.y);
bitStream.Read(startingPoint.z);
ASSERT_EQ(startingPoint, NiPoint3(25, 26, 27));
NiQuaternion startingRotation;
bitStream.Read(startingRotation.w);
bitStream.Read(startingRotation.x);
bitStream.Read(startingRotation.y);
bitStream.Read(startingRotation.z);
ASSERT_EQ(startingRotation, NiQuaternion(28, 29, 30, 31));
bool isDirty;
bitStream.Read(isDirty);
ASSERT_TRUE(isDirty);
eMovementPlatformState state;
bitStream.Read(state);
ASSERT_EQ(state, eMovementPlatformState::Stopped | eMovementPlatformState::ReachedDesiredWaypoint);
int32_t currentWaypointIndex;
bitStream.Read(currentWaypointIndex);
ASSERT_EQ(currentWaypointIndex, 0);
bool isInReverse;
bitStream.Read(isInReverse);
ASSERT_FALSE(isInReverse);
}
void DeserializeMovingPlatformSubComponent() {
bool isDirty;
ASSERT_TRUE(bitStream.Read(isDirty));
ASSERT_TRUE(isDirty);
eMovementPlatformState state;
ASSERT_TRUE(bitStream.Read(state));
ASSERT_EQ(state, eMovementPlatformState::Stopped | eMovementPlatformState::ReachedDesiredWaypoint);
int32_t desiredWaypointIndex;
ASSERT_TRUE(bitStream.Read(desiredWaypointIndex));
ASSERT_EQ(desiredWaypointIndex, -1);
bool shouldStopAtDesiredWaypoint;
ASSERT_TRUE(bitStream.Read(shouldStopAtDesiredWaypoint));
ASSERT_FALSE(shouldStopAtDesiredWaypoint);
bool isInReverse2;
ASSERT_TRUE(bitStream.Read(isInReverse2));
ASSERT_FALSE(isInReverse2);
float percentBetweenPoints;
ASSERT_TRUE(bitStream.Read(percentBetweenPoints));
ASSERT_EQ(percentBetweenPoints, 0);
NiPoint3 position;
ASSERT_TRUE(bitStream.Read(position.x));
ASSERT_TRUE(bitStream.Read(position.y));
ASSERT_TRUE(bitStream.Read(position.z));
ASSERT_EQ(position, NiPoint3(25, 26, 27));
uint32_t currentWaypointIndex;
ASSERT_TRUE(bitStream.Read(currentWaypointIndex));
ASSERT_EQ(currentWaypointIndex, 0);
uint32_t nextWaypointIndex;
ASSERT_TRUE(bitStream.Read(nextWaypointIndex));
ASSERT_EQ(nextWaypointIndex, -1);
float idleTimeElapsed;
ASSERT_TRUE(bitStream.Read(idleTimeElapsed));
ASSERT_FLOAT_EQ(idleTimeElapsed, 0.0f);
float moveTimeElapsed;
ASSERT_TRUE(bitStream.Read(moveTimeElapsed));
ASSERT_FLOAT_EQ(moveTimeElapsed, 0.0f);
}
void DeserializeMovingPlatformComponent() {
// read in the full BitStream and check the values match what they were set to above
bool hasPlatformSubComponents = false;
ASSERT_TRUE(bitStream.Read(hasPlatformSubComponents));
ASSERT_TRUE(hasPlatformSubComponents);
bool dirtyPathInfo;
ASSERT_TRUE(bitStream.Read(dirtyPathInfo));
ASSERT_TRUE(dirtyPathInfo);
bool hasPath;
ASSERT_TRUE(bitStream.Read(hasPath));
ASSERT_TRUE(hasPath);
std::u16string pathName;
uint16_t pathNameLength;
ASSERT_TRUE(bitStream.Read(pathNameLength));
pathName.resize(pathNameLength);
ASSERT_TRUE(bitStream.ReadBits(reinterpret_cast<unsigned char*>(pathName.data()), BYTES_TO_BITS(pathNameLength) * 2));
ASSERT_EQ(pathName, u"ExampleOncePath");
uint32_t pathStartIndex;
ASSERT_TRUE(bitStream.Read(pathStartIndex));
ASSERT_EQ(pathStartIndex, 0);
bool isInReverse;
ASSERT_TRUE(bitStream.Read(isInReverse));
ASSERT_FALSE(isInReverse);
bool hasPlatformData;
ASSERT_TRUE(bitStream.Read(hasPlatformData));
ASSERT_TRUE(hasPlatformData);
eMoverSubComponentType platformType;
ASSERT_TRUE(bitStream.Read(platformType));
ASSERT_EQ(platformType, eMoverSubComponentType::Mover);
DeserializeMovingPlatformSubComponent();
ASSERT_TRUE(bitStream.Read(hasPlatformData));
ASSERT_TRUE(hasPlatformData);
ASSERT_TRUE(bitStream.Read(platformType));
ASSERT_EQ(platformType, eMoverSubComponentType::SimpleMover);
DeserializeSimpleMoverPlatformSubComponent();
ASSERT_TRUE(bitStream.Read(hasPlatformData));
ASSERT_TRUE(hasPlatformData);
ASSERT_TRUE(bitStream.Read(platformType));
ASSERT_EQ(platformType, eMoverSubComponentType::Rotator);
DeserializeMovingPlatformSubComponent();
ASSERT_TRUE(bitStream.Read(hasPlatformData));
ASSERT_FALSE(hasPlatformData);
}
void TestSerialization() {
auto* movingPlatformComponent = baseEntity->GetComponent<MovingPlatformComponent>();
ASSERT_NE(movingPlatformComponent, nullptr);
movingPlatformComponent->Serialize(&bitStream, true);
DeserializeMovingPlatformComponent();
}
};
TEST_F(MovingPlatformComponentTests, MovingPlatformConstructionTest) {
TestSerialization();
}
TEST_F(MovingPlatformComponentTests, MovingPlatformSerializationTest) {
TestSerialization();
}
TEST_F(MovingPlatformComponentTests, MovingPlatformSubComponentPathOnceAdvanceTest) {
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExampleOncePath", 0, false);
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 0);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 1);
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 1);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 2);
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 2);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 2);
ASSERT_FALSE(moverPlatformSubComponent.GetInReverse());
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 2);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 2);
ASSERT_FALSE(moverPlatformSubComponent.GetInReverse());
}
TEST_F(MovingPlatformComponentTests, MovingPlatformSubComponentPathBounceAdvanceTest) {
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExampleBouncePath", 0, false);
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 0);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 1);
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 1);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 2);
ASSERT_FALSE(moverPlatformSubComponent.GetInReverse());
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 2);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 1);
ASSERT_TRUE(moverPlatformSubComponent.GetInReverse());
moverPlatformSubComponent.AdvanceToNextReverseWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 1);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 0);
ASSERT_TRUE(moverPlatformSubComponent.GetInReverse());
moverPlatformSubComponent.AdvanceToNextReverseWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 0);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 1);
ASSERT_FALSE(moverPlatformSubComponent.GetInReverse());
}
TEST_F(MovingPlatformComponentTests, MovingPlatformSubComponentLoopAdvanceTest) {
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExampleLoopPath", 0, false);
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 0);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 1);
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 1);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 2);
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 2);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 0);
ASSERT_FALSE(moverPlatformSubComponent.GetInReverse());
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 0);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 1);
ASSERT_FALSE(moverPlatformSubComponent.GetInReverse());
}
TEST_F(MovingPlatformComponentTests, MovingPlatformSubComponentLoopAdvanceReverseTest) {
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExampleLoopPath", 0, true);
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 0);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 2);
ASSERT_TRUE(moverPlatformSubComponent.GetInReverse());
moverPlatformSubComponent.AdvanceToNextReverseWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 2);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 1);
ASSERT_TRUE(moverPlatformSubComponent.GetInReverse());
moverPlatformSubComponent.AdvanceToNextReverseWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 1);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 0);
ASSERT_TRUE(moverPlatformSubComponent.GetInReverse());
moverPlatformSubComponent.AdvanceToNextReverseWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypointIndex(), 0);
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypointIndex(), 2);
ASSERT_TRUE(moverPlatformSubComponent.GetInReverse());
}
TEST_F(MovingPlatformComponentTests, MovingPlatformMoverSpeedCalculationTest) {
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExampleOncePath", 0, false);
ASSERT_EQ(moverPlatformSubComponent.CalculateSpeed(), 16.0f);
NiPoint3 r = moverPlatformSubComponent.CalculateLinearVelocity();
ASSERT_FLOAT_EQ(r.x, 9.2376051);
ASSERT_FLOAT_EQ(r.y, 9.2376051);
ASSERT_FLOAT_EQ(r.z, 9.2376051);
moverPlatformSubComponent.AdvanceToNextWaypoint();
}
TEST_F(MovingPlatformComponentTests, MovingPlatformNextAndCurrentWaypointAccess) {
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExampleOncePath", 0, false);
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypoint().position, NiPoint3(1, 2, 3));
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypoint().position, NiPoint3(4, 5, 6));
moverPlatformSubComponent.AdvanceToNextWaypoint();
ASSERT_EQ(moverPlatformSubComponent.GetCurrentWaypoint().position, NiPoint3(4, 5, 6));
ASSERT_EQ(moverPlatformSubComponent.GetNextWaypoint().position, NiPoint3(4, 5, 7));
}
TEST_F(MovingPlatformComponentTests, MovingPlatformRunTest) {
baseEntity->SetPosition(NiPoint3(99.296440f, 419.293335f, 207.219498f));
Path path;
PathWaypoint waypointStart;
waypointStart.position = NiPoint3(99.296440f, 419.293335f, 207.219498f);
waypointStart.rotation = NiQuaternion(0, 0, 0, 0);
waypointStart.movingPlatform.speed = 16.0f;
PathWaypoint waypointEnd;
waypointEnd.position = NiPoint3(141.680099f, 419.990051f, 208.680450f);
waypointEnd.rotation = NiQuaternion(0, 0, 0, 0);
waypointStart.movingPlatform.speed = 16.0f;
path.movingPlatform.timeBasedMovement = false;
path.pathBehavior = PathBehavior::Once;
path.pathName = "ExamplePath";
path.pathWaypoints.push_back(waypointStart);
path.pathWaypoints.push_back(waypointEnd);
Game::zoneManager->GetZone()->AddPath(path);
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExamplePath", 0, false);
moverPlatformSubComponent.UpdateLinearVelocity();
moverPlatformSubComponent.StartPathing();
moverPlatformSubComponent.Update(2.65f);
auto [x,y,z] = moverPlatformSubComponent.GetPosition();
// just check that its close enough
EXPECT_LT(141.680099f - x, 0.1f);
EXPECT_LT(419.990051f - y, 0.1f);
EXPECT_LT(208.680450f - z, 0.1f);
}
TEST_F(MovingPlatformComponentTests, MovingPlatformPercentBetweenPointsTest) {
Path path;
PathWaypoint waypointStart;
waypointStart.position = NiPoint3(0, 0, 0);
waypointStart.rotation = NiQuaternion(0, 0, 0, 0);
waypointStart.movingPlatform.speed = 16.0f;
PathWaypoint waypointEnd;
waypointEnd.position = NiPoint3(2, 0, 0);
waypointEnd.rotation = NiQuaternion(0, 0, 0, 0);
waypointStart.movingPlatform.speed = 16.0f;
path.movingPlatform.timeBasedMovement = false;
path.pathBehavior = PathBehavior::Once;
path.pathName = "ExamplePath";
path.pathWaypoints.push_back(waypointStart);
path.pathWaypoints.push_back(waypointEnd);
Game::zoneManager->GetZone()->AddPath(path);
{
baseEntity->SetPosition(NiPoint3(0, 0, 0));
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExamplePath", 0, false);
EXPECT_FLOAT_EQ(moverPlatformSubComponent.CalculatePercentToNextWaypoint(), 0.0f);
}
{
baseEntity->SetPosition(NiPoint3(1, 0, 0));
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExamplePath", 0, false);
EXPECT_FLOAT_EQ(moverPlatformSubComponent.CalculatePercentToNextWaypoint(), 0.5f);
}
{
baseEntity->SetPosition(NiPoint3(2, 0, 0));
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExamplePath", 0, false);
EXPECT_FLOAT_EQ(moverPlatformSubComponent.CalculatePercentToNextWaypoint(), 1.0f);
}
{
path.movingPlatform.timeBasedMovement = true;
path.pathName = "ExamplePath2";
Game::zoneManager->GetZone()->AddPath(path);
baseEntity->SetPosition(NiPoint3(1, 0, 0));
MoverPlatformSubComponent moverPlatformSubComponent(baseEntity->GetComponent<MovingPlatformComponent>());
moverPlatformSubComponent.SetupPath("ExamplePath2", 0, false);
ASSERT_FLOAT_EQ(moverPlatformSubComponent.CalculatePercentToNextWaypoint(), 0.0f);
}
}