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
sol2-server-rewrite
David Cernat 6 years ago
commit 3d5860d6f4

@ -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
/// 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;
font.setPointSize (12);

@ -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)
{
lua.getState()->new_usertype<Container>("Container",

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

@ -22,6 +22,8 @@ public:
void setDifficulty(int difficulty);
void setEnforcedLogLevel(int logLevel);
void setPhysicsFramerate(double physicsFramerate);
void setBedRestAllowed(bool state);
@ -33,4 +35,4 @@ public:
private:
void processUpdate() final;
};
};

@ -30,8 +30,10 @@ namespace mwmp
if (serverCell != nullptr)
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 containers = objCtrl.copyContainers(event);

@ -145,6 +145,17 @@ namespace MWBase
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 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
virtual void useItem(const MWWorld::Ptr& item) = 0;
@ -411,7 +422,7 @@ namespace MWBase
/// Cycle to next or previous weapon
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
virtual std::string correctIconPath(const std::string& path) = 0;

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

@ -233,24 +233,6 @@ namespace MWClass
if (!(ref->mBase->mData.mFlags & ESM::Light::Carry))
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,"");
}

@ -892,7 +892,7 @@ protected:
public:
typedef TypesetBookImpl::StyleImpl Style;
typedef std::map <TextFormat::Id, TextFormat*> ActiveTextFormats;
typedef std::map <TextFormat::Id, std::unique_ptr<TextFormat>> ActiveTextFormats;
int mViewTop;
int mViewBottom;
@ -1048,7 +1048,7 @@ public:
{
if (mNode != NULL)
i->second->destroyDrawItem (mNode);
delete i->second;
i->second.reset();
}
mActiveTextFormats.clear ();
@ -1115,11 +1115,11 @@ public:
if (j == this_->mActiveTextFormats.end ())
{
TextFormat * textFormat = new TextFormat (Font, this_);
std::unique_ptr<TextFormat> textFormat(new TextFormat (Font, this_));
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;

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

@ -115,35 +115,35 @@ namespace MWGui
worldEvent->reset();
worldEvent->cell = *mPtr.getCell()->getCell();
worldEvent->action = mwmp::BaseEvent::Action::Remove;
worldEvent->containerSubAction = mwmp::BaseEvent::ContainerSubAction::Drag;
mwmp::WorldObject worldObject;
worldObject.refId = mPtr.getCellRef().getRefId();
worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex;
worldObject.mpNum = mPtr.getCellRef().getMpNum();
mwmp::WorldObject worldObject = worldEvent->getWorldObject(mPtr);
MWWorld::Ptr itemPtr = mModel->getItem(mSelectedItem).mBase;
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 = 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->addContainerItem(worldObject, itemPtr, count);
worldEvent->worldObjects.push_back(std::move(worldObject));
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\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
*/
/*
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);
}
@ -162,39 +162,47 @@ namespace MWGui
worldEvent->reset();
worldEvent->cell = *mPtr.getCell()->getCell();
worldEvent->action = mwmp::BaseEvent::Action::Add;
worldEvent->containerSubAction = mwmp::BaseEvent::ContainerSubAction::Drop;
mwmp::WorldObject worldObject;
worldObject.refId = mPtr.getCellRef().getRefId();
worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex;
worldObject.mpNum = mPtr.getCellRef().getMpNum();
mwmp::WorldObject worldObject = worldEvent->getWorldObject(mPtr);
MWWorld::Ptr itemPtr = mDragAndDrop->mItem.mBase;
mwmp::ContainerItem containerItem;
containerItem.refId = itemPtr.getCellRef().getRefId();
// Make sure we get the drag and drop count, not the count of the original item
containerItem.count = mDragAndDrop->mDraggedCount;
containerItem.charge = itemPtr.getCellRef().getCharge();
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));
worldEvent->worldObjects.push_back(std::move(worldObject));
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\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
*/
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()
@ -283,6 +291,40 @@ namespace MWGui
if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop)
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
ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel();
mModel->update();
@ -322,33 +364,6 @@ namespace MWGui
}
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)
@ -392,4 +407,48 @@ namespace MWGui
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
*/
}

@ -41,6 +41,29 @@ namespace MWGui
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:
DragAndDrop* mDragAndDrop;

@ -127,10 +127,35 @@ void DragAndDrop::onFrame()
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;
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()
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();

@ -31,7 +31,20 @@ namespace MWGui
void drop (ItemModel* targetModel, ItemView* targetView);
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)
*/
};
}

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

@ -384,10 +384,19 @@ namespace MWGui
mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow);
mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete);
ContainerWindow* containerWindow = new ContainerWindow(mDragAndDrop);
mWindows.push_back(containerWindow);
trackWindow(containerWindow, "container");
mGuiModeStates[GM_Container] = GuiModeState({containerWindow, mInventoryWindow});
/*
Start of tes3mp change (major)
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);
mWindows.push_back(mHud);
@ -1399,6 +1408,17 @@ namespace MWGui
MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }
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)
{
if (mInventoryWindow)
@ -2022,11 +2042,16 @@ namespace MWGui
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())
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()

@ -184,6 +184,17 @@ namespace MWGui
virtual MWGui::ConfirmationDialog* getConfirmationDialog();
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
virtual void useItem(const MWWorld::Ptr& item);
@ -440,7 +451,7 @@ namespace MWGui
/// Cycle to next or previous weapon
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
virtual std::string correctIconPath(const std::string& path);
@ -510,6 +521,17 @@ namespace MWGui
DebugWindow* mDebugWindow;
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;
Translation::Storage& mTranslationDataStorage;

@ -1066,6 +1066,17 @@ namespace MWInput
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

@ -949,7 +949,7 @@ namespace MWMechanics
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());
@ -964,7 +964,7 @@ namespace MWMechanics
Start of tes3mp change (major)
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
*/
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())
{
@ -991,16 +991,11 @@ namespace MWMechanics
// 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())
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);
// If we have a torch and can equip it (left slot free, no
// twohanded weapon in right slot), then equip it now.
// If we have a torch and can equip it, then equip it now.
if (heldIter == inventoryStore.end()
&& 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();
if (ptr != player && ptr.getClass().isNpc())
@ -1293,6 +1288,9 @@ namespace MWMechanics
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 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();
/// \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))
updateCrimePersuit(iter->first, duration);
updateCrimePursuit(iter->first, duration);
if (iter->first != player && (isLocalActor || isAIActive))
{
@ -1456,7 +1454,7 @@ namespace MWMechanics
updateNpc(iter->first, duration);
if (timerUpdateEquippedLight == 0)
updateEquippedLight(iter->first, updateEquippedLightInterval);
updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches);
}
}
}

@ -39,9 +39,9 @@ namespace MWMechanics
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 ();

@ -189,7 +189,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
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
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);
}
}
else // any other obstacle (NPC, crate, etc.)
else
{
mObstacleCheck.takeEvasiveAction(movement);
}

@ -40,7 +40,6 @@
#include "../mwrender/animation.hpp"
#include "magiceffects.hpp"
#include "npcstats.hpp"
#include "actorutil.hpp"
#include "aifollow.hpp"
@ -625,7 +624,6 @@ namespace MWMechanics
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;
// Note: in case of non actor, a free effect should be fine as well
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();
}
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);
}
}

@ -6,6 +6,8 @@
#include "../mwworld/ptr.hpp"
#include "magiceffects.hpp"
namespace ESM
{
struct Spell;
@ -119,6 +121,21 @@ namespace MWMechanics
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

@ -218,7 +218,6 @@ void mwmp::GUIController::showCustomWindow(const mwmp::BasePlayer::GUIWindow &gu
void mwmp::GUIController::onInputBoxDone(MWGui::WindowBase *parWindow)
{
//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().getNetworking()->getPlayerPacket(ID_GUI_MESSAGEBOX)->setPlayer(Main::get().getLocalPlayer());

@ -205,16 +205,8 @@ void LocalActor::updateEquipment(bool forceUpdate)
item.refId = cellRef.getRefId();
item.charge = cellRef.getCharge();
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
{
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());
item.enchantmentCharge = it->getCellRef().getEnchantmentCharge();
item.count = it->getRefData().getCount();
}
}
else if (!item.refId.empty())
@ -222,7 +214,8 @@ void LocalActor::updateEquipment(bool forceUpdate)
equipmentChanged = true;
item.refId = "";
item.count = 0;
item.charge = 0;
item.charge = -1;
item.enchantmentCharge = -1;
}
}

@ -49,6 +49,7 @@ LocalPlayer::LocalPlayer()
charGenState.isFinished = false;
difficulty = 0;
enforcedLogLevel = -1;
physicsFramerate = 60.0;
consoleAllowed = false;
bedRestAllowed = true;
@ -455,17 +456,7 @@ void LocalPlayer::updateEquipment(bool forceUpdate)
item.refId = it->getCellRef().getRefId();
item.charge = it->getCellRef().getCharge();
item.enchantmentCharge = it->getCellRef().getEnchantmentCharge();
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());
item.count = it->getRefData().getCount();
}
}
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());
}
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
}
void LocalPlayer::addSpells()
@ -1080,6 +1072,8 @@ void LocalPlayer::setEquipment()
else
ptrInventory.unequipSlot(slot, ptrPlayer);
}
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updatePlayer();
}
void LocalPlayer::setInventory()

@ -15,6 +15,8 @@
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwgui/container.hpp"
#include "../mwmechanics/aifollow.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/summoning.hpp"
@ -50,14 +52,20 @@ void WorldEvent::reset()
cell.blank();
worldObjects.clear();
guid = mwmp::Main::get().getNetworking()->getLocalPlayer()->guid;
action = BaseEvent::Action::Request;
containerSubAction = BaseEvent::ContainerSubAction::None;
}
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)
{
//LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
//LOG_APPEND(Log::LOG_VERBOSE, "- container cellRef: %s %i-%i", worldObject.refId.c_str(), 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(),
// 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);
// If we are setting the entire contents, clear the current ones
if (action == BaseEvent::Action::Set)
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();
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)
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
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);
}
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
// 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 &&
ptr.getCellRef().getCharge() == containerItem.charge &&
ptr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge)
if (itemPtr.getCellRef().getCharge() == containerItem.charge &&
itemPtr.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
if (ptrFound.getClass().isActor())
{
MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound);
if (invStore.isEquipped(ptr))
invStore.unequipItemQuantity(ptr, ptrFound, containerItem.count);
if (invStore.isEquipped(itemPtr))
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);
}
// If we are in a container, and it happens to be this container, update its view
if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container))
// If this container was open for us, update its view
if (isCurrentContainer)
{
CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer;
if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex &&
currentContainer->mpNum == ptrFound.getCellRef().getMpNum())
if (isLocalTakeAll)
{
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)
{
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();
}
void WorldEvent::sendContainers(MWWorld::CellStore* cellStore)
void WorldEvent::sendCellContainers(MWWorld::CellStore* cellStore)
{
reset();
cell = *cellStore->getCell();
@ -999,13 +1091,7 @@ void WorldEvent::sendContainers(MWWorld::CellStore* cellStore)
for (const auto &itemPtr : containerStore)
{
mwmp::ContainerItem containerItem;
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));
addContainerItem(worldObject, itemPtr, 0);
}
worldObjects.push_back(move(worldObject));

@ -40,6 +40,10 @@ namespace mwmp
void playMusic();
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 addObjectSpawn(const MWWorld::Ptr& ptr);
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 addObjectState(const MWWorld::Ptr& ptr, bool objectState);
void addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode);
void addDoorState(const MWWorld::Ptr& ptr, int state);
void addMusicPlay(std::string filename);
void addVideoPlay(std::string filename, bool allowSkipping);
@ -73,7 +78,7 @@ namespace mwmp
void sendScriptMemberShort();
void sendScriptGlobalShort();
void sendContainers(MWWorld::CellStore* cellStore);
void sendCellContainers(MWWorld::CellStore* cellStore);
private:
Networking *getNetworking();

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

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

@ -19,8 +19,12 @@ namespace mwmp
virtual void Do(PlayerPacket &packet, BasePlayer *player)
{
static const int initialLogLevel = Log::GetLevel();
if (isLocal())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_GAME_SETTINGS");
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Console && !player->consoleAllowed)
@ -30,6 +34,18 @@ namespace mwmp
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);
}
}

@ -19,7 +19,7 @@ namespace mwmp
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);
}
};

@ -17,12 +17,12 @@ namespace mwmp
{
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 (event.action == mwmp::BaseEvent::Action::Request)
event.sendContainers(ptrCellStore);
// Otherwise, edit containers based on the information received
event.sendCellContainers(ptrCellStore);
// Otherwise, edit containers based on the information received
else
event.editContainers(ptrCellStore);
}

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

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

@ -9,7 +9,6 @@
#include <components/fallback/fallback.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwmechanics/actorutil.hpp"
@ -520,6 +519,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const Fall
, mSecunda("Secunda", fallback)
, mWindSpeed(0.f)
, mIsStorm(false)
, mPrecipitation(false)
, mStormDirection(0,1,0)
, mCurrentRegion()
, 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
// 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);
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();
MWBase::World& world = *MWBase::Environment::get().getWorld();
TimeStamp time = world.getTimeStamp();
if(!paused || mFastForward)
{
@ -647,8 +642,7 @@ void WeatherManager::update(float duration, bool paused)
updateWeatherTransitions(duration);
}
const bool exterior = (world.isCellExterior() || world.isCellQuasiExterior());
if(!exterior)
if(!isExterior)
{
mRendering.setSkyEnabled(false);
stopSounds();
@ -660,6 +654,10 @@ void WeatherManager::update(float duration, bool paused)
mWindSpeed = mResult.mWindSpeed;
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)
{
osg::Vec3f playerPos (player.getRefData().getPosition().asVec3());
@ -777,12 +775,11 @@ unsigned int WeatherManager::getWeatherID() const
return mCurrentWeather;
}
bool WeatherManager::isDark() const
bool WeatherManager::useTorches(float hour) const
{
TimeStamp time = MWBase::Environment::get().getWorld()->getTimeStamp();
bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior()
|| MWBase::Environment::get().getWorld()->isCellQuasiExterior());
return exterior && (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart - 1);
bool isDark = hour < mSunriseTime || hour > mTimeSettings.mNightStart - 1;
return isDark && !mPrecipitation;
}
void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)

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

@ -2359,6 +2359,8 @@ namespace MWWorld
model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS());
mPhysics->remove(getPlayerPtr());
mPhysics->addActor(getPlayerPtr(), model);
applyLoopingParticles(player);
}
int World::canRest ()
@ -3019,6 +3021,19 @@ namespace MWWorld
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
{
return mContentFiles;
@ -3035,11 +3050,17 @@ namespace MWWorld
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();
if (cell->isExterior())
return mWeatherManager->isDark();
{
float hour = getTimeStamp().getHour();
return mWeatherManager->useTorches(hour);
}
else
{
uint32_t ambient = cell->getCell()->mAmbi.mAmbient;
@ -3200,13 +3221,17 @@ namespace MWWorld
void World::updateWeather(float duration, bool paused)
{
bool isExterior = isCellExterior() || isCellQuasiExterior();
if (mPlayer->wasTeleported())
{
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

@ -658,12 +658,13 @@ namespace MWWorld
void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
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;
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;

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

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

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

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

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

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

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

Loading…
Cancel
Save