Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add MovingAI pathing for NPCs without combatAI #1509

Merged
merged 16 commits into from
Mar 27, 2024
20 changes: 13 additions & 7 deletions dGame/Entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,15 +731,21 @@ void Entity::Initialize() {
// if we have a moving platform path, then we need a moving platform component
if (path->pathType == PathType::MovingPlatform) {
AddComponent<MovingPlatformComponent>(pathName);
// else if we are a movement path
} /*else if (path->pathType == PathType::Movement) {
auto movementAIcomp = GetComponent<MovementAIComponent>();
if (movementAIcomp){
// TODO: set path in existing movementAIComp
} else if (path->pathType == PathType::Movement) {
auto movementAIcomponent = GetComponent<MovementAIComponent>();
if (movementAIcomponent && combatAiId == 0) {
movementAIcomponent->SetPath(pathName);
} 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 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);
Expand Down
99 changes: 79 additions & 20 deletions dGame/dComponents/MovementAIComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,42 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) :
m_CurrentSpeed = 0;
m_MaxSpeed = 0;
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) {
if (m_Paused) return;

if (m_PullingToPoint) {
const auto source = GetCurrentWaypoint();

Expand Down Expand Up @@ -81,27 +114,24 @@ void MovementAIComponent::Update(const float deltaTime) {
}

m_TimeTravelled += deltaTime;

SetPosition(ApproximateLocation());

if (m_TimeTravelled < m_TimeToTravel) return;
m_TimeTravelled = 0.0f;

const auto source = GetCurrentWaypoint();

SetPosition(source);

NiPoint3 velocity = NiPoint3Constant::ZERO;
m_SourcePosition = source;

if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek?
{
m_NextWaypoint = GetCurrentWaypoint();

if (m_NextWaypoint == source) {
m_TimeToTravel = 0.0f;
} else {
if (m_CurrentSpeed < m_MaxSpeed) {
m_CurrentSpeed += m_Acceleration;
}

m_CurrentSpeed = std::min(m_CurrentSpeed, m_MaxSpeed);
m_CurrentSpeed = std::min(m_CurrentSpeed + m_Acceleration, m_MaxSpeed);

const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed

Expand All @@ -110,7 +140,7 @@ void MovementAIComponent::Update(const float deltaTime) {
// Normalize the vector
const auto length = delta.Length();
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
Expand All @@ -122,17 +152,27 @@ void MovementAIComponent::Update(const float deltaTime) {
} else {
// Check if there are more waypoints in the queue, if so set our next destination to the next waypoint
if (m_CurrentPath.empty()) {
Stop();

return;
if (m_Path) {
if (m_Path->pathBehavior == PathBehavior::Loop) {
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();
}

SetVelocity(velocity);

Game::entityManager->SerializeEntity(m_Parent);
}

Expand All @@ -155,7 +195,7 @@ NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
}

NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = m_Parent->GetPosition();
auto source = m_SourcePosition;

if (AtFinalWaypoint()) return source;

Expand Down Expand Up @@ -221,13 +261,13 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
m_PullPoint = point;
}

void MovementAIComponent::SetPath(std::vector<NiPoint3> path) {
void MovementAIComponent::SetPath(std::vector<PathWaypoint> path) {
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);
});

SetDestination(path.front());
SetDestination(path.front().position);
}

float MovementAIComponent::GetBaseSpeed(LOT lot) {
Expand Down Expand Up @@ -272,6 +312,23 @@ void MovementAIComponent::SetRotation(const NiQuaternion& 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) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();

Expand All @@ -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;

const auto location = ApproximateLocation();
Expand All @@ -297,6 +354,8 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) {
SetPosition(location);
}

m_SourcePosition = location;

std::vector<NiPoint3> computedPath;
if (dpWorld::IsLoaded()) {
computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed);
Expand Down
25 changes: 22 additions & 3 deletions dGame/dComponents/MovementAIComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
#include "Logger.h"
#include "Component.h"
#include "eReplicaComponentType.h"
#include "Zone.h"
#include <vector>

class ControllablePhysicsComponent;
class BaseCombatAIComponent;

struct Path;

/**
* Information that describes the different variables used to make an entity move around
*/
Expand Down Expand Up @@ -61,6 +64,8 @@ class MovementAIComponent final : public Component {

MovementAIComponent(Entity* parentEntity, MovementAIInfo info);

void SetPath(const std::string pathName);

void Update(float deltaTime) override;

/**
Expand All @@ -73,7 +78,7 @@ class MovementAIComponent final : public Component {
* Set a destination point for the entity 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
Expand Down Expand Up @@ -189,7 +194,13 @@ class MovementAIComponent final : public Component {
* Sets a path to follow for the AI
* @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
Expand Down Expand Up @@ -301,7 +312,15 @@ class MovementAIComponent final : public Component {
/**
* 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
1 change: 1 addition & 0 deletions dGame/dComponents/ProximityMonitorComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void ProximityMonitorComponent::Update(float deltaTime) {
for (const auto& prox : m_ProximitiesData) {
if (!prox.second) continue;

prox.second->SetPosition(m_Parent->GetPosition());
//Process enter events
for (auto* en : prox.second->GetNewObjects()) {
m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "ENTER");
Expand Down
5 changes: 2 additions & 3 deletions dScripts/02_server/Map/AM/WanderingVendor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
void WanderingVendor::OnStartup(Entity* self) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Resume();
self->SetProximityRadius(10, "playermonitor");
}

void WanderingVendor::OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status) {
if (status == "ENTER" && entering->IsPlayer()) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Pause();
movementAIComponent->Pause();
self->CancelTimer("startWalking");
} else if (status == "LEAVE") {
auto* proximityMonitorComponent = self->GetComponent<ProximityMonitorComponent>();
Expand All @@ -28,6 +27,6 @@ void WanderingVendor::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "startWalking") {
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Resume();
movementAIComponent->Resume();
}
}
6 changes: 1 addition & 5 deletions dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,7 @@ void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
movementAI->SetCurrentSpeed(toSpawn.initialSpeed);
movementAI->SetHaltDistance(0.0f);

std::vector<NiPoint3> pathWaypoints;

for (const auto& waypoint : path->pathWaypoints) {
pathWaypoints.push_back(waypoint.position);
}
std::vector<PathWaypoint> pathWaypoints = path->pathWaypoints;

if (GeneralUtils::GenerateRandomNumber<float_t>(0, 1) < 0.5f) {
std::reverse(pathWaypoints.begin(), pathWaypoints.end());
Expand Down
9 changes: 4 additions & 5 deletions dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@
void WblRobotCitizen::OnStartup(Entity* self) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Resume();
}

void WblRobotCitizen::OnUse(Entity* self, Entity* user) {
// auto movementAIComponent = self->GetComponent<MovementAIComponent>();
// if (!movementAIComponent) movementAIComponent->Pause();
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (movementAIComponent) movementAIComponent->Pause();
auto face = NiQuaternion::LookAt(self->GetPosition(), user->GetPosition());
self->SetRotation(face);
auto timer = RenderComponent::PlayAnimation(self, "wave");
auto timer = RenderComponent::PlayAnimation(self, "wave", 0.4f);
self->AddTimer("animation time", timer);
}

void WblRobotCitizen::OnTimerDone(Entity* self, std::string timerName) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Resume();
movementAIComponent->Resume();
}
Loading
Loading