Compare commits

...

71 Commits

Author SHA1 Message Date
David Markowitz
f71b774a0a Update ControllablePhysicsComponent.cpp 2024-03-10 01:51:22 -08:00
David Markowitz
0f6b47a833 Update ControllablePhysicsComponent.cpp 2024-03-10 01:51:10 -08:00
David Markowitz
cf8332d3fd Update ControllablePhysicsComponent.cpp 2024-03-10 01:50:33 -08:00
David Markowitz
3cd91d6771 Merge branch 'main' into moreMovementAi 2024-03-10 01:49:58 -08:00
David Markowitz
db46fa8792 Update Zone.cpp 2024-03-09 21:12:14 -08:00
David Markowitz
b2fee30588 Update TriggerComponent.h 2024-03-09 21:11:45 -08:00
David Markowitz
e3fc8b90b2 Update TriggerComponent.cpp 2024-03-09 21:11:11 -08:00
David Markowitz
3e26b444b9 Update TriggerComponent.cpp 2024-03-09 21:10:50 -08:00
David Markowitz
f2efa4f4f9 Update eWaypointCommandType.h 2024-03-09 21:09:48 -08:00
David Markowitz
8c8fc2ae87 Merge branch 'main' into moreMovementAi 2024-03-09 21:09:19 -08:00
David Markowitz
243ae9a69b more reverts for diff purposes 2024-03-07 00:46:04 -08:00
David Markowitz
9225ae7d44 Update CppScripts.cpp 2024-03-07 00:44:50 -08:00
David Markowitz
e6bf86da84 Update CppScripts.cpp 2024-03-07 00:44:09 -08:00
David Markowitz
e56d3c6579 Update CppScripts.cpp 2024-03-07 00:43:05 -08:00
David Markowitz
02de7ef28a Merge branch 'main' into moreMovementAi 2024-03-07 00:40:26 -08:00
David Markowitz
ae891aa4c3 Merge branch 'main' into moreMovementAi 2024-02-27 04:14:36 -08:00
David Markowitz
124f6dad2a auroooga 2024-02-25 22:00:14 -08:00
David Markowitz
15fc39f3bb Update CMakeVariables.txt 2024-02-25 21:44:08 -08:00
David Markowitz
152a33f937 Merge branch 'main' into moreMovementAi 2024-02-25 21:41:52 -08:00
David Markowitz
772ac06e94 this took 4 eons to get compiling again
Update Zone.cpp

Update Zone.h

Update Zone.h

Update MovingPlatformComponent.cpp

Update ProximityMonitorComponent.cpp

quick cleanup

Use correct logging
2024-02-25 01:35:49 -08:00
David Markowitz
6863ee9d76 Merge branch 'main' into moreMovementAi 2024-02-24 22:19:45 -08:00
David Markowitz
458db4682b Use better height and path checking
Fix issue with paths entering infinite loop
fix default position height being at height 0.
2023-08-24 03:40:10 -07:00
dd2338601e Combine files
Add LUtrigger interfaces and calling
2023-08-23 14:53:02 -05:00
David Markowitz
daf5a7ffa9 fixes 2023-08-22 10:25:09 -07:00
David Markowitz
67cd990d98 Merge branch 'moreMovementAi' of https://github.com/DarkflameUniverse/DarkflameServer into moreMovementAi 2023-08-21 12:33:19 -07:00
David Markowitz
294efe0fe0 Fixed speed stuff
Should be tested more.
2023-08-21 12:33:13 -07:00
c70eb77c14 use SetMaxSpeed 2023-08-20 02:19:34 -05:00
David Markowitz
e3ae0b6304 fix frame stats in vehicle racing 2023-08-19 23:17:12 -07:00
David Markowitz
a9fd7c5e08 Always extract navmeshes 2023-08-19 23:09:47 -07:00
David Markowitz
4f447eb441 Merge branch 'moreMovementAi' of https://github.com/DarkflameUniverse/DarkflameServer into moreMovementAi 2023-08-19 22:44:13 -07:00
David Markowitz
11b1f5b9d4 Remove flags in controllablePhysics 2023-08-19 22:44:02 -07:00
8dd7553421 fix delay 2023-08-20 00:36:28 -05:00
5612e57590 respect try-parse
consolidate space trimming
2023-08-20 00:13:01 -05:00
2bb39f4fa5 fixe some spacey string
be smater about pausing
2023-08-16 15:53:13 -05:00
691a42ba20 Merge RC script from differnt branch
Add wandering vendor script
Update proximity monitors to move with their respective entityies
2023-08-15 14:35:35 -05:00
e35b95f3c8 Merge branch 'wbl-generic-and-rc-citizen' into moreMovementAi 2023-08-15 10:13:38 -05:00
b37574c05e Merge branch 'main' into wbl-generic-and-rc-citizen 2023-08-15 10:11:29 -05:00
5168404567 fix path starting waypoint 2023-08-15 09:47:45 -05:00
David Markowitz
92e2ab55d9 Update MovementAIComponent.cpp 2023-08-14 20:42:07 -07:00
David Markowitz
959d19c8eb remove hard coded start 2023-08-14 20:28:45 -07:00
David Markowitz
b659edd980 Yes 2023-08-14 20:28:27 -07:00
David Markowitz
abce40f17f format 2023-08-14 20:15:25 -07:00
a63d7df0d2 Add waypoint command handling 2023-08-14 09:31:10 -05:00
David Markowitz
1b54387677 Merge branch 'main' into moreMovementAi 2023-08-12 21:57:07 -07:00
David Markowitz
56ab6bd4c3 updates 2023-08-12 00:40:18 -07:00
84ba38bd1d Add some command handlers 2023-08-12 00:40:48 -05:00
David Markowitz
2db8caadde Fix serialization for Controllable Physics 2023-08-11 21:37:35 -07:00
David Markowitz
40c59c7f51 Ensure physics are predicted correctly
Update the PhysicsEntity position so it always matched the physics position.
2023-08-10 02:47:27 -07:00
David Markowitz
fba8fc9c45 Move blocks around 2023-08-10 02:09:58 -07:00
David Markowitz
a3b62d60f0 fixes and testing
Bounce works
add null check and default
2023-08-10 01:55:35 -07:00
facc225b82 block out some 2023-08-09 23:29:04 -05:00
David Markowitz
97b683fd59 Update Zone.cpp 2023-08-09 20:34:49 -07:00
1207d896a8 waypoint commands wip 2023-08-09 21:37:36 -05:00
David Markowitz
5f7a108154 woirking 2023-08-09 00:34:39 -07:00
David Markowitz
fc56777969 Update dNavMesh.cpp 2023-08-08 23:54:47 -07:00
David Markowitz
cceaa96415 Use proper initializer 2023-08-08 00:19:44 -07:00
David Markowitz
1d4d1414e9 lol lmao 2023-08-07 20:45:26 -07:00
David Markowitz
4fbd536e74 add movementAI on load 2023-08-07 19:23:17 -07:00
David Markowitz
ed0c979544 Add file for u aronwk 2023-08-07 18:40:06 -07:00
David Markowitz
d372278b25 add arrived handler 2023-08-07 02:03:09 -07:00
David Markowitz
b546c96193 Add reversing 2023-08-07 01:46:03 -07:00
David Markowitz
aa734ef7ae Working Pausing and Resuming 2023-08-07 00:44:57 -07:00
David Markowitz
6a5ff30a32 Working built in reverse for movementAI 2023-08-06 23:40:30 -07:00
David Markowitz
3e12dd782b working forward 2023-08-06 21:15:06 -07:00
David Markowitz
bce87aaa06 use indexes for pathing 2023-08-06 20:25:22 -07:00
David Markowitz
20b2a62932 Merge branch 'movementAiPathing' into moreMovementAi 2023-08-06 16:13:18 -07:00
David Markowitz
600f0974e7 Increase height because Nexus Tower is tall 2023-08-06 16:06:12 -07:00
David Markowitz
a50e56e8ff replace with operator+= 2023-08-06 15:53:37 -07:00
David Markowitz
8117773c56 Add path height correction on world load 2023-08-06 15:41:38 -07:00
2946a612fb remove unused headers
comment about how controllable physics is the correct way to do this
right now they have moving plat comps, and that is not correct
2022-09-03 22:26:00 -05:00
67ec10ac0d Add LupGenericInteraction and Robot Citizen script 2022-09-02 21:52:57 -05:00
7 changed files with 492 additions and 46 deletions

View File

@@ -731,15 +731,9 @@ 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 {
// TODO: create movementAIcomp and set path
}
}*/
} else if (path->pathType == PathType::Movement) {
AddComponent<MovementAIComponent>(MovementAIInfo{})->SetupPath(pathName);
}
} 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);

View File

@@ -648,7 +648,7 @@ void BaseCombatAIComponent::Wander() {
const NiPoint3 delta =
{
radius * cos(theta),
0,
m_Parent->GetPosition().y,
radius * sin(theta)
};

View File

@@ -65,7 +65,9 @@ ControllablePhysicsComponent::~ControllablePhysicsComponent() {
}
void ControllablePhysicsComponent::Update(float deltaTime) {
if (m_Velocity == NiPoint3Constant::ZERO) return;
SetPosition(m_Position + (m_Velocity * deltaTime));
Game::entityManager->SerializeEntity(m_Parent);
}
void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {

View File

@@ -316,6 +316,7 @@ public:
*/
const std::vector<int32_t>& GetFactionIDs() const { return m_FactionIDs; }
const bool BelongsToFaction(int32_t factionID) const {return std::find(m_FactionIDs.begin(), m_FactionIDs.end(), factionID) != m_FactionIDs.end(); }
/**
* Returns all the faction IDs that this entity considers an enemy
* @return all the faction IDs that this entity considers an enemy

View File

@@ -12,6 +12,13 @@
#include "CDClientManager.h"
#include "Game.h"
#include "dZoneManager.h"
#include "eTriggerEventType.h"
#include "eWaypointCommandType.h"
#include "RenderComponent.h"
#include "SkillComponent.h"
#include "InventoryComponent.h"
#include "ProximityMonitorComponent.h"
#include "DestroyableComponent.h"
#include "CDComponentsRegistryTable.h"
#include "CDPhysicsComponentTable.h"
@@ -27,6 +34,7 @@ namespace {
MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) {
m_Info = info;
m_IsPaused = true;
m_AtFinalWaypoint = true;
m_BaseCombatAI = nullptr;
@@ -49,19 +57,61 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) :
m_TimeTravelled = 0;
m_CurrentSpeed = 0;
m_MaxSpeed = 0;
m_StartingWaypointIndex = -1;
m_CurrentPathWaypointIndex = 0;
m_LockRotation = false;
m_IsInReverse = false;
m_NextPathWaypointIndex = 0;
}
float MovementAIComponent::GetCurrentPathWaypointSpeed() const {
if (!m_Path || m_CurrentPathWaypointIndex >= m_CurrentPath.size() || m_CurrentPathWaypointIndex < 0) {
return 1.0f;
}
return m_Path->pathWaypoints.at(m_CurrentPathWaypointIndex).movingPlatform.speed;
}
void MovementAIComponent::SetupPath(const std::string& pathname) {
std::string path = pathname;
if (path.empty()) {
path = m_Parent->GetVarAsString(u"attached_path");
if (path.empty()) {
LOG("No path to load for %i:%llu", m_Parent->GetLOT(), m_Parent->GetObjectID());
return;
}
}
const Path* pathData = Game::zoneManager->GetZone()->GetPath(path);
if (pathData) {
LOG("found path %i %s", m_Parent->GetLOT(), path.c_str());
m_Path = pathData;
if (!HasAttachedPathStart() && m_Parent->HasVar(u"attached_path_start")) m_StartingWaypointIndex = m_Parent->GetVar<uint32_t>(u"attached_path_start");
if (m_Path && HasAttachedPathStart() && (m_StartingWaypointIndex < 0 || m_StartingWaypointIndex >= m_Path->pathWaypoints.size())) {
LOG("WARNING: attached path start is out of bounds for %i:%llu, defaulting path start to 0",
m_Parent->GetLOT(), m_Parent->GetObjectID());
m_StartingWaypointIndex = 0;
}
std::vector<NiPoint3> waypoints;
for (const auto& waypoint : m_Path->pathWaypoints) {
waypoints.push_back(waypoint.position);
}
SetPath(waypoints);
} else {
LOG("No path found for %i:%llu", m_Parent->GetLOT(), m_Parent->GetObjectID());
}
}
void MovementAIComponent::Update(const float deltaTime) {
if (m_PullingToPoint) {
const auto source = GetCurrentWaypoint();
// Just a guess at the speed...
const auto speed = deltaTime * 2.5f;
NiPoint3 velocity = (m_PullPoint - source) * speed;
SetPosition(source + velocity);
// We are close enough to the pulled to point, stop pulling
if (Vector3::DistanceSquared(m_Parent->GetPosition(), m_PullPoint) < std::pow(2, 2)) {
m_PullingToPoint = false;
}
@@ -69,8 +119,8 @@ void MovementAIComponent::Update(const float deltaTime) {
return;
}
// Are we done?
if (AtFinalWaypoint()) return;
// Are we done or paused?
if (AtFinalWaypoint() || IsPaused()) return;
if (m_HaltDistance > 0) {
// Prevent us from hugging the target
@@ -90,14 +140,16 @@ void MovementAIComponent::Update(const float deltaTime) {
NiPoint3 velocity = NiPoint3Constant::ZERO;
if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek?
{
// If we have no acceleration, then we have no max speed.
// If we have no base speed, then we cannot scale the speed by it.
// Do we have another waypoint to seek?
if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) {
m_NextWaypoint = GetCurrentWaypoint();
if (m_NextWaypoint == source) {
m_TimeToTravel = 0.0f;
goto nextAction;
return;
}
if (m_CurrentSpeed < m_MaxSpeed) {
@@ -108,14 +160,14 @@ void MovementAIComponent::Update(const float deltaTime) {
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 * current speed
const auto delta = m_NextWaypoint - source;
// Normalize the vector
const auto length = delta.Length();
if (length > 0) {
velocity = (delta / length) * speed;
velocity = (delta / length).Unitize() * speed;
}
// Calclute the time it will take to reach the next waypoint with the current speed
@@ -125,14 +177,9 @@ void MovementAIComponent::Update(const float deltaTime) {
SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint));
} 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;
}
SetDestination(m_CurrentPath.top());
m_CurrentPath.pop();
// All checks for how to progress when you arrive at a waypoint will be handled in this else block.
HandleWaypointArrived(0);
return;
}
nextAction:
@@ -142,6 +189,27 @@ nextAction:
Game::entityManager->SerializeEntity(m_Parent);
}
void MovementAIComponent::ReversePath() {
if (m_CurrentPath.empty()) return;
if (m_NextPathWaypointIndex < 0) m_NextPathWaypointIndex = 0;
if (m_NextPathWaypointIndex >= m_CurrentPath.size()) m_NextPathWaypointIndex = m_CurrentPath.size() - 1;
m_CurrentPathWaypointIndex = m_NextPathWaypointIndex;
m_IsInReverse = !m_IsInReverse;
AdvancePathWaypointIndex();
}
bool MovementAIComponent::AdvancePathWaypointIndex() {
if (m_CurrentPath.empty()) return false;
m_CurrentPathWaypointIndex = m_NextPathWaypointIndex;
if (m_IsInReverse) {
if (m_CurrentPathWaypointIndex >= 0) m_NextPathWaypointIndex--;
return m_CurrentPathWaypointIndex >= 0;
} else {
if (m_CurrentPathWaypointIndex <= m_CurrentPath.size()) m_NextPathWaypointIndex++;
return m_CurrentPathWaypointIndex < m_CurrentPath.size();
}
}
const MovementAIInfo& MovementAIComponent::GetInfo() const {
return m_Info;
}
@@ -162,14 +230,15 @@ NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = m_Parent->GetPosition();
if (AtFinalWaypoint()) return source;
NiPoint3 approximation = source;
auto destination = m_NextWaypoint;
auto percentageToWaypoint = m_TimeToTravel > 0 ? m_TimeTravelled / m_TimeToTravel : 0;
auto approximation = source + ((destination - source) * percentageToWaypoint);
// Only have physics sim for controllable physics
if (!m_Parent->HasComponent(ControllablePhysicsComponent::ComponentType)) {
auto destination = GetNextWaypoint();
auto percentageToWaypoint = m_TimeToTravel > 0 ? m_TimeTravelled / m_TimeToTravel : 0;
approximation = source + ((destination - source) * percentageToWaypoint);
}
if (dpWorld::IsLoaded()) {
approximation.y = dpWorld::GetNavMesh()->GetHeightAtPoint(approximation);
@@ -198,6 +267,26 @@ bool MovementAIComponent::Warp(const NiPoint3& point) {
return true;
}
void MovementAIComponent::Pause() {
if (AtFinalWaypoint() || IsPaused()) return;
SetPosition(ApproximateLocation());
SetVelocity(NiPoint3Constant::ZERO);
// Clear this as we may be somewhere else when we resume movement.
m_InterpolatedWaypoints.clear();
m_IsPaused = true;
m_PathIndex = 0;
m_TimeToTravel = 0;
m_TimeTravelled = 0;
}
void MovementAIComponent::Resume() {
if (AtFinalWaypoint() || !IsPaused()) return;
m_IsPaused = false;
SetDestination(GetCurrentPathWaypoint());
SetMaxSpeed(GetCurrentPathWaypointSpeed());
}
void MovementAIComponent::Stop() {
if (AtFinalWaypoint()) return;
@@ -209,13 +298,15 @@ void MovementAIComponent::Stop() {
m_TimeTravelled = 0;
m_AtFinalWaypoint = true;
m_IsPaused = true;
m_InterpolatedWaypoints.clear();
while (!m_CurrentPath.empty()) m_CurrentPath.pop();
m_CurrentPath.clear();
m_PathIndex = 0;
m_CurrentSpeed = 0;
m_CurrentPathWaypointIndex = 0;
Game::entityManager->SerializeEntity(m_Parent);
}
@@ -227,13 +318,31 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
m_PullPoint = point;
}
void MovementAIComponent::SetPath(std::vector<NiPoint3> path) {
if (path.empty()) return;
std::for_each(path.rbegin(), path.rend() - 1, [this](const NiPoint3& point) {
this->m_CurrentPath.push(point);
});
const NiPoint3& MovementAIComponent::GetCurrentPathWaypoint() const {
if (m_CurrentPathWaypointIndex >= m_CurrentPath.size() || m_CurrentPathWaypointIndex < 0) {
return m_Parent->GetPosition();
}
return m_CurrentPath.at(m_CurrentPathWaypointIndex);
}
SetDestination(path.front());
void MovementAIComponent::SetPath(const std::vector<NiPoint3>& path, bool startInReverse) {
if (path.empty()) return;
m_CurrentPath = path;
m_IsInReverse = startInReverse;
// Start the Entity out at the first waypoint with their next waypoint being the same one.
// This is so AdvancePathWaypointIndex can do the recovery from effectively a paused state.
m_CurrentPathWaypointIndex = m_IsInReverse ? m_CurrentPath.size() - 1 : 0;
m_NextPathWaypointIndex = m_IsInReverse ? m_CurrentPath.size() - 1 : 0;
if (HasAttachedPathStart()) {
m_CurrentPathWaypointIndex = m_StartingWaypointIndex;
m_NextPathWaypointIndex = m_StartingWaypointIndex;
}
AdvancePathWaypointIndex();
SetDestination(GetCurrentPathWaypoint());
SetMaxSpeed(GetCurrentPathWaypointSpeed());
}
float MovementAIComponent::GetBaseSpeed(LOT lot) {
@@ -306,6 +415,9 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) {
std::vector<NiPoint3> computedPath;
if (dpWorld::IsLoaded()) {
computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed);
} else {
// If we do not have a navmesh, we do not want an AI to be going towards points that are far below or above the map.
//
}
// Somehow failed
@@ -319,8 +431,7 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) {
auto step = delta / 10.0f;
for (int i = 0; i < 10; i++) {
// TODO: Replace this with += when the NiPoint3::operator+= is fixed
start = start + step;
start += step;
computedPath.push_back(start);
}
@@ -343,6 +454,7 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) {
m_TimeToTravel = 0;
m_AtFinalWaypoint = false;
m_IsPaused = false;
}
NiPoint3 MovementAIComponent::GetDestination() const {
@@ -354,3 +466,253 @@ void MovementAIComponent::SetMaxSpeed(const float value) {
m_MaxSpeed = value;
m_Acceleration = value / 5;
}
void MovementAIComponent::HandleWaypointArrived(uint32_t commandIndex) {
m_Parent->TriggerEvent(eTriggerEventType::ARRIVED);
m_Parent->TriggerEvent(eTriggerEventType::ARRIVED_AT_DESIRED_WAYPOINT);
if (!m_Path || commandIndex >= m_Path->pathWaypoints.at(m_CurrentPathWaypointIndex).commands.size()) {
if (!AdvancePathWaypointIndex()) {
// We only want to handle path logic if we actually have a path setup for following
if (m_Path && !m_CurrentPath.empty()) {
if (m_Path->pathBehavior == PathBehavior::Bounce) {
ReversePath();
} else if (m_Path->pathBehavior == PathBehavior::Loop) {
m_CurrentPathWaypointIndex = 0;
m_NextPathWaypointIndex = 0;
AdvancePathWaypointIndex();
SetDestination(GetCurrentPathWaypoint());
SetMaxSpeed(GetCurrentPathWaypointSpeed());
} else {
Stop();
m_Parent->TriggerEvent(eTriggerEventType::ARRIVED_AT_END_OF_PATH);
}
} else {
Stop();
}
return;
}
SetDestination(GetCurrentPathWaypoint());
SetMaxSpeed(GetCurrentPathWaypointSpeed());
return;
}
if (!IsPaused()) Pause();
const auto& data = m_Path->pathWaypoints.at(m_CurrentPathWaypointIndex).commands.at(commandIndex).data;
const auto& command = m_Path->pathWaypoints.at(m_CurrentPathWaypointIndex).commands.at(commandIndex).command;
float delay = 0.0f;
switch (command) {
case eWaypointCommandType::STOP:
Stop();
break;
case eWaypointCommandType::GROUP_EMOTE:
delay = HandleWaypointCommandGroupEmote(data);
break;
case eWaypointCommandType::SET_VARIABLE:
HandleWaypointCommandSetVariable(data);
break;
case eWaypointCommandType::CAST_SKILL:
HandleWaypointCommandCastSkill(data);
break;
case eWaypointCommandType::EQUIP_INVENTORY:
HandleWaypointCommandEquipInventory(data);
break;
case eWaypointCommandType::UNEQUIP_INVENTORY:
HandleWaypointCommandUnequipInventory(data);
break;
case eWaypointCommandType::DELAY:
delay = HandleWaypointCommandDelay(data);
break;
case eWaypointCommandType::EMOTE:
delay = RenderComponent::PlayAnimation(m_Parent, data);
break;
case eWaypointCommandType::TELEPORT:
HandleWaypointCommandTeleport(data);
break;
case eWaypointCommandType::PATH_SPEED:
HandleWaypointCommandPathSpeed(data);
break;
case eWaypointCommandType::REMOVE_NPC:
HandleWaypointCommandRemoveNPC(data);
break;
case eWaypointCommandType::CHANGE_WAYPOINT:
HandleWaypointCommandChangeWaypoint(data);
break;
case eWaypointCommandType::KILL_SELF:
m_Parent->Smash(LWOOBJID_EMPTY, eKillType::SILENT);
break;
case eWaypointCommandType::DELETE_SELF:
m_Parent->Kill();
break;
case eWaypointCommandType::SPAWN_OBJECT:
HandleWaypointCommandSpawnObject(data);
break;
case eWaypointCommandType::PLAY_SOUND:
GameMessages::SendPlayNDAudioEmitter(m_Parent, UNASSIGNED_SYSTEM_ADDRESS, data);
break;
case eWaypointCommandType::BOUNCE:
LOG("Unable to process bounce waypoint command server side!");
break;
case eWaypointCommandType::INVALID:
default:
LOG("Got invalid waypoint command %i", command);
break;
}
m_Parent->AddCallbackTimer(delay, [this, commandIndex]() {
this->HandleWaypointArrived(commandIndex + 1);
}
);
}
float MovementAIComponent::HandleWaypointCommandGroupEmote(const std::string& data) {
const auto& split = GeneralUtils::SplitString(data, ';');
if (split.size() != 2) return 0.0f;
const auto& entities = Game::entityManager->GetEntitiesInGroup(split.at(0));
float delay = 0.0f;
for (auto& entity : entities) {
delay = RenderComponent::PlayAnimation(entity, split.at(1));
}
return delay;
}
void MovementAIComponent::HandleWaypointCommandSetVariable(const std::string& data) {
const auto& split = GeneralUtils::SplitString(data, ',');
m_Parent->SetNetworkVar(GeneralUtils::ASCIIToUTF16(split.at(0)), split.at(1));
}
void MovementAIComponent::HandleWaypointCommandCastSkill(const std::string& data) {
if (data.empty()) return;
auto* skillComponent = m_Parent->GetComponent<SkillComponent>();
if (!skillComponent) {
LOG("Skill component not found!");
return;
}
auto skillId = GeneralUtils::TryParse<uint32_t>(data);
if (skillId && skillId != 0) skillComponent->CastSkill(skillId.value());
}
void MovementAIComponent::HandleWaypointCommandEquipInventory(const std::string& data) {
if (data.empty()) return;
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
if (!inventoryComponent) {
LOG("Inventory component not found!");
return;
}
// the client says use slot 0 of items
const auto inventory = inventoryComponent->GetInventory(eInventoryType::ITEMS);
if (!inventory) return;
const auto slots = inventory->GetSlots();
const auto item = slots.find(0);
if (item != slots.end()) inventoryComponent->EquipItem(item->second);
}
void MovementAIComponent::HandleWaypointCommandUnequipInventory(const std::string& data) {
if (data.empty()) return;
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
if (!inventoryComponent) {
LOG("Inventory component not found!");
return;
}
// the client says use slot 0 of items
const auto inventory = inventoryComponent->GetInventory(eInventoryType::ITEMS);
if (!inventory) return;
const auto slots = inventory->GetSlots();
const auto item = slots.find(0);
if (item != slots.end()) inventoryComponent->UnEquipItem(item->second);
}
float MovementAIComponent::HandleWaypointCommandDelay(const std::string& data) {
auto delay = GeneralUtils::TryParse<float>(data);
if (!delay) {
LOG("Failed to parse delay %s", data.c_str());
}
return delay.value_or(0.0f);
}
void MovementAIComponent::HandleWaypointCommandTeleport(const std::string& data) {
auto posString = GeneralUtils::SplitString(data, ',');
if (posString.size() == 0) return;
auto newPos = NiPoint3();
std::optional<float> intermediate;
if (posString.size() >= 1) {
intermediate = GeneralUtils::TryParse<float>(posString.at(0));
if (!intermediate) return;
newPos.x = intermediate.value();
if (posString.size() >= 2) {
intermediate = GeneralUtils::TryParse<float>(posString.at(1));
if (!intermediate) return;
newPos.y = intermediate.value();
if (posString.size() >= 3) {
intermediate = GeneralUtils::TryParse<float>(posString.at(2));
if (!intermediate) return;
newPos.z = intermediate.value();
}
}
}
GameMessages::SendTeleport(m_Parent->GetObjectID(), newPos, NiQuaternionConstant::IDENTITY, UNASSIGNED_SYSTEM_ADDRESS);
}
void MovementAIComponent::HandleWaypointCommandPathSpeed(const std::string& data) {
auto speed = GeneralUtils::TryParse<float>(data);
if (!speed) return;
SetMaxSpeed(speed.value());
}
void MovementAIComponent::HandleWaypointCommandRemoveNPC(const std::string& data) {
if (data.empty()) return;
auto* proximityMonitorComponent = m_Parent->GetComponent<ProximityMonitorComponent>();
if (!proximityMonitorComponent) {
LOG("Proximity monitor component not found!");
return;
}
const auto foundObjs = proximityMonitorComponent->GetProximityObjects("KillOBJS");
for (auto& [objid, phyEntity] : foundObjs) {
auto entity = Game::entityManager->GetEntity(objid);
if (!entity) return;
auto* destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
if (!destroyableComponent) {
LOG("Destroyable component not found!");
return;
}
int32_t factionID = -1;
auto parsed = GeneralUtils::TryParse<uint32_t>(data);
if (!parsed) return;
factionID = parsed.value();
if (destroyableComponent->BelongsToFaction(factionID)) m_Parent->Kill();
}
}
void MovementAIComponent::HandleWaypointCommandChangeWaypoint(const std::string& data) {
std::string path_string = "";
int32_t index = 0;
// sometimes there's a path and what waypoint to start, which are comma separated
if (data.find(",") != std::string::npos) {
auto datas = GeneralUtils::SplitString(data, ',');
path_string = datas.at(0);
auto parsed = GeneralUtils::TryParse<int32_t>(datas.at(1));
if (!parsed) return;
index = parsed.value();
} else path_string = data;
if (path_string != "") {
SetPathStartingWaypointIndex(index);
SetupPath(path_string);
}
}
void MovementAIComponent::HandleWaypointCommandSpawnObject(const std::string& data) {
LOT newObjectLOT = 0;
auto parsed = GeneralUtils::TryParse<LOT>(data);
if (!parsed) return;
newObjectLOT = parsed.value();
EntityInfo info{};
info.lot = newObjectLOT;
info.pos = m_Parent->GetPosition();
info.rot = m_Parent->GetRotation();
auto* spawnedEntity = Game::entityManager->CreateEntity(info, nullptr, m_Parent);
Game::entityManager->ConstructEntity(spawnedEntity);
m_Parent->Smash(LWOOBJID_EMPTY, eKillType::SILENT);
}

View File

@@ -18,6 +18,7 @@
class ControllablePhysicsComponent;
class BaseCombatAIComponent;
class Path;
/**
* Information that describes the different variables used to make an entity move around
@@ -53,7 +54,7 @@ struct MovementAIInfo {
/**
* Component that handles the movement settings of an entity. Not to be confused with the BaseCombatAI component that
* actually handles attackig and following enemy entities.
* actually handles attacking and following enemy entities.
*/
class MovementAIComponent final : public Component {
public:
@@ -153,6 +154,19 @@ public:
*/
NiPoint3 GetNextWaypoint() const { return m_NextWaypoint; }
NiPoint3 GetNextPathWaypoint() const {
if (m_CurrentPath.empty()) return GetNextWaypoint();
if (m_IsInReverse) {
return m_CurrentPathWaypointIndex - 1 < 0 ?
m_CurrentPath.front() :
m_CurrentPath.at(m_CurrentPathWaypointIndex - 1);
} else {
return m_CurrentPathWaypointIndex + 1 >= m_CurrentPath.size() ?
m_CurrentPath.back() :
m_CurrentPath.at(m_CurrentPathWaypointIndex + 1);
}
}
/**
* Returns the approximate current location of the entity, including y coordinates
* @return the approximate current location of the entity
@@ -173,11 +187,32 @@ public:
*/
bool AtFinalWaypoint() const { return m_AtFinalWaypoint; }
bool IsPaused() const { return m_IsPaused; }
/**
* Pauses the current pathing of this entity. The current path waypoint will be saved for resuming later.
*/
void Pause();
/**
* Resumes pathing from the current position to the destination that was set
* when the entity was paused.
*/
void Resume();
/**
* Renders the entity stationary
*/
void Stop();
void ReversePath();
void HandleWaypointArrived(uint32_t commandIndex);
void SetupPath(const std::string& pathname);
float GetCurrentPathWaypointSpeed() const;
/**
* Stops the current movement and moves the entity to a certain point. Will continue until it's close enough,
* after which its AI is enabled again.
@@ -189,7 +224,16 @@ public:
* Sets a path to follow for the AI
* @param path the path to follow
*/
void SetPath(std::vector<NiPoint3> path);
void SetPath(const std::vector<NiPoint3>& path, bool startsInReverse = false);
// Advance the path waypoint index and return if there is a next waypoint
bool AdvancePathWaypointIndex();
const NiPoint3& GetCurrentPathWaypoint() const;
void SetPathStartingWaypointIndex(int32_t value) { m_StartingWaypointIndex = value; }
bool HasAttachedPathStart() const { return m_StartingWaypointIndex != -1; }
/**
* Returns the base speed from the DB for a given LOT
@@ -198,7 +242,20 @@ public:
*/
static float GetBaseSpeed(LOT lot);
const bool GetIsInReverse(){ return m_IsInReverse; };
private:
float HandleWaypointCommandGroupEmote(const std::string& data);
void HandleWaypointCommandSetVariable(const std::string& data);
void HandleWaypointCommandCastSkill(const std::string& data);
void HandleWaypointCommandEquipInventory(const std::string& data);
void HandleWaypointCommandUnequipInventory(const std::string& data);
float HandleWaypointCommandDelay(const std::string& data);
void HandleWaypointCommandTeleport(const std::string& data);
void HandleWaypointCommandPathSpeed(const std::string& data);
void HandleWaypointCommandRemoveNPC(const std::string& data);
void HandleWaypointCommandChangeWaypoint(const std::string& data);
void HandleWaypointCommandSpawnObject(const std::string& data);
/**
* Sets the current position of the entity
@@ -241,7 +298,7 @@ private:
/**
* The path this entity is currently traversing
*/
uint32_t m_PathIndex;
int32_t m_PathIndex;
/**
* If the entity has reached it last waypoint
@@ -301,7 +358,37 @@ private:
/**
* The path from the current position to the destination.
*/
std::stack<NiPoint3> m_CurrentPath;
std::vector<NiPoint3> m_CurrentPath;
/**
* The index of the current waypoint in the path
*/
int32_t m_CurrentPathWaypointIndex;
/**
* The index of the next waypoint in the path
*/
int32_t m_NextPathWaypointIndex;
/**
* Whether or not the path is being read in reverse
*/
bool m_IsInReverse;
/**
* Whether or not the current movement via pathing is paused.
*/
bool m_IsPaused;
/**
* The optional path this component will follow.
*/
const Path* m_Path = nullptr;
/**
* The starting index for the provided path
*/
int32_t m_StartingWaypointIndex = -1;
};
#endif // MOVEMENTAICOMPONENT_H

View File

@@ -63,7 +63,7 @@ bool ProximityMonitorComponent::IsInProximity(const std::string& name, LWOOBJID
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");