feat: Add MovingAI pathing for NPCs without combatAI (#1509)

* remove goto

* Update MovementAIComponent.cpp

* convert to PathWaypoint

Easier for usage with paths

* add path parsing

* ref removal, simplification of work

* it works

* Update MovementAIComponent.cpp

* disable pathing for combat

we just need it for npcs for now, combat ai can be done later

* fixed stuttery enemies

wow

* start at ramped up speed

* add pausing and resuming

* Update MovementAIComponent.cpp

* Update MovementAIComponent.h

* Update CMakeLists.txt
This commit is contained in:
David Markowitz 2024-03-26 19:06:22 -07:00 committed by GitHub
parent 39b81b6263
commit bd9b790e1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 164 additions and 85 deletions

View File

@ -731,15 +731,21 @@ void Entity::Initialize() {
// if we have a moving platform path, then we need a moving platform component // if we have a moving platform path, then we need a moving platform component
if (path->pathType == PathType::MovingPlatform) { if (path->pathType == PathType::MovingPlatform) {
AddComponent<MovingPlatformComponent>(pathName); AddComponent<MovingPlatformComponent>(pathName);
// else if we are a movement path } else if (path->pathType == PathType::Movement) {
} /*else if (path->pathType == PathType::Movement) { auto movementAIcomponent = GetComponent<MovementAIComponent>();
auto movementAIcomp = GetComponent<MovementAIComponent>(); if (movementAIcomponent && combatAiId == 0) {
if (movementAIcomp){ movementAIcomponent->SetPath(pathName);
// TODO: set path in existing movementAIComp
} else { } else {
// TODO: create movementAIcomp and set path MovementAIInfo moveInfo = MovementAIInfo();
moveInfo.movementType = "";
moveInfo.wanderChance = 0;
moveInfo.wanderRadius = 16;
moveInfo.wanderSpeed = 2.5f;
moveInfo.wanderDelayMax = 5;
moveInfo.wanderDelayMin = 2;
AddComponent<MovementAIComponent>(moveInfo);
} }
}*/ }
} else { } else {
// else we still need to setup moving platform if it has a moving platform comp but no path // else we still need to setup moving platform if it has a moving platform comp but no path
int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1); int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);

View File

@ -50,9 +50,42 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) :
m_CurrentSpeed = 0; m_CurrentSpeed = 0;
m_MaxSpeed = 0; m_MaxSpeed = 0;
m_LockRotation = false; m_LockRotation = false;
m_Path = nullptr;
m_SourcePosition = m_Parent->GetPosition();
m_Paused = false;
m_SavedVelocity = NiPoint3Constant::ZERO;
if (!m_Parent->GetComponent<BaseCombatAIComponent>()) SetPath(m_Parent->GetVarAsString(u"attached_path"));
}
void MovementAIComponent::SetPath(const std::string pathName) {
m_Path = Game::zoneManager->GetZone()->GetPath(pathName);
if (!pathName.empty()) LOG("WARNING: %s path %s", m_Path ? "Found" : "Failed to find", pathName.c_str());
if (!m_Path) return;
SetMaxSpeed(1);
SetCurrentSpeed(m_BaseSpeed);
SetPath(m_Path->pathWaypoints);
}
void MovementAIComponent::Pause() {
m_Paused = true;
SetPosition(ApproximateLocation());
m_SavedVelocity = GetVelocity();
SetVelocity(NiPoint3Constant::ZERO);
Game::entityManager->SerializeEntity(m_Parent);
}
void MovementAIComponent::Resume() {
m_Paused = false;
SetVelocity(m_SavedVelocity);
m_SavedVelocity = NiPoint3Constant::ZERO;
SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), m_NextWaypoint));
Game::entityManager->SerializeEntity(m_Parent);
} }
void MovementAIComponent::Update(const float deltaTime) { void MovementAIComponent::Update(const float deltaTime) {
if (m_Paused) return;
if (m_PullingToPoint) { if (m_PullingToPoint) {
const auto source = GetCurrentWaypoint(); const auto source = GetCurrentWaypoint();
@ -81,27 +114,24 @@ void MovementAIComponent::Update(const float deltaTime) {
} }
m_TimeTravelled += deltaTime; m_TimeTravelled += deltaTime;
SetPosition(ApproximateLocation());
if (m_TimeTravelled < m_TimeToTravel) return; if (m_TimeTravelled < m_TimeToTravel) return;
m_TimeTravelled = 0.0f; m_TimeTravelled = 0.0f;
const auto source = GetCurrentWaypoint(); const auto source = GetCurrentWaypoint();
SetPosition(source); SetPosition(source);
m_SourcePosition = source;
NiPoint3 velocity = NiPoint3Constant::ZERO;
if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek? if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek?
{ {
m_NextWaypoint = GetCurrentWaypoint(); m_NextWaypoint = GetCurrentWaypoint();
if (m_NextWaypoint == source) { if (m_NextWaypoint == source) {
m_TimeToTravel = 0.0f; m_TimeToTravel = 0.0f;
} else { } else {
if (m_CurrentSpeed < m_MaxSpeed) { m_CurrentSpeed = std::min(m_CurrentSpeed + m_Acceleration, m_MaxSpeed);
m_CurrentSpeed += m_Acceleration;
}
m_CurrentSpeed = std::min(m_CurrentSpeed, m_MaxSpeed);
const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed
@ -110,7 +140,7 @@ void MovementAIComponent::Update(const float deltaTime) {
// Normalize the vector // Normalize the vector
const auto length = delta.Length(); const auto length = delta.Length();
if (length > 0.0f) { if (length > 0.0f) {
velocity = (delta / length) * speed; SetVelocity((delta / length) * speed);
} }
// Calclute the time it will take to reach the next waypoint with the current speed // Calclute the time it will take to reach the next waypoint with the current speed
@ -122,17 +152,27 @@ void MovementAIComponent::Update(const float deltaTime) {
} else { } else {
// Check if there are more waypoints in the queue, if so set our next destination to the next waypoint // Check if there are more waypoints in the queue, if so set our next destination to the next waypoint
if (m_CurrentPath.empty()) { if (m_CurrentPath.empty()) {
Stop(); if (m_Path) {
if (m_Path->pathBehavior == PathBehavior::Loop) {
return; SetPath(m_Path->pathWaypoints);
} else if (m_Path->pathBehavior == PathBehavior::Bounce) {
std::vector<PathWaypoint> waypoints = m_Path->pathWaypoints;
std::reverse(waypoints.begin(), waypoints.end());
SetPath(waypoints);
} else if (m_Path->pathBehavior == PathBehavior::Once) {
Stop();
return;
}
} else {
Stop();
return;
}
} }
SetDestination(m_CurrentPath.top()); SetDestination(m_CurrentPath.top().position);
m_CurrentPath.pop(); m_CurrentPath.pop();
} }
SetVelocity(velocity);
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }
@ -155,7 +195,7 @@ NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
} }
NiPoint3 MovementAIComponent::ApproximateLocation() const { NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = m_Parent->GetPosition(); auto source = m_SourcePosition;
if (AtFinalWaypoint()) return source; if (AtFinalWaypoint()) return source;
@ -221,13 +261,13 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
m_PullPoint = point; m_PullPoint = point;
} }
void MovementAIComponent::SetPath(std::vector<NiPoint3> path) { void MovementAIComponent::SetPath(std::vector<PathWaypoint> path) {
if (path.empty()) return; if (path.empty()) return;
std::for_each(path.rbegin(), path.rend() - 1, [this](const NiPoint3& point) { std::for_each(path.rbegin(), path.rend() - 1, [this](const PathWaypoint& point) {
this->m_CurrentPath.push(point); this->m_CurrentPath.push(point);
}); });
SetDestination(path.front()); SetDestination(path.front().position);
} }
float MovementAIComponent::GetBaseSpeed(LOT lot) { float MovementAIComponent::GetBaseSpeed(LOT lot) {
@ -272,6 +312,23 @@ void MovementAIComponent::SetRotation(const NiQuaternion& value) {
if (!m_LockRotation) m_Parent->SetRotation(value); if (!m_LockRotation) m_Parent->SetRotation(value);
} }
NiPoint3 MovementAIComponent::GetVelocity() const {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
return controllablePhysicsComponent->GetVelocity();
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
return simplePhysicsComponent->GetVelocity();
}
return NiPoint3Constant::ZERO;
}
void MovementAIComponent::SetVelocity(const NiPoint3& value) { void MovementAIComponent::SetVelocity(const NiPoint3& value) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
@ -288,7 +345,7 @@ void MovementAIComponent::SetVelocity(const NiPoint3& value) {
} }
} }
void MovementAIComponent::SetDestination(const NiPoint3& destination) { void MovementAIComponent::SetDestination(const NiPoint3 destination) {
if (m_PullingToPoint) return; if (m_PullingToPoint) return;
const auto location = ApproximateLocation(); const auto location = ApproximateLocation();
@ -297,6 +354,8 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) {
SetPosition(location); SetPosition(location);
} }
m_SourcePosition = location;
std::vector<NiPoint3> computedPath; std::vector<NiPoint3> computedPath;
if (dpWorld::IsLoaded()) { if (dpWorld::IsLoaded()) {
computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed); computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed);

View File

@ -14,11 +14,14 @@
#include "Logger.h" #include "Logger.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "Zone.h"
#include <vector> #include <vector>
class ControllablePhysicsComponent; class ControllablePhysicsComponent;
class BaseCombatAIComponent; class BaseCombatAIComponent;
struct Path;
/** /**
* Information that describes the different variables used to make an entity move around * Information that describes the different variables used to make an entity move around
*/ */
@ -61,6 +64,8 @@ public:
MovementAIComponent(Entity* parentEntity, MovementAIInfo info); MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
void SetPath(const std::string pathName);
void Update(float deltaTime) override; void Update(float deltaTime) override;
/** /**
@ -73,7 +78,7 @@ public:
* Set a destination point for the entity to move towards * Set a destination point for the entity to move towards
* @param value the destination point to move towards * @param value the destination point to move towards
*/ */
void SetDestination(const NiPoint3& value); void SetDestination(const NiPoint3 value);
/** /**
* Returns the current rotation this entity is moving towards * Returns the current rotation this entity is moving towards
@ -189,7 +194,13 @@ public:
* Sets a path to follow for the AI * Sets a path to follow for the AI
* @param path the path to follow * @param path the path to follow
*/ */
void SetPath(std::vector<NiPoint3> path); void SetPath(std::vector<PathWaypoint> path);
void Pause();
void Resume();
NiPoint3 GetVelocity() const;
/** /**
* Returns the base speed from the DB for a given LOT * Returns the base speed from the DB for a given LOT
@ -301,7 +312,15 @@ private:
/** /**
* The path from the current position to the destination. * The path from the current position to the destination.
*/ */
std::stack<NiPoint3> m_CurrentPath; std::stack<PathWaypoint> m_CurrentPath;
const Path* m_Path = nullptr;
NiPoint3 m_SourcePosition;
bool m_Paused;
NiPoint3 m_SavedVelocity;
}; };
#endif // MOVEMENTAICOMPONENT_H #endif // MOVEMENTAICOMPONENT_H

View File

@ -64,6 +64,7 @@ void ProximityMonitorComponent::Update(float deltaTime) {
for (const auto& prox : m_ProximitiesData) { for (const auto& prox : m_ProximitiesData) {
if (!prox.second) continue; if (!prox.second) continue;
prox.second->SetPosition(m_Parent->GetPosition());
//Process enter events //Process enter events
for (auto* en : prox.second->GetNewObjects()) { for (auto* en : prox.second->GetNewObjects()) {
m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "ENTER"); m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "ENTER");

View File

@ -5,7 +5,6 @@
void WanderingVendor::OnStartup(Entity* self) { void WanderingVendor::OnStartup(Entity* self) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>(); auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return; if (!movementAIComponent) return;
// movementAIComponent->Resume();
self->SetProximityRadius(10, "playermonitor"); self->SetProximityRadius(10, "playermonitor");
} }
@ -13,7 +12,7 @@ void WanderingVendor::OnProximityUpdate(Entity* self, Entity* entering, std::str
if (status == "ENTER" && entering->IsPlayer()) { if (status == "ENTER" && entering->IsPlayer()) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>(); auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return; if (!movementAIComponent) return;
// movementAIComponent->Pause(); movementAIComponent->Pause();
self->CancelTimer("startWalking"); self->CancelTimer("startWalking");
} else if (status == "LEAVE") { } else if (status == "LEAVE") {
auto* proximityMonitorComponent = self->GetComponent<ProximityMonitorComponent>(); auto* proximityMonitorComponent = self->GetComponent<ProximityMonitorComponent>();
@ -28,6 +27,6 @@ void WanderingVendor::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "startWalking") { if (timerName == "startWalking") {
auto movementAIComponent = self->GetComponent<MovementAIComponent>(); auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return; if (!movementAIComponent) return;
// movementAIComponent->Resume(); movementAIComponent->Resume();
} }
} }

View File

@ -307,11 +307,7 @@ void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
movementAI->SetCurrentSpeed(toSpawn.initialSpeed); movementAI->SetCurrentSpeed(toSpawn.initialSpeed);
movementAI->SetHaltDistance(0.0f); movementAI->SetHaltDistance(0.0f);
std::vector<NiPoint3> pathWaypoints; std::vector<PathWaypoint> pathWaypoints = path->pathWaypoints;
for (const auto& waypoint : path->pathWaypoints) {
pathWaypoints.push_back(waypoint.position);
}
if (GeneralUtils::GenerateRandomNumber<float_t>(0, 1) < 0.5f) { if (GeneralUtils::GenerateRandomNumber<float_t>(0, 1) < 0.5f) {
std::reverse(pathWaypoints.begin(), pathWaypoints.end()); std::reverse(pathWaypoints.begin(), pathWaypoints.end());

View File

@ -5,20 +5,19 @@
void WblRobotCitizen::OnStartup(Entity* self) { void WblRobotCitizen::OnStartup(Entity* self) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>(); auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return; if (!movementAIComponent) return;
// movementAIComponent->Resume();
} }
void WblRobotCitizen::OnUse(Entity* self, Entity* user) { void WblRobotCitizen::OnUse(Entity* self, Entity* user) {
// auto movementAIComponent = self->GetComponent<MovementAIComponent>(); auto movementAIComponent = self->GetComponent<MovementAIComponent>();
// if (!movementAIComponent) movementAIComponent->Pause(); if (movementAIComponent) movementAIComponent->Pause();
auto face = NiQuaternion::LookAt(self->GetPosition(), user->GetPosition()); auto face = NiQuaternion::LookAt(self->GetPosition(), user->GetPosition());
self->SetRotation(face); self->SetRotation(face);
auto timer = RenderComponent::PlayAnimation(self, "wave"); auto timer = RenderComponent::PlayAnimation(self, "wave", 0.4f);
self->AddTimer("animation time", timer); self->AddTimer("animation time", timer);
} }
void WblRobotCitizen::OnTimerDone(Entity* self, std::string timerName) { void WblRobotCitizen::OnTimerDone(Entity* self, std::string timerName) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>(); auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return; if (!movementAIComponent) return;
// movementAIComponent->Resume(); movementAIComponent->Resume();
} }

View File

@ -16,57 +16,57 @@ class Level;
enum class eWaypointCommandType : uint32_t; enum class eWaypointCommandType : uint32_t;
struct WaypointCommand { struct WaypointCommand {
eWaypointCommandType command; eWaypointCommandType command{};
std::string data; std::string data;
}; };
struct SceneRef { struct SceneRef {
std::string filename; std::string filename;
uint32_t id; uint32_t id{};
uint32_t sceneType; //0 = general, 1 = audio? uint32_t sceneType{}; //0 = general, 1 = audio?
std::string name; std::string name;
NiPoint3 unknown1; NiPoint3 unknown1;
float unknown2; float unknown2{};
uint8_t color_r; uint8_t color_r{};
uint8_t color_g; uint8_t color_g{};
uint8_t color_b; uint8_t color_b{};
Level* level; Level* level;
std::map<uint32_t, LUTriggers::Trigger*> triggers; std::map<uint32_t, LUTriggers::Trigger*> triggers;
}; };
struct SceneTransitionInfo { struct SceneTransitionInfo {
uint64_t sceneID; //id of the scene being transitioned to. uint64_t sceneID{}; //id of the scene being transitioned to.
NiPoint3 position; NiPoint3 position;
}; };
struct SceneTransition { struct SceneTransition {
std::string name; std::string name;
std::vector<SceneTransitionInfo> points; std::vector<SceneTransitionInfo> points;
float width; float width{};
}; };
struct MovingPlatformPathWaypoint { struct MovingPlatformPathWaypoint {
uint8_t lockPlayer; uint8_t lockPlayer{};
float wait; float wait{};
std::string departSound; std::string departSound;
std::string arriveSound; std::string arriveSound;
}; };
struct CameraPathWaypoint { struct CameraPathWaypoint {
float time; float time{};
float fov; float fov{};
float tension; float tension{};
float continuity; float continuity{};
float bias; float bias{};
}; };
struct RacingPathWaypoint { struct RacingPathWaypoint {
uint8_t isResetNode; uint8_t isResetNode{};
uint8_t isNonHorizontalCamera; uint8_t isNonHorizontalCamera{};
float planeWidth; float planeWidth{};
float planeHeight; float planeHeight{};
float shortestDistanceToEnd; float shortestDistanceToEnd{};
}; };
struct PathWaypoint { struct PathWaypoint {
@ -75,7 +75,7 @@ struct PathWaypoint {
MovingPlatformPathWaypoint movingPlatform; MovingPlatformPathWaypoint movingPlatform;
CameraPathWaypoint camera; CameraPathWaypoint camera;
RacingPathWaypoint racing; RacingPathWaypoint racing;
float speed; float speed{};
std::vector<LDFBaseData*> config; std::vector<LDFBaseData*> config;
std::vector<WaypointCommand> commands; std::vector<WaypointCommand> commands;
}; };
@ -137,49 +137,49 @@ enum class PropertyAchievmentRequired : uint32_t {
struct MovingPlatformPath { struct MovingPlatformPath {
std::string platformTravelSound; std::string platformTravelSound;
uint8_t timeBasedMovement; uint8_t timeBasedMovement{};
}; };
struct PropertyPath { struct PropertyPath {
PropertyPathType pathType; PropertyPathType pathType{};
int32_t price; int32_t price{};
uint32_t rentalTime; uint32_t rentalTime{};
uint64_t associatedZone; uint64_t associatedZone{};
std::string displayName; std::string displayName;
std::string displayDesc; std::string displayDesc;
PropertyType type; PropertyType type{};
uint32_t cloneLimit; uint32_t cloneLimit{};
float repMultiplier; float repMultiplier{};
PropertyRentalPeriod rentalPeriod; PropertyRentalPeriod rentalPeriod{};
PropertyAchievmentRequired achievementRequired; PropertyAchievmentRequired achievementRequired{};
// Player respawn coordinates in the main zone (not the property zone) // Player respawn coordinates in the main zone (not the property zone)
NiPoint3 playerZoneCoords; NiPoint3 playerZoneCoords;
float maxBuildHeight; float maxBuildHeight{};
}; };
struct CameraPath { struct CameraPath {
std::string nextPath; std::string nextPath;
uint8_t rotatePlayer; uint8_t rotatePlayer{};
}; };
struct SpawnerPath { struct SpawnerPath {
LOT spawnedLOT; LOT spawnedLOT{};
uint32_t respawnTime; uint32_t respawnTime{};
int32_t maxToSpawn; int32_t maxToSpawn{};
uint32_t amountMaintained; uint32_t amountMaintained{};
LWOOBJID spawnerObjID; LWOOBJID spawnerObjID;
uint8_t spawnerNetActive; uint8_t spawnerNetActive{};
}; };
struct Path { struct Path {
uint32_t pathVersion; uint32_t pathVersion{};
PathType pathType; PathType pathType;
std::string pathName; std::string pathName;
uint32_t flags; uint32_t flags{};
PathBehavior pathBehavior; PathBehavior pathBehavior;
uint32_t waypointCount; uint32_t waypointCount{};
std::vector<PathWaypoint> pathWaypoints; std::vector<PathWaypoint> pathWaypoints;
SpawnerPath spawner; SpawnerPath spawner;
MovingPlatformPath movingPlatform; MovingPlatformPath movingPlatform;