diff --git a/apps/openmw-mp/Script/Functions/Mechanics.cpp b/apps/openmw-mp/Script/Functions/Mechanics.cpp index 29c3464fd..c237ded55 100644 --- a/apps/openmw-mp/Script/Functions/Mechanics.cpp +++ b/apps/openmw-mp/Script/Functions/Mechanics.cpp @@ -11,6 +11,14 @@ using namespace std; static std::string tempCellDescription; +void MechanicsFunctions::ClearTeamMembersForPlayer(unsigned short pid) noexcept +{ + Player *player; + GET_PLAYER(pid, player, ); + + player->teamMembers.clear(); +} + unsigned char MechanicsFunctions::GetMiscellaneousChangeType(unsigned short pid) noexcept { Player *player; @@ -181,6 +189,17 @@ void MechanicsFunctions::SetSelectedSpellId(unsigned short pid, const char *spel player->selectedSpellId = spellId; } +void MechanicsFunctions::AddTeamMemberForPlayer(unsigned short pid, unsigned short teamMemberPid) noexcept +{ + Player *player; + GET_PLAYER(pid, player, ); + + Player *teamMember; + GET_PLAYER(teamMemberPid, teamMember, ); + + player->teamMembers.push_back(teamMember->guid); +} + void MechanicsFunctions::SendMarkLocation(unsigned short pid) { Player *player; @@ -207,6 +226,19 @@ void MechanicsFunctions::SendSelectedSpell(unsigned short pid) packet->Send(false); } +void MechanicsFunctions::SendTeam(unsigned short pid, bool sendToOtherPlayers) +{ + Player *player; + GET_PLAYER(pid, player, ); + + mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_TEAM); + packet->setPlayer(player); + + packet->Send(false); + if (sendToOtherPlayers) + packet->Send(true); +} + void MechanicsFunctions::Jail(unsigned short pid, int jailDays, bool ignoreJailTeleportation, bool ignoreJailSkillIncreases, const char* jailProgressText, const char* jailEndText) noexcept { diff --git a/apps/openmw-mp/Script/Functions/Mechanics.hpp b/apps/openmw-mp/Script/Functions/Mechanics.hpp index edc5d8133..2699009d3 100644 --- a/apps/openmw-mp/Script/Functions/Mechanics.hpp +++ b/apps/openmw-mp/Script/Functions/Mechanics.hpp @@ -4,7 +4,9 @@ #include "../Types.hpp" #define MECHANICSAPI \ - {"GetMiscellaneousChangeType", MechanicsFunctions::GetMiscellaneousChangeType},\ + {"ClearTeamMembersForPlayer", MechanicsFunctions::ClearTeamMembersForPlayer},\ + \ + {"GetMiscellaneousChangeType", MechanicsFunctions::GetMiscellaneousChangeType},\ \ {"GetMarkCell", MechanicsFunctions::GetMarkCell},\ {"GetMarkPosX", MechanicsFunctions::GetMarkPosX},\ @@ -29,8 +31,11 @@ {"SetMarkRot", MechanicsFunctions::SetMarkRot},\ {"SetSelectedSpellId", MechanicsFunctions::SetSelectedSpellId},\ \ + {"AddTeamMemberForPlayer", MechanicsFunctions::AddTeamMemberForPlayer},\ + \ {"SendMarkLocation", MechanicsFunctions::SendMarkLocation},\ {"SendSelectedSpell", MechanicsFunctions::SendSelectedSpell},\ + {"SendTeam", MechanicsFunctions::SendTeam},\ \ {"Jail", MechanicsFunctions::Jail},\ {"Resurrect", MechanicsFunctions::Resurrect},\ @@ -42,6 +47,15 @@ class MechanicsFunctions { public: + /** + * \brief Clear the list of players who will be regarded as being on this player's + * team. + * + * \param pid The player ID. + * \return void + */ + static void ClearTeamMembersForPlayer(unsigned short pid) noexcept; + /** * \brief Get the type of a PlayerMiscellaneous packet. * @@ -225,6 +239,15 @@ public: */ static void SetSelectedSpellId(unsigned short pid, const char *spellId) noexcept; + /** + * \brief Add a team member to a player's list of team members. + * + * \param pid The player ID. + * \param teamMemberPid The team member's player ID. + * \return void + */ + static void AddTeamMemberForPlayer(unsigned short pid, unsigned short teamMemberPid) noexcept; + /** * \brief Send a PlayerMiscellaneous packet with a Mark location to a player. * @@ -241,6 +264,16 @@ public: */ static void SendSelectedSpell(unsigned short pid); + /** + * \brief Send a PlayerTeam packet with a list of team member IDs to a player. + * + * \param pid The player ID. + * \param sendToOtherPlayers Whether this packet should be sent to players other than the + * player attached to the packet (false by default). + * \return void + */ + static void SendTeam(unsigned short pid, bool sendToOtherPlayers); + /** * \brief Send a PlayerJail packet about a player. * diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 00b1f8612..20bcbc0a3 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -122,7 +122,7 @@ add_openmw_dir (mwmp/processors/player ProcessorChatMessage ProcessorGUIMessageB ProcessorPlayerInventory ProcessorPlayerItemUse ProcessorPlayerJail ProcessorPlayerJournal ProcessorPlayerLevel ProcessorPlayerMiscellaneous ProcessorPlayerMomentum ProcessorPlayerPosition ProcessorPlayerQuickKeys ProcessorPlayerReputation ProcessorPlayerResurrect ProcessorPlayerShapeshift ProcessorPlayerSkill ProcessorPlayerSpeech ProcessorPlayerSpellbook - ProcessorPlayerStatsDynamic ProcessorPlayerTopic ProcessorPlayerPlaceholder + ProcessorPlayerStatsDynamic ProcessorPlayerTopic ProcessorPlayerTeam ) add_openmw_dir (mwmp/processors/object BaseObjectProcessor diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 939d5bcc3..7d67d407f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -14,10 +14,12 @@ Include additional headers for multiplayer purposes */ #include +#include #include "../mwmp/Main.hpp" #include "../mwmp/Networking.hpp" #include "../mwmp/LocalPlayer.hpp" #include "../mwmp/PlayerList.hpp" +#include "../mwmp/DedicatedPlayer.hpp" #include "../mwmp/CellController.hpp" #include "../mwmp/MechanicsHelper.hpp" #include "../mwmp/ObjectList.hpp" @@ -2368,6 +2370,23 @@ namespace MWMechanics if (stats.isDead()) continue; + /* + Start of tes3mp addition + + If we're checking a player and the iteratedActor is another player belonging to this one's teamMembers, + include the iteratedActor in the actors siding with the player + */ + if (actor == getPlayer() && mwmp::PlayerList::isDedicatedPlayer(iteratedActor)) + { + if (Utils::vectorContains(mwmp::Main::get().getLocalPlayer()->teamMembers, mwmp::PlayerList::getPlayer(iteratedActor)->guid)) + { + list.push_back(iteratedActor); + } + } + /* + End of tes3mp addition + */ + // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package // Actors that are targeted by this actor's Follow or Escort packages also side with them for (const AiPackage* package : stats.getAiSequence()) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index ed005c97d..986c540fc 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -12,6 +12,7 @@ Include additional headers for multiplayer purposes */ +#include #include "../mwmp/Main.hpp" #include "../mwmp/LocalPlayer.hpp" #include "../mwmp/PlayerList.hpp" @@ -1577,15 +1578,49 @@ namespace MWMechanics return false; MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target); - if (attacker == player) + /* + Start of tes3mp change (major) + + Allow collateral damage from dedicated players as well + */ + if (attacker == player || mwmp::PlayerList::isDedicatedPlayer(attacker)) + /* + End of tes3mp change (major) + */ { std::set followersAttacker; getActorsSidingWith(attacker, followersAttacker); - if (followersAttacker.find(target) != followersAttacker.end()) + + /* + Start of tes3mp change (major) + + Check not only whether the target is on the same side as the attacker, + but also whether the attacker is on the same side as the target, + thus allowing for NPC companions of one player to forgive another player + when those players are allied + */ + std::set followersTarget; + getActorsSidingWith(target, followersTarget); + + if (followersAttacker.find(target) != followersAttacker.end() || followersTarget.find(attacker) != followersTarget.end()) + /* + End of tes3mp change (major) + */ { statsTarget.friendlyHit(); - if (statsTarget.getFriendlyHits() < 4) + /* + Start of tes3mp change (major) + + Due to a greater propensity for collateral damage in multiplayer, + allow more friendly hits + + TODO: Allow the server to change the count of the friendly hits + */ + if (statsTarget.getFriendlyHits() < 8) + /* + End of tes3mp change (major) + */ { MWBase::Environment::get().getDialogueManager()->say(target, "hit"); return false; diff --git a/apps/openmw/mwmp/processors/ProcessorInitializer.cpp b/apps/openmw/mwmp/processors/ProcessorInitializer.cpp index 964cfa435..2ff22cfa7 100644 --- a/apps/openmw/mwmp/processors/ProcessorInitializer.cpp +++ b/apps/openmw/mwmp/processors/ProcessorInitializer.cpp @@ -30,7 +30,6 @@ #include "player/ProcessorPlayerItemUse.hpp" #include "player/ProcessorPlayerJail.hpp" #include "player/ProcessorPlayerJournal.hpp" -#include "player/ProcessorPlayerPlaceholder.hpp" #include "player/ProcessorPlayerLevel.hpp" #include "player/ProcessorPlayerMiscellaneous.hpp" #include "player/ProcessorPlayerMomentum.hpp" @@ -44,6 +43,7 @@ #include "player/ProcessorPlayerSpeech.hpp" #include "player/ProcessorPlayerSpellbook.hpp" #include "player/ProcessorPlayerStatsDynamic.hpp" +#include "player/ProcessorPlayerTeam.hpp" #include "player/ProcessorPlayerTopic.hpp" #include "ObjectProcessor.hpp" @@ -133,7 +133,6 @@ void ProcessorInitializer() PlayerProcessor::AddProcessor(new ProcessorPlayerItemUse()); PlayerProcessor::AddProcessor(new ProcessorPlayerJail()); PlayerProcessor::AddProcessor(new ProcessorPlayerJournal()); - PlayerProcessor::AddProcessor(new ProcessorPlayerPlaceholder()); PlayerProcessor::AddProcessor(new ProcessorPlayerLevel()); PlayerProcessor::AddProcessor(new ProcessorPlayerMiscellaneous()); PlayerProcessor::AddProcessor(new ProcessorPlayerMomentum()); @@ -147,6 +146,7 @@ void ProcessorInitializer() PlayerProcessor::AddProcessor(new ProcessorPlayerSpeech()); PlayerProcessor::AddProcessor(new ProcessorPlayerSpellbook()); PlayerProcessor::AddProcessor(new ProcessorPlayerStatsDynamic()); + PlayerProcessor::AddProcessor(new ProcessorPlayerTeam()); PlayerProcessor::AddProcessor(new ProcessorPlayerTopic()); ObjectProcessor::AddProcessor(new ProcessorConsoleCommand()); diff --git a/apps/openmw/mwmp/processors/player/ProcessorPlayerPlaceholder.hpp b/apps/openmw/mwmp/processors/player/ProcessorPlayerPlaceholder.hpp deleted file mode 100644 index 456fab101..000000000 --- a/apps/openmw/mwmp/processors/player/ProcessorPlayerPlaceholder.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef OPENMW_PROCESSORPLACEHOLDER_HPP -#define OPENMW_PROCESSORPLACEHOLDER_HPP - -#include "../PlayerProcessor.hpp" - -namespace mwmp -{ - class ProcessorPlayerPlaceholder final: public PlayerProcessor - { - public: - ProcessorPlayerPlaceholder() - { - BPP_INIT(ID_PLACEHOLDER) - } - - virtual void Do(PlayerPacket &packet, BasePlayer *player) - { - if (isRequest()) - { - // Entire list of topics cannot currently be requested from players - } - else if (player != 0) - { - // Placeholder - } - } - }; -} - -#endif //OPENMW_PROCESSORPLACEHOLDER_HPP diff --git a/apps/openmw/mwmp/processors/player/ProcessorPlayerTeam.hpp b/apps/openmw/mwmp/processors/player/ProcessorPlayerTeam.hpp new file mode 100644 index 000000000..8d7464cb9 --- /dev/null +++ b/apps/openmw/mwmp/processors/player/ProcessorPlayerTeam.hpp @@ -0,0 +1,42 @@ +#ifndef OPENMW_PROCESSORPLAYERTEAM_HPP +#define OPENMW_PROCESSORPLAYERTEAM_HPP + +#include "../PlayerProcessor.hpp" +#include "apps/openmw/mwmp/Main.hpp" +#include "apps/openmw/mwmp/LocalPlayer.hpp" + +namespace mwmp +{ + class ProcessorPlayerTeam final: public PlayerProcessor + { + public: + ProcessorPlayerTeam() + { + BPP_INIT(ID_PLAYER_TEAM) + } + + virtual void Do(PlayerPacket &packet, BasePlayer *player) + { + if (isLocal()) + { + LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, "Received ID_PLAYER_TEAM about LocalPlayer from server"); + + mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer(); + + for (std::vector::iterator iter = localPlayer->teamMembers.begin(); iter != localPlayer->teamMembers.end(); ) + { + DedicatedPlayer *dedicatedPlayer = PlayerList::getPlayer(*iter); + + if (dedicatedPlayer) + { + LOG_APPEND(TimedLog::LOG_INFO, "- Adding %s to our team members", dedicatedPlayer->npc.mName.c_str()); + } + + ++iter; + } + } + } + }; +} + +#endif //OPENMW_PROCESSORPLAYERTEAM_HPP diff --git a/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp b/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp index 7958f45f3..da94c64c4 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp @@ -3,6 +3,7 @@ #include "../PlayerProcessor.hpp" +#include #include #include "apps/openmw/mwstate/statemanagerimp.hpp" @@ -22,7 +23,23 @@ namespace mwmp if (isLocal()) MWBase::Environment::get().getStateManager()->requestQuit(); else if (player != 0) + { + mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer(); + + for (std::vector::iterator iter = localPlayer->teamMembers.begin(); iter != localPlayer->teamMembers.end(); ) + { + if (*iter == guid) + { + DedicatedPlayer *dedicatedPlayer = PlayerList::getPlayer(guid); + LOG_APPEND(TimedLog::LOG_INFO, "- Deleting %s from our team members", dedicatedPlayer->npc.mName.c_str()); + iter = localPlayer->teamMembers.erase(iter); + } + else + ++iter; + } + PlayerList::deletePlayer(guid); + } } }; } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 5ff6a56a1..050daa63d 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -198,7 +198,7 @@ add_component_dir (openmw-mp/Packets/Player PacketPlayerShapeshift PacketPlayerSkill PacketPlayerSpeech PacketPlayerSpellbook PacketPlayerStatsDynamic PacketPlayerTopic - PacketPlayerPlaceholder + PacketPlayerTeam ) add_component_dir (openmw-mp/Packets/Object diff --git a/components/openmw-mp/Base/BasePlayer.hpp b/components/openmw-mp/Base/BasePlayer.hpp index 2bddb2515..882526c90 100644 --- a/components/openmw-mp/Base/BasePlayer.hpp +++ b/components/openmw-mp/Base/BasePlayer.hpp @@ -216,6 +216,7 @@ namespace mwmp std::vector cellStateChanges; ESM::ActiveSpells activeSpells; + std::vector teamMembers; CurrentContainer currentContainer; int difficulty = 0; diff --git a/components/openmw-mp/Controllers/PlayerPacketController.cpp b/components/openmw-mp/Controllers/PlayerPacketController.cpp index 7bcb6315d..ae71d01b3 100644 --- a/components/openmw-mp/Controllers/PlayerPacketController.cpp +++ b/components/openmw-mp/Controllers/PlayerPacketController.cpp @@ -25,7 +25,7 @@ #include "../Packets/Player/PacketPlayerItemUse.hpp" #include "../Packets/Player/PacketPlayerJail.hpp" #include "../Packets/Player/PacketPlayerJournal.hpp" -#include "../Packets/Player/PacketPlayerPlaceholder.hpp" +#include "../Packets/Player/PacketPlayerTeam.hpp" #include "../Packets/Player/PacketPlayerLevel.hpp" #include "../Packets/Player/PacketPlayerMiscellaneous.hpp" #include "../Packets/Player/PacketPlayerMomentum.hpp" @@ -81,7 +81,7 @@ mwmp::PlayerPacketController::PlayerPacketController(RakNet::RakPeerInterface *p AddPacket(&packets, peer); AddPacket(&packets, peer); AddPacket(&packets, peer); - AddPacket(&packets, peer); + AddPacket(&packets, peer); AddPacket(&packets, peer); AddPacket(&packets, peer); AddPacket(&packets, peer); diff --git a/components/openmw-mp/NetworkMessages.hpp b/components/openmw-mp/NetworkMessages.hpp index 01f088ac1..5ba9ba322 100644 --- a/components/openmw-mp/NetworkMessages.hpp +++ b/components/openmw-mp/NetworkMessages.hpp @@ -109,6 +109,7 @@ enum GameMessages ID_PLAYER_ITEM_USE, ID_PLAYER_CAST, + ID_PLAYER_TEAM, ID_PLACEHOLDER }; diff --git a/components/openmw-mp/Packets/Player/PacketPlayerPlaceholder.cpp b/components/openmw-mp/Packets/Player/PacketPlayerPlaceholder.cpp deleted file mode 100644 index 7d9785122..000000000 --- a/components/openmw-mp/Packets/Player/PacketPlayerPlaceholder.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include "PacketPlayerPlaceholder.hpp" - -mwmp::PacketPlayerPlaceholder::PacketPlayerPlaceholder(RakNet::RakPeerInterface *peer) : PlayerPacket(peer) -{ - packetID = ID_PLACEHOLDER; -} - -void mwmp::PacketPlayerPlaceholder::Packet(RakNet::BitStream *bs, bool send) -{ - // Placeholder -} diff --git a/components/openmw-mp/Packets/Player/PacketPlayerTeam.cpp b/components/openmw-mp/Packets/Player/PacketPlayerTeam.cpp new file mode 100644 index 000000000..bd53797eb --- /dev/null +++ b/components/openmw-mp/Packets/Player/PacketPlayerTeam.cpp @@ -0,0 +1,30 @@ +#include +#include "PacketPlayerTeam.hpp" + +mwmp::PacketPlayerTeam::PacketPlayerTeam(RakNet::RakPeerInterface *peer) : PlayerPacket(peer) +{ + packetID = ID_PLAYER_TEAM; +} + +void mwmp::PacketPlayerTeam::Packet(RakNet::BitStream *bs, bool send) +{ + PlayerPacket::Packet(bs, send); + + uint32_t count; + + if (send) + count = static_cast(player->teamMembers.size()); + + RW(count, send); + + if (!send) + { + player->teamMembers.clear(); + player->teamMembers.resize(count); + } + + for (auto &&teamPlayerGuid : player->teamMembers) + { + RW(teamPlayerGuid, send, true); + } +} diff --git a/components/openmw-mp/Packets/Player/PacketPlayerPlaceholder.hpp b/components/openmw-mp/Packets/Player/PacketPlayerTeam.hpp similarity index 50% rename from components/openmw-mp/Packets/Player/PacketPlayerPlaceholder.hpp rename to components/openmw-mp/Packets/Player/PacketPlayerTeam.hpp index bffb844d1..e7e72fcff 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerPlaceholder.hpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerTeam.hpp @@ -1,17 +1,17 @@ -#ifndef OPENMW_PACKETPLACEHOLDER_HPP -#define OPENMW_PACKETPLACEHOLDER_HPP +#ifndef OPENMW_PACKETTEAM_HPP +#define OPENMW_PACKETTEAM_HPP #include namespace mwmp { - class PacketPlayerPlaceholder : public PlayerPacket + class PacketPlayerTeam : public PlayerPacket { public: - PacketPlayerPlaceholder(RakNet::RakPeerInterface *peer); + PacketPlayerTeam(RakNet::RakPeerInterface *peer); virtual void Packet(RakNet::BitStream *bs, bool send); }; } -#endif //OPENMW_PACKETPLACEHOLDER_HPP +#endif //OPENMW_PACKETTEAM_HPP