You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
12 KiB
C++
377 lines
12 KiB
C++
#include <components/openmw-mp/Log.hpp>
|
|
|
|
#include "../mwbase/environment.hpp"
|
|
|
|
#include "../mwgui/windowmanagerimp.hpp"
|
|
|
|
#include "../mwworld/player.hpp"
|
|
#include "../mwworld/worldimp.hpp"
|
|
|
|
#include "Worldstate.hpp"
|
|
#include "Main.hpp"
|
|
#include "Networking.hpp"
|
|
#include "RecordHelper.hpp"
|
|
|
|
using namespace mwmp;
|
|
using namespace std;
|
|
|
|
Worldstate::Worldstate()
|
|
{
|
|
hasPlayerCollision = true;
|
|
hasActorCollision = true;
|
|
hasPlacedObjectCollision = false;
|
|
useActorCollisionForPlacedObjects = false;
|
|
}
|
|
|
|
Worldstate::~Worldstate()
|
|
{
|
|
|
|
}
|
|
|
|
Networking *Worldstate::getNetworking()
|
|
{
|
|
return mwmp::Main::get().getNetworking();
|
|
}
|
|
|
|
void Worldstate::addRecords()
|
|
{
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_RECORD_DYNAMIC with %i records of type %i",
|
|
recordsCount, recordsType);
|
|
|
|
if (recordsType == mwmp::RECORD_TYPE::SPELL)
|
|
{
|
|
for (auto &&record : spellRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- spell record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideSpellRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::POTION)
|
|
{
|
|
for (auto &&record : potionRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- potion record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overridePotionRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)
|
|
{
|
|
for (auto &&record : enchantmentRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- enchantment record %s, %i\n-- baseId is %s", record.data.mId.c_str(), record.data.mData.mType,
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideEnchantmentRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::CREATURE)
|
|
{
|
|
for (auto &&record : creatureRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- creature record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideCreatureRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::NPC)
|
|
{
|
|
for (auto &&record : npcRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- NPC record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideNpcRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::ARMOR)
|
|
{
|
|
for (auto &&record : armorRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- armor record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideArmorRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::BOOK)
|
|
{
|
|
for (auto &&record : bookRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- book record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideBookRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::CLOTHING)
|
|
{
|
|
for (auto &&record : clothingRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- clothing record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideClothingRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)
|
|
{
|
|
for (auto &&record : miscellaneousRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- miscellaneous record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideMiscellaneousRecord(record);
|
|
}
|
|
}
|
|
else if (recordsType == mwmp::RECORD_TYPE::WEAPON)
|
|
{
|
|
for (auto &&record : weaponRecords)
|
|
{
|
|
bool hasBaseId = !record.baseId.empty();
|
|
|
|
LOG_APPEND(Log::LOG_INFO, "- weapon record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
|
hasBaseId ? record.baseId.c_str() : "empty");
|
|
|
|
RecordHelper::overrideWeaponRecord(record);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Worldstate::containsExploredMapTile(int cellX, int cellY)
|
|
{
|
|
for (const auto &mapTile : exploredMapTiles)
|
|
{
|
|
if (mapTile.x == cellX && mapTile.y == cellY)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Worldstate::markExploredMapTile(int cellX, int cellY)
|
|
{
|
|
mwmp::MapTile exploredTile;
|
|
exploredTile.x = cellX;
|
|
exploredTile.y = cellY;
|
|
exploredMapTiles.push_back(exploredTile);
|
|
}
|
|
|
|
void Worldstate::setMapExplored()
|
|
{
|
|
for (const auto &mapTile : mapTiles)
|
|
{
|
|
const MWWorld::CellStore *cellStore = MWBase::Environment::get().getWorld()->getExterior(mapTile.x, mapTile.y);
|
|
|
|
if (!cellStore->getCell()->mName.empty())
|
|
MWBase::Environment::get().getWindowManager()->addVisitedLocation(cellStore->getCell()->mName, mapTile.x, mapTile.y);
|
|
|
|
MWBase::Environment::get().getWindowManager()->setGlobalMapImage(mapTile.x, mapTile.y, mapTile.imageData);
|
|
|
|
// Keep this tile marked as explored so we don't send any more packets for it
|
|
markExploredMapTile(mapTile.x, mapTile.y);
|
|
}
|
|
}
|
|
|
|
void Worldstate::setWeather()
|
|
{
|
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
|
|
// There's a chance we've been sent the weather for a region right after a teleportation
|
|
// that hasn't been registered in the WeatherManager yet, meaning the WeatherManager
|
|
// doesn't have the correct new region set for us, so make sure we update it
|
|
world->updateWeather(0);
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Setting weather for region: %s, currentWeather: %i, "
|
|
"nextWeather: %i, queuedWeather: %i, transitionFactor: %f, forceWeather is %s",
|
|
weather.region.c_str(), weather.currentWeather, weather.nextWeather,
|
|
weather.queuedWeather, weather.transitionFactor, forceWeather ? "true" : "false");
|
|
|
|
world->setRegionWeather(weather.region.c_str(), weather.currentWeather, weather.nextWeather,
|
|
weather.queuedWeather, weather.transitionFactor, forceWeather);
|
|
}
|
|
|
|
void Worldstate::sendMapExplored(int cellX, int cellY, const std::vector<char>& imageData)
|
|
{
|
|
mapTiles.clear();
|
|
|
|
mwmp::MapTile mapTile;
|
|
mapTile.x = cellX;
|
|
mapTile.y = cellY;
|
|
mapTile.imageData = imageData;
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_PLAYER_MAP with x: %i, y: %i", cellX, cellY);
|
|
|
|
mapTiles.push_back(mapTile);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_WORLD_MAP)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_WORLD_MAP)->Send();
|
|
}
|
|
|
|
void Worldstate::sendWeather(std::string region, int currentWeather, int nextWeather, int queuedWeather, float transitionFactor)
|
|
{
|
|
forceWeather = false;
|
|
weather.region = region;
|
|
weather.currentWeather = currentWeather;
|
|
weather.nextWeather = nextWeather;
|
|
weather.queuedWeather = queuedWeather;
|
|
weather.transitionFactor = transitionFactor;
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_PLAYER_WEATHER with region: %s, currentWeather: %i, "
|
|
"nextWeather: %i, queuedWeather, %i, transitionFactor: %f",
|
|
region.c_str(), currentWeather, nextWeather, queuedWeather, transitionFactor);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_WORLD_WEATHER)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_WORLD_WEATHER)->Send();
|
|
}
|
|
|
|
void Worldstate::sendEnchantmentRecord(const ESM::Enchantment* enchantment)
|
|
{
|
|
enchantmentRecords.clear();
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with enchantment");
|
|
|
|
recordsType = mwmp::RECORD_TYPE::ENCHANTMENT;
|
|
|
|
mwmp::EnchantmentRecord record;
|
|
record.data = *enchantment;
|
|
enchantmentRecords.push_back(record);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
|
|
}
|
|
|
|
void Worldstate::sendPotionRecord(const ESM::Potion* potion)
|
|
{
|
|
potionRecords.clear();
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with potion %s", potion->mName.c_str());
|
|
|
|
recordsType = mwmp::RECORD_TYPE::POTION;
|
|
|
|
mwmp::PotionRecord record;
|
|
record.data = *potion;
|
|
potionRecords.push_back(record);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
|
|
}
|
|
|
|
void Worldstate::sendSpellRecord(const ESM::Spell* spell)
|
|
{
|
|
spellRecords.clear();
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with spell %s", spell->mName.c_str());
|
|
|
|
recordsType = mwmp::RECORD_TYPE::SPELL;
|
|
|
|
mwmp::SpellRecord record;
|
|
record.data = *spell;
|
|
spellRecords.push_back(record);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
|
|
}
|
|
|
|
void Worldstate::sendArmorRecord(const ESM::Armor* armor, std::string baseId)
|
|
{
|
|
armorRecords.clear();
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with armor %s", armor->mName.c_str());
|
|
|
|
recordsType = mwmp::RECORD_TYPE::ARMOR;
|
|
|
|
mwmp::ArmorRecord record;
|
|
record.data = *armor;
|
|
record.baseId = baseId;
|
|
record.baseOverrides.hasName = true;
|
|
record.baseOverrides.hasEnchantmentId = true;
|
|
record.baseOverrides.hasEnchantmentCharge = true;
|
|
armorRecords.push_back(record);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
|
|
}
|
|
|
|
void Worldstate::sendBookRecord(const ESM::Book* book, std::string baseId)
|
|
{
|
|
bookRecords.clear();
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with book %s", book->mName.c_str());
|
|
|
|
recordsType = mwmp::RECORD_TYPE::BOOK;
|
|
|
|
mwmp::BookRecord record;
|
|
record.data = *book;
|
|
record.baseId = baseId;
|
|
record.baseOverrides.hasName = true;
|
|
record.baseOverrides.hasEnchantmentId = true;
|
|
record.baseOverrides.hasEnchantmentCharge = true;
|
|
bookRecords.push_back(record);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
|
|
}
|
|
|
|
void Worldstate::sendClothingRecord(const ESM::Clothing* clothing, std::string baseId)
|
|
{
|
|
clothingRecords.clear();
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with clothing %s", clothing->mName.c_str());
|
|
|
|
recordsType = mwmp::RECORD_TYPE::CLOTHING;
|
|
|
|
mwmp::ClothingRecord record;
|
|
record.data = *clothing;
|
|
record.baseId = baseId;
|
|
record.baseOverrides.hasName = true;
|
|
record.baseOverrides.hasEnchantmentId = true;
|
|
record.baseOverrides.hasEnchantmentCharge = true;
|
|
clothingRecords.push_back(record);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
|
|
}
|
|
|
|
void Worldstate::sendWeaponRecord(const ESM::Weapon* weapon, std::string baseId)
|
|
{
|
|
weaponRecords.clear();
|
|
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with weapon %s", weapon->mName.c_str());
|
|
|
|
recordsType = mwmp::RECORD_TYPE::WEAPON;
|
|
|
|
mwmp::WeaponRecord record;
|
|
record.data = *weapon;
|
|
record.baseId = baseId;
|
|
record.baseOverrides.hasName = true;
|
|
record.baseOverrides.hasEnchantmentId = true;
|
|
record.baseOverrides.hasEnchantmentCharge = true;
|
|
weaponRecords.push_back(record);
|
|
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
|
|
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
|
|
}
|