diff --git a/apps/openmw-mp/Script/Functions/Actors.cpp b/apps/openmw-mp/Script/Functions/Actors.cpp index e41b2e9a6..65b69772f 100644 --- a/apps/openmw-mp/Script/Functions/Actors.cpp +++ b/apps/openmw-mp/Script/Functions/Actors.cpp @@ -267,6 +267,31 @@ void ActorFunctions::SetActorFatigueModified(double value) noexcept tempActor.creatureStats.mDynamic[2].mMod = value; } +void ActorFunctions::SetActorAIAction(unsigned int action) noexcept +{ + tempActor.aiAction = action; +} + +void ActorFunctions::SetActorAITargetToPlayer(unsigned short pid) noexcept +{ + Player *player; + GET_PLAYER(pid, player, ); + + tempActor.hasAiTarget = true; + tempActor.aiTarget.isPlayer = true; + + tempActor.aiTarget.guid = player->guid; +} + +void ActorFunctions::SetActorAITargetToActor(int refNumIndex, int mpNum) noexcept +{ + tempActor.hasAiTarget = true; + tempActor.aiTarget.isPlayer = false; + + tempActor.aiTarget.refNumIndex = refNumIndex; + tempActor.aiTarget.mpNum = mpNum; +} + void ActorFunctions::EquipActorItem(unsigned short slot, const char *refId, unsigned int count, int charge, double enchantmentCharge) noexcept { tempActor.equipmentItems[slot].refId = refId; @@ -328,6 +353,12 @@ void ActorFunctions::SendActorEquipment() noexcept mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_EQUIPMENT)->Send(writeActorList.guid); } +void ActorFunctions::SendActorAI() noexcept +{ + mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AI)->setActorList(&writeActorList); + mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AI)->Send(writeActorList.guid); +} + void ActorFunctions::SendActorCellChange() noexcept { mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_CELL_CHANGE)->setActorList(&writeActorList); diff --git a/apps/openmw-mp/Script/Functions/Actors.hpp b/apps/openmw-mp/Script/Functions/Actors.hpp index d3dbc8200..cc2792fd6 100644 --- a/apps/openmw-mp/Script/Functions/Actors.hpp +++ b/apps/openmw-mp/Script/Functions/Actors.hpp @@ -60,6 +60,10 @@ {"SetActorFatigueCurrent", ActorFunctions::SetActorFatigueCurrent},\ {"SetActorFatigueModified", ActorFunctions::SetActorFatigueModified},\ \ + {"SetActorAIAction", ActorFunctions::SetActorAIAction},\ + {"SetActorAITargetToPlayer", ActorFunctions::SetActorAITargetToPlayer},\ + {"SetActorAITargetToActor", ActorFunctions::SetActorAITargetToActor},\ + \ {"EquipActorItem", ActorFunctions::EquipActorItem},\ {"UnequipActorItem", ActorFunctions::UnequipActorItem},\ \ @@ -70,9 +74,10 @@ {"SendActorPosition", ActorFunctions::SendActorPosition},\ {"SendActorStatsDynamic", ActorFunctions::SendActorStatsDynamic},\ {"SendActorEquipment", ActorFunctions::SendActorEquipment},\ + {"SendActorAI", ActorFunctions::SendActorAI},\ {"SendActorCellChange", ActorFunctions::SendActorCellChange} -class ActorFunctions +class ActorFunctions { public: @@ -481,6 +486,31 @@ public: */ static void SetActorFatigueModified(double value) noexcept; + /** + * \brief Set the AI action of the temporary actor stored on the server. + * + * \param action The new action. + * \return void + */ + static void SetActorAIAction(unsigned int action) noexcept; + + /** + * \brief Set a player as the AI target of the temporary actor stored on the server. + * + * \param pid The player ID. + * \return void + */ + static void SetActorAITargetToPlayer(unsigned short pid) noexcept; + + /** + * \brief Set another actor as the AI target of the temporary actor stored on the server. + * + * \param refNumIndex The refNumIndex of the target actor. + * \param mpNum The mpNum of the target actor. + * \return void + */ + static void SetActorAITargetToActor(int refNumIndex, int mpNum) noexcept; + /** * \brief Equip an item in a certain slot of the equipment of the temporary actor stored * on the server. @@ -561,6 +591,15 @@ public: */ static void SendActorEquipment() noexcept; + /** + * \brief Send an ActorAI packet. + * + * It is sent only to the player for whom the current actor list was initialized. + * + * \return void + */ + static void SendActorAI() noexcept; + /** * \brief Send an ActorCellChange packet. * diff --git a/apps/openmw/mwmp/Cell.cpp b/apps/openmw/mwmp/Cell.cpp index 364121b80..b20cf4796 100644 --- a/apps/openmw/mwmp/Cell.cpp +++ b/apps/openmw/mwmp/Cell.cpp @@ -244,6 +244,24 @@ void Cell::readSpeech(ActorList& actorList) } } +void Cell::readAI(ActorList& actorList) +{ + initializeDedicatedActors(actorList); + + for (const auto &baseActor : actorList.baseActors) + { + std::string mapIndex = Main::get().getCellController()->generateMapIndex(baseActor); + + if (dedicatedActors.count(mapIndex) > 0) + { + DedicatedActor *actor = dedicatedActors[mapIndex]; + actor->aiAction = baseActor.aiAction; + actor->aiTarget = baseActor.aiTarget; + actor->setAI(); + } + } +} + void Cell::readAttack(ActorList& actorList) { for (const auto &baseActor : actorList.baseActors) diff --git a/apps/openmw/mwmp/Cell.hpp b/apps/openmw/mwmp/Cell.hpp index 29608c3d7..f54589747 100644 --- a/apps/openmw/mwmp/Cell.hpp +++ b/apps/openmw/mwmp/Cell.hpp @@ -24,6 +24,7 @@ namespace mwmp void readStatsDynamic(ActorList& actorList); void readEquipment(ActorList& actorList); void readSpeech(ActorList& actorList); + void readAI(ActorList& actorList); void readAttack(ActorList& actorList); void readCellChange(ActorList& actorList); diff --git a/apps/openmw/mwmp/CellController.cpp b/apps/openmw/mwmp/CellController.cpp index cca055a7f..b1f118db5 100644 --- a/apps/openmw/mwmp/CellController.cpp +++ b/apps/openmw/mwmp/CellController.cpp @@ -154,6 +154,17 @@ void CellController::readSpeech(ActorList& actorList) cellsInitialized[mapIndex]->readSpeech(actorList); } +void CellController::readAI(ActorList& actorList) +{ + std::string mapIndex = actorList.cell.getDescription(); + + initializeCell(actorList.cell); + + // If this now exists, send it the data + if (cellsInitialized.count(mapIndex) > 0) + cellsInitialized[mapIndex]->readAI(actorList); +} + void CellController::readAttack(ActorList& actorList) { std::string mapIndex = actorList.cell.getDescription(); diff --git a/apps/openmw/mwmp/CellController.hpp b/apps/openmw/mwmp/CellController.hpp index d93989e9d..2e21eb306 100644 --- a/apps/openmw/mwmp/CellController.hpp +++ b/apps/openmw/mwmp/CellController.hpp @@ -27,6 +27,7 @@ namespace mwmp void readStatsDynamic(mwmp::ActorList& actorList); void readEquipment(mwmp::ActorList& actorList); void readSpeech(mwmp::ActorList& actorList); + void readAI(mwmp::ActorList& actorList); void readAttack(mwmp::ActorList& actorList); void readCellChange(mwmp::ActorList& actorList); diff --git a/apps/openmw/mwmp/DedicatedActor.cpp b/apps/openmw/mwmp/DedicatedActor.cpp index 61260cf3d..9e429901c 100644 --- a/apps/openmw/mwmp/DedicatedActor.cpp +++ b/apps/openmw/mwmp/DedicatedActor.cpp @@ -6,6 +6,7 @@ #include "../mwdialogue/dialoguemanagerimp.hpp" +#include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/mechanicsmanagerimp.hpp" #include "../mwmechanics/movement.hpp" @@ -203,6 +204,41 @@ void DedicatedActor::setEquipment() } } +void DedicatedActor::setAI() +{ + if (hasAiTarget) + { + MWWorld::Ptr targetPtr; + + if (aiTarget.isPlayer) + targetPtr = MechanicsHelper::getPlayerPtr(aiTarget); + else + { + if (mwmp::Main::get().getCellController()->isLocalActor(aiTarget.refNumIndex, aiTarget.mpNum)) + targetPtr = mwmp::Main::get().getCellController()->getLocalActor(aiTarget.refNumIndex, aiTarget.mpNum)->getPtr(); + else if (mwmp::Main::get().getCellController()->isDedicatedActor(aiTarget.refNumIndex, aiTarget.mpNum)) + targetPtr = mwmp::Main::get().getCellController()->getDedicatedActor(aiTarget.refNumIndex, aiTarget.mpNum)->getPtr(); + else + LOG_APPEND(Log::LOG_VERBOSE, "-- DedicatedActor %s %i-%i has invalid target AI target %i-%i", + ptr.getCellRef().getRefId().c_str(), ptr.getCellRef().getRefNum().mIndex, ptr.getCellRef().getMpNum(), + aiTarget.refNumIndex, aiTarget.mpNum); + } + + if (targetPtr) + { + LOG_APPEND(Log::LOG_VERBOSE, "-- DedicatedActor %s %i-%i has AI target %s %i-%i", + ptr.getCellRef().getRefId().c_str(), ptr.getCellRef().getRefNum().mIndex, ptr.getCellRef().getMpNum(), + targetPtr.getCellRef().getRefId().c_str(), aiTarget.refNumIndex, aiTarget.mpNum); + + if (aiAction == mwmp::BaseActorList::FOLLOW) + { + MWMechanics::AiFollow package(targetPtr.getCellRef().getRefId()); + ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(package, ptr); + } + } + } +} + void DedicatedActor::playAnimation() { if (!animation.groupname.empty()) diff --git a/apps/openmw/mwmp/DedicatedActor.hpp b/apps/openmw/mwmp/DedicatedActor.hpp index f58dca15f..3b952f3c4 100644 --- a/apps/openmw/mwmp/DedicatedActor.hpp +++ b/apps/openmw/mwmp/DedicatedActor.hpp @@ -22,6 +22,7 @@ namespace mwmp void setAnimFlags(); void setStatsDynamic(); void setEquipment(); + void setAI(); void playAnimation(); void playSound(); diff --git a/apps/openmw/mwmp/processors/actor/ProcessorActorAI.hpp b/apps/openmw/mwmp/processors/actor/ProcessorActorAI.hpp index 43cef9001..7da35a937 100644 --- a/apps/openmw/mwmp/processors/actor/ProcessorActorAI.hpp +++ b/apps/openmw/mwmp/processors/actor/ProcessorActorAI.hpp @@ -17,7 +17,7 @@ namespace mwmp virtual void Do(ActorPacket &packet, ActorList &actorList) { - //Main::get().getCellController()->readAI(actorList); + Main::get().getCellController()->readAI(actorList); } }; } diff --git a/components/openmw-mp/Base/BaseActor.hpp b/components/openmw-mp/Base/BaseActor.hpp index a3c1ba3f2..8cbd32dcc 100644 --- a/components/openmw-mp/Base/BaseActor.hpp +++ b/components/openmw-mp/Base/BaseActor.hpp @@ -39,6 +39,10 @@ namespace mwmp Animation animation; Attack attack; + bool hasAiTarget; + Target aiTarget; + unsigned int aiAction; + bool hasPositionData; bool hasStatsDynamicData; @@ -62,6 +66,11 @@ namespace mwmp REQUEST = 3 }; + enum AI_ACTION + { + FOLLOW = 0 + }; + RakNet::RakNetGUID guid; std::vector baseActors; diff --git a/components/openmw-mp/Packets/Actor/PacketActorAI.cpp b/components/openmw-mp/Packets/Actor/PacketActorAI.cpp index 774536460..99270bddd 100644 --- a/components/openmw-mp/Packets/Actor/PacketActorAI.cpp +++ b/components/openmw-mp/Packets/Actor/PacketActorAI.cpp @@ -11,5 +11,22 @@ PacketActorAI::PacketActorAI(RakNet::RakPeerInterface *peer) : ActorPacket(peer) void PacketActorAI::Actor(BaseActor &actor, bool send) { - // Placeholder to be filled in later + RW(actor.aiAction, send); + RW(actor.hasAiTarget, send); + + if (actor.hasAiTarget) + { + RW(actor.aiTarget.isPlayer, send); + + if (actor.aiTarget.isPlayer) + { + RW(actor.aiTarget.guid, send); + } + else + { + RW(actor.aiTarget.refId, send, 1); + RW(actor.aiTarget.refNumIndex, send); + RW(actor.aiTarget.mpNum, send); + } + } }