Merge pull request #396 from TES3MP/0.6.3 while resolving conflicts

Conflicts:
	apps/openmw-mp/Player.cpp
	apps/openmw-mp/Script/Functions/Settings.cpp
	apps/openmw-mp/Script/Functions/Settings.hpp
	apps/openmw-mp/Script/Functions/World.cpp
	apps/openmw-mp/Script/Functions/World.hpp
	apps/openmw/mwgui/container.cpp
	apps/openmw/mwmp/LocalActor.cpp
	apps/openmw/mwmp/LocalPlayer.cpp
	apps/openmw/mwmp/WorldEvent.cpp
	apps/openmw/mwmp/processors/world/ProcessorContainer.hpp
	components/openmw-mp/Base/BaseEvent.hpp
	components/openmw-mp/Log.cpp
	components/openmw-mp/Log.hpp
This commit is contained in:
David Cernat 2018-03-30 09:32:43 +03:00
commit 3d5860d6f4
46 changed files with 565 additions and 207 deletions

View file

@ -106,7 +106,7 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
/// \todo remove this label once we are feature complete and convinced that this thing is /// \todo remove this label once we are feature complete and convinced that this thing is
/// working properly. /// working properly.
QLabel *warning = new QLabel ("<font color=Red>WARNING: OpenMW-CS is in alpha stage.<p>The editor is not feature complete and not sufficiently tested.<br>In theory your data should be safe. But we strongly advice to make backups regularly if you are working with live data.</font color>"); QLabel *warning = new QLabel ("<font color=Red>WARNING: OpenMW-CS is in alpha stage.<p>The editor is not feature complete and not sufficiently tested.<br>In theory your data should be safe. But we strongly advise to make backups regularly if you are working with live data.</font color>");
QFont font; QFont font;
font.setPointSize (12); font.setPointSize (12);

View file

@ -228,6 +228,7 @@ void Object::setMasterState(bool state)
} }
// TODO: Make this actually reflect the capabilities offered by containers in 0.6.3
void Container::Init(LuaState &lua) void Container::Init(LuaState &lua)
{ {
lua.getState()->new_usertype<Container>("Container", lua.getState()->new_usertype<Container>("Container",

View file

@ -14,6 +14,7 @@ void GameSettings::Init(LuaState &lua)
{ {
lua.getState()->new_usertype<GameSettings>("Settings", lua.getState()->new_usertype<GameSettings>("Settings",
"setDifficulty", &GameSettings::setDifficulty, "setDifficulty", &GameSettings::setDifficulty,
"setEnforcedLogLevel", &GameSettings::setEnforcedLogLevel,
"setPhysicsFramerate", &GameSettings::setPhysicsFramerate, "setPhysicsFramerate", &GameSettings::setPhysicsFramerate,
"setConsoleAllowed", &GameSettings::setConsoleAllowed, "setConsoleAllowed", &GameSettings::setConsoleAllowed,
"setBedRestAllowed", &GameSettings::setBedRestAllowed, "setBedRestAllowed", &GameSettings::setBedRestAllowed,
@ -39,6 +40,12 @@ void GameSettings::setDifficulty(int difficulty)
setChanged(); setChanged();
} }
void GameSettings::setEnforcedLogLevel(int logLevel)
{
player->enforcedLogLevel = logLevel;
setChanged();
}
void GameSettings::setPhysicsFramerate(double physicsFramerate) void GameSettings::setPhysicsFramerate(double physicsFramerate)
{ {
player->physicsFramerate = physicsFramerate; player->physicsFramerate = physicsFramerate;

View file

@ -22,6 +22,8 @@ public:
void setDifficulty(int difficulty); void setDifficulty(int difficulty);
void setEnforcedLogLevel(int logLevel);
void setPhysicsFramerate(double physicsFramerate); void setPhysicsFramerate(double physicsFramerate);
void setBedRestAllowed(bool state); void setBedRestAllowed(bool state);

View file

@ -30,8 +30,10 @@ namespace mwmp
if (serverCell != nullptr) if (serverCell != nullptr)
serverCell->sendToLoaded(&packet, &event); serverCell->sendToLoaded(&packet, &event);
} }
else
packet.Send(true); // Otherwise, don't have any hardcoded sync and expect Lua scripts to forward
// container packets to ensure their integrity based on what exists in the
// server data
auto objCtrl = Networking::get().getState().getObjectCtrl(); auto objCtrl = Networking::get().getState().getObjectCtrl();
auto containers = objCtrl.copyContainers(event); auto containers = objCtrl.copyContainers(event);

View file

@ -145,6 +145,17 @@ namespace MWBase
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
virtual MWGui::TradeWindow* getTradeWindow() = 0; virtual MWGui::TradeWindow* getTradeWindow() = 0;
/*
Start of tes3mp addition
Make it possible to get the ContainerWindow from elsewhere
in the code
*/
virtual MWGui::ContainerWindow* getContainerWindow() = 0;
/*
End of tes3mp addition
*/
/// Make the player use an item, while updating GUI state accordingly /// Make the player use an item, while updating GUI state accordingly
virtual void useItem(const MWWorld::Ptr& item) = 0; virtual void useItem(const MWWorld::Ptr& item) = 0;
@ -411,7 +422,7 @@ namespace MWBase
/// Cycle to next or previous weapon /// Cycle to next or previous weapon
virtual void cycleWeapon(bool next) = 0; virtual void cycleWeapon(bool next) = 0;
virtual void playSound(const std::string& soundId, float volume = 1.f, float pitch = 1.f) = 0; virtual void playSound(const std::string& soundId, bool preventOverlapping = false, float volume = 1.f, float pitch = 1.f) = 0;
// In WindowManager for now since there isn't a VFS singleton // In WindowManager for now since there isn't a VFS singleton
virtual std::string correctIconPath(const std::string& path) = 0; virtual std::string correctIconPath(const std::string& path) = 0;

View file

@ -547,12 +547,14 @@ namespace MWBase
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0;
virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0;
virtual const std::vector<std::string>& getContentFiles() const = 0; virtual const std::vector<std::string>& getContentFiles() const = 0;
virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0;
// Are we in an exterior or pseudo-exterior cell and it's night? // Allow NPCs to use torches?
virtual bool isDark() const = 0; virtual bool useTorches() const = 0;
virtual bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) = 0; virtual bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) = 0;

View file

@ -233,24 +233,6 @@ namespace MWClass
if (!(ref->mBase->mData.mFlags & ESM::Light::Carry)) if (!(ref->mBase->mData.mFlags & ESM::Light::Carry))
return std::make_pair(0,""); return std::make_pair(0,"");
const MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc);
MWWorld::ConstContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == invStore.end())
return std::make_pair(1,"");
/// \todo the 2h check is repeated many times; put it in a function
if(weapon->getTypeName() == typeid(ESM::Weapon).name() &&
(weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::BluntTwoClose ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::BluntTwoWide ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::SpearTwoWide ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::AxeTwoHand ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanBow ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow))
{
return std::make_pair(3,"");
}
return std::make_pair(1,""); return std::make_pair(1,"");
} }

View file

@ -892,7 +892,7 @@ protected:
public: public:
typedef TypesetBookImpl::StyleImpl Style; typedef TypesetBookImpl::StyleImpl Style;
typedef std::map <TextFormat::Id, TextFormat*> ActiveTextFormats; typedef std::map <TextFormat::Id, std::unique_ptr<TextFormat>> ActiveTextFormats;
int mViewTop; int mViewTop;
int mViewBottom; int mViewBottom;
@ -1048,7 +1048,7 @@ public:
{ {
if (mNode != NULL) if (mNode != NULL)
i->second->destroyDrawItem (mNode); i->second->destroyDrawItem (mNode);
delete i->second; i->second.reset();
} }
mActiveTextFormats.clear (); mActiveTextFormats.clear ();
@ -1115,11 +1115,11 @@ public:
if (j == this_->mActiveTextFormats.end ()) if (j == this_->mActiveTextFormats.end ())
{ {
TextFormat * textFormat = new TextFormat (Font, this_); std::unique_ptr<TextFormat> textFormat(new TextFormat (Font, this_));
textFormat->mTexture = Font->getTextureFont (); textFormat->mTexture = Font->getTextureFont ();
j = this_->mActiveTextFormats.insert (std::make_pair (Font, textFormat)).first; j = this_->mActiveTextFormats.insert (std::make_pair (Font, std::move(textFormat))).first;
} }
j->second->mCountVertex += run.mPrintableChars * 6; j->second->mCountVertex += run.mPrintableChars * 6;

View file

@ -200,7 +200,7 @@ namespace MWGui
{ {
if ((mCurrentPage+1)*2 < mPages.size()) if ((mCurrentPage+1)*2 < mPages.size())
{ {
MWBase::Environment::get().getWindowManager()->playSound("book page2"); MWBase::Environment::get().getWindowManager()->playSound("book page2", true);
++mCurrentPage; ++mCurrentPage;
@ -211,7 +211,7 @@ namespace MWGui
{ {
if (mCurrentPage > 0) if (mCurrentPage > 0)
{ {
MWBase::Environment::get().getWindowManager()->playSound("book page"); MWBase::Environment::get().getWindowManager()->playSound("book page", true);
--mCurrentPage; --mCurrentPage;

View file

@ -115,35 +115,35 @@ namespace MWGui
worldEvent->reset(); worldEvent->reset();
worldEvent->cell = *mPtr.getCell()->getCell(); worldEvent->cell = *mPtr.getCell()->getCell();
worldEvent->action = mwmp::BaseEvent::Action::Remove; worldEvent->action = mwmp::BaseEvent::Action::Remove;
worldEvent->containerSubAction = mwmp::BaseEvent::ContainerSubAction::Drag;
mwmp::WorldObject worldObject; mwmp::WorldObject worldObject = worldEvent->getWorldObject(mPtr);
worldObject.refId = mPtr.getCellRef().getRefId();
worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex;
worldObject.mpNum = mPtr.getCellRef().getMpNum();
MWWorld::Ptr itemPtr = mModel->getItem(mSelectedItem).mBase; MWWorld::Ptr itemPtr = mModel->getItem(mSelectedItem).mBase;
mwmp::ContainerItem containerItem; worldEvent->addContainerItem(worldObject, itemPtr, count);
containerItem.refId =itemPtr.getCellRef().getRefId();
containerItem.count = itemPtr.getRefData().getCount();
containerItem.charge = itemPtr.getCellRef().getCharge();
containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();
containerItem.actionCount = count;
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i",
worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(),
containerItem.refId.c_str(), containerItem.count);
worldObject.containerItems.push_back(std::move(containerItem));
worldEvent->worldObjects.push_back(std::move(worldObject)); worldEvent->worldObjects.push_back(std::move(worldObject));
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent);
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send();
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i",
worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(),
itemPtr.getCellRef().getRefId().c_str(), itemPtr.getRefData().getCount());
/* /*
End of tes3mp addition End of tes3mp addition
*/ */
/*
Start of tes3mp change (major)
Avoid running any of the original code for dragging items, to prevent possibilities
for item duping or interaction with restricted containers
*/
return;
/*
End of tes3mp change (major)
*/
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
} }
@ -162,14 +162,10 @@ namespace MWGui
worldEvent->reset(); worldEvent->reset();
worldEvent->cell = *mPtr.getCell()->getCell(); worldEvent->cell = *mPtr.getCell()->getCell();
worldEvent->action = mwmp::BaseEvent::Action::Add; worldEvent->action = mwmp::BaseEvent::Action::Add;
worldEvent->containerSubAction = mwmp::BaseEvent::ContainerSubAction::Drop;
mwmp::WorldObject worldObject; mwmp::WorldObject worldObject = worldEvent->getWorldObject(mPtr);
worldObject.refId = mPtr.getCellRef().getRefId();
worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex;
worldObject.mpNum = mPtr.getCellRef().getMpNum();
MWWorld::Ptr itemPtr = mDragAndDrop->mItem.mBase; MWWorld::Ptr itemPtr = mDragAndDrop->mItem.mBase;
mwmp::ContainerItem containerItem; mwmp::ContainerItem containerItem;
containerItem.refId = itemPtr.getCellRef().getRefId(); containerItem.refId = itemPtr.getCellRef().getRefId();
@ -179,22 +175,34 @@ namespace MWGui
containerItem.charge = itemPtr.getCellRef().getCharge(); containerItem.charge = itemPtr.getCellRef().getCharge();
containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge(); containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i",
worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(),
containerItem.refId.c_str(), containerItem.count);
worldObject.containerItems.push_back(std::move(containerItem)); worldObject.containerItems.push_back(std::move(containerItem));
worldEvent->worldObjects.push_back(std::move(worldObject)); worldEvent->worldObjects.push_back(std::move(worldObject));
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent);
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send();
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i, %i",
worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(),
containerItem.refId.c_str(), containerItem.count, containerItem.charge);
} }
/* /*
End of tes3mp addition End of tes3mp addition
*/ */
if (success) /*
mDragAndDrop->drop(mModel, mItemView); Start of tes3mp change (major)
Avoid running any of the original code for dropping items, to prevent possibilities
for item duping or interaction with restricted containers
Instead, finish the drag in a way that removes the items in it
*/
//if (success)
// mDragAndDrop->drop(mModel, mItemView);
mDragAndDrop->finish(true);
/*
End of tes3mp change (major)
*/
} }
void ContainerWindow::onBackgroundSelected() void ContainerWindow::onBackgroundSelected()
@ -283,6 +291,40 @@ namespace MWGui
if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop) if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop)
return; return;
/*
Start of tes3mp addition
Send an ID_CONTAINER packet every time the Take All button is used on
a container
*/
mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent();
worldEvent->reset();
worldEvent->cell = *mPtr.getCell()->getCell();
worldEvent->action = mwmp::BaseEvent::Action::Remove;
worldEvent->containerSubAction = mwmp::BaseEvent::ContainerSubAction::TakeAll;
worldEvent->addEntireContainer(mPtr);
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent);
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send();
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i-%i\n- cell: %s",
mPtr.getCellRef().getRefId().c_str(), mPtr.getCellRef().getRefNum().mIndex, mPtr.getCellRef().getMpNum(),
worldEvent->cell.getDescription().c_str());
/*
End of tes3mp addition
*/
/*
Start of tes3mp change (major)
Avoid running any of the original code for taking all items, to prevent
possibilities for item duping or interaction with restricted containers
*/
return;
/*
End of tes3mp change (major)
*/
// transfer everything into the player's inventory // transfer everything into the player's inventory
ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel();
mModel->update(); mModel->update();
@ -322,33 +364,6 @@ namespace MWGui
} }
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
/*
Start of tes3mp addition
Send an ID_CONTAINER packet every time the Take All button is used on
a container
*/
mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent();
worldEvent->reset();
worldEvent->cell = *mPtr.getCell()->getCell();
worldEvent->action = mwmp::BaseEvent::Action::Set;
mwmp::WorldObject worldObject;
worldObject.refId = mPtr.getCellRef().getRefId();
worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex;
worldObject.mpNum = mPtr.getCellRef().getMpNum();
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s",
worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str());
worldEvent->worldObjects.push_back(std::move(worldObject));
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent);
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send();
/*
End of tes3mp addition
*/
} }
void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender)
@ -392,4 +407,48 @@ namespace MWGui
return mModel->onTakeItem(item.mBase, count); return mModel->onTakeItem(item.mBase, count);
} }
/*
Start of tes3mp addition
Make it possible to check from elsewhere whether there is currently an
item being dragged in the container window
*/
bool ContainerWindow::isOnDragAndDrop()
{
return mDragAndDrop->mIsOnDragAndDrop;
}
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to drag a specific item Ptr instead of having to rely
on an index that may have changed in the meantime, for drags that
require approval from the server
*/
bool ContainerWindow::dragItemByPtr(const MWWorld::Ptr& itemPtr, int dragCount)
{
ItemModel::ModelIndex newIndex = -1;
for (unsigned int i = 0; i < mModel->getItemCount(); ++i)
{
if (mModel->getItem(i).mBase == itemPtr)
{
newIndex = i;
break;
}
}
if (newIndex != -1)
{
mDragAndDrop->startDrag(newIndex, mSortModel, mModel, mItemView, dragCount);
return true;
}
return false;
}
/*
End of tes3mp addition
*/
} }

View file

@ -41,6 +41,29 @@ namespace MWGui
virtual void resetReference(); virtual void resetReference();
/*
Start of tes3mp addition
Make it possible to check from elsewhere whether there is currently an
item being dragged in the container window
*/
bool isOnDragAndDrop();
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to drag a specific item Ptr instead of having to rely
on an index that may have changed in the meantime, for drags that
require approval from the server
*/
bool dragItemByPtr(const MWWorld::Ptr& itemPtr, int dragCount);
/*
End of tes3mp addition
*/
private: private:
DragAndDrop* mDragAndDrop; DragAndDrop* mDragAndDrop;

View file

@ -127,10 +127,35 @@ void DragAndDrop::onFrame()
finish(); finish();
} }
void DragAndDrop::finish() /*
Start of tes3mp change (minor)
Add a deleteDragItems argument that allows the deletion of the
items in the drag as oppposed to the regular behavior of returning
them to their source model
This is required to reduce unpredictable behavior for drags approved
or rejected by the server
*/
void DragAndDrop::finish(bool deleteDragItems)
/*
End of tes3mp change (minor)
*/
{ {
mIsOnDragAndDrop = false; mIsOnDragAndDrop = false;
mSourceSortModel->clearDragItems(); mSourceSortModel->clearDragItems();
/*
Start of tes3mp addition
Make it possible to entirely delete the items in the drag
*/
if (deleteDragItems)
mSourceModel->removeItem(mItem, mDraggedCount);
/*
End of tes3mp addition
*/
// since mSourceView doesn't get updated in drag() // since mSourceView doesn't get updated in drag()
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();

View file

@ -31,7 +31,20 @@ namespace MWGui
void drop (ItemModel* targetModel, ItemView* targetView); void drop (ItemModel* targetModel, ItemView* targetView);
void onFrame(); void onFrame();
void finish(); /*
Start of tes3mp change (minor)
Add a deleteDragItems argument that allows the deletion of the
items in the drag as oppposed to the regular behavior of returning
them to their source model
This is required to reduce unpredictable behavior for drags approved
or rejected by the server
*/
void finish(bool deleteDragItems = false);
/*
End of tes3mp change (minor)
*/
}; };
} }

View file

@ -616,7 +616,7 @@ namespace
if (page+2 < book->pageCount()) if (page+2 < book->pageCount())
{ {
MWBase::Environment::get().getWindowManager()->playSound("book page"); MWBase::Environment::get().getWindowManager()->playSound("book page", true);
page += 2; page += 2;
updateShowingPages (); updateShowingPages ();
@ -634,7 +634,7 @@ namespace
if(page >= 2) if(page >= 2)
{ {
MWBase::Environment::get().getWindowManager()->playSound("book page"); MWBase::Environment::get().getWindowManager()->playSound("book page", true);
page -= 2; page -= 2;
updateShowingPages (); updateShowingPages ();

View file

@ -384,10 +384,19 @@ namespace MWGui
mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow); mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow);
mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete); mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete);
ContainerWindow* containerWindow = new ContainerWindow(mDragAndDrop); /*
mWindows.push_back(containerWindow); Start of tes3mp change (major)
trackWindow(containerWindow, "container");
mGuiModeStates[GM_Container] = GuiModeState({containerWindow, mInventoryWindow}); Use a member variable (mContainerWIndow) instead of a local one so
we can access it from elsewhere
*/
mContainerWindow = new ContainerWindow(mDragAndDrop);
mWindows.push_back(mContainerWindow);
trackWindow(mContainerWindow, "container");
mGuiModeStates[GM_Container] = GuiModeState({mContainerWindow, mInventoryWindow});
/*
End of tes3mp change (major)
*/
mHud = new HUD(mCustomMarkers, mDragAndDrop, mLocalMapRender); mHud = new HUD(mCustomMarkers, mDragAndDrop, mLocalMapRender);
mWindows.push_back(mHud); mWindows.push_back(mHud);
@ -1399,6 +1408,17 @@ namespace MWGui
MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }
MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; }
/*
Start of tes3mp addition
Make it possible to get the ContainerWindow from elsewhere
in the code
*/
MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; }
/*
End of tes3mp addition
*/
void WindowManager::useItem(const MWWorld::Ptr &item) void WindowManager::useItem(const MWWorld::Ptr &item)
{ {
if (mInventoryWindow) if (mInventoryWindow)
@ -2022,11 +2042,16 @@ namespace MWGui
mInventoryWindow->cycle(next); mInventoryWindow->cycle(next);
} }
void WindowManager::playSound(const std::string& soundId, float volume, float pitch) void WindowManager::playSound(const std::string& soundId, bool preventOverlapping, float volume, float pitch)
{ {
if (soundId.empty()) if (soundId.empty())
return; return;
MWBase::Environment::get().getSoundManager()->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);
MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager();
if (preventOverlapping && sndmgr->getSoundPlaying(MWWorld::Ptr(), soundId))
return;
sndmgr->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);
} }
void WindowManager::updateSpellWindow() void WindowManager::updateSpellWindow()

View file

@ -184,6 +184,17 @@ namespace MWGui
virtual MWGui::ConfirmationDialog* getConfirmationDialog(); virtual MWGui::ConfirmationDialog* getConfirmationDialog();
virtual MWGui::TradeWindow* getTradeWindow(); virtual MWGui::TradeWindow* getTradeWindow();
/*
Start of tes3mp addition
Make it possible to get the ContainerWindow from elsewhere
in the code
*/
virtual MWGui::ContainerWindow* getContainerWindow();
/*
End of tes3mp addition
*/
/// Make the player use an item, while updating GUI state accordingly /// Make the player use an item, while updating GUI state accordingly
virtual void useItem(const MWWorld::Ptr& item); virtual void useItem(const MWWorld::Ptr& item);
@ -440,7 +451,7 @@ namespace MWGui
/// Cycle to next or previous weapon /// Cycle to next or previous weapon
virtual void cycleWeapon(bool next); virtual void cycleWeapon(bool next);
virtual void playSound(const std::string& soundId, float volume = 1.f, float pitch = 1.f); virtual void playSound(const std::string& soundId, bool preventOverlapping = false, float volume = 1.f, float pitch = 1.f);
// In WindowManager for now since there isn't a VFS singleton // In WindowManager for now since there isn't a VFS singleton
virtual std::string correctIconPath(const std::string& path); virtual std::string correctIconPath(const std::string& path);
@ -510,6 +521,17 @@ namespace MWGui
DebugWindow* mDebugWindow; DebugWindow* mDebugWindow;
JailScreen* mJailScreen; JailScreen* mJailScreen;
/*
Start of tes3mp addition
Keep a pointer to the container window because of its usefulness
in multiplayer for container sync
*/
ContainerWindow* mContainerWindow;
/*
End of tes3mp addition
*/
std::vector<WindowBase*> mWindows; std::vector<WindowBase*> mWindows;
Translation::Storage& mTranslationDataStorage; Translation::Storage& mTranslationDataStorage;

View file

@ -1066,6 +1066,17 @@ namespace MWInput
return; return;
} }
/*
Start of tes3mp addition
Ignore attempts to rest if the player has not finished character generation yet
*/
if (!mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen())
return;
/*
End of tes3mp addition
*/
/* /*
Start of tes3mp addition Start of tes3mp addition

View file

@ -949,7 +949,7 @@ namespace MWMechanics
stats.setTimeToStartDrowning(fHoldBreathTime); stats.setTimeToStartDrowning(fHoldBreathTime);
} }
void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration) void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip)
{ {
bool isPlayer = (ptr == getPlayer()); bool isPlayer = (ptr == getPlayer());
@ -964,7 +964,7 @@ namespace MWMechanics
Start of tes3mp change (major) Start of tes3mp change (major)
We need DedicatedPlayers and DedicatedActors to not automatically We need DedicatedPlayers and DedicatedActors to not automatically
equip their light-emitting items, so additions conditions have been equip their light-emitting items, so additional conditions have been
added for them added for them
*/ */
if (!isPlayer && !mwmp::PlayerList::isDedicatedPlayer(ptr) && !mwmp::Main::get().getCellController()->isDedicatedActor(ptr)) if (!isPlayer && !mwmp::PlayerList::isDedicatedPlayer(ptr) && !mwmp::Main::get().getCellController()->isDedicatedActor(ptr))
@ -982,7 +982,7 @@ namespace MWMechanics
} }
} }
if (MWBase::Environment::get().getWorld()->isDark()) if (mayEquip)
{ {
if (torch != inventoryStore.end()) if (torch != inventoryStore.end())
{ {
@ -991,16 +991,11 @@ namespace MWMechanics
// For non-hostile NPCs, unequip whatever is in the left slot in favor of a light. // For non-hostile NPCs, unequip whatever is in the left slot in favor of a light.
if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name())
inventoryStore.unequipItem(*heldIter, ptr); inventoryStore.unequipItem(*heldIter, ptr);
// Also unequip twohanded weapons which conflict with anything in CarriedLeft
if (torch->getClass().canBeEquipped(*torch, ptr).first == 3)
inventoryStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, ptr);
} }
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
// If we have a torch and can equip it (left slot free, no // If we have a torch and can equip it, then equip it now.
// twohanded weapon in right slot), then equip it now.
if (heldIter == inventoryStore.end() if (heldIter == inventoryStore.end()
&& torch->getClass().canBeEquipped(*torch, ptr).first == 1) && torch->getClass().canBeEquipped(*torch, ptr).first == 1)
{ {
@ -1057,7 +1052,7 @@ namespace MWMechanics
} }
} }
void Actors::updateCrimePersuit(const MWWorld::Ptr& ptr, float duration) void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration)
{ {
MWWorld::Ptr player = getPlayer(); MWWorld::Ptr player = getPlayer();
if (ptr != player && ptr.getClass().isNpc()) if (ptr != player && ptr.getClass().isNpc())
@ -1293,6 +1288,9 @@ namespace MWMechanics
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0; if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0; if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
// show torches only when there are darkness and no precipitations
bool showTorches = MWBase::Environment::get().getWorld()->useTorches();
MWWorld::Ptr player = getPlayer(); MWWorld::Ptr player = getPlayer();
/// \todo move update logic to Actor class where appropriate /// \todo move update logic to Actor class where appropriate
@ -1438,7 +1436,7 @@ namespace MWMechanics
} }
if (iter->first.getClass().isNpc() && iter->first != player && (isLocalActor || isAIActive)) if (iter->first.getClass().isNpc() && iter->first != player && (isLocalActor || isAIActive))
updateCrimePersuit(iter->first, duration); updateCrimePursuit(iter->first, duration);
if (iter->first != player && (isLocalActor || isAIActive)) if (iter->first != player && (isLocalActor || isAIActive))
{ {
@ -1456,7 +1454,7 @@ namespace MWMechanics
updateNpc(iter->first, duration); updateNpc(iter->first, duration);
if (timerUpdateEquippedLight == 0) if (timerUpdateEquippedLight == 0)
updateEquippedLight(iter->first, updateEquippedLightInterval); updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches);
} }
} }
} }

View file

@ -39,9 +39,9 @@ namespace MWMechanics
void updateDrowning (const MWWorld::Ptr& ptr, float duration); void updateDrowning (const MWWorld::Ptr& ptr, float duration);
void updateEquippedLight (const MWWorld::Ptr& ptr, float duration); void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip);
void updateCrimePersuit (const MWWorld::Ptr& ptr, float duration); void updateCrimePursuit (const MWWorld::Ptr& ptr, float duration);
void killDeadActors (); void killDeadActors ();

View file

@ -189,7 +189,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
MWWorld::Ptr door = getNearbyDoor(actor, distance); MWWorld::Ptr door = getNearbyDoor(actor, distance);
if (door != MWWorld::Ptr()) if (door != MWWorld::Ptr() && actor.getClass().isBipedal(actor))
{ {
// note: AiWander currently does not open doors // note: AiWander currently does not open doors
if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == 0) if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == 0)
@ -224,7 +224,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
MWBase::Environment::get().getWorld()->activate(door, actor); MWBase::Environment::get().getWorld()->activate(door, actor);
} }
} }
else // any other obstacle (NPC, crate, etc.) else
{ {
mObstacleCheck.takeEvasiveAction(movement); mObstacleCheck.takeEvasiveAction(movement);
} }

View file

@ -40,7 +40,6 @@
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
#include "magiceffects.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
#include "aifollow.hpp" #include "aifollow.hpp"
@ -625,7 +624,6 @@ namespace MWMechanics
std::string texture = magicEffect->mParticle; std::string texture = magicEffect->mParticle;
// TODO: VFX are no longer active after saving/reloading the game
bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
// Note: in case of non actor, a free effect should be fine as well // Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
@ -1383,4 +1381,24 @@ namespace MWMechanics
return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(it->second)->getString(); return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(it->second)->getString();
} }
void ApplyLoopingParticlesVisitor::visit (MWMechanics::EffectKey key,
const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,
float /*magnitude*/, float /*remainingTime*/, float /*totalTime*/)
{
const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(key.mId);
const ESM::Static* castStatic;
if (!magicEffect->mHit.empty())
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
else
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");
std::string texture = magicEffect->mParticle;
bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mActor);
if (anim && loop)
anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "", texture);
}
} }

View file

@ -6,6 +6,8 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "magiceffects.hpp"
namespace ESM namespace ESM
{ {
struct Spell; struct Spell;
@ -119,6 +121,21 @@ namespace MWMechanics
bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude);
}; };
class ApplyLoopingParticlesVisitor : public EffectSourceVisitor
{
private:
MWWorld::Ptr mActor;
public:
ApplyLoopingParticlesVisitor(const MWWorld::Ptr& actor)
: mActor(actor)
{
}
virtual void visit (MWMechanics::EffectKey key,
const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,
float /*magnitude*/, float /*remainingTime*/ = -1, float /*totalTime*/ = -1);
};
} }
#endif #endif

View file

@ -218,7 +218,6 @@ void mwmp::GUIController::showCustomWindow(const mwmp::BasePlayer::GUIWindow &gu
void mwmp::GUIController::onInputBoxDone(MWGui::WindowBase *parWindow) void mwmp::GUIController::onInputBoxDone(MWGui::WindowBase *parWindow)
{ {
//MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager(); //MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "GUIController::onInputBoxDone: %s.",mInputBox->getTextInput().c_str());
Main::get().getLocalPlayer()->guiMessageBox.data = mInputBox->getTextInput(); Main::get().getLocalPlayer()->guiMessageBox.data = mInputBox->getTextInput();
Main::get().getNetworking()->getPlayerPacket(ID_GUI_MESSAGEBOX)->setPlayer(Main::get().getLocalPlayer()); Main::get().getNetworking()->getPlayerPacket(ID_GUI_MESSAGEBOX)->setPlayer(Main::get().getLocalPlayer());

View file

@ -205,16 +205,8 @@ void LocalActor::updateEquipment(bool forceUpdate)
item.refId = cellRef.getRefId(); item.refId = cellRef.getRefId();
item.charge = cellRef.getCharge(); item.charge = cellRef.getCharge();
if (slot == MWWorld::InventoryStore::Slot_CarriedRight) item.enchantmentCharge = it->getCellRef().getEnchantmentCharge();
{ item.count = it->getRefData().getCount();
MWMechanics::WeaponType weaptype;
auto &_class = ptr.getClass();
MWMechanics::getActiveWeapon(_class.getCreatureStats(ptr), _class.getInventoryStore(ptr), &weaptype);
if (weaptype != MWMechanics::WeapType_Thrown)
item.count = 1;
}
else
item.count = invStore.count(cellRef.getRefId());
} }
} }
else if (!item.refId.empty()) else if (!item.refId.empty())
@ -222,7 +214,8 @@ void LocalActor::updateEquipment(bool forceUpdate)
equipmentChanged = true; equipmentChanged = true;
item.refId = ""; item.refId = "";
item.count = 0; item.count = 0;
item.charge = 0; item.charge = -1;
item.enchantmentCharge = -1;
} }
} }

View file

@ -49,6 +49,7 @@ LocalPlayer::LocalPlayer()
charGenState.isFinished = false; charGenState.isFinished = false;
difficulty = 0; difficulty = 0;
enforcedLogLevel = -1;
physicsFramerate = 60.0; physicsFramerate = 60.0;
consoleAllowed = false; consoleAllowed = false;
bedRestAllowed = true; bedRestAllowed = true;
@ -455,17 +456,7 @@ void LocalPlayer::updateEquipment(bool forceUpdate)
item.refId = it->getCellRef().getRefId(); item.refId = it->getCellRef().getRefId();
item.charge = it->getCellRef().getCharge(); item.charge = it->getCellRef().getCharge();
item.enchantmentCharge = it->getCellRef().getEnchantmentCharge(); item.enchantmentCharge = it->getCellRef().getEnchantmentCharge();
item.count = it->getRefData().getCount();
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
{
MWMechanics::WeaponType weaptype;
MWMechanics::getActiveWeapon(ptrPlayer.getClass().getCreatureStats(ptrPlayer),
ptrPlayer.getClass().getInventoryStore(ptrPlayer), &weaptype);
if (weaptype != MWMechanics::WeapType_Thrown)
item.count = 1;
}
else
item.count = invStore.count(it->getCellRef().getRefId());
} }
} }
else if (!item.refId.empty()) else if (!item.refId.empty())
@ -688,6 +679,7 @@ void LocalPlayer::addItem(const Item &item)
LOG_APPEND(Log::LOG_INFO, "- Ignored addition of invalid inventory item %s", item.refId.c_str()); LOG_APPEND(Log::LOG_INFO, "- Ignored addition of invalid inventory item %s", item.refId.c_str());
} }
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
} }
void LocalPlayer::addSpells() void LocalPlayer::addSpells()
@ -1080,6 +1072,8 @@ void LocalPlayer::setEquipment()
else else
ptrInventory.unequipSlot(slot, ptrPlayer); ptrInventory.unequipSlot(slot, ptrPlayer);
} }
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updatePlayer();
} }
void LocalPlayer::setInventory() void LocalPlayer::setInventory()

View file

@ -15,6 +15,8 @@
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwgui/container.hpp"
#include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/aifollow.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/summoning.hpp" #include "../mwmechanics/summoning.hpp"
@ -50,14 +52,20 @@ void WorldEvent::reset()
cell.blank(); cell.blank();
worldObjects.clear(); worldObjects.clear();
guid = mwmp::Main::get().getNetworking()->getLocalPlayer()->guid; guid = mwmp::Main::get().getNetworking()->getLocalPlayer()->guid;
action = BaseEvent::Action::Request;
containerSubAction = BaseEvent::ContainerSubAction::None;
} }
void WorldEvent::editContainers(MWWorld::CellStore* cellStore) void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
{ {
bool isLocalEvent = guid == Main::get().getLocalPlayer()->guid;
LOG_APPEND(Log::LOG_VERBOSE, "- isLocalEvent? %s", isLocalEvent ? "true" : "false");
for (const auto &worldObject : worldObjects) for (const auto &worldObject : worldObjects)
{ {
//LOG_APPEND(Log::LOG_VERBOSE, "- container cellRef: %s %i-%i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
//LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
@ -66,19 +74,41 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
//LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(), //LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
// ptrFound.getCellRef().getRefNum().mIndex, ptrFound.getCellRef().getMpNum()); // ptrFound.getCellRef().getRefNum().mIndex, ptrFound.getCellRef().getMpNum());
bool isCurrentContainer = false;
// If we are in a container, and it happens to be this container, keep track of that
if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container))
{
CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer;
if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex &&
currentContainer->mpNum == ptrFound.getCellRef().getMpNum())
{
isCurrentContainer = true;
}
}
MWWorld::ContainerStore& containerStore = ptrFound.getClass().getContainerStore(ptrFound); MWWorld::ContainerStore& containerStore = ptrFound.getClass().getContainerStore(ptrFound);
// If we are setting the entire contents, clear the current ones // If we are setting the entire contents, clear the current ones
if (action == BaseEvent::Action::Set) if (action == BaseEvent::Action::Set)
containerStore.clear(); containerStore.clear();
bool isLocalDrop = isLocalEvent && containerSubAction == BaseEvent::ContainerSubAction::Drop;
bool isLocalDrag = isLocalEvent && containerSubAction == BaseEvent::ContainerSubAction::Drag;
bool isLocalTakeAll = isLocalEvent && containerSubAction == BaseEvent::ContainerSubAction::TakeAll;
std::string takeAllSound = "";
MWWorld::Ptr ownerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr ownerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr();
for (const auto &containerItem : worldObject.containerItems) for (const auto &containerItem : worldObject.containerItems)
{ {
//LOG_APPEND(Log::LOG_VERBOSE, "-- containerItem cellRef: %s, count: %i, actionCount: %i",
// containerItem.refId.c_str(), containerItem.count, containerItem.actionCount);
if (containerItem.refId.find("$dynamic") != string::npos) if (containerItem.refId.find("$dynamic") != string::npos)
continue; continue;
if (action == BaseEvent::Action::Add || action == BaseEvent::Action::Set) if (action == BaseEvent::Action::Set || action == BaseEvent::Action::Add)
{ {
// Create a ManualRef to be able to set item charge // Create a ManualRef to be able to set item charge
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), containerItem.refId, 1); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), containerItem.refId, 1);
@ -95,28 +125,53 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
containerStore.add(newPtr, containerItem.count, ownerPtr, true); containerStore.add(newPtr, containerItem.count, ownerPtr, true);
} }
else if (action == BaseEvent::Action::Remove) else if (action == BaseEvent::Action::Remove && containerItem.actionCount > 0)
{ {
// We have to find the right item ourselves because ContainerStore has no method // We have to find the right item ourselves because ContainerStore has no method
// accounting for charge // accounting for charge
for (const auto ptr : containerStore) for (const auto itemPtr : containerStore)
{ {
if (Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), containerItem.refId)) if (Misc::StringUtils::ciEqual(itemPtr.getCellRef().getRefId(), containerItem.refId))
{ {
if (ptr.getRefData().getCount() == containerItem.count && if (itemPtr.getCellRef().getCharge() == containerItem.charge &&
ptr.getCellRef().getCharge() == containerItem.charge && itemPtr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge)
ptr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge)
{ {
// Store the sound of the first item in a TAKE_ALL
if (isLocalTakeAll && takeAllSound.empty())
takeAllSound = itemPtr.getClass().getUpSoundId(itemPtr);
// Is this an actor's container? If so, unequip this item if it was equipped // Is this an actor's container? If so, unequip this item if it was equipped
if (ptrFound.getClass().isActor()) if (ptrFound.getClass().isActor())
{ {
MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound); MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound);
if (invStore.isEquipped(ptr)) if (invStore.isEquipped(itemPtr))
invStore.unequipItemQuantity(ptr, ptrFound, containerItem.count); invStore.unequipItemQuantity(itemPtr, ptrFound, containerItem.count);
} }
containerStore.remove(ptr, containerItem.actionCount, ownerPtr); bool isResolved = false;
if (isLocalDrag && isCurrentContainer)
{
MWGui::ContainerWindow* containerWindow = MWBase::Environment::get().getWindowManager()->getContainerWindow();
if (!containerWindow->isOnDragAndDrop())
{
isResolved = containerWindow->dragItemByPtr(itemPtr, containerItem.actionCount);
}
}
if (!isResolved)
{
containerStore.remove(itemPtr, containerItem.actionCount, ownerPtr);
if (isLocalDrag || isLocalTakeAll)
{
MWWorld::Ptr ptrPlayer = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWWorld::ContainerStore &playerStore = ptrPlayer.getClass().getContainerStore(ptrPlayer);
*playerStore.add(containerItem.refId, containerItem.actionCount, ptrPlayer);
}
}
} }
} }
} }
@ -132,16 +187,18 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
invStore.autoEquip(ptrFound); invStore.autoEquip(ptrFound);
} }
// If we are in a container, and it happens to be this container, update its view // If this container was open for us, update its view
if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container)) if (isCurrentContainer)
{ {
CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer; if (isLocalTakeAll)
if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex &&
currentContainer->mpNum == ptrFound.getCellRef().getMpNum())
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container, ptrFound); MWBase::Environment::get().getWindowManager()->playSound(takeAllSound);
}
else
{
MWGui::ContainerWindow* containerWindow = MWBase::Environment::get().getWindowManager()->getContainerWindow();
containerWindow->setPtr(ptrFound);
} }
} }
} }
@ -610,6 +667,41 @@ void WorldEvent::playVideo()
} }
} }
WorldObject WorldEvent::getWorldObject(const MWWorld::Ptr& ptr)
{
mwmp::WorldObject worldObject;
worldObject.refId = ptr.getCellRef().getRefId();
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
worldObject.mpNum = ptr.getCellRef().getMpNum();
return worldObject;
}
void WorldEvent::addContainerItem(mwmp::WorldObject& worldObject, const MWWorld::Ptr& itemPtr, int actionCount)
{
mwmp::ContainerItem containerItem;
containerItem.refId = itemPtr.getCellRef().getRefId();
containerItem.count = itemPtr.getRefData().getCount();
containerItem.charge = itemPtr.getCellRef().getCharge();
containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();
containerItem.actionCount = actionCount;
worldObject.containerItems.push_back(move(containerItem));
}
void WorldEvent::addEntireContainer(const MWWorld::Ptr& ptr)
{
MWWorld::ContainerStore& containerStore = ptr.getClass().getContainerStore(ptr);
mwmp::WorldObject worldObject = getWorldObject(ptr);
for (const auto itemPtr : containerStore)
{
addContainerItem(worldObject, itemPtr, itemPtr.getRefData().getCount());
}
worldObjects.push_back(move(worldObject));
}
void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer) void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer)
{ {
if (ptr.getCellRef().getRefId().find("$dynamic") != string::npos) if (ptr.getCellRef().getRefId().find("$dynamic") != string::npos)
@ -980,7 +1072,7 @@ void WorldEvent::sendScriptGlobalShort()
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_GLOBAL_SHORT)->Send(); mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_GLOBAL_SHORT)->Send();
} }
void WorldEvent::sendContainers(MWWorld::CellStore* cellStore) void WorldEvent::sendCellContainers(MWWorld::CellStore* cellStore)
{ {
reset(); reset();
cell = *cellStore->getCell(); cell = *cellStore->getCell();
@ -999,13 +1091,7 @@ void WorldEvent::sendContainers(MWWorld::CellStore* cellStore)
for (const auto &itemPtr : containerStore) for (const auto &itemPtr : containerStore)
{ {
mwmp::ContainerItem containerItem; addContainerItem(worldObject, itemPtr, 0);
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));
} }
worldObjects.push_back(move(worldObject)); worldObjects.push_back(move(worldObject));

View file

@ -40,6 +40,10 @@ namespace mwmp
void playMusic(); void playMusic();
void playVideo(); void playVideo();
WorldObject getWorldObject(const MWWorld::Ptr& ptr);
void addContainerItem(mwmp::WorldObject& worldObject, const MWWorld::Ptr& itemPtr, int actionCount);
void addEntireContainer(const MWWorld::Ptr& ptr);
void addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer = false); void addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer = false);
void addObjectSpawn(const MWWorld::Ptr& ptr); void addObjectSpawn(const MWWorld::Ptr& ptr);
void addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master); void addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master);
@ -49,6 +53,7 @@ namespace mwmp
void addObjectScale(const MWWorld::Ptr& ptr, float scale); void addObjectScale(const MWWorld::Ptr& ptr, float scale);
void addObjectState(const MWWorld::Ptr& ptr, bool objectState); void addObjectState(const MWWorld::Ptr& ptr, bool objectState);
void addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode); void addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode);
void addDoorState(const MWWorld::Ptr& ptr, int state); void addDoorState(const MWWorld::Ptr& ptr, int state);
void addMusicPlay(std::string filename); void addMusicPlay(std::string filename);
void addVideoPlay(std::string filename, bool allowSkipping); void addVideoPlay(std::string filename, bool allowSkipping);
@ -73,7 +78,7 @@ namespace mwmp
void sendScriptMemberShort(); void sendScriptMemberShort();
void sendScriptGlobalShort(); void sendScriptGlobalShort();
void sendContainers(MWWorld::CellStore* cellStore); void sendCellContainers(MWWorld::CellStore* cellStore);
private: private:
Networking *getNetworking(); Networking *getNetworking();

View file

@ -15,6 +15,7 @@ bool ActorProcessor::Process(RakNet::Packet &packet, ActorList &actorList)
{ {
RakNet::BitStream bsIn(&packet.data[1], packet.length, false); RakNet::BitStream bsIn(&packet.data[1], packet.length, false);
bsIn.Read(guid); bsIn.Read(guid);
actorList.guid = guid;
ActorPacket *myPacket = Main::get().getNetworking()->getActorPacket(packet.data[0]); ActorPacket *myPacket = Main::get().getNetworking()->getActorPacket(packet.data[0]);

View file

@ -15,6 +15,7 @@ bool WorldProcessor::Process(RakNet::Packet &packet, WorldEvent &event)
{ {
RakNet::BitStream bsIn(&packet.data[1], packet.length, false); RakNet::BitStream bsIn(&packet.data[1], packet.length, false);
bsIn.Read(guid); bsIn.Read(guid);
event.guid = guid;
WorldPacket *myPacket = Main::get().getNetworking()->getWorldPacket(packet.data[0]); WorldPacket *myPacket = Main::get().getNetworking()->getWorldPacket(packet.data[0]);

View file

@ -19,8 +19,12 @@ namespace mwmp
virtual void Do(PlayerPacket &packet, BasePlayer *player) virtual void Do(PlayerPacket &packet, BasePlayer *player)
{ {
static const int initialLogLevel = Log::GetLevel();
if (isLocal()) if (isLocal())
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_GAME_SETTINGS");
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{ {
if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Console && !player->consoleAllowed) if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Console && !player->consoleAllowed)
@ -30,6 +34,18 @@ namespace mwmp
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
} }
if (player->enforcedLogLevel > -1)
{
LOG_APPEND(Log::LOG_INFO, "- server is enforcing log level %i", player->enforcedLogLevel);
Log::Get().SetLevel(player->enforcedLogLevel);
}
else if (initialLogLevel != Log::GetLevel())
{
LOG_APPEND(Log::LOG_INFO, "- log level has been reset to initial value %i", initialLogLevel);
Log::Get().SetLevel(initialLogLevel);
}
MWBase::Environment::get().getWorld()->setPhysicsFramerate(player->physicsFramerate); MWBase::Environment::get().getWorld()->setPhysicsFramerate(player->physicsFramerate);
} }
} }

View file

@ -19,7 +19,7 @@ namespace mwmp
virtual void Do(PlayerPacket &packet, BasePlayer *player) virtual void Do(PlayerPacket &packet, BasePlayer *player)
{ {
packet.setPlayer(getLocalPlayer()); // player is 0 because myGuid will be setted after ProcessUserMyID packet.setPlayer(getLocalPlayer()); // player is 0 because myGuid will be set after ProcessUserMyID
packet.Send(serverAddr); packet.Send(serverAddr);
} }
}; };

View file

@ -17,11 +17,11 @@ namespace mwmp
{ {
BaseObjectProcessor::Do(packet, event); BaseObjectProcessor::Do(packet, event);
LOG_APPEND(Log::LOG_VERBOSE, "- action: %i", (int)event.action); LOG_APPEND(Log::LOG_VERBOSE, "- action: %i, containerSubAction: %i", (int) event.action, (int) event.containerSubAction);
// If we've received a request for information, comply with it // If we've received a request for information, comply with it
if (event.action == mwmp::BaseEvent::Action::Request) if (event.action == mwmp::BaseEvent::Action::Request)
event.sendContainers(ptrCellStore); event.sendCellContainers(ptrCellStore);
// Otherwise, edit containers based on the information received // Otherwise, edit containers based on the information received
else else
event.editContainers(ptrCellStore); event.editContainers(ptrCellStore);

View file

@ -13,7 +13,6 @@
#include <BulletCollision/CollisionDispatch/btCollisionObject.h> #include <BulletCollision/CollisionDispatch/btCollisionObject.h>
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h> #include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h> #include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h> #include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <LinearMath/btQuickprof.h> #include <LinearMath/btQuickprof.h>

View file

@ -90,6 +90,9 @@ namespace
if (ptr.getClass().isActor()) if (ptr.getClass().isActor())
rendering.addWaterRippleEmitter(ptr); rendering.addWaterRippleEmitter(ptr);
// Restore effect particles
MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr);
} }
void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,

View file

@ -9,7 +9,6 @@
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
@ -520,6 +519,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const Fall
, mSecunda("Secunda", fallback) , mSecunda("Secunda", fallback)
, mWindSpeed(0.f) , mWindSpeed(0.f)
, mIsStorm(false) , mIsStorm(false)
, mPrecipitation(false)
, mStormDirection(0,1,0) , mStormDirection(0,1,0)
, mCurrentRegion() , mCurrentRegion()
, mTimePassed(0) , mTimePassed(0)
@ -608,14 +608,11 @@ void WeatherManager::modRegion(const std::string& regionID, const std::vector<ch
} }
} }
void WeatherManager::playerTeleported() void WeatherManager::playerTeleported(const std::string& playerRegion, bool isExterior)
{ {
// If the player teleports to an outdoors cell in a new region (for instance, by travelling), the weather needs to // If the player teleports to an outdoors cell in a new region (for instance, by travelling), the weather needs to
// be changed immediately, and any transitions for the previous region discarded. // be changed immediately, and any transitions for the previous region discarded.
MWBase::World* world = MWBase::Environment::get().getWorld();
if(world->isCellExterior() || world->isCellQuasiExterior())
{ {
std::string playerRegion = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->getCell()->mRegion);
std::map<std::string, RegionWeather>::iterator it = mRegions.find(playerRegion); std::map<std::string, RegionWeather>::iterator it = mRegions.find(playerRegion);
if(it != mRegions.end() && playerRegion != mCurrentRegion) if(it != mRegions.end() && playerRegion != mCurrentRegion)
{ {
@ -625,11 +622,9 @@ void WeatherManager::playerTeleported()
} }
} }
void WeatherManager::update(float duration, bool paused) void WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior)
{ {
MWWorld::ConstPtr player = MWMechanics::getPlayer(); MWWorld::ConstPtr player = MWMechanics::getPlayer();
MWBase::World& world = *MWBase::Environment::get().getWorld();
TimeStamp time = world.getTimeStamp();
if(!paused || mFastForward) if(!paused || mFastForward)
{ {
@ -647,8 +642,7 @@ void WeatherManager::update(float duration, bool paused)
updateWeatherTransitions(duration); updateWeatherTransitions(duration);
} }
const bool exterior = (world.isCellExterior() || world.isCellQuasiExterior()); if(!isExterior)
if(!exterior)
{ {
mRendering.setSkyEnabled(false); mRendering.setSkyEnabled(false);
stopSounds(); stopSounds();
@ -660,6 +654,10 @@ void WeatherManager::update(float duration, bool paused)
mWindSpeed = mResult.mWindSpeed; mWindSpeed = mResult.mWindSpeed;
mIsStorm = mResult.mIsStorm; mIsStorm = mResult.mIsStorm;
// For some reason Ash Storm is not considered as a precipitation weather in game
mPrecipitation = !(mResult.mParticleEffect.empty() && mResult.mRainEffect.empty())
&& mResult.mParticleEffect != "meshes\\ashcloud.nif";
if (mIsStorm) if (mIsStorm)
{ {
osg::Vec3f playerPos (player.getRefData().getPosition().asVec3()); osg::Vec3f playerPos (player.getRefData().getPosition().asVec3());
@ -777,12 +775,11 @@ unsigned int WeatherManager::getWeatherID() const
return mCurrentWeather; return mCurrentWeather;
} }
bool WeatherManager::isDark() const bool WeatherManager::useTorches(float hour) const
{ {
TimeStamp time = MWBase::Environment::get().getWorld()->getTimeStamp(); bool isDark = hour < mSunriseTime || hour > mTimeSettings.mNightStart - 1;
bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior()
|| MWBase::Environment::get().getWorld()->isCellQuasiExterior()); return isDark && !mPrecipitation;
return exterior && (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart - 1);
} }
void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)

View file

@ -224,14 +224,14 @@ namespace MWWorld
*/ */
void changeWeather(const std::string& regionID, const unsigned int weatherID); void changeWeather(const std::string& regionID, const unsigned int weatherID);
void modRegion(const std::string& regionID, const std::vector<char>& chances); void modRegion(const std::string& regionID, const std::vector<char>& chances);
void playerTeleported(); void playerTeleported(const std::string& playerRegion, bool isExterior);
/** /**
* Per-frame update * Per-frame update
* @param duration * @param duration
* @param paused * @param paused
*/ */
void update(float duration, bool paused = false); void update(float duration, bool paused, const TimeStamp& time, bool isExterior);
void stopSounds(); void stopSounds();
@ -246,8 +246,7 @@ namespace MWWorld
unsigned int getWeatherID() const; unsigned int getWeatherID() const;
/// @see World::isDark bool useTorches(float hour) const;
bool isDark() const;
void write(ESM::ESMWriter& writer, Loading::Listener& progress); void write(ESM::ESMWriter& writer, Loading::Listener& progress);
@ -281,6 +280,7 @@ namespace MWWorld
float mWindSpeed; float mWindSpeed;
bool mIsStorm; bool mIsStorm;
bool mPrecipitation;
osg::Vec3f mStormDirection; osg::Vec3f mStormDirection;
std::string mCurrentRegion; std::string mCurrentRegion;

View file

@ -2359,6 +2359,8 @@ namespace MWWorld
model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS()); model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS());
mPhysics->remove(getPlayerPtr()); mPhysics->remove(getPlayerPtr());
mPhysics->addActor(getPlayerPtr(), model); mPhysics->addActor(getPlayerPtr(), model);
applyLoopingParticles(player);
} }
int World::canRest () int World::canRest ()
@ -3019,6 +3021,19 @@ namespace MWWorld
mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection); mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection);
} }
void World::applyLoopingParticles(const MWWorld::Ptr& ptr)
{
const MWWorld::Class &cls = ptr.getClass();
if (cls.isActor())
{
MWMechanics::ApplyLoopingParticlesVisitor visitor(ptr);
cls.getCreatureStats(ptr).getActiveSpells().visitEffectSources(visitor);
cls.getCreatureStats(ptr).getSpells().visitEffectSources(visitor);
if (cls.hasInventoryStore(ptr))
cls.getInventoryStore(ptr).visitEffectSources(visitor);
}
}
const std::vector<std::string>& World::getContentFiles() const const std::vector<std::string>& World::getContentFiles() const
{ {
return mContentFiles; return mContentFiles;
@ -3035,11 +3050,17 @@ namespace MWWorld
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor); MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor);
} }
bool World::isDark() const bool World::useTorches() const
{ {
// If we are in exterior, check the weather manager.
// In interiors there are no precipitations and sun, so check the ambient
// Looks like pseudo-exteriors considered as interiors in this case
MWWorld::CellStore* cell = mPlayer->getPlayer().getCell(); MWWorld::CellStore* cell = mPlayer->getPlayer().getCell();
if (cell->isExterior()) if (cell->isExterior())
return mWeatherManager->isDark(); {
float hour = getTimeStamp().getHour();
return mWeatherManager->useTorches(hour);
}
else else
{ {
uint32_t ambient = cell->getCell()->mAmbi.mAmbient; uint32_t ambient = cell->getCell()->mAmbi.mAmbient;
@ -3200,13 +3221,17 @@ namespace MWWorld
void World::updateWeather(float duration, bool paused) void World::updateWeather(float duration, bool paused)
{ {
bool isExterior = isCellExterior() || isCellQuasiExterior();
if (mPlayer->wasTeleported()) if (mPlayer->wasTeleported())
{ {
mPlayer->setTeleported(false); mPlayer->setTeleported(false);
mWeatherManager->playerTeleported();
const std::string playerRegion = Misc::StringUtils::lowerCase(getPlayerPtr().getCell()->getCell()->mRegion);
mWeatherManager->playerTeleported(playerRegion, isExterior);
} }
mWeatherManager->update(duration, paused); const TimeStamp time = getTimeStamp();
mWeatherManager->update(duration, paused, time, isExterior);
} }
struct AddDetectedReferenceVisitor struct AddDetectedReferenceVisitor

View file

@ -658,12 +658,13 @@ namespace MWWorld
void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) override; const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) override;
void applyLoopingParticles(const MWWorld::Ptr& ptr);
const std::vector<std::string>& getContentFiles() const override; const std::vector<std::string>& getContentFiles() const override;
void breakInvisibility (const MWWorld::Ptr& actor) override; void breakInvisibility (const MWWorld::Ptr& actor) override;
// Are we in an exterior or pseudo-exterior cell and it's night?
bool isDark() const override; // Allow NPCs to use torches?
bool useTorches() const override;
bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) override; bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) override;

View file

@ -1,7 +1,7 @@
find_package(GTest REQUIRED) find_package(GTest REQUIRED)
if (GTEST_FOUND) if (GTEST_FOUND)
include_directories(${GTEST_INCLUDE_DIRS}) include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
file(GLOB UNITTEST_SRC_FILES file(GLOB UNITTEST_SRC_FILES
../openmw/mwworld/store.cpp ../openmw/mwworld/store.cpp

View file

@ -1,5 +1,7 @@
#include "fallback.hpp" #include "fallback.hpp"
#include <iostream>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@ -17,6 +19,7 @@ namespace Fallback
std::map<std::string,std::string>::const_iterator it; std::map<std::string,std::string>::const_iterator it;
if((it = mFallbackMap.find(fall)) == mFallbackMap.end()) if((it = mFallbackMap.find(fall)) == mFallbackMap.end())
{ {
std::cerr << "Warning: fallback value " << fall << " not found." << std::endl;
return ""; return "";
} }
return it->second; return it->second;
@ -25,7 +28,7 @@ namespace Fallback
float Map::getFallbackFloat(const std::string& fall) const float Map::getFallbackFloat(const std::string& fall) const
{ {
std::string fallback=getFallbackString(fall); std::string fallback=getFallbackString(fall);
if(fallback.empty()) if (fallback.empty())
return 0; return 0;
else else
return boost::lexical_cast<float>(fallback); return boost::lexical_cast<float>(fallback);
@ -34,7 +37,7 @@ namespace Fallback
int Map::getFallbackInt(const std::string& fall) const int Map::getFallbackInt(const std::string& fall) const
{ {
std::string fallback=getFallbackString(fall); std::string fallback=getFallbackString(fall);
if(fallback.empty()) if (fallback.empty())
return 0; return 0;
else else
return std::stoi(fallback); return std::stoi(fallback);
@ -43,7 +46,7 @@ namespace Fallback
bool Map::getFallbackBool(const std::string& fall) const bool Map::getFallbackBool(const std::string& fall) const
{ {
std::string fallback=getFallbackString(fall); std::string fallback=getFallbackString(fall);
if(fallback.empty()) if (fallback.empty())
return false; return false;
else else
return stob(fallback); return stob(fallback);
@ -52,8 +55,8 @@ namespace Fallback
osg::Vec4f Map::getFallbackColour(const std::string& fall) const osg::Vec4f Map::getFallbackColour(const std::string& fall) const
{ {
std::string sum=getFallbackString(fall); std::string sum=getFallbackString(fall);
if(sum.empty()) if (sum.empty())
return osg::Vec4f(0.f,0.f,0.f,1.f); return osg::Vec4f(0.5f,0.5f,0.5f,1.f);
else else
{ {
std::string ret[3]; std::string ret[3];

View file

@ -85,6 +85,14 @@ namespace mwmp
Request Request
}; };
enum class ContainerSubAction : uint8_t
{
None = 0,
Drag,
Drop,
TakeAll
};
RakNet::RakNetGUID guid; RakNet::RakNetGUID guid;
std::vector<WorldObject> worldObjects; std::vector<WorldObject> worldObjects;
@ -93,6 +101,7 @@ namespace mwmp
std::string consoleCommand; std::string consoleCommand;
Action action; Action action;
ContainerSubAction containerSubAction;
bool isValid; bool isValid;
}; };

View file

@ -303,6 +303,7 @@ namespace mwmp
} weather; } weather;
int difficulty; int difficulty;
int enforcedLogLevel;
float physicsFramerate; float physicsFramerate;
bool consoleAllowed; bool consoleAllowed;
bool bedRestAllowed; bool bedRestAllowed;

View file

@ -72,6 +72,11 @@ public:
return instance; return instance;
} }
static int GetLevel()
{
return Log::Get().logLevel;
}
void SetLevel(int level) void SetLevel(int level)
{ {
logLevel = level; logLevel = level;

View file

@ -14,9 +14,10 @@ void PacketGameSettings::Packet(RakNet::BitStream *bs, bool send)
PlayerPacket::Packet(bs, send); PlayerPacket::Packet(bs, send);
RW(player->difficulty, send); RW(player->difficulty, send);
RW(player->physicsFramerate, send);
RW(player->consoleAllowed, send); RW(player->consoleAllowed, send);
RW(player->bedRestAllowed, send); RW(player->bedRestAllowed, send);
RW(player->wildernessRestAllowed, send); RW(player->wildernessRestAllowed, send);
RW(player->waitAllowed, send); RW(player->waitAllowed, send);
RW(player->enforcedLogLevel, send);
RW(player->physicsFramerate, send);
} }

View file

@ -16,6 +16,7 @@ void PacketContainer::Packet(RakNet::BitStream *bs, bool send)
return; return;
RW(event->action, send); RW(event->action, send);
RW(event->containerSubAction, send);
for (auto &&worldObject : event->worldObjects) for (auto &&worldObject : event->worldObjects)
{ {