diff --git a/dGame/dComponents/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt index 67f4ece5..71bd7e5f 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -15,6 +15,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" "ModelComponent.cpp" "ModuleAssemblyComponent.cpp" "MovementAIComponent.cpp" + "MovementAIComponentAronwk.cpp" "MovingPlatformComponent.cpp" "PetComponent.cpp" "PhantomPhysicsComponent.cpp" diff --git a/dGame/dComponents/DestroyableComponent.h b/dGame/dComponents/DestroyableComponent.h index ed091066..1d37385c 100644 --- a/dGame/dComponents/DestroyableComponent.h +++ b/dGame/dComponents/DestroyableComponent.h @@ -310,6 +310,7 @@ public: */ const std::vector& 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 diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index 975c0deb..587b0ef5 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -155,7 +155,7 @@ 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 // All checks for how to progress when you arrive at a waypoint will be handled in this else block. - HandleWaypointArrived(); + HandleWaypointArrived(0); if (!AdvancePathWaypointIndex()) { if (m_Path) { if (m_Path->pathBehavior == PathBehavior::Bounce) { @@ -401,7 +401,7 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) { computedPath = dpWorld::Instance().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 diff --git a/dGame/dComponents/MovementAIComponent.h b/dGame/dComponents/MovementAIComponent.h index 98c3c7c3..9473957a 100644 --- a/dGame/dComponents/MovementAIComponent.h +++ b/dGame/dComponents/MovementAIComponent.h @@ -194,7 +194,7 @@ public: void ReversePath(); - void HandleWaypointArrived(); + void HandleWaypointArrived(uint32_t commandIndex); void SetupPath(const std::string& pathname); @@ -228,18 +228,17 @@ private: // TODO: Advance properly void SetCurrentPathWaypointIndex(uint32_t value) { }; void SetNextPathWaypointIndex(uint32_t value) { }; - void HandleWaypointCommandGroupEmote(std::string data); - void HandleWaypointCommandSetVariable(std::string data); - void HandleWaypointCommandCastSkill(std::string data); - void HandleWaypointCommandEquipInventory(std::string data); - void HandleWaypointCommandUnequipInventory(std::string data); - void HandleWaypointCommandDelay(std::string data); - void HandleWaypointCommandEmote(std::string data); - void HandleWaypointCommandTeleport(std::string data); - void HandleWaypointCommandPathSpeed(std::string data); - void HandleWaypointCommandRemoveNPC(std::string data); - void HandleWaypointCommandChangeWaypoint(std::string data); - void HandleWaypointCommandSpawnObject(std::string data); + 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 diff --git a/dGame/dComponents/MovementAIComponentAronwk.cpp b/dGame/dComponents/MovementAIComponentAronwk.cpp index 4d7039a6..f1566390 100644 --- a/dGame/dComponents/MovementAIComponentAronwk.cpp +++ b/dGame/dComponents/MovementAIComponentAronwk.cpp @@ -4,145 +4,222 @@ #include "eWaypointCommandType.h" #include "RenderComponent.h" #include "SkillComponent.h" +#include "InventoryComponent.h" +#include "Zone.h" +#include "EntityInfo.h" +#include "ProximityMonitorComponent.h" +#include "DestroyableComponent.h" -void MovementAIComponent::HandleWaypointArrived() { - if (!m_Path) return; - if (m_Path->pathWaypoints.at(m_CurrentPathWaypointIndex).commands.empty()) return; - for(auto [command, data] : m_Path->pathWaypoints.at(m_CurrentPathWaypointIndex).commands){ - switch(command){ - case eWaypointCommandType::STOP: - Stop(); - break; - case eWaypointCommandType::GROUP_EMOTE: - 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: - HandleWaypointCommandDelay(data); - break; - case eWaypointCommandType::EMOTE: - HandleWaypointCommandEmote(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: - Game::logger->LogDebug("MovementAIComponent", "Unusable Command %i", command); - break; - case eWaypointCommandType::INVALID: - default: - Game::logger->LogDebug("MovementAIComponent", "Got invalid waypoint command %i", command); - break; +void MovementAIComponent::HandleWaypointArrived(uint32_t commandIndex) { + Pause(); + if (!m_Path){ + Resume(); + return; + } + if (commandIndex >= m_Path->pathWaypoints.at(m_CurrentPathWaypointIndex).commands.size()){ + Resume(); + return; + } + + 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: + Game::logger->LogDebug("MovementAIComponent", "Unable to process bounce waypoint command server side"); + break; + case eWaypointCommandType::INVALID: + default: + Game::logger->LogDebug("MovementAIComponent", "Got invalid waypoint command %i", command); + break; + } + + m_Parent->AddCallbackTimer(delay, [this, commandIndex](){ + auto newCommandIndex = commandIndex + 1; + this->HandleWaypointArrived(newCommandIndex); } - } + ); } -void MovementAIComponent::HandleWaypointCommandGroupEmote(std::string data) { +float MovementAIComponent::HandleWaypointCommandGroupEmote(const std::string& data) { const auto& split = GeneralUtils::SplitString(data, ';'); - if (split.size() != 2) return; - const auto& entities = Game::entityManager->GetEntitiesInGroup(split[0]); + if (split.size() != 2) return 0.0f; + const auto& entities = Game::entityManager->GetEntitiesInGroup(split.at(0)); + float delay = 0.0f; for (auto& entity: entities){ - RenderComponent::PlayAnimation(entity, split[1]); + delay = RenderComponent::PlayAnimation(entity, split.at(1)); } - // delay for animation time + return delay; } -void MovementAIComponent::HandleWaypointCommandSetVariable(std::string data) { - const auto& split = GeneralUtils::SplitString(data, '='); - m_Parent->SetNetworkVar(GeneralUtils::ASCIIToUTF16(split[0]), split[1]); + +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(std::string data) { + +void MovementAIComponent::HandleWaypointCommandCastSkill(const std::string& data) { if (data.empty()) return; auto* skillComponent = m_Parent->GetComponent(); if (!skillComponent) { - Game::logger->LogDebug("MovementAIComponent::HandleWaypointArrived", "Skill component not found!"); + Game::logger->LogDebug("MovementAIComponent::HandleWaypointCommandCastSkill", "Skill component not found!"); return; } uint32_t skillId = 0; GeneralUtils::TryParse(data, skillId); if (skillId != 0) skillComponent->CastSkill(skillId); - // add some delay?? + return; } -void MovementAIComponent::HandleWaypointCommandEquipInventory(std::string data) { - // equip item via ID (not lot???) + +void MovementAIComponent::HandleWaypointCommandEquipInventory(const std::string& data) { + if (data.empty()) return; + auto* inventoryComponent = m_Parent->GetComponent(); + if (!inventoryComponent) { + Game::logger->LogDebug("MovementAIComponent::HandleWaypointCommandEquipInventory", "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(std::string data) { - // unequip item via ID (not lot??) + +void MovementAIComponent::HandleWaypointCommandUnequipInventory(const std::string& data) { + if (data.empty()) return; + auto* inventoryComponent = m_Parent->GetComponent(); + if (!inventoryComponent) { + Game::logger->LogDebug("MovementAIComponent::HandleWaypointCommandEquipInventory", "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); } -void MovementAIComponent::HandleWaypointCommandDelay(std::string data) { - Pause(); - std::remove_if(data.begin(), data.end(), ::isspace); - // delay for time + +float MovementAIComponent::HandleWaypointCommandDelay(const std::string& data) { + float delay = 0.0f; + std::string delayString = data; + std::remove_if(delayString.begin(), delayString.end(), ::isspace); + GeneralUtils::TryParse(delayString, delay); + return delay; } -void MovementAIComponent::HandleWaypointCommandEmote(std::string data) { - // pause for animation time - auto delay = RenderComponent::PlayAnimation(m_Parent, data); -} -void MovementAIComponent::HandleWaypointCommandTeleport(std::string data) { + +void MovementAIComponent::HandleWaypointCommandTeleport(const std::string& data) { auto posString = GeneralUtils::SplitString(data, ','); if (posString.size() == 0) return; auto newPos = NiPoint3(); - if (posString.size() == 1) GeneralUtils::TryParse(posString.at(0), newPos.x); - if (posString.size() == 2) GeneralUtils::TryParse(posString.at(1), newPos.y); - if (posString.size() == 3) GeneralUtils::TryParse(posString.at(2), newPos.z); + if (posString.size() == 1 && !GeneralUtils::TryParse(posString.at(0), newPos.x)) return; + if (posString.size() == 2 && !GeneralUtils::TryParse(posString.at(1), newPos.y)) return; + if (posString.size() == 3 && !GeneralUtils::TryParse(posString.at(2), newPos.z)) return; GameMessages::SendTeleport(m_Parent->GetObjectID(), newPos, NiQuaternion::IDENTITY, UNASSIGNED_SYSTEM_ADDRESS); } -void MovementAIComponent::HandleWaypointCommandPathSpeed(std::string data) { + +void MovementAIComponent::HandleWaypointCommandPathSpeed(const std::string& data) { float speed = 0.0; GeneralUtils::TryParse(data, speed); - SetMaxSpeed(speed); + SetCurrentSpeed(speed); } -void MovementAIComponent::HandleWaypointCommandRemoveNPC(std::string data) { - // get objects in proximity - // KillOBJS ??? + +void MovementAIComponent::HandleWaypointCommandRemoveNPC(const std::string& data) { + if (data.empty()) return; + auto* proximityMonitorComponent = m_Parent->GetComponent(); + if (!proximityMonitorComponent) { + Game::logger->LogDebug("MovementAIComponent::HandleWaypointCommandRemoveNPC", "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(); + if (!destroyableComponent) { + Game::logger->LogDebug("MovementAIComponent::HandleWaypointCommandRemoveNPC", "Destroyable component not found!"); + return; + } + uint32_t factionID = -1; + GeneralUtils::TryParse(data, factionID); + if (destroyableComponent->BelongsToFaction(factionID)) m_Parent->Kill(); + } } -void MovementAIComponent::HandleWaypointCommandChangeWaypoint(std::string data) { + +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[0]; - index = stoi(datas[1]); + path_string = datas.at(0); + GeneralUtils::TryParse(datas.at(1), index); } else path_string = data; if (path_string != "") { SetupPath(path_string); + // TODO: do better? talk to emo SetCurrentPathWaypointIndex(index); SetNextPathWaypointIndex(index); } } -void MovementAIComponent::HandleWaypointCommandSpawnObject(std::string data) { - // just do it + +void MovementAIComponent::HandleWaypointCommandSpawnObject(const std::string& data) { + LOT newObjectLOT = 0; + GeneralUtils::TryParse(data, newObjectLOT); + 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); }