diff --git a/apps/openmw-mp/CMakeLists.txt b/apps/openmw-mp/CMakeLists.txt index 8a3e4ea68..a16c9b18a 100644 --- a/apps/openmw-mp/CMakeLists.txt +++ b/apps/openmw-mp/CMakeLists.txt @@ -85,11 +85,12 @@ set(PROCESSORS_PLAYER processors/player/ProcessorPlayerFaction.hpp processors/player/ProcessorPlayerInventory.hpp processors/player/ProcessorPlayerJournal.hpp processors/player/ProcessorPlayerKillCount.hpp processors/player/ProcessorPlayerLevel.hpp processors/player/ProcessorPlayerMap.hpp - processors/player/ProcessorPlayerPosition.hpp processors/player/ProcessorPlayerRest.hpp - processors/player/ProcessorPlayerResurrect.hpp processors/player/ProcessorPlayerShapeshift.hpp - processors/player/ProcessorPlayerSkill.hpp processors/player/ProcessorPlayerSpeech.hpp - processors/player/ProcessorPlayerSpellbook.hpp processors/player/ProcessorPlayerStatsDynamic.hpp - processors/player/ProcessorPlayerTopic.hpp processors/player/ProcessorGUIWindow.hpp + processors/player/ProcessorPlayerPosition.hpp processors/player/ProcessorPlayerQuickKeys.hpp + processors/player/ProcessorPlayerRest.hpp processors/player/ProcessorPlayerResurrect.hpp + processors/player/ProcessorPlayerShapeshift.hpp processors/player/ProcessorPlayerSkill.hpp + processors/player/ProcessorPlayerSpeech.hpp processors/player/ProcessorPlayerSpellbook.hpp + processors/player/ProcessorPlayerStatsDynamic.hpp processors/player/ProcessorPlayerTopic.hpp + processors/player/ProcessorGUIWindow.hpp ) source_group(tes3mp-server\\processors\\player FILES ${PROCESSORS_PLAYER}) diff --git a/apps/openmw-mp/Dialogue.cpp b/apps/openmw-mp/Dialogue.cpp index c35d66136..064137fd0 100644 --- a/apps/openmw-mp/Dialogue.cpp +++ b/apps/openmw-mp/Dialogue.cpp @@ -15,7 +15,7 @@ void Dialogue::Init(LuaState &lua) lua.getState()->new_usertype("Dialogue", "addTopic", &Dialogue::addTopic, "getTopicId", &Dialogue::getTopicId, - "getChanges", &Dialogue::getChanges, + "size", &Dialogue::size, "reset", &Dialogue::reset); } @@ -57,7 +57,30 @@ std::string Dialogue::getTopicId(unsigned int i) const return player->topicChanges.topics.at(i).topicId; } -unsigned int Dialogue::getChanges() const +size_t Dialogue::size() const { return player->topicChanges.topics.size(); } + +void Dialogue::playAnimation(const std::string &groupname, int mode, int count, bool persist) +{ + player->animation.groupname = groupname; + player->animation.mode = mode; + player->animation.count = count; + player->animation.persist = persist; + + auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ANIM_PLAY); + packet->setPlayer(player); + packet->Send(false); + player->sendToLoaded(*packet); +} + +void Dialogue::playSpeech(const std::string &sound) +{ + player->sound = sound; + + auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SPEECH); + packet->setPlayer(player); + packet->Send(false); + player->sendToLoaded(*packet); +} diff --git a/apps/openmw-mp/Dialogue.hpp b/apps/openmw-mp/Dialogue.hpp index a43a30b19..f2f054c52 100644 --- a/apps/openmw-mp/Dialogue.hpp +++ b/apps/openmw-mp/Dialogue.hpp @@ -18,7 +18,10 @@ public: void addTopic(const std::string &topicId); std::string getTopicId(unsigned int i) const; - unsigned int getChanges() const; + size_t size() const; + + void playAnimation(const std::string &groupname, int mode, int count, bool persist); + void playSpeech(const std::string &sound); void reset(); void update(); diff --git a/apps/openmw-mp/Factions.cpp b/apps/openmw-mp/Factions.cpp index a3e40915d..8c1a7339d 100644 --- a/apps/openmw-mp/Factions.cpp +++ b/apps/openmw-mp/Factions.cpp @@ -19,22 +19,7 @@ void Factions::Init(LuaState &lua) "setFaction", &Factions::setFaction, "clear", &Factions::clear, "size", &Factions::size - ); -/*"InitializeFactionChanges", FactionFunctions::InitializeFactionChanges, - "GetFactionChangesSize", FactionFunctions::GetFactionChangesSize, - "GetFactionChangesAction", FactionFunctions::GetFactionChangesAction, - "GetFactionId", FactionFunctions::GetFactionId, - "GetFactionRank", FactionFunctions::GetFactionRank, - "GetFactionExpulsionState", FactionFunctions::GetFactionExpulsionState, - "GetFactionReputation", FactionFunctions::GetFactionReputation, - "SetFactionChangesAction", FactionFunctions::SetFactionChangesAction, - "SetFactionId", FactionFunctions::SetFactionId, - "SetFactionRank", FactionFunctions::SetFactionRank, - "SetFactionExpulsionState", FactionFunctions::SetFactionExpulsionState, - "SetFactionReputation", FactionFunctions::SetFactionReputation, - "AddFaction", FactionFunctions::AddFaction, - "SendFactionChanges", FactionFunctions::SendFactionChanges*/ } Factions::Factions(Player *player): player(player), changed(false) diff --git a/apps/openmw-mp/GUI.cpp b/apps/openmw-mp/GUI.cpp index 579dae600..1601be65b 100644 --- a/apps/openmw-mp/GUI.cpp +++ b/apps/openmw-mp/GUI.cpp @@ -143,3 +143,109 @@ void GUI::onGUIWindowAction() it->second->call(player->guiWindow); } } + +void QuickKeys::Init(LuaState &lua) +{ + lua.getState()->new_usertype("QuickKeys", + "addQuickKey", &QuickKeys::addQuickKey, + "getQuickKey", &QuickKeys::getQuickKey, + "setQuickKey", &QuickKeys::setQuickKey, + "clear", &QuickKeys::clear, + "size", &QuickKeys::size + ); +} + +QuickKeys::QuickKeys(Player *player) : player(player), changed(false) +{ + +} + +QuickKeys::~QuickKeys() +{ + +} + +void QuickKeys::update() +{ + if (!changed) + return; + changed = false; + + auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_QUICKKEYS); + packet->setPlayer(player); + packet->Send(false); + clear(); +} + +void QuickKeys::addQuickKey(QuickKey quickKey) +{ + player->quickKeyChanges.quickKeys.push_back(quickKey.quickKey); + changed = true; +} + + +QuickKey QuickKeys::getQuickKey(int id) const +{ + return QuickKey(player->quickKeyChanges.quickKeys.at(id)); +} + +void QuickKeys::setQuickKey(int id, QuickKey quickKey) +{ + player->quickKeyChanges.quickKeys.at(id) = quickKey.quickKey; + changed = true; +} + +void QuickKeys::clear() +{ + player->quickKeyChanges.quickKeys.clear(); + changed = true; +} + +size_t QuickKeys::size() const +{ + return player->quickKeyChanges.quickKeys.size(); +} + +void QuickKey::Init(LuaState &lua) +{ + lua.getState()->new_usertype("QuickKey", + "slot", sol::property(&QuickKey::getSlot, &QuickKey::setSlot), + "type", sol::property(&QuickKey::getType, &QuickKey::setType), + "itemId", sol::property(&QuickKey::getItemId, &QuickKey::setItemId) + ); +} + +QuickKey::QuickKey(mwmp::QuickKey &quickKey): quickKey(quickKey) +{ + +} + +int QuickKey::getSlot() const +{ + return quickKey.slot; +} + +void QuickKey::setSlot(unsigned short slot) +{ + quickKey.slot = slot; +} + +int QuickKey::getType() const +{ + return quickKey.type; +} + +void QuickKey::setType(int type) +{ + quickKey.type = type; +} + +std::string QuickKey::getItemId() const +{ + return quickKey.itemId; +} + +void QuickKey::setItemId(const std::string &itemId) +{ + quickKey.itemId = itemId; +} diff --git a/apps/openmw-mp/GUI.hpp b/apps/openmw-mp/GUI.hpp index d059e7b1a..854027f0c 100644 --- a/apps/openmw-mp/GUI.hpp +++ b/apps/openmw-mp/GUI.hpp @@ -34,6 +34,11 @@ public: std::shared_ptr createWindow(short x, short y, sol::function fn, sol::this_environment te); void deleteWindow(std::shared_ptr window); void onGUIWindowAction(); + + void addQuickKey(unsigned short slot, int type, const std::string &itemId); + std::string getTopicId(unsigned int i) const; + unsigned int getChanges() const; + private: Player *player; bool changed; @@ -41,4 +46,44 @@ private: int lastWindowId; }; +class QuickKey +{ + friend class QuickKeys; +public: + static void Init(LuaState &lua); +public: + explicit QuickKey(mwmp::QuickKey &quickKey); + + int getSlot() const; + void setSlot(unsigned short slot); + + int getType() const; + void setType(int slot); + + std::string getItemId() const; + void setItemId(const std::string &itemId); + mwmp::QuickKey quickKey; +}; + +class QuickKeys +{ +public: + static void Init(LuaState &lua); +public: + explicit QuickKeys(Player *player); + ~QuickKeys(); + + void update(); + + void addQuickKey(QuickKey quickKey); + QuickKey getQuickKey(int id) const; + void setQuickKey(int id, QuickKey quickKey); + size_t size() const; + void clear(); + +private: + mwmp::QuickKey tempQuickKey; + Player *player; + bool changed; +}; diff --git a/apps/openmw-mp/Inventory.cpp b/apps/openmw-mp/Inventory.cpp index 77fa74329..c69f44436 100644 --- a/apps/openmw-mp/Inventory.cpp +++ b/apps/openmw-mp/Inventory.cpp @@ -76,11 +76,12 @@ int Inventory::getChangesSize() const return netActor->getNetCreature()->inventoryChanges.items.size(); } -void Inventory::equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge) +void Inventory::equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge, int enchantmentCharge) { netActor->getNetCreature()->equipmentItems[slot].refId = refId; netActor->getNetCreature()->equipmentItems[slot].count = count; netActor->getNetCreature()->equipmentItems[slot].charge = charge; + netActor->getNetCreature()->equipmentItems[slot].enchantmentCharge = enchantmentCharge; if (!Utils::vectorContains(&netActor->getNetCreature()->equipmentIndexChanges, slot)) netActor->getNetCreature()->equipmentIndexChanges.push_back(slot); @@ -90,11 +91,11 @@ void Inventory::equipItem(unsigned short slot, const std::string& refId, unsigne void Inventory::unequipItem( unsigned short slot) { - equipItem(slot, "", 0, -1); + equipItem(slot, "", 0, -1, -1); } -void Inventory::addItem(const std::string &refId, unsigned int count, int charge) +void Inventory::addItem(const std::string &refId, unsigned int count, int charge, int enchantmentCharge) { if (inventoryChanged == mwmp::InventoryChanges::REMOVE) return; @@ -105,6 +106,7 @@ void Inventory::addItem(const std::string &refId, unsigned int count, int charge item.refId = refId; item.count = count; item.charge = charge; + item.enchantmentCharge = enchantmentCharge; netActor->getNetCreature()->inventoryChanges.items.push_back(item); netActor->getNetCreature()->inventoryChanges.action = mwmp::InventoryChanges::ADD; @@ -135,16 +137,16 @@ bool Inventory::hasItemEquipped(const std::string &refId) const return false; } -std::tuple Inventory::getEquipmentItem(unsigned short slot) const +std::tuple Inventory::getEquipmentItem(unsigned short slot) const { const auto &item = netActor->getNetCreature()->equipmentItems[slot]; - return make_tuple(item.refId, item.count, item.charge); + return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge); } -std::tuple Inventory::getInventoryItem(unsigned int slot) const +std::tuple Inventory::getInventoryItem(unsigned int slot) const { const auto &item = netActor->getNetCreature()->inventoryChanges.items.at(slot); - return make_tuple(item.refId, item.count, item.charge); + return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge); } void Inventory::resetEquipmentFlag() diff --git a/apps/openmw-mp/Inventory.hpp b/apps/openmw-mp/Inventory.hpp index e0e1c7eea..eb9901e5e 100644 --- a/apps/openmw-mp/Inventory.hpp +++ b/apps/openmw-mp/Inventory.hpp @@ -25,19 +25,19 @@ public: //inventory int getChangesSize() const; - void addItem(const std::string& refId, unsigned int count, int charge); + void addItem(const std::string& refId, unsigned int count, int charge, int enchantmentCharge); void removeItem(const std::string& refId, unsigned short count); /** * * @param slot - * @return refid, count, charge + * @return refid, count, charge, enchantmentCharge */ - std::tuple getInventoryItem(unsigned int slot) const; + std::tuple getInventoryItem(unsigned int slot) const; // equipment - void equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge); + void equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge, int enchantmentCharge); void unequipItem(unsigned short slot); bool hasItemEquipped(const std::string& refId) const; @@ -45,9 +45,9 @@ public: /** * * @param slot - * @return refid, count, charge + * @return refid, count, charge, enchantmentCharge */ - std::tuple getEquipmentItem(unsigned short slot) const; + std::tuple getEquipmentItem(unsigned short slot) const; private: diff --git a/apps/openmw-mp/Object.cpp b/apps/openmw-mp/Object.cpp index 731726d0e..eea5419f1 100644 --- a/apps/openmw-mp/Object.cpp +++ b/apps/openmw-mp/Object.cpp @@ -17,6 +17,7 @@ void Object::Init(LuaState &lua) "refId", sol::property(&BaseObject::getRefId, &BaseObject::setRefId), "refNum", sol::property(&BaseObject::getRefNum, &BaseObject::setRefNum), "mpNum", sol::property(&BaseObject::getMpNum, &BaseObject::setMpNum), + "guid", sol::property(&BaseObject::getGuid, &BaseObject::setGuid), "getPosition", &Object::getPosition, "setPosition", &Object::setPosition, "getRotation", &Object::getRotation, @@ -111,6 +112,18 @@ void BaseObject::setMpNum(unsigned mpNum) object.mpNum = mpNum; } +RakNet::RakNetGUID BaseObject::getGuid() const +{ + return object.guid; +} + +void BaseObject::setGuid(const RakNet::RakNetGUID &guid) +{ + changedBase = true; + object.guid = guid; + object.isPlayer = true; +} + int Object::getCount() const { return object.count; @@ -134,6 +147,18 @@ void Object::setCharge(int charge) } +int Object::getEnchantmentCharge() const +{ + return object.enchantmentCharge; +} + +void Object::setEnchantmentCharge(int enchantmentCharge) +{ + changedObjectPlace = true; + object.enchantmentCharge = enchantmentCharge; + +} + int Object::getGoldValue() const { return object.goldValue; @@ -220,27 +245,29 @@ Container::Container() } -tuple Container::getItem(int i) const +tuple Container::getItem(int i) const { auto &item = object.containerItems.at(i); - return make_tuple(item.refId, item.count, item.charge); + return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge); } -void Container::setItem(int i, const string &refId, int count, int charge) +void Container::setItem(int i, const string &refId, int count, int charge, int enchantmentCharge) { auto &item = object.containerItems.at(i); item.refId = refId; item.count = count; item.charge = charge; + item.enchantmentCharge = enchantmentCharge; changed = true; } -void Container::addItem(const string &refId, int count, int charge) +void Container::addItem(const string &refId, int count, int charge, int enchantmentCharge) { mwmp::ContainerItem item; item.refId = refId; item.count = count; item.charge = charge; + item.enchantmentCharge = enchantmentCharge; object.containerItems.push_back(item); changed = true; } @@ -304,7 +331,7 @@ shared_ptr>> ObjectController::copyContainers(mwmp: return containers; } -void ObjectController::sendObjects(shared_ptr player, shared_ptr>> objects, const ESM::Cell &cell) +void ObjectController::sendObjects(shared_ptr player, shared_ptr>> objects, const ESM::Cell &cell, bool broadcast) { enum Type { @@ -384,60 +411,105 @@ void ObjectController::sendObjects(shared_ptr player, shared_ptrGetPacket(ID_DOOR_STATE); auto &event = events[Type::DOOR_STATE]; packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } if (changed[Type::OBJECT_STATE]) { auto packet = worldCtrl->GetPacket(ID_OBJECT_STATE); auto &event = events[Type::OBJECT_STATE]; packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } if (changed[Type::OBJECT_SCALE]) { auto packet = worldCtrl->GetPacket(ID_OBJECT_SCALE); auto &event = events[Type::OBJECT_SCALE]; packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } if (changed[Type::OBJECT_TRAP]) { auto packet = worldCtrl->GetPacket(ID_OBJECT_TRAP); auto &event = events[Type::OBJECT_TRAP]; packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } if (changed[Type::OBJECT_LOCK]) { auto packet = worldCtrl->GetPacket(ID_OBJECT_LOCK); auto &event = events[Type::OBJECT_LOCK]; packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } if (changed[Type::OBJECT_DELETE]) { auto packet = worldCtrl->GetPacket(ID_OBJECT_DELETE); auto &event = events[Type::OBJECT_DELETE]; packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } if (changed[Type::OBJECT_SCALE]) { auto packet = worldCtrl->GetPacket(ID_OBJECT_SPAWN); auto &event = events[Type::OBJECT_SCALE]; packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } if (changed[Type::OBJECT_PLACE]) { auto packet = worldCtrl->GetPacket(ID_OBJECT_PLACE); auto &event = events[Type::OBJECT_PLACE]; packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } } -void ObjectController::sendContainers(shared_ptr player, shared_ptr>> objects, const ESM::Cell &cell) +void ObjectController::sendConsoleCommand(shared_ptr player, shared_ptr>> objects, + const ESM::Cell &cell, const std::string &consoleCommand, bool broadcast) +{ + + mwmp::BaseEvent event; + event.cell = cell; + event.consoleCommand = consoleCommand; + event.guid = player->guid; + + for (auto &object : *objects) + event.worldObjects.push_back(object->object); + + auto packet = mwmp::Networking::get().getWorldPacketController()->GetPacket(ID_CONSOLE_COMMAND); + packet->setEvent(&event); + packet->Send(false); + + if (broadcast) + packet->Send(true); +} + +void ObjectController::sendContainers(shared_ptr player, shared_ptr>> objects, + const ESM::Cell &cell, bool broadcast) { mwmp::BaseEvent event; @@ -454,7 +526,10 @@ void ObjectController::sendContainers(shared_ptr player, shared_ptrGetPacket(ID_CONTAINER); packet->setEvent(&event); - packet->Send(event.guid); + packet->Send(false); + + if (broadcast) + packet->Send(true); } void ObjectController::requestContainers(shared_ptr player) diff --git a/apps/openmw-mp/Object.hpp b/apps/openmw-mp/Object.hpp index 30c44b82a..6537b6d97 100644 --- a/apps/openmw-mp/Object.hpp +++ b/apps/openmw-mp/Object.hpp @@ -24,6 +24,9 @@ public: unsigned getMpNum() const; void setMpNum(unsigned mpNum); + RakNet::RakNetGUID getGuid() const; + void setGuid(const RakNet::RakNetGUID &guid); + //void setEventCell(const std::string &cellDescription); @@ -63,6 +66,9 @@ public: int getCharge() const; void setCharge(int charge); + int getEnchantmentCharge() const; + void setEnchantmentCharge(int enchantmentCharge); + int getGoldValue() const; void setGoldValue(int gold); @@ -92,10 +98,10 @@ public: public: Container(); - std::tuple getItem(int i) const; - void addItem(const std::string &refId, int count, int charge); + std::tuple getItem(int i) const; + void addItem(const std::string &refId, int count, int charge, int enchantmentCharge); - void setItem(int i, const std::string &refId, int count, int charge); + void setItem(int i, const std::string &refId, int count, int charge, int enchantmentCharge); int getActionCount(int i) const; size_t size() const; @@ -112,8 +118,12 @@ public: std::shared_ptr>> copyObjects(mwmp::BaseEvent &event); std::shared_ptr>> copyContainers(mwmp::BaseEvent &event); - void sendObjects(std::shared_ptr player, std::shared_ptr>> objects, const ESM::Cell &cell); - void sendContainers(std::shared_ptr player, std::shared_ptr>> objects, const ESM::Cell &cell); + void sendObjects(std::shared_ptr player, std::shared_ptr>> objects, + const ESM::Cell &cell, bool broadcast = false); + void sendConsoleCommand(std::shared_ptr player, std::shared_ptr>> objects, + const ESM::Cell &cell, const std::string &consoleCommand, bool broadcast = false); + void sendContainers(std::shared_ptr player, std::shared_ptr>> objects, + const ESM::Cell &cell, bool broadcast = false); void requestContainers(std::shared_ptr player); -}; \ No newline at end of file +}; diff --git a/apps/openmw-mp/Player.cpp b/apps/openmw-mp/Player.cpp index 2d8fcc6e0..9ba8b3579 100644 --- a/apps/openmw-mp/Player.cpp +++ b/apps/openmw-mp/Player.cpp @@ -86,6 +86,7 @@ void Player::Init(LuaState &lua) "getFactions", &Player::getFactions, "getQuests", &Player::getQuests, "getSpells", &Player::getSpells, + "getQuickKeys", &Player::getQuickKeys, "getWeatherMgr", &Player::getWeatherMgr, "getCellState", &Player::getCellState, @@ -106,7 +107,7 @@ void Player::Init(LuaState &lua) Player::Player(RakNet::RakNetGUID guid) : BasePlayer(guid), NetActor(), changedMap(false), cClass(this), settings(this), books(this), gui(this), dialogue(this), factions(this), - quests(this), spells(this), weatherMgr(this) + quests(this), spells(this), quickKeys(this), weatherMgr(this) { basePlayer = this; netCreature = this; @@ -231,6 +232,7 @@ void Player::update() factions.update(); quests.update(); spells.update(); + quickKeys.update(); weatherMgr.update(); resetUpdateFlags(); @@ -635,16 +637,18 @@ std::tuple Player::getAttribute(unsigned short attributeId) const if (attributeId >= ESM::Attribute::Length) return make_tuple(0, 0); - return make_tuple(creatureStats.mAttributes[attributeId].mBase, creatureStats.mAttributes[attributeId].mCurrent); + return make_tuple(creatureStats.mAttributes[attributeId].mBase, creatureStats.mAttributes[attributeId].mMod); } -void Player::setAttribute(unsigned short attributeId, int base, int current) +void Player::setAttribute(unsigned short attributeId, int base, bool clearModifier) { if (attributeId >= ESM::Attribute::Length) return; creatureStats.mAttributes[attributeId].mBase = base; - creatureStats.mAttributes[attributeId].mCurrent = current; + + if (clearModifier) + creatureStats.mAttributes[attributeId].mMod = 0; if (!Utils::vectorContains(&attributeIndexChanges, attributeId)) attributeIndexChanges.push_back(attributeId); @@ -659,17 +663,18 @@ std::tuple Player::getSkill(unsigned short skillId) const const auto &skill = npcStats.mSkills[skillId]; - return make_tuple(skill.mBase, skill.mCurrent, skill.mProgress); + return make_tuple(skill.mBase, skill.mMod, skill.mProgress); } -void Player::setSkill(unsigned short skillId, int base, int current, float progress) +void Player::setSkill(unsigned short skillId, int base, bool clearModifier, float progress) { if (skillId >= ESM::Skill::Length) return; auto &skill = npcStats.mSkills[skillId]; skill.mBase = base; - skill.mCurrent = current; + if (clearModifier) + skill.mMod = 0; skill.mProgress = progress; if (!Utils::vectorContains(&skillIndexChanges, skillId)) @@ -736,6 +741,11 @@ Spells &Player::getSpells() return spells; } +QuickKeys &Player::getQuickKeys() +{ + return quickKeys; +} + WeatherMgr &Player::getWeatherMgr() { return weatherMgr; diff --git a/apps/openmw-mp/Player.hpp b/apps/openmw-mp/Player.hpp index 8097fe703..6a8d8e835 100644 --- a/apps/openmw-mp/Player.hpp +++ b/apps/openmw-mp/Player.hpp @@ -160,14 +160,14 @@ public: * @return base, current */ std::tuple getAttribute(unsigned short id) const; - void setAttribute(unsigned short id, int base, int current); + void setAttribute(unsigned short id, int base, bool clearModifier); /** * * @return base, current, progress, increase */ std::tuple getSkill(unsigned short id) const; - void setSkill(unsigned short id, int base, int current, float progress); + void setSkill(unsigned short id, int base, bool clearModifier, float progress); int getSkillIncrease(unsigned short attributeId) const; void setSkillIncrease(unsigned short attributeId, int increase); @@ -184,6 +184,7 @@ public: Factions &getFactions(); Quests &getQuests(); Spells &getSpells(); + QuickKeys &getQuickKeys(); WeatherMgr &getWeatherMgr(); void setAuthority(); @@ -203,6 +204,7 @@ private: Factions factions; Quests quests; Spells spells; + QuickKeys quickKeys; WeatherMgr weatherMgr; sol::table storedData; sol::table customData; diff --git a/apps/openmw-mp/Script/EventController.hpp b/apps/openmw-mp/Script/EventController.hpp index d32fe9043..e2db3a0e6 100644 --- a/apps/openmw-mp/Script/EventController.hpp +++ b/apps/openmw-mp/Script/EventController.hpp @@ -33,6 +33,7 @@ namespace CoreEvent ON_PLAYER_FACTION, ON_PLAYER_SHAPESHIFT, ON_PLAYER_SPELLBOOK, + ON_PLAYER_QUICKKEYS, ON_PLAYER_TOPIC, ON_PLAYER_DISPOSITION, ON_PLAYER_BOOK, diff --git a/apps/openmw-mp/Settings.cpp b/apps/openmw-mp/Settings.cpp index 6049f0c3a..4d297ebac 100644 --- a/apps/openmw-mp/Settings.cpp +++ b/apps/openmw-mp/Settings.cpp @@ -13,8 +13,11 @@ void GameSettings::Init(LuaState &lua) { lua.getState()->new_usertype("Settings", - "setConsoleAllow", &GameSettings::SetConsoleAllow, - "setDifficulty", &GameSettings::SetDifficulty + "setDifficulty", &GameSettings::setDifficulty, + "setConsoleAllowed", &GameSettings::setConsoleAllowed, + "setBedRestAllowed", &GameSettings::setConsoleAllowed, + "setWildernessRestAllowed", &GameSettings::setWildernessRestAllowed, + "setWaitAllowed", &GameSettings::setWaitAllowed ); } @@ -27,18 +30,36 @@ GameSettings::~GameSettings() { } -void GameSettings::SetConsoleAllow(bool state) +void GameSettings::setConsoleAllowed(bool state) { player->consoleAllowed = state; changed = true; } -void GameSettings::SetDifficulty(int difficulty) +void GameSettings::setDifficulty(int difficulty) { player->difficulty = difficulty; changed = true; } +void GameSettings::setBedRestAllowed(bool state) +{ + player->bedRestAllowed = state; + changed = true; +} + +void GameSettings::setWildernessRestAllowed(bool state) +{ + player->wildernessRestAllowed = state; + changed = true; +} + +void GameSettings::setWaitAllowed(bool state) +{ + player->waitAllowed = state; + changed = true; +} + void GameSettings::update() { if (!changed) diff --git a/apps/openmw-mp/Settings.hpp b/apps/openmw-mp/Settings.hpp index 7862419eb..85b0d67e5 100644 --- a/apps/openmw-mp/Settings.hpp +++ b/apps/openmw-mp/Settings.hpp @@ -16,25 +16,16 @@ public: explicit GameSettings(Player *player); ~GameSettings(); - /** - * \brief Set whether the console is allowed for a player. - * - * This changes the console permission for that player in the server memory, but does not - * by itself send a packet. - * - * \param The console permission state. - */ - void SetConsoleAllow(bool state); - - /** - * \brief Set the difficulty for a player. - * - * This changes the difficulty for that player in the server memory, but does not by itself - * send a packet. - * - * \param The difficulty. - */ - void SetDifficulty(int difficulty); + + void setConsoleAllowed(bool state); + + void setDifficulty(int difficulty); + + void setBedRestAllowed(bool state); + + void setWildernessRestAllowed(bool state); + + void setWaitAllowed(bool state); void update(); private: diff --git a/apps/openmw-mp/processors/ProcessorInitializer.cpp b/apps/openmw-mp/processors/ProcessorInitializer.cpp index 2eba8e4a1..9547045aa 100644 --- a/apps/openmw-mp/processors/ProcessorInitializer.cpp +++ b/apps/openmw-mp/processors/ProcessorInitializer.cpp @@ -31,6 +31,7 @@ #include "player/ProcessorPlayerLevel.hpp" #include "player/ProcessorPlayerMap.hpp" #include "player/ProcessorPlayerPosition.hpp" +#include "player/ProcessorPlayerQuickKeys.hpp" #include "player/ProcessorPlayerRest.hpp" #include "player/ProcessorPlayerResurrect.hpp" #include "player/ProcessorPlayerShapeshift.hpp" @@ -101,6 +102,7 @@ void ProcessorInitializer() PlayerProcessor::AddProcessor(new ProcessorPlayerLevel()); PlayerProcessor::AddProcessor(new ProcessorPlayerMap()); PlayerProcessor::AddProcessor(new ProcessorPlayerPosition()); + PlayerProcessor::AddProcessor(new ProcessorPlayerQuickKeys()); PlayerProcessor::AddProcessor(new ProcessorPlayerRest()); PlayerProcessor::AddProcessor(new ProcessorPlayerResurrect()); PlayerProcessor::AddProcessor(new ProcessorPlayerShapeshift()); diff --git a/apps/openmw-mp/processors/player/ProcessorPlayerQuickKeys.hpp b/apps/openmw-mp/processors/player/ProcessorPlayerQuickKeys.hpp new file mode 100644 index 000000000..812b7b2d8 --- /dev/null +++ b/apps/openmw-mp/processors/player/ProcessorPlayerQuickKeys.hpp @@ -0,0 +1,26 @@ +#ifndef OPENMW_PROCESSORPLAYERQUICKKEYS_HPP +#define OPENMW_PROCESSORPLAYERQUICKKEYS_HPP + +#include "../PlayerProcessor.hpp" + +namespace mwmp +{ + class ProcessorPlayerQuickKeys : public PlayerProcessor + { + public: + ProcessorPlayerQuickKeys() + { + BPP_INIT(ID_PLAYER_QUICKKEYS) + } + + void Do(PlayerPacket &packet, const std::shared_ptr &player) override + { + DEBUG_PRINTF(strPacketID.c_str()); + + Networking::get().getState().getEventCtrl().Call(player); + } + }; +} + + +#endif //OPENMW_PROCESSORPLAYERQUICKKEYS_HPP diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a681a6f40..f2d1e4956 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -118,8 +118,8 @@ add_openmw_dir (mwmp/processors/player ProcessorChatMessage ProcessorGUIMessageB ProcessorPlayerBook ProcessorPlayerBounty ProcessorPlayerCellChange ProcessorPlayerCellState ProcessorPlayerCharClass ProcessorPlayerCharGen ProcessorPlayerDeath ProcessorPlayerDisposition ProcessorPlayerEquipment ProcessorPlayerFaction ProcessorPlayerInventory ProcessorPlayerJail ProcessorPlayerJournal ProcessorPlayerKillCount ProcessorPlayerLevel - ProcessorPlayerMap ProcessorPlayerPosition ProcessorPlayerResurrect ProcessorPlayerShapeshift ProcessorPlayerSkill - ProcessorPlayerSpeech ProcessorPlayerSpellbook ProcessorPlayerStatsDynamic ProcessorPlayerTopic + ProcessorPlayerMap ProcessorPlayerPosition ProcessorPlayerQuickKeys ProcessorPlayerResurrect ProcessorPlayerShapeshift + ProcessorPlayerSkill ProcessorPlayerSpeech ProcessorPlayerSpellbook ProcessorPlayerStatsDynamic ProcessorPlayerTopic ) add_openmw_dir (mwmp/processors/world BaseObjectProcessor ProcessorConsoleCommand ProcessorContainer ProcessorDoorState diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 1346ca74c..74221f8a0 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -122,8 +122,17 @@ bool OMW::Engine::frame(float frametime) // When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug. // If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2), // and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21) - if (!mEnvironment.getInputManager()->isWindowVisible()) - return false; + + /* + Start of tes3mp change (major) + + The game cannot be paused in multiplayer, so prevent that from happening even here + */ + //if (!mEnvironment.getInputManager()->isWindowVisible()) + // return false; + /* + End of tes3mp change (major) + */ // sound if (mUseSound) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index eb43acec7..213bebe8b 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -114,6 +114,16 @@ namespace MWBase /* End of tes3mp addition */ + + /* + Start of tes3mp addition + + Make it possible to get the caption of a voice dialogue + */ + virtual std::string getVoiceCaption(const std::string& sound) const = 0; + /* + End of tes3mp addition + */ }; } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 24bf910d3..11d96f16c 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -152,6 +152,29 @@ namespace MWBase virtual void setConsoleSelectedObject(const MWWorld::Ptr& object) = 0; + /* + Start of tes3mp addition + + Allow the direct setting of a console's Ptr, without the assumption that an object + was clicked and that key focus should be restored to the console window, for console + commands executed via server scripts + */ + virtual void setConsolePtr(const MWWorld::Ptr& object) = 0; + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Allow the clearing of the console's Ptr from elsewhere in the code, so that + Ptrs used in console commands run from server scripts do not stay selected + */ + virtual void clearConsolePtr() = 0; + /* + End of tes3mp addition + */ + /// Set value for the given ID. virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value) = 0; virtual void setValue (int parSkill, const MWMechanics::SkillValue& value) = 0; @@ -194,6 +217,17 @@ namespace MWBase virtual void getMousePosition(int &x, int &y) = 0; virtual void getMousePosition(float &x, float &y) = 0; virtual void setDragDrop(bool dragDrop) = 0; + + /* + Start of tes3mp addition + + Allow the completion of a drag and drop from elsewhere in the code + */ + virtual void finishDragDrop() = 0; + /* + End of tes3mp addition + */ + virtual bool getWorldMouseOver() = 0; virtual bool toggleFogOfWar() = 0; @@ -223,6 +257,16 @@ namespace MWBase /// update activated quick key state (if action executing was delayed for some reason) virtual void updateActivatedQuickKey () = 0; + /* + Start of tes3mp addition + + Make it possible to add quickKeys from elsewhere in the code + */ + virtual void setQuickKey(int slot, int quickKeyType, MWWorld::Ptr item, const std::string& spellId = "") = 0; + /* + End of tes3mp addition + */ + virtual std::string getSelectedSpell() = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0; @@ -291,6 +335,16 @@ namespace MWBase virtual void executeInConsole (const std::string& path) = 0; + /* + Start of tes3mp addition + + Allow the execution of console commands from elsewhere in the code + */ + virtual void executeCommandInConsole(const std::string& command) = 0; + /* + End of tes3mp addition + */ + virtual void enableRest() = 0; virtual bool getRestEnabled() = 0; virtual bool getJournalAllowed() = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 9ef7e19f1..f531779a3 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -636,7 +636,6 @@ namespace MWDialogue if (mwmp::Main::get().getCellController()->isLocalActor(actor)) { mwmp::LocalActor *localActor = mwmp::Main::get().getCellController()->getLocalActor(actor); - localActor->response = info->mResponse; localActor->sound = info->mSound; } /* @@ -741,4 +740,32 @@ namespace MWDialogue Misc::StringUtils::lowerCase(mLastTopic), actor.getClass().getName(actor)); } } + + /* + Start of tes3mp addition + + Make it possible to get the caption of a voice dialogue + */ + std::string DialogueManager::getVoiceCaption(const std::string& sound) const + { + const MWWorld::Store& dialogues = MWBase::Environment::get().getWorld()->getStore().get(); + + for (MWWorld::Store::iterator dialogueIter = dialogues.begin(); dialogueIter != dialogues.end(); ++dialogueIter) + { + if (dialogueIter->mType == ESM::Dialogue::Voice) + { + for (ESM::Dialogue::InfoContainer::const_iterator infoIter = dialogueIter->mInfo.begin(); + infoIter != dialogueIter->mInfo.end(); ++infoIter) + { + if (!infoIter->mSound.empty() && Misc::StringUtils::ciEqual(sound, infoIter->mSound)) + return infoIter->mResponse; + } + } + } + + return "???"; + } + /* + End of tes3mp addition + */ } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 5d7e9f9a2..a859b020c 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -124,6 +124,16 @@ namespace MWDialogue /// Removes the last added topic response for the given actor from the journal virtual void clearInfoActor (const MWWorld::Ptr& actor) const; + + /* + Start of tes3mp addition + + Make it possible to get the caption of a voice dialogue + */ + virtual std::string getVoiceCaption(const std::string& sound) const; + /* + End of tes3mp addition + */ }; } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index e8ac33f6d..b5937ce80 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -439,6 +439,21 @@ namespace MWGui } } + /* + Start of tes3mp addition + + Allow the direct setting of a console's Ptr, without the assumption that an object + was clicked and that key focus should be restored to the console window, for console + commands executed via server scripts + */ + void Console::setPtr(const MWWorld::Ptr& object) + { + mPtr = object; + } + /* + End of tes3mp addition + */ + void Console::onReferenceUnavailable() { setSelectedObject(MWWorld::Ptr()); diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index bbff34c8d..1848705fc 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -27,6 +27,18 @@ namespace MWGui /// Set the implicit object for script execution void setSelectedObject(const MWWorld::Ptr& object); + /* + Start of tes3mp addition + + Allow the direct setting of a console's Ptr, without the assumption that an object + was clicked and that key focus should be restored to the console window, for console + commands executed via server scripts + */ + void setPtr(const MWWorld::Ptr& object); + /* + End of tes3mp addition + */ + MyGUI::EditBox* mCommandLine; MyGUI::EditBox* mHistory; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index d5e315c35..0b34141c2 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -1,5 +1,12 @@ #include "hud.hpp" +#include +#include +#include +#include +#include +#include + /* Start of tes3mp addition @@ -14,13 +21,6 @@ End of tes3mp addition */ -#include -#include -#include -#include -#include -#include - #include #include diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 6da7b0905..aa2726799 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -8,6 +8,18 @@ #include #include +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include +#include "../mwmp/Main.hpp" +#include "../mwmp/LocalPlayer.hpp" +/* + End of tes3mp addition +*/ + #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -106,8 +118,36 @@ namespace MWGui textBox->setCaption (MyGUI::utility::toString(index+1)); textBox->setNeedMouseFocus (false); } + + /* + Start of tes3mp addition + + Send a PLAYER_QUICKKEYS packet whenever a key is unassigned, but only if the player + has finished character generation, so as to avoid doing anything doing startup when all + quick keys get unassigned by default + */ + if (mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen() && !mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys) + { + mwmp::Main::get().getLocalPlayer()->sendQuickKey(index, Type_Unassigned); + } + /* + End of tes3mp addition + */ } + /* + Start of tes3mp addition + + Allow unassigning an index directly from elsewhere in the code + */ + void QuickKeysMenu::unassignIndex(int index) + { + unassign(mQuickKeyButtons[index], index); + } + /* + End of tes3mp addition + */ + void QuickKeysMenu::onQuickKeyButtonClicked(MyGUI::Widget* sender) { int index = -1; @@ -188,6 +228,17 @@ namespace MWGui if (mItemSelectionDialog) mItemSelectionDialog->setVisible(false); + + /* + Start of tes3mp addition + + Send a PLAYER_QUICKKEYS packet whenever a key is assigned to an item + */ + if (!mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys) + mwmp::Main::get().getLocalPlayer()->sendQuickKey(mSelectedIndex, Type_Item, item.getCellRef().getRefId()); + /* + End of tes3mp addition + */ } void QuickKeysMenu::onAssignItemCancel() @@ -212,6 +263,17 @@ namespace MWGui if (mMagicSelectionDialog) mMagicSelectionDialog->setVisible(false); + + /* + Start of tes3mp addition + + Send a PLAYER_QUICKKEYS packet whenever a key is assigned to an item's magic + */ + if (!mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys) + mwmp::Main::get().getLocalPlayer()->sendQuickKey(mSelectedIndex, Type_MagicItem, item.getCellRef().getRefId()); + /* + End of tes3mp addition + */ } void QuickKeysMenu::onAssignMagic (const std::string& spellId) @@ -246,6 +308,17 @@ namespace MWGui if (mMagicSelectionDialog) mMagicSelectionDialog->setVisible(false); + + /* + Start of tes3mp addition + + Send a PLAYER_QUICKKEYS packet whenever a key is assigned to a spell + */ + if (!mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys) + mwmp::Main::get().getLocalPlayer()->sendQuickKey(mSelectedIndex, Type_Magic, spellId); + /* + End of tes3mp addition + */ } void QuickKeysMenu::onAssignMagicCancel () @@ -397,6 +470,19 @@ namespace MWGui } } + /* + Start of tes3mp addition + + Make it possible to add quickKeys from elsewhere in the code + */ + void QuickKeysMenu::setSelectedIndex(int index) + { + mSelectedIndex = index; + } + /* + End of tes3mp addition + */ + // --------------------------------------------------------------------------------------------------------- QuickKeysMenuAssign::QuickKeysMenuAssign (QuickKeysMenu* parent) diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 0070aa55b..55096d064 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -38,6 +38,16 @@ namespace MWGui void activateQuickKey(int index); void updateActivatedQuickKey(); + /* + Start of tes3mp addition + + Allow the setting of the selected index from elsewhere in the code + */ + void setSelectedIndex(int index); + /* + End of tes3mp addition + */ + /// @note This enum is serialized, so don't move the items around! enum QuickKeyType { @@ -52,6 +62,16 @@ namespace MWGui void readRecord (ESM::ESMReader& reader, uint32_t type); void clear(); + /* + Start of tes3mp addition + + Allow unassigning an index directly from elsewhere in the code + */ + void unassignIndex(int index); + /* + End of tes3mp addition + */ + private: MyGUI::EditBox* mInstructionLabel; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 2eeeafe0d..033d55e0f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -6,6 +6,17 @@ #include #include +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include "../mwmp/Main.hpp" +#include "../mwmp/LocalPlayer.hpp" +/* + End of tes3mp addition +*/ + #include #include "../mwbase/environment.hpp" @@ -357,6 +368,16 @@ namespace MWGui mPtr.getClass().getCreatureStats(mPtr).getGoldPool() - mCurrentBalance ); } + /* + Start of tes3mp addition + + Send an ID_PLAYER_INVENTORY packet every time a player completes a trade + */ + mwmp::Main::get().getLocalPlayer()->sendInventory(); + /* + End of tes3mp addition + */ + eventTradeDone(); MWBase::Environment::get().getWindowManager()->playSound("Item Gold Up"); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 04b051e16..c6d1285f3 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -2,6 +2,17 @@ #include +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include "../mwmp/Main.hpp" +#include "../mwmp/LocalPlayer.hpp" +/* + End of tes3mp addition +*/ + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -167,6 +178,16 @@ namespace MWGui // remove gold player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + /* + Start of tes3mp addition + + Send an ID_PLAYER_INVENTORY packet every time a player buys training + */ + mwmp::Main::get().getLocalPlayer()->sendInventory(); + /* + End of tes3mp addition + */ + // add gold to NPC trading gold pool npcStats.setGoldPool(npcStats.getGoldPool() + price); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7142a5ce1..a5c3a3b2a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -26,6 +26,7 @@ Include additional headers for multiplayer purposes */ +#include #include "../mwmp/Main.hpp" #include "../mwmp/GUIController.hpp" /* @@ -1061,6 +1062,20 @@ namespace MWGui MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); } + /* + Start of tes3mp addition + + Allow the completion of a drag and drop from elsewhere in the code + */ + void WindowManager::finishDragDrop() + { + if (mDragAndDrop->mIsOnDragAndDrop) + mDragAndDrop->finish(); + } + /* + End of tes3mp addition + */ + void WindowManager::setCursorVisible(bool visible) { if (visible == mCursorVisible) @@ -1347,6 +1362,19 @@ namespace MWGui mConsole->executeFile (path); } + /* + Start of tes3mp addition + + Allow the execution of console commands from elsewhere in the code + */ + void WindowManager::executeCommandInConsole(const std::string& command) + { + mConsole->execute(command); + } + /* + End of tes3mp addition + */ + MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } @@ -1487,6 +1515,35 @@ namespace MWGui mQuickKeysMenu->activateQuickKey(index); } + /* + Start of tes3mp addition + + Make it possible to add quickKeys from elsewhere in the code + */ + void WindowManager::setQuickKey(int slot, int quickKeyType, MWWorld::Ptr item, const std::string& spellId) + { + mQuickKeysMenu->setSelectedIndex(slot); + + switch (quickKeyType) + { + case QuickKeysMenu::Type_Unassigned: + mQuickKeysMenu->unassignIndex(slot); + break; + case QuickKeysMenu::Type_Item: + mQuickKeysMenu->onAssignItem(item); + break; + case QuickKeysMenu::Type_MagicItem: + mQuickKeysMenu->onAssignMagicItem(item); + break; + case QuickKeysMenu::Type_Magic: + mQuickKeysMenu->onAssignMagic(spellId); + break; + } + } + /* + End of tes3mp addition + */ + bool WindowManager::getSubtitlesEnabled () { return mSubtitlesEnabled; @@ -1964,6 +2021,35 @@ namespace MWGui mConsole->setSelectedObject(object); } + /* + Start of tes3mp addition + + Allow the direct setting of a console's Ptr, without the assumption that an object + was clicked and that key focus should be restored to the console window, for console + commands executed via server scripts + */ + void WindowManager::setConsolePtr(const MWWorld::Ptr &object) + { + mConsole->setPtr(object); + } + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Allow the clearing of the console's Ptr from elsewhere in the code, so that + Ptrs used in console commands run from server scripts do not stay selected + */ + void WindowManager::clearConsolePtr() + { + mConsole->resetReference(); + } + /* + End of tes3mp addition + */ + std::string WindowManager::correctIconPath(const std::string& path) { return Misc::ResourceHelpers::correctIconPath(path, mResourceSystem->getVFS()); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index f84e924aa..f1fa3e7c5 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -191,6 +191,29 @@ namespace MWGui virtual void setConsoleSelectedObject(const MWWorld::Ptr& object); + /* + Start of tes3mp addition + + Allow the direct setting of a console's Ptr, without the assumption that an object + was clicked and that key focus should be restored to the console window, for console + commands executed via server scripts + */ + virtual void setConsolePtr(const MWWorld::Ptr& object); + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Allow the clearing of the console's Ptr from elsewhere in the code, so that + Ptrs used in console commands run from server scripts do not stay selected + */ + virtual void clearConsolePtr(); + /* + End of tes3mp addition + */ + ///< Set value for the given ID. virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value); virtual void setValue (int parSkill, const MWMechanics::SkillValue& value); @@ -225,6 +248,17 @@ namespace MWGui virtual void getMousePosition(int &x, int &y); virtual void getMousePosition(float &x, float &y); virtual void setDragDrop(bool dragDrop); + + /* + Start of tes3mp addition + + Allow the completion of a drag and drop from elsewhere in the code + */ + virtual void finishDragDrop(); + /* + End of tes3mp addition + */ + virtual bool getWorldMouseOver(); virtual bool toggleFogOfWar(); @@ -250,6 +284,16 @@ namespace MWGui /// update activated quick key state (if action executing was delayed for some reason) virtual void updateActivatedQuickKey (); + /* + Start of tes3mp addition + + Make it possible to add quickKeys from elsewhere in the code + */ + virtual void setQuickKey(int slot, int quickKeyType, MWWorld::Ptr item, const std::string& spellId = ""); + /* + End of tes3mp addition + */ + virtual std::string getSelectedSpell() { return mSelectedSpell; } virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); @@ -318,6 +362,16 @@ namespace MWGui virtual void executeInConsole (const std::string& path); + /* + Start of tes3mp addition + + Allow the execution of console commands from elsewhere in the code + */ + virtual void executeCommandInConsole(const std::string& command); + /* + End of tes3mp addition + */ + virtual void enableRest() { mRestAllowed = true; } virtual bool getRestEnabled(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9ee01acc6..816f9e18d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1050,6 +1050,28 @@ namespace MWInput MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); //Nope, return; } + + /* + Start of tes3mp addition + + Prevent resting and waiting if they have been disabled by the server for the local player + */ + int canRest = MWBase::Environment::get().getWorld()->canRest(); + + if (canRest == 0 && !mwmp::Main::get().getLocalPlayer()->wildernessRestAllowed) + { + MWBase::Environment::get().getWindowManager()->messageBox("You are not allowed to rest in the wilderness."); + return; + } + else if (canRest == 1 && !mwmp::Main::get().getLocalPlayer()->waitAllowed) + { + MWBase::Environment::get().getWindowManager()->messageBox("You are not allowed to wait."); + return; + } + /* + End of tes3mp addition + */ + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI } diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 90d29f686..9d8fe60c3 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -295,6 +295,30 @@ namespace MWMechanics mSpellsChanged = true; } + /* + Start of tes3mp addition + + Allow the purging of an effect for a specific arg (attribute or skill) + */ + void ActiveSpells::purgeEffectByArg(short effectId, int effectArg) + { + for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) + { + for (std::vector::iterator effectIt = it->second.mEffects.begin(); + effectIt != it->second.mEffects.end();) + { + if (effectIt->mEffectId == effectId && effectIt->mArg == effectArg) + effectIt = it->second.mEffects.erase(effectIt); + else + ++effectIt; + } + } + mSpellsChanged = true; + } + /* + End of tes3mp addition + */ + void ActiveSpells::clear() { mSpells.clear(); diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index a19c8a51d..7f4c8ea3f 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -94,6 +94,16 @@ namespace MWMechanics /// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId void purge (int casterActorId); + /* + Start of tes3mp addition + + Allow the purging of an effect for a specific arg (attribute or skill) + */ + void purgeEffectByArg(short effectId, int effectArg); + /* + End of tes3mp addition + */ + /// Remove all spells void clear(); diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index edc8448e4..c3aff89b8 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -87,18 +87,20 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte if (pathTo(actor, dest, duration, pathTolerance) && std::abs(dest.mZ - aPos.pos[2]) < pathTolerance) // check the true distance in case the target is far away in Z-direction { - target.getClass().activate(target,actor).get()->execute(actor); //Arrest player when reached - /* Start of tes3mp addition Record that the player has not died since the last attempt to arrest them + + Close the player's inventory or open container and cancel any drag and drops */ LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "After being pursued by %s, diedSinceArrestAttempt is now false", actor.getCellRef().getRefId().c_str()); mwmp::Main::get().getLocalPlayer()->diedSinceArrestAttempt = false; + mwmp::Main::get().getLocalPlayer()->closeInventoryWindows(); /* End of tes3mp addition */ + target.getClass().activate(target,actor).get()->execute(actor); //Arrest player when reached return true; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6e6570b00..fbcb7371a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -390,29 +390,21 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState } } - if (!mCurrentJump.empty()) - { - mAnimation->disable(mCurrentJump); - mCurrentJump.clear(); - } - if(mJumpState == JumpState_InAir) { - if (mAnimation->hasAnimation(jumpAnimName)) - { - mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, + mAnimation->disable(mCurrentJump); + mCurrentJump = jumpAnimName; + if (mAnimation->hasAnimation("jump")) + mAnimation->play(mCurrentJump, Priority_Jump, jumpmask, false, 1.0f, (startAtLoop?"loop start":"start"), "stop", 0.0f, ~0ul); - mCurrentJump = jumpAnimName; - } } - else if (mJumpState == JumpState_Landing) + else { - if (mAnimation->hasAnimation(jumpAnimName)) - { + mAnimation->disable(mCurrentJump); + mCurrentJump.clear(); + if (mAnimation->hasAnimation("jump")) mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, 1.0f, "loop stop", "stop", 0.0f, 0); - mCurrentJump = jumpAnimName; - } } } } @@ -1833,6 +1825,7 @@ void CharacterController::update(float duration) mHasMovedInXY = std::abs(vec.x())+std::abs(vec.y()) > 0.0f; isrunning = isrunning && mHasMovedInXY; + // advance athletics if(mHasMovedInXY && mPtr == getPlayer()) { @@ -1987,8 +1980,7 @@ void CharacterController::update(float duration) } else { - jumpstate = mAnimation->isPlaying(mCurrentJump) ? JumpState_Landing : JumpState_None; - + jumpstate = JumpState_None; vec.z() = 0.0f; inJump = false; @@ -2018,15 +2010,9 @@ void CharacterController::update(float duration) else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) { if(rot.z() > 0.0f) - { movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; - mAnimation->disable(mCurrentJump); - } else if(rot.z() < 0.0f) - { movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; - mAnimation->disable(mCurrentJump); - } } } diff --git a/apps/openmw/mwmp/Cell.cpp b/apps/openmw/mwmp/Cell.cpp index b63716d4e..239f6c6ff 100644 --- a/apps/openmw/mwmp/Cell.cpp +++ b/apps/openmw/mwmp/Cell.cpp @@ -237,7 +237,6 @@ void Cell::readSpeech(ActorList& actorList) if (dedicatedActors.count(mapIndex) > 0) { DedicatedActor *actor = dedicatedActors[mapIndex]; - actor->response = baseActor->response; actor->sound = baseActor->sound; } } diff --git a/apps/openmw/mwmp/DedicatedActor.cpp b/apps/openmw/mwmp/DedicatedActor.cpp index c73c027f6..302da0b82 100644 --- a/apps/openmw/mwmp/DedicatedActor.cpp +++ b/apps/openmw/mwmp/DedicatedActor.cpp @@ -4,6 +4,8 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwdialogue/dialoguemanagerimp.hpp" + #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/mechanicsmanagerimp.hpp" #include "../mwmechanics/movement.hpp" @@ -217,7 +219,7 @@ void DedicatedActor::playSound() MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); if (winMgr->getSubtitlesEnabled()) - winMgr->messageBox(response, MWGui::ShowInDialogueMode_Never); + winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never); sound.clear(); } diff --git a/apps/openmw/mwmp/DedicatedPlayer.cpp b/apps/openmw/mwmp/DedicatedPlayer.cpp index bdabede71..5a79aef35 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.cpp +++ b/apps/openmw/mwmp/DedicatedPlayer.cpp @@ -7,11 +7,14 @@ #include #include "../mwbase/environment.hpp" - -#include "../mwgui/windowmanagerimp.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwclass/npc.hpp" +#include "../mwdialogue/dialoguemanagerimp.hpp" + +#include "../mwgui/windowmanagerimp.hpp" + #include "../mwinput/inputmanagerimp.hpp" #include "../mwmechanics/actor.hpp" @@ -283,6 +286,21 @@ void DedicatedPlayer::setMarkerState(bool state) removeMarker(); } +void DedicatedPlayer::playAnimation() +{ + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(getPtr(), + animation.groupname, animation.mode, animation.count, animation.persist); +} + +void DedicatedPlayer::playSpeech() +{ + MWBase::Environment::get().getSoundManager()->say(getPtr(), sound); + + MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); + if (winMgr->getSubtitlesEnabled()) + winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never); +} + MWWorld::Ptr DedicatedPlayer::getPtr() { return ptr; diff --git a/apps/openmw/mwmp/DedicatedPlayer.hpp b/apps/openmw/mwmp/DedicatedPlayer.hpp index d63e6b7b6..f2c793f2a 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.hpp +++ b/apps/openmw/mwmp/DedicatedPlayer.hpp @@ -41,6 +41,9 @@ namespace mwmp void removeMarker(); void setMarkerState(bool state); + void playAnimation(); + void playSpeech(); + MWWorld::Ptr getPtr(); MWWorld::Ptr getLiveCellPtr(); MWWorld::ManualRef* getRef(); diff --git a/apps/openmw/mwmp/LocalPlayer.cpp b/apps/openmw/mwmp/LocalPlayer.cpp index 1ab7c85d6..63676a449 100644 --- a/apps/openmw/mwmp/LocalPlayer.cpp +++ b/apps/openmw/mwmp/LocalPlayer.cpp @@ -1,12 +1,9 @@ -// -// Created by koncord on 14.01.16. -// - #include #include #include "../mwbase/environment.hpp" #include "../mwbase/journal.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwclass/creature.hpp" #include "../mwclass/npc.hpp" @@ -18,6 +15,7 @@ #include "../mwinput/inputmanagerimp.hpp" +#include "../mwmechanics/activespells.hpp" #include "../mwmechanics/aitravel.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/mechanicsmanagerimp.hpp" @@ -48,8 +46,11 @@ LocalPlayer::LocalPlayer() charGenState.endStage = 1; charGenState.isFinished = false; - consoleAllowed = false; difficulty = 0; + consoleAllowed = false; + bedRestAllowed = true; + wildernessRestAllowed = true; + waitAllowed = true; ignorePosPacket = false; ignoreJailTeleportation = false; @@ -66,6 +67,8 @@ LocalPlayer::LocalPlayer() isWerewolf = false; diedSinceArrestAttempt = false; + isReceivingQuickKeys = false; + isPlayingAnimation = false; } LocalPlayer::~LocalPlayer() @@ -241,6 +244,7 @@ void LocalPlayer::updateAttributes(bool forceUpdate) for (int i = 0; i < 8; ++i) { if (ptrNpcStats.getAttribute(i).getBase() != creatureStats.mAttributes[i].mBase || + ptrNpcStats.getAttribute(i).getModifier() != creatureStats.mAttributes[i].mMod || ptrNpcStats.getSkillIncrease(i) != npcStats.mSkillIncrease[i] || forceUpdate) { @@ -271,6 +275,7 @@ void LocalPlayer::updateSkills(bool forceUpdate) { // Update a skill if its base value has changed at all or its progress has changed enough if (ptrNpcStats.getSkill(i).getBase() != npcStats.mSkills[i].mBase || + ptrNpcStats.getSkill(i).getModifier() != npcStats.mSkills[i].mMod || ptrNpcStats.getSkill(i).getProgress() != npcStats.mSkills[i].mProgress || forceUpdate) { @@ -331,6 +336,16 @@ void LocalPlayer::updatePosition(bool forceUpdate) bool posIsChanging = (direction.pos[0] != 0 || direction.pos[1] != 0 || position.rot[0] != oldRot[0] || position.rot[2] != oldRot[1]); + // Animations can change a player's position without actually creating directional movement, + // so update positions accordingly + if (!posIsChanging && isPlayingAnimation) + { + if (MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(ptrPlayer, animation.groupname)) + posIsChanging = true; + else + isPlayingAnimation = false; + } + if (forceUpdate || posIsChanging || posWasChanged) { oldRot[0] = position.rot[0]; @@ -420,6 +435,7 @@ void LocalPlayer::updateEquipment(bool forceUpdate) equipmentIndexChanges.push_back(slot); item.refId = it->getCellRef().getRefId(); item.charge = it->getCellRef().getCharge(); + item.enchantmentCharge = it->getCellRef().getEnchantmentCharge(); if (slot == MWWorld::InventoryStore::Slot_CarriedRight) { @@ -438,7 +454,8 @@ void LocalPlayer::updateEquipment(bool forceUpdate) equipmentIndexChanges.push_back(slot); item.refId = ""; item.count = 0; - item.charge = 0; + item.charge = -1; + item.enchantmentCharge = -1; } } @@ -467,6 +484,7 @@ void LocalPlayer::updateInventory(bool forceUpdate) return true; item.count = iter.getRefData().getCount(); item.charge = iter.getCellRef().getCharge(); + item.enchantmentCharge = iter.getCellRef().getEnchantmentCharge(); return false; }; @@ -642,6 +660,9 @@ void LocalPlayer::addItems() MWWorld::Ptr itemPtr = *ptrStore.add(item.refId, item.count, ptrPlayer); if (item.charge != -1) itemPtr.getCellRef().setCharge(item.charge); + + if (item.enchantmentCharge != -1) + itemPtr.getCellRef().setEnchantmentCharge(item.enchantmentCharge); } catch (std::exception&) { @@ -732,6 +753,15 @@ void LocalPlayer::removeSpells() } } +void LocalPlayer::closeInventoryWindows() +{ + if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container) || + MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Inventory)) + MWBase::Environment::get().getWindowManager()->popGuiMode(); + + MWBase::Environment::get().getWindowManager()->finishDragDrop(); +} + void LocalPlayer::setDynamicStats() { MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -759,6 +789,12 @@ void LocalPlayer::setAttributes() for (int i = 0; i < 8; ++i) { + // If the server wants to clear our attribute's non-zero modifier, we need to remove + // the spell effect causing it, to avoid an infinite loop where the effect keeps resetting + // the modifier + if (creatureStats.mAttributes[i].mMod == 0 && ptrCreatureStats->getAttribute(i).getModifier() > 0) + ptrCreatureStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifyAttribute, i); + attributeValue.readState(creatureStats.mAttributes[i]); ptrCreatureStats->setAttribute(i, attributeValue); } @@ -774,6 +810,12 @@ void LocalPlayer::setSkills() for (int i = 0; i < 27; ++i) { + // If the server wants to clear our skill's non-zero modifier, we need to remove + // the spell effect causing it, to avoid an infinite loop where the effect keeps resetting + // the modifier + if (npcStats.mSkills[i].mMod == 0 && ptrNpcStats->getSkill(i).getModifier() > 0) + ptrNpcStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifySkill, i); + skillValue.readState(npcStats.mSkills[i]); ptrNpcStats->setSkill(i, skillValue); } @@ -832,12 +874,8 @@ void LocalPlayer::setCell() MWWorld::Ptr ptrPlayer = world->getPlayerPtr(); ESM::Position pos; - // To avoid crashes, close any container menus this player may be in - if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container)) - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - } + // To avoid crashes, close container windows this player may be in + closeInventoryWindows(); world->getPlayer().setTeleported(true); @@ -940,6 +978,9 @@ void LocalPlayer::setInventory() MWWorld::Ptr ptrPlayer = getPlayerPtr(); MWWorld::ContainerStore &ptrStore = ptrPlayer.getClass().getContainerStore(ptrPlayer); + // Ensure no item is being drag and dropped + MWBase::Environment::get().getWindowManager()->finishDragDrop(); + // Clear items in inventory ptrStore.clear(); @@ -979,6 +1020,57 @@ void LocalPlayer::setSpellbook() addSpells(); } +void LocalPlayer::setQuickKeys() +{ + MWWorld::Ptr ptrPlayer = getPlayerPtr(); + + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_QUICKKEYS from server"); + + // Because we send QuickKeys packets from the same OpenMW methods that we use to set received ones with, + // we need a boolean to prevent their sending here + isReceivingQuickKeys = true; + + for (const auto &quickKey : quickKeyChanges.quickKeys) + { + LOG_APPEND(Log::LOG_INFO, "- slot: %i, type: %i, itemId: %s", quickKey.slot, quickKey.type, quickKey.itemId.c_str()); + + if (quickKey.type == QuickKey::ITEM || quickKey.type == QuickKey::ITEM_MAGIC) + { + MWWorld::InventoryStore &ptrInventory = ptrPlayer.getClass().getInventoryStore(ptrPlayer); + + auto it = find_if(ptrInventory.begin(), ptrInventory.end(), [&quickKey](const MWWorld::Ptr &inventoryItem) { + return Misc::StringUtils::ciEqual(inventoryItem.getCellRef().getRefId(), quickKey.itemId); + }); + + if (it != ptrInventory.end()) + MWBase::Environment::get().getWindowManager()->setQuickKey(quickKey.slot, quickKey.type, (*it)); + } + else if (quickKey.type == QuickKey::MAGIC) + { + MWMechanics::Spells &ptrSpells = ptrPlayer.getClass().getCreatureStats(ptrPlayer).getSpells(); + bool hasSpell = false; + + MWMechanics::Spells::TIterator iter = ptrSpells.begin(); + for (; iter != ptrSpells.end(); iter++) + { + const ESM::Spell *spell = iter->first; + if (Misc::StringUtils::ciEqual(spell->mId, quickKey.itemId)) + { + hasSpell = true; + break; + } + } + + if (hasSpell) + MWBase::Environment::get().getWindowManager()->setQuickKey(quickKey.slot, quickKey.type, 0, quickKey.itemId); + } + else + MWBase::Environment::get().getWindowManager()->setQuickKey(quickKey.slot, quickKey.type, 0); + } + + isReceivingQuickKeys = false; +} + void LocalPlayer::setFactions() { MWWorld::Ptr ptrPlayer = getPlayerPtr(); @@ -1088,6 +1180,7 @@ void LocalPlayer::sendInventory() item.count = iter.getRefData().getCount(); item.charge = iter.getCellRef().getCharge(); + item.enchantmentCharge = iter.getCellRef().getEnchantmentCharge(); inventoryChanges.items.push_back(item); } @@ -1181,6 +1274,24 @@ void LocalPlayer::sendSpellRemoval(const ESM::Spell &spell) */ } +void LocalPlayer::sendQuickKey(unsigned short slot, int type, const std::string& itemId) +{ + quickKeyChanges.quickKeys.clear(); + + mwmp::QuickKey quickKey; + quickKey.slot = slot; + quickKey.type = type; + quickKey.itemId = itemId; + + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_PLAYER_QUICKKEYS", itemId.c_str()); + LOG_APPEND(Log::LOG_INFO, "- slot: %i, type: %i, itemId: %s", quickKey.slot, quickKey.type, quickKey.itemId.c_str()); + + quickKeyChanges.quickKeys.push_back(quickKey); + + getNetworking()->getPlayerPacket(ID_PLAYER_QUICKKEYS)->setPlayer(this); + getNetworking()->getPlayerPacket(ID_PLAYER_QUICKKEYS)->Send(); +} + void LocalPlayer::sendJournalEntry(const std::string& quest, int index, const MWWorld::Ptr& actor) { journalChanges.journalItems.clear(); @@ -1357,3 +1468,20 @@ void LocalPlayer::storeCurrentContainer(const MWWorld::Ptr &container) currentContainer.refNumIndex = container.getCellRef().getRefNum().mIndex; currentContainer.mpNum = container.getCellRef().getMpNum(); } + +void LocalPlayer::playAnimation() +{ + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(getPlayerPtr(), + animation.groupname, animation.mode, animation.count, animation.persist); + + isPlayingAnimation = true; +} + +void LocalPlayer::playSpeech() +{ + MWBase::Environment::get().getSoundManager()->say(getPlayerPtr(), sound); + + MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); + if (winMgr->getSubtitlesEnabled()) + winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never); +} diff --git a/apps/openmw/mwmp/LocalPlayer.hpp b/apps/openmw/mwmp/LocalPlayer.hpp index 592245cf1..b351de95c 100644 --- a/apps/openmw/mwmp/LocalPlayer.hpp +++ b/apps/openmw/mwmp/LocalPlayer.hpp @@ -46,6 +46,8 @@ namespace mwmp void removeItems(); void removeSpells(); + void closeInventoryWindows(); + void setDynamicStats(); void setAttributes(); void setSkills(); @@ -57,6 +59,7 @@ namespace mwmp void setEquipment(); void setInventory(); void setSpellbook(); + void setQuickKeys(); void setFactions(); void setKills(); void setBooks(); @@ -71,6 +74,7 @@ namespace mwmp void sendSpellAddition(const ESM::Spell &spell); void sendSpellRemoval(std::string id); void sendSpellRemoval(const ESM::Spell &spell); + void sendQuickKey(unsigned short slot, int type, const std::string& itemId = ""); void sendJournalEntry(const std::string& quest, int index, const MWWorld::Ptr& actor); void sendJournalIndex(const std::string& quest, int index); void sendFactionRank(const std::string& factionId, int rank); @@ -87,9 +91,13 @@ namespace mwmp void storeCurrentContainer(const MWWorld::Ptr& container); void storeCellState(const ESM::Cell& cell, int stateType); + void playAnimation(); + void playSpeech(); + + MWWorld::Ptr getPlayerPtr(); + private: Networking *getNetworking(); - MWWorld::Ptr getPlayerPtr(); }; } diff --git a/apps/openmw/mwmp/WorldEvent.cpp b/apps/openmw/mwmp/WorldEvent.cpp index f4221a950..c7afe757e 100644 --- a/apps/openmw/mwmp/WorldEvent.cpp +++ b/apps/openmw/mwmp/WorldEvent.cpp @@ -87,6 +87,9 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore) if (containerItem.charge > -1) newPtr.getCellRef().setCharge(containerItem.charge); + if (containerItem.enchantmentCharge > -1) + newPtr.getCellRef().setEnchantmentCharge(containerItem.enchantmentCharge); + containerStore.add(newPtr, containerItem.count, ownerPtr, true); } else if (action == BaseEvent::REMOVE) @@ -97,8 +100,9 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore) { if (Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), containerItem.refId)) { - if (ptr.getCellRef().getCharge() == containerItem.charge && - ptr.getRefData().getCount() == containerItem.count) + if (ptr.getRefData().getCount() == containerItem.count && + ptr.getCellRef().getCharge() == containerItem.charge && + ptr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge) { // Is this an actor's container? If so, unequip this item if it was equipped if (ptrFound.getClass().isActor()) @@ -145,8 +149,8 @@ void WorldEvent::placeObjects(MWWorld::CellStore* cellStore) { for (const auto &worldObject : worldObjects) { - LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, charge: %i, count: %i", worldObject.refId.c_str(), - worldObject.refNumIndex, worldObject.mpNum, worldObject.charge, worldObject.count); + LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, count: %i, charge: %i, enchantmentCharge: %i", worldObject.refId.c_str(), + worldObject.refNumIndex, worldObject.mpNum, worldObject.count, worldObject.charge, worldObject.enchantmentCharge); MWWorld::Ptr ptrFound = cellStore->searchExact(0, worldObject.mpNum); @@ -156,11 +160,14 @@ void WorldEvent::placeObjects(MWWorld::CellStore* cellStore) MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), worldObject.refId, 1); MWWorld::Ptr newPtr = ref.getPtr(); + if (worldObject.count > 1) + newPtr.getRefData().setCount(worldObject.count); + if (worldObject.charge > -1) newPtr.getCellRef().setCharge(worldObject.charge); - if (worldObject.count > 1) - newPtr.getRefData().setCount(worldObject.count); + if (worldObject.enchantmentCharge > -1) + newPtr.getCellRef().setEnchantmentCharge(worldObject.enchantmentCharge); newPtr.getCellRef().setGoldValue(worldObject.goldValue); newPtr = MWBase::Environment::get().getWorld()->placeObject(newPtr, cellStore, worldObject.position); @@ -424,6 +431,67 @@ void WorldEvent::activateDoors(MWWorld::CellStore* cellStore) } } +void WorldEvent::runConsoleCommands(MWWorld::CellStore* cellStore) +{ + MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager(); + + LOG_APPEND(Log::LOG_VERBOSE, "- console command: %s", consoleCommand.c_str()); + + if (worldObjects.empty()) + { + windowManager->clearConsolePtr(); + + LOG_APPEND(Log::LOG_VERBOSE, "-- running with no object reference"); + windowManager->executeCommandInConsole(consoleCommand); + } + else + { + for (const auto &worldObject : worldObjects) + { + windowManager->clearConsolePtr(); + + if (worldObject.isPlayer) + { + BasePlayer *player = 0; + + if (worldObject.guid == Main::get().getLocalPlayer()->guid) + { + player = Main::get().getLocalPlayer(); + + LOG_APPEND(Log::LOG_VERBOSE, "-- running on local player"); + windowManager->setConsolePtr(static_cast(player)->getPlayerPtr()); + windowManager->executeCommandInConsole(consoleCommand); + } + else if (player != 0) + { + player = PlayerList::getPlayer(guid); + + LOG_APPEND(Log::LOG_VERBOSE, "-- running on player %s", player->npc.mName.c_str()); + windowManager->setConsolePtr(static_cast(player)->getPtr()); + windowManager->executeCommandInConsole(consoleCommand); + } + } + else + { + LOG_APPEND(Log::LOG_VERBOSE, "-- running on cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum); + + MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum); + + if (ptrFound) + { + LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(), + ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum()); + + windowManager->setConsolePtr(ptrFound); + windowManager->executeCommandInConsole(consoleCommand); + } + } + } + + windowManager->clearConsolePtr(); + } +} + void WorldEvent::setLocalShorts(MWWorld::CellStore* cellStore) { for (const auto &worldObject : worldObjects) @@ -527,6 +595,7 @@ void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr) worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex; worldObject.mpNum = 0; worldObject.charge = ptr.getCellRef().getCharge(); + worldObject.enchantmentCharge = ptr.getCellRef().getEnchantmentCharge(); // Make sure we send the RefData position instead of the CellRef one, because that's what // we actually see on this client @@ -891,6 +960,7 @@ void WorldEvent::sendContainers(MWWorld::CellStore* cellStore) containerItem.refId = itemPtr.getCellRef().getRefId(); containerItem.count = itemPtr.getRefData().getCount(); containerItem.charge = itemPtr.getCellRef().getCharge(); + containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge(); worldObject.containerItems.push_back(move(containerItem)); } diff --git a/apps/openmw/mwmp/WorldEvent.hpp b/apps/openmw/mwmp/WorldEvent.hpp index 35322165d..c693c928a 100644 --- a/apps/openmw/mwmp/WorldEvent.hpp +++ b/apps/openmw/mwmp/WorldEvent.hpp @@ -30,6 +30,7 @@ namespace mwmp void rotateObjects(MWWorld::CellStore* cellStore); void animateObjects(MWWorld::CellStore* cellStore); void activateDoors(MWWorld::CellStore* cellStore); + void runConsoleCommands(MWWorld::CellStore* cellStore); void setLocalShorts(MWWorld::CellStore* cellStore); void setLocalFloats(MWWorld::CellStore* cellStore); diff --git a/apps/openmw/mwmp/processors/ProcessorInitializer.cpp b/apps/openmw/mwmp/processors/ProcessorInitializer.cpp index 08e5922f4..ad3e36152 100644 --- a/apps/openmw/mwmp/processors/ProcessorInitializer.cpp +++ b/apps/openmw/mwmp/processors/ProcessorInitializer.cpp @@ -36,6 +36,7 @@ #include "player/ProcessorPlayerLevel.hpp" #include "player/ProcessorPlayerMap.hpp" #include "player/ProcessorPlayerPosition.hpp" +#include "player/ProcessorPlayerQuickKeys.hpp" #include "player/ProcessorPlayerRegionAuthority.hpp" #include "player/ProcessorPlayerRest.hpp" #include "player/ProcessorPlayerResurrect.hpp" @@ -115,6 +116,7 @@ void ProcessorInitializer() PlayerProcessor::AddProcessor(new ProcessorPlayerLevel()); PlayerProcessor::AddProcessor(new ProcessorPlayerMap()); PlayerProcessor::AddProcessor(new ProcessorPlayerPosition()); + PlayerProcessor::AddProcessor(new ProcessorPlayerQuickKeys()); PlayerProcessor::AddProcessor(new ProcessorPlayerRegionAuthority()); PlayerProcessor::AddProcessor(new ProcessorPlayerRest()); PlayerProcessor::AddProcessor(new ProcessorPlayerResurrect()); @@ -125,6 +127,7 @@ void ProcessorInitializer() PlayerProcessor::AddProcessor(new ProcessorPlayerStatsDynamic()); PlayerProcessor::AddProcessor(new ProcessorPlayerTopic()); + WorldProcessor::AddProcessor(new ProcessorConsoleCommand()); WorldProcessor::AddProcessor(new ProcessorContainer()); WorldProcessor::AddProcessor(new ProcessorDoorState()); WorldProcessor::AddProcessor(new ProcessorMusicPlay()); diff --git a/apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp b/apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp index 370b54548..2a9b0a2c6 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp @@ -23,9 +23,10 @@ namespace mwmp if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Console && !player->consoleAllowed) - { MWBase::Environment::get().getWindowManager()->popGuiMode(); - } + else if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Rest && + (!player->bedRestAllowed || !player->wildernessRestAllowed || !player->waitAllowed)) + MWBase::Environment::get().getWindowManager()->popGuiMode(); } } } diff --git a/apps/openmw/mwmp/processors/player/ProcessorPlayerAnimPlay.hpp b/apps/openmw/mwmp/processors/player/ProcessorPlayerAnimPlay.hpp index 4d1f5ae5d..7ac6d770c 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorPlayerAnimPlay.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorPlayerAnimPlay.hpp @@ -15,7 +15,13 @@ namespace mwmp virtual void Do(PlayerPacket &packet, BasePlayer *player) { - // Placeholder to be filled in later + if (isLocal()) + { + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_ANIM_PLAY about LocalPlayer from server"); + static_cast(player)->playAnimation(); + } + else if (player != 0) + static_cast(player)->playAnimation(); } }; } diff --git a/apps/openmw/mwmp/processors/player/ProcessorPlayerQuickKeys.hpp b/apps/openmw/mwmp/processors/player/ProcessorPlayerQuickKeys.hpp new file mode 100644 index 000000000..5515162a6 --- /dev/null +++ b/apps/openmw/mwmp/processors/player/ProcessorPlayerQuickKeys.hpp @@ -0,0 +1,32 @@ +#ifndef OPENMW_PROCESSORPLAYERQUICKKEYS_HPP +#define OPENMW_PROCESSORPLAYERQUICKKEYS_HPP + + +#include "../PlayerProcessor.hpp" + +namespace mwmp +{ + class ProcessorPlayerQuickKeys : public PlayerProcessor + { + public: + ProcessorPlayerQuickKeys() + { + BPP_INIT(ID_PLAYER_QUICKKEYS) + } + + virtual void Do(PlayerPacket &packet, BasePlayer *player) + { + if (!isLocal()) return; + + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_QUICKKEYS about LocalPlayer from server"); + + if (!isRequest()) + { + LocalPlayer &localPlayer = static_cast(*player); + localPlayer.setQuickKeys(); + } + } + }; +} + +#endif //OPENMW_PROCESSORPLAYERQUICKKEYS_HPP diff --git a/apps/openmw/mwmp/processors/player/ProcessorPlayerSpeech.hpp b/apps/openmw/mwmp/processors/player/ProcessorPlayerSpeech.hpp index 2383940bd..e5ff89f1a 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorPlayerSpeech.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorPlayerSpeech.hpp @@ -15,7 +15,13 @@ namespace mwmp virtual void Do(PlayerPacket &packet, BasePlayer *player) { - // Placeholder to be filled in later + if (isLocal()) + { + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_SPEECH about LocalPlayer from server"); + static_cast(player)->playSpeech(); + } + else if (player != 0) + static_cast(player)->playSpeech(); } }; } diff --git a/apps/openmw/mwmp/processors/world/ProcessorConsoleCommand.hpp b/apps/openmw/mwmp/processors/world/ProcessorConsoleCommand.hpp index 1f723dfba..bf8492733 100644 --- a/apps/openmw/mwmp/processors/world/ProcessorConsoleCommand.hpp +++ b/apps/openmw/mwmp/processors/world/ProcessorConsoleCommand.hpp @@ -1,11 +1,11 @@ #ifndef OPENMW_PROCESSORCONSOLECOMMAND_HPP #define OPENMW_PROCESSORCONSOLECOMMAND_HPP -#include "../WorldProcessor.hpp" +#include "BaseObjectProcessor.hpp" namespace mwmp { - class ProcessorConsoleCommand final: public WorldProcessor + class ProcessorConsoleCommand : public BaseObjectProcessor { public: ProcessorConsoleCommand() @@ -15,8 +15,9 @@ namespace mwmp virtual void Do(WorldPacket &packet, WorldEvent &event) { - LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Received %s", strPacketID.c_str()); - //event.runConsoleCommand(); + BaseObjectProcessor::Do(packet, event); + + event.runConsoleCommands(ptrCellStore); } }; } diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index cea176ff6..e96322427 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -1,5 +1,16 @@ #include "guiextensions.hpp" +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include "../mwmp/Main.hpp" +#include "../mwmp/LocalPlayer.hpp" +/* + End of tes3mp addition +*/ + #include #include @@ -57,7 +68,20 @@ namespace MWScript if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWMechanics::getPlayer(), bed)) - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Rest, bed); + /* + Start of tes3mp change (minor) + + Prevent resting if it has been disabled by the server for the local player + */ + { + if (!mwmp::Main::get().getLocalPlayer()->bedRestAllowed) + MWBase::Environment::get().getWindowManager()->messageBox("You are not allowed to rest in beds."); + else + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Rest, bed); + } + /* + End of tes3mp change (minor) + */ } }; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 64318f646..85491274f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -187,9 +187,8 @@ add_component_dir (openmw-mp/Packets/Player PacketPlayerAttack PacketPlayerAttribute PacketPlayerBook PacketPlayerBounty PacketPlayerCellChange PacketPlayerCellState PacketPlayerClass PacketPlayerDeath PacketPlayerEquipment PacketPlayerFaction PacketPlayerInventory PacketPlayerJail PacketPlayerJournal PacketPlayerKillCount PacketPlayerLevel - PacketPlayerMap PacketPlayerPosition PacketPlayerRegionAuthority PacketPlayerRest PacketPlayerResurrect - PacketPlayerShapeshift PacketPlayerSkill PacketPlayerSpeech PacketPlayerSpellbook PacketPlayerStatsDynamic - PacketPlayerTopic PacketGUIWindow + PacketPlayerMap PacketPlayerPosition PacketPlayerQuickKeys PacketPlayerRegionAuthority PacketPlayerRest + PacketPlayerResurrect PacketPlayerShapeshift PacketPlayerSkill PacketPlayerSpeech PacketPlayerSpellbook PacketPlayerStatsDynamic PacketPlayerTopic PacketGUIWindow ) add_component_dir (openmw-mp/Packets/World diff --git a/components/openmw-mp/Base/BaseActor.hpp b/components/openmw-mp/Base/BaseActor.hpp index fda4d1331..64f2fa460 100644 --- a/components/openmw-mp/Base/BaseActor.hpp +++ b/components/openmw-mp/Base/BaseActor.hpp @@ -29,7 +29,6 @@ namespace mwmp unsigned refNumIndex; unsigned mpNum; - std::string response; std::string sound; Animation animation; diff --git a/components/openmw-mp/Base/BaseEvent.hpp b/components/openmw-mp/Base/BaseEvent.hpp index b9e6e09c6..33d62ceec 100644 --- a/components/openmw-mp/Base/BaseEvent.hpp +++ b/components/openmw-mp/Base/BaseEvent.hpp @@ -12,12 +12,13 @@ namespace mwmp std::string refId; int count; int charge; + int enchantmentCharge; int actionCount; inline bool operator==(const ContainerItem& rhs) { - return refId == rhs.refId && count == rhs.count && charge == rhs.charge; + return refId == rhs.refId && count == rhs.count && charge == rhs.charge && enchantmentCharge == rhs.enchantmentCharge; } }; @@ -28,6 +29,7 @@ namespace mwmp unsigned mpNum; int count; int charge; + int enchantmentCharge; int goldValue; ESM::Position position; @@ -55,6 +57,9 @@ namespace mwmp std::vector containerItems; unsigned int containerItemCount; + + RakNet::RakNetGUID guid; // only for events that can also affect players + bool isPlayer; }; class BaseEvent @@ -84,6 +89,7 @@ namespace mwmp std::vector worldObjects; ESM::Cell cell; + std::string consoleCommand; unsigned char action; // 0 - Clear and set in entirety, 1 - Add item, 2 - Remove item, 3 - Request items diff --git a/components/openmw-mp/Base/BasePlayer.hpp b/components/openmw-mp/Base/BasePlayer.hpp index ccacef0f1..c9cb7966d 100644 --- a/components/openmw-mp/Base/BasePlayer.hpp +++ b/components/openmw-mp/Base/BasePlayer.hpp @@ -81,6 +81,22 @@ namespace mwmp std::string bookId; }; + struct QuickKey + { + std::string itemId; + + enum QUICKKEY_TYPE + { + ITEM = 0, + MAGIC = 1, + ITEM_MAGIC = 2, + UNASSIGNED = 3 + }; + + unsigned short slot; + int type; + }; + struct CellState { ESM::Cell cell; @@ -146,6 +162,11 @@ namespace mwmp int action; // 0 - Clear and set in entirety, 1 - Add spell, 2 - Remove spell }; + struct QuickKeyChanges + { + std::vector quickKeys; + }; + struct CellStateChanges { std::vector cellStates; @@ -248,6 +269,7 @@ namespace mwmp std::vector skillIndexChanges; SpellbookChanges spellbookChanges; + QuickKeyChanges quickKeyChanges; JournalChanges journalChanges; FactionChanges factionChanges; TopicChanges topicChanges; @@ -259,8 +281,17 @@ namespace mwmp ESM::ActiveSpells activeSpells; CurrentContainer currentContainer; - bool consoleAllowed; + struct + { + int currentWeather, nextWeather; + float updateTime, transitionFactor; + } weather; + int difficulty; + bool consoleAllowed; + bool bedRestAllowed; + bool wildernessRestAllowed; + bool waitAllowed; bool ignorePosPacket; @@ -273,6 +304,9 @@ namespace mwmp CharGenState charGenState; std::string passw; + std::string sound; + Animation animation; + bool isWerewolf; std::string creatureModel; bool useCreatureName; @@ -288,12 +322,8 @@ namespace mwmp ResurrectType resurrectType; bool diedSinceArrestAttempt; - - struct - { - int currentWeather, nextWeather; - float updateTime, transitionFactor; - } weather; + bool isReceivingQuickKeys; + bool isPlayingAnimation; }; } diff --git a/components/openmw-mp/Base/BaseStructs.hpp b/components/openmw-mp/Base/BaseStructs.hpp index f75048866..0ac6018ed 100644 --- a/components/openmw-mp/Base/BaseStructs.hpp +++ b/components/openmw-mp/Base/BaseStructs.hpp @@ -12,10 +12,11 @@ namespace mwmp std::string refId; int count; int charge; + int enchantmentCharge; inline bool operator==(const Item& rhs) { - return refId == rhs.refId && count == rhs.count && charge == rhs.charge; + return refId == rhs.refId && count == rhs.count && charge == rhs.charge && enchantmentCharge == rhs.enchantmentCharge; } }; diff --git a/components/openmw-mp/Controllers/PlayerPacketController.cpp b/components/openmw-mp/Controllers/PlayerPacketController.cpp index 2850702d7..586fada01 100644 --- a/components/openmw-mp/Controllers/PlayerPacketController.cpp +++ b/components/openmw-mp/Controllers/PlayerPacketController.cpp @@ -29,6 +29,7 @@ #include "../Packets/Player/PacketPlayerLevel.hpp" #include "../Packets/Player/PacketPlayerMap.hpp" #include "../Packets/Player/PacketPlayerPosition.hpp" +#include "../Packets/Player/PacketPlayerQuickKeys.hpp" #include "../Packets/Player/PacketPlayerRegionAuthority.hpp" #include "../Packets/Player/PacketPlayerRest.hpp" #include "../Packets/Player/PacketPlayerResurrect.hpp" @@ -83,6 +84,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); diff --git a/components/openmw-mp/NetworkMessages.hpp b/components/openmw-mp/NetworkMessages.hpp index bd63ace19..bc21e8486 100644 --- a/components/openmw-mp/NetworkMessages.hpp +++ b/components/openmw-mp/NetworkMessages.hpp @@ -45,6 +45,7 @@ enum GameMessages ID_PLAYER_LEVEL, ID_PLAYER_MAP, ID_PLAYER_POSITION, + ID_PLAYER_QUICKKEYS, ID_PLAYER_REGION_AUTHORITY, ID_PLAYER_RESURRECT, ID_PLAYER_REST, diff --git a/components/openmw-mp/Packets/Actor/PacketActorEquipment.cpp b/components/openmw-mp/Packets/Actor/PacketActorEquipment.cpp index 47a96a1c7..6cdec8ec8 100644 --- a/components/openmw-mp/Packets/Actor/PacketActorEquipment.cpp +++ b/components/openmw-mp/Packets/Actor/PacketActorEquipment.cpp @@ -16,5 +16,6 @@ void PacketActorEquipment::Actor(BaseActor &actor, bool send) RW(equipmentItem.refId, send); RW(equipmentItem.count, send); RW(equipmentItem.charge, send); + RW(equipmentItem.enchantmentCharge, send); } } diff --git a/components/openmw-mp/Packets/Actor/PacketActorSpeech.cpp b/components/openmw-mp/Packets/Actor/PacketActorSpeech.cpp index 565097ccf..20fe76a47 100644 --- a/components/openmw-mp/Packets/Actor/PacketActorSpeech.cpp +++ b/components/openmw-mp/Packets/Actor/PacketActorSpeech.cpp @@ -11,6 +11,5 @@ PacketActorSpeech::PacketActorSpeech(RakNet::RakPeerInterface *peer) : ActorPack void PacketActorSpeech::Actor(BaseActor &actor, bool send) { - RW(actor.response, send); RW(actor.sound, send); } diff --git a/components/openmw-mp/Packets/Player/PacketGameSettings.cpp b/components/openmw-mp/Packets/Player/PacketGameSettings.cpp index 4e90d6c34..c02527e8d 100644 --- a/components/openmw-mp/Packets/Player/PacketGameSettings.cpp +++ b/components/openmw-mp/Packets/Player/PacketGameSettings.cpp @@ -13,6 +13,9 @@ void PacketGameSettings::Packet(RakNet::BitStream *bs, bool send) { PlayerPacket::Packet(bs, send); - RW(player->consoleAllowed, send); RW(player->difficulty, send); + RW(player->consoleAllowed, send); + RW(player->bedRestAllowed, send); + RW(player->wildernessRestAllowed, send); + RW(player->waitAllowed, send); } diff --git a/components/openmw-mp/Packets/Player/PacketPlayerAnimPlay.cpp b/components/openmw-mp/Packets/Player/PacketPlayerAnimPlay.cpp index 34c601673..bcc7562e8 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerAnimPlay.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerAnimPlay.cpp @@ -10,5 +10,8 @@ void mwmp::PacketPlayerAnimPlay::Packet(RakNet::BitStream *bs, bool send) { PlayerPacket::Packet(bs, send); - // Placeholder to be filled in later + RW(player->animation.groupname, send); + RW(player->animation.mode, send); + RW(player->animation.count, send); + RW(player->animation.persist, send); } diff --git a/components/openmw-mp/Packets/Player/PacketPlayerInventory.cpp b/components/openmw-mp/Packets/Player/PacketPlayerInventory.cpp index ecbb9d249..0a8aa32f3 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerInventory.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerInventory.cpp @@ -37,5 +37,6 @@ void PacketPlayerInventory::Packet(RakNet::BitStream *bs, bool send) RW(item.refId, send, true); RW(item.count, send); RW(item.charge, send); + RW(item.enchantmentCharge, send); } } diff --git a/components/openmw-mp/Packets/Player/PacketPlayerQuickKeys.cpp b/components/openmw-mp/Packets/Player/PacketPlayerQuickKeys.cpp new file mode 100644 index 000000000..770c31b18 --- /dev/null +++ b/components/openmw-mp/Packets/Player/PacketPlayerQuickKeys.cpp @@ -0,0 +1,31 @@ +#include +#include "PacketPlayerQuickKeys.hpp" + +using namespace std; +using namespace mwmp; + +PacketPlayerQuickKeys::PacketPlayerQuickKeys(RakNet::RakPeerInterface *peer) : PlayerPacket(peer) +{ + packetID = ID_PLAYER_QUICKKEYS; +} + +void PacketPlayerQuickKeys::Packet(RakNet::BitStream *bs, bool send) +{ + PlayerPacket::Packet(bs, send); + + uint32_t count; + + if (send) + count = static_cast(player->quickKeyChanges.quickKeys.size()); + + RW(count, send); + + for (auto &&quickKey : player->quickKeyChanges.quickKeys) + { + RW(quickKey.type, send); + RW(quickKey.slot, send); + + if (quickKey.type != QuickKey::UNASSIGNED) + RW(quickKey.itemId, send); + } +} diff --git a/components/openmw-mp/Packets/Player/PacketPlayerQuickKeys.hpp b/components/openmw-mp/Packets/Player/PacketPlayerQuickKeys.hpp new file mode 100644 index 000000000..26a6f64b0 --- /dev/null +++ b/components/openmw-mp/Packets/Player/PacketPlayerQuickKeys.hpp @@ -0,0 +1,17 @@ +#ifndef OPENMW_PACKETPLAYERQUICKKEYS_HPP +#define OPENMW_PACKETPLAYERQUICKKEYS_HPP + +#include + +namespace mwmp +{ + class PacketPlayerQuickKeys : public PlayerPacket + { + public: + PacketPlayerQuickKeys(RakNet::RakPeerInterface *peer); + + virtual void Packet(RakNet::BitStream *bs, bool send); + }; +} + +#endif //OPENMW_PACKETPLAYERQUICKKEYS_HPP diff --git a/components/openmw-mp/Packets/Player/PacketPlayerSpeech.cpp b/components/openmw-mp/Packets/Player/PacketPlayerSpeech.cpp index 38b30be5f..7f4a24cfc 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerSpeech.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerSpeech.cpp @@ -10,5 +10,5 @@ void mwmp::PacketPlayerSpeech::Packet(RakNet::BitStream *bs, bool send) { PlayerPacket::Packet(bs, send); - // Placeholder to be filled in later + RW(player->sound, send); } diff --git a/components/openmw-mp/Packets/Player/PacketPlayerSpellbook.cpp b/components/openmw-mp/Packets/Player/PacketPlayerSpellbook.cpp index f04f8990d..f4c510add 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerSpellbook.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerSpellbook.cpp @@ -30,41 +30,6 @@ void PacketPlayerSpellbook::Packet(RakNet::BitStream *bs, bool send) for (auto &&spell : player->spellbookChanges.spells) { - RW(spell.mId, send, true); - - if (spell.mId.find("$dynamic") != string::npos) - { - RW(spell.mName, send, true); - - RW(spell.mData.mType, send, true); - RW(spell.mData.mCost, send, true); - RW(spell.mData.mFlags, send, true); - - uint32_t effectCount; - if (send) - effectCount = static_cast(spell.mEffects.mList.size()); - - RW(effectCount, send, true); - - if (!send) - { - spell.mEffects.mList.resize(effectCount); - } - - for (auto &&effect : spell.mEffects.mList) - { - - RW(effect.mEffectID, send, true); - RW(effect.mSkill, send, true); - RW(effect.mAttribute, send, true); - RW(effect.mRange, send, true); - RW(effect.mArea, send, true); - RW(effect.mDuration, send, true); - RW(effect.mMagnMin, send, true); - RW(effect.mMagnMax, send, true); - } - } } - } diff --git a/components/openmw-mp/Packets/World/PacketConsoleCommand.cpp b/components/openmw-mp/Packets/World/PacketConsoleCommand.cpp index 796f7988e..8d6489e9c 100644 --- a/components/openmw-mp/Packets/World/PacketConsoleCommand.cpp +++ b/components/openmw-mp/Packets/World/PacketConsoleCommand.cpp @@ -8,7 +8,21 @@ PacketConsoleCommand::PacketConsoleCommand(RakNet::RakPeerInterface *peer) : Wor packetID = ID_CONSOLE_COMMAND; } -void PacketConsoleCommand::Object(WorldObject &worldObject, bool send) +void PacketConsoleCommand::Packet(RakNet::BitStream *bs, bool send) { - WorldPacket::Object(worldObject, send); + if (!PacketHeader(bs, send)) + return; + + RW(event->consoleCommand, send); + + WorldObject worldObject; + for (auto &&worldObject : event->worldObjects) + { + RW(worldObject.isPlayer, send); + + if (worldObject.isPlayer) + RW(worldObject.guid, send); + else + Object(worldObject, send); + } } diff --git a/components/openmw-mp/Packets/World/PacketConsoleCommand.hpp b/components/openmw-mp/Packets/World/PacketConsoleCommand.hpp index 6ce630ea1..c8de1b550 100644 --- a/components/openmw-mp/Packets/World/PacketConsoleCommand.hpp +++ b/components/openmw-mp/Packets/World/PacketConsoleCommand.hpp @@ -9,8 +9,8 @@ namespace mwmp { public: explicit PacketConsoleCommand(RakNet::RakPeerInterface *peer); - - void Object(WorldObject &obj, bool send) override; + + void Packet(RakNet::BitStream *bs, bool send) override; }; } diff --git a/components/openmw-mp/Packets/World/PacketContainer.cpp b/components/openmw-mp/Packets/World/PacketContainer.cpp index 6a17d9b61..221333a18 100644 --- a/components/openmw-mp/Packets/World/PacketContainer.cpp +++ b/components/openmw-mp/Packets/World/PacketContainer.cpp @@ -46,6 +46,7 @@ void PacketContainer::Packet(RakNet::BitStream *bs, bool send) RW(containerItem.refId, send); RW(containerItem.count, send); RW(containerItem.charge, send); + RW(containerItem.enchantmentCharge, send); RW(containerItem.actionCount, send); } } diff --git a/components/openmw-mp/Packets/World/PacketObjectPlace.cpp b/components/openmw-mp/Packets/World/PacketObjectPlace.cpp index 7a13964cb..031913074 100644 --- a/components/openmw-mp/Packets/World/PacketObjectPlace.cpp +++ b/components/openmw-mp/Packets/World/PacketObjectPlace.cpp @@ -14,6 +14,7 @@ void PacketObjectPlace::Object(WorldObject &worldObject, bool send) WorldPacket::Object(worldObject, send); RW(worldObject.count, send); RW(worldObject.charge, send); + RW(worldObject.enchantmentCharge, send); RW(worldObject.goldValue, send); RW(worldObject.position, send); } diff --git a/tes3mp-changelog.md b/tes3mp-changelog.md index 3a7aaf3ed..3478edf1b 100644 --- a/tes3mp-changelog.md +++ b/tes3mp-changelog.md @@ -1,3 +1,19 @@ +0.6.2 +----- + +* Packet for quick keys +* Packet for player sounds +* Packet for player animations +* Packet for console commands +* Enchantment charge values are now included in item and object placement packets +* Settings packet can now be used to separately enable or disable resting in beds, resting in the wilderness and waiting +* Changes in attribute and skill modifiers now send their respective packets +* Attribute and skill packets using 0 as a modifier can now remove all attribute or skill fortification effects from a player +* Completion of vendor trades and skill training now sends inventory packets +* Item drag and dropping is now finished when arrested or when moved to another cell by the server +* Window minimization no longer pauses the game +* Actor speech captions are now searched for in dialogues instead of being sent in packets + 0.6.1 ----- diff --git a/tes3mp-credits.md b/tes3mp-credits.md index ad603c16f..0569e4a00 100644 --- a/tes3mp-credits.md +++ b/tes3mp-credits.md @@ -23,6 +23,13 @@ Community administrators Testman +Community moderators +-------------------- + + Capostrophic + Michael Fitzmayer (mupf) + + Art --- @@ -33,8 +40,7 @@ Special thanks (in alphabetical order) -------------------------------------- Camul - Capostrophic - Dave + David Wery DestinedToDie Gluka Goodevil @@ -45,6 +51,7 @@ Special thanks (in alphabetical order) Jeremiah Lewis Sadlier Luc Keating + Malseph Michael Zagar (Zoops) Olaxan psi29a