1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-10-04 22:26:30 +00:00

Add OpenMW commits up to 2 Jan 2020

# Conflicts:
#	apps/openmw/mwmechanics/enchanting.cpp
#	apps/openmw/mwworld/scene.cpp
This commit is contained in:
David Cernat 2020-01-02 22:09:54 +02:00
commit a58f09fd6c
29 changed files with 420 additions and 76 deletions

View file

@ -189,6 +189,7 @@
Feature #2229: Improve pathfinding AI Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls Feature #3025: Analogue gamepad movement controls
Feature #3442: Default values for fallbacks from ini file Feature #3442: Default values for fallbacks from ini file
Feature #3517: Multiple projectiles enchantment
Feature #3610: Option to invert X axis Feature #3610: Option to invert X axis
Feature #3871: Editor: Terrain Selection Feature #3871: Editor: Terrain Selection
Feature #3893: Implicit target for "set" function in console Feature #3893: Implicit target for "set" function in console
@ -235,6 +236,7 @@
Feature #5147: Show spell magicka cost in spell buying window Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection Feature #5170: Editor: Land shape editing, land selection
Feature #5193: Weapon sheathing Feature #5193: Weapon sheathing
Feature #5219: Impelement TestCells console command
Feature #5224: Handle NiKeyframeController for NiTriShape Feature #5224: Handle NiKeyframeController for NiTriShape
Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4686: Upgrade media decoder to a more current FFmpeg API
Task #4695: Optimize Distant Terrain memory consumption Task #4695: Optimize Distant Terrain memory consumption

View file

@ -2,7 +2,6 @@
brew update brew update
brew outdated pkgconfig || brew upgrade pkgconfig brew outdated pkgconfig || brew upgrade pkgconfig
brew install cmake
brew install qt brew install qt
brew install ccache brew install ccache

View file

@ -375,7 +375,12 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
// The following code will run only if there is not another thread currently running it // The following code will run only if there is not another thread currently running it
CellNameLoader cellNameLoader; CellNameLoader cellNameLoader;
#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
QSet<QString> set = cellNameLoader.getCellNames(selectedFiles);
QStringList cellNamesList(set.begin(), set.end());
#else
QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles)); QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles));
#endif
std::sort(cellNamesList.begin(), cellNamesList.end()); std::sort(cellNamesList.begin(), cellNamesList.end());
emit signalLoadedCellsChanged(cellNamesList); emit signalLoadedCellsChanged(cellNamesList);
} }

View file

@ -1054,7 +1054,11 @@ void CSVDoc::View::updateWidth(bool isGrowLimit, int minSubViewWidth)
if (isGrowLimit) if (isGrowLimit)
rect = dw->screenGeometry(this); rect = dw->screenGeometry(this);
else else
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
rect = QGuiApplication::screens().at(dw->screenNumber(this))->geometry(); rect = QGuiApplication::screens().at(dw->screenNumber(this))->geometry();
#else
rect = dw->screenGeometry(dw->screen(dw->screenNumber(this)));
#endif
if (!mScrollbarOnly && mScroll && mSubViews.size() > 1) if (!mScrollbarOnly && mScroll && mSubViews.size() > 1)
{ {

View file

@ -119,6 +119,9 @@ namespace MWBase
virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0; virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0;
virtual void testExteriorCells() = 0;
virtual void testInteriorCells() = 0;
virtual void useDeathCamera() = 0; virtual void useDeathCamera() = 0;
virtual void setWaterHeight(const float height) = 0; virtual void setWaterHeight(const float height) = 0;

View file

@ -46,7 +46,7 @@ namespace MWClass
class DoorCustomData : public MWWorld::CustomData class DoorCustomData : public MWWorld::CustomData
{ {
public: public:
MWWorld::DoorState mDoorState; MWWorld::DoorState mDoorState = MWWorld::DoorState::Idle;
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
@ -435,8 +435,6 @@ namespace MWClass
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::unique_ptr<DoorCustomData> data(new DoorCustomData); std::unique_ptr<DoorCustomData> data(new DoorCustomData);
data->mDoorState = MWWorld::DoorState::Idle;
ptr.getRefData().setCustomData(data.release()); ptr.getRefData().setCustomData(data.release());
} }
} }

View file

@ -1,6 +1,7 @@
#include "enchanting.hpp" #include "enchanting.hpp"
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -75,7 +76,6 @@ namespace MWMechanics
const MWWorld::Ptr& player = getPlayer(); const MWWorld::Ptr& player = getPlayer();
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
ESM::Enchantment enchantment; ESM::Enchantment enchantment;
enchantment.mData.mCharge = getGemCharge();
enchantment.mData.mAutocalc = 0; enchantment.mData.mAutocalc = 0;
enchantment.mData.mType = mCastStyle; enchantment.mData.mType = mCastStyle;
enchantment.mData.mCost = getBaseCastCost(); enchantment.mData.mCost = getBaseCastCost();
@ -94,14 +94,21 @@ namespace MWMechanics
mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2); mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);
} }
if(mCastStyle==ESM::Enchantment::ConstantEffect)
{
enchantment.mData.mCharge=0;
}
enchantment.mEffects = mEffectList; enchantment.mEffects = mEffectList;
int count = getEnchantItemsCount();
if(mCastStyle==ESM::Enchantment::ConstantEffect)
enchantment.mData.mCharge = 0;
else
enchantment.mData.mCharge = getGemCharge() / count;
// Try to find a dynamic enchantment with the same stats, create a new one if not found.
const ESM::Enchantment* enchantmentPtr = getRecord(enchantment);
if (enchantmentPtr == nullptr)
enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
// Apply the enchantment // Apply the enchantment
const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
/* /*
Start of tes3mp change (major) Start of tes3mp change (major)
@ -117,7 +124,7 @@ namespace MWMechanics
*/ */
mwmp::Main::get().getNetworking()->getWorldstate()->sendEnchantmentRecord(enchantmentPtr); mwmp::Main::get().getNetworking()->getWorldstate()->sendEnchantmentRecord(enchantmentPtr);
store.remove(mOldItemPtr, 1, player); store.remove(mOldItemPtr, count, player);
if(!mSelfEnchanting) if(!mSelfEnchanting)
payForEnchantment(); payForEnchantment();
@ -152,20 +159,22 @@ namespace MWMechanics
} }
else if (mWeaponType != -1) else if (mWeaponType != -1)
{ // Weapon { // Weapon
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;
switch(mCastStyle) switch(mCastStyle)
{ {
case ESM::Enchantment::WhenStrikes: case ESM::Enchantment::WhenStrikes:
mCastStyle = ESM::Enchantment::WhenUsed; if (weapclass == ESM::WeaponType::Melee || weapclass == ESM::WeaponType::Ranged)
mCastStyle = ESM::Enchantment::WhenUsed;
return; return;
case ESM::Enchantment::WhenUsed: case ESM::Enchantment::WhenUsed:
if (powerfulSoul) if (powerfulSoul && weapclass != ESM::WeaponType::Ammo && weapclass != ESM::WeaponType::Thrown)
mCastStyle = ESM::Enchantment::ConstantEffect; mCastStyle = ESM::Enchantment::ConstantEffect;
else if (getWeaponType(mWeaponType)->mWeaponClass != ESM::WeaponType::Ranged) else if (weapclass != ESM::WeaponType::Ranged)
mCastStyle = ESM::Enchantment::WhenStrikes; mCastStyle = ESM::Enchantment::WhenStrikes;
return; return;
default: // takes care of Constant effect too default: // takes care of Constant effect too
mCastStyle = ESM::Enchantment::WhenUsed; mCastStyle = ESM::Enchantment::WhenUsed;
if (getWeaponType(mWeaponType)->mWeaponClass != ESM::WeaponType::Ranged) if (weapclass != ESM::WeaponType::Ranged)
mCastStyle = ESM::Enchantment::WhenStrikes; mCastStyle = ESM::Enchantment::WhenStrikes;
return; return;
} }
@ -229,6 +238,53 @@ namespace MWMechanics
return enchantmentCost; return enchantmentCost;
} }
const ESM::Enchantment* Enchanting::getRecord(const ESM::Enchantment& toFind) const
{
const MWWorld::Store<ESM::Enchantment>& enchantments = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>();
MWWorld::Store<ESM::Enchantment>::iterator iter (enchantments.begin());
iter += (enchantments.getSize() - enchantments.getDynamicSize());
for (; iter != enchantments.end(); ++iter)
{
if (iter->mEffects.mList.size() != toFind.mEffects.mList.size())
continue;
if (iter->mData.mAutocalc != toFind.mData.mAutocalc
|| iter->mData.mType != toFind.mData.mType
|| iter->mData.mCost != toFind.mData.mCost
|| iter->mData.mCharge != toFind.mData.mCharge)
continue;
// Don't choose an ID that came from the content files, would have unintended side effects
if (!enchantments.isDynamic(iter->mId))
continue;
bool mismatch = false;
for (int i=0; i<static_cast<int> (iter->mEffects.mList.size()); ++i)
{
const ESM::ENAMstruct& first = iter->mEffects.mList[i];
const ESM::ENAMstruct& second = toFind.mEffects.mList[i];
if (first.mEffectID!=second.mEffectID ||
first.mArea!=second.mArea ||
first.mRange!=second.mRange ||
first.mSkill!=second.mSkill ||
first.mAttribute!=second.mAttribute ||
first.mMagnMin!=second.mMagnMin ||
first.mMagnMax!=second.mMagnMax ||
first.mDuration!=second.mDuration)
{
mismatch = true;
break;
}
}
if (!mismatch)
return &(*iter);
}
return nullptr;
}
int Enchanting::getBaseCastCost() const int Enchanting::getBaseCastCost() const
{ {
@ -253,6 +309,7 @@ namespace MWMechanics
float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->mValue.getFloat(); float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->mValue.getFloat();
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast<int>(getEnchantPoints() * priceMultipler), true); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast<int>(getEnchantPoints() * priceMultipler), true);
price *= getEnchantItemsCount() * getTypeMultiplier();
return price; return price;
} }
@ -311,13 +368,45 @@ namespace MWMechanics
const float fEnchantmentChanceMult = gmst.find("fEnchantmentChanceMult")->mValue.getFloat(); const float fEnchantmentChanceMult = gmst.find("fEnchantmentChanceMult")->mValue.getFloat();
const float fEnchantmentConstantChanceMult = gmst.find("fEnchantmentConstantChanceMult")->mValue.getFloat(); const float fEnchantmentConstantChanceMult = gmst.find("fEnchantmentConstantChanceMult")->mValue.getFloat();
float x = (a - getEnchantPoints()*fEnchantmentChanceMult + 0.2f * b + 0.1f * c) * stats.getFatigueTerm(); float x = (a - getEnchantPoints() * fEnchantmentChanceMult * getTypeMultiplier() * getEnchantItemsCount() + 0.2f * b + 0.1f * c) * stats.getFatigueTerm();
if (mCastStyle == ESM::Enchantment::ConstantEffect) if (mCastStyle == ESM::Enchantment::ConstantEffect)
x *= fEnchantmentConstantChanceMult; x *= fEnchantmentConstantChanceMult;
return static_cast<int>(x); return static_cast<int>(x);
} }
int Enchanting::getEnchantItemsCount() const
{
int count = 1;
float enchantPoints = getEnchantPoints();
if (mWeaponType != -1 && enchantPoints > 0)
{
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;
if (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo)
{
static const float multiplier = std::max(0.f, std::min(1.0f, Settings::Manager::getFloat("projectiles enchant multiplier", "Game")));
MWWorld::Ptr player = getPlayer();
int itemsInInventoryCount = player.getClass().getContainerStore(player).count(mOldItemPtr.getCellRef().getRefId());
count = std::min(itemsInInventoryCount, std::max(1, int(getGemCharge() * multiplier / enchantPoints)));
}
}
return count;
}
float Enchanting::getTypeMultiplier() const
{
static const bool useMultiplier = Settings::Manager::getFloat("projectiles enchant multiplier", "Game") > 0;
if (useMultiplier && mWeaponType != -1 && getEnchantPoints() > 0)
{
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;
if (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo)
return 0.125f;
}
return 1.f;
}
void Enchanting::payForEnchantment() const void Enchanting::payForEnchantment() const
{ {
const MWWorld::Ptr& player = getPlayer(); const MWWorld::Ptr& player = getPlayer();

View file

@ -4,6 +4,7 @@
#include <string> #include <string>
#include <components/esm/effectlist.hpp> #include <components/esm/effectlist.hpp>
#include <components/esm/loadench.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -25,6 +26,8 @@ namespace MWMechanics
std::string mObjectType; std::string mObjectType;
int mWeaponType; int mWeaponType;
const ESM::Enchantment* getRecord(const ESM::Enchantment& newEnchantment) const;
public: public:
Enchanting(); Enchanting();
void setEnchanter(const MWWorld::Ptr& enchanter); void setEnchanter(const MWWorld::Ptr& enchanter);
@ -45,6 +48,8 @@ namespace MWMechanics
int getMaxEnchantValue() const; int getMaxEnchantValue() const;
int getGemCharge() const; int getGemCharge() const;
int getEnchantChance() const; int getEnchantChance() const;
int getEnchantItemsCount() const;
float getTypeMultiplier() const;
bool soulEmpty() const; //Return true if empty bool soulEmpty() const; //Return true if empty
bool itemEmpty() const; //Return true if empty bool itemEmpty() const; //Return true if empty
void payForEnchantment() const; void payForEnchantment() const;

View file

@ -15,7 +15,6 @@
#include <osg/TextureCubeMap> #include <osg/TextureCubeMap>
#include <osgUtil/LineSegmentIntersector> #include <osgUtil/LineSegmentIntersector>
#include <osgUtil/IncrementalCompileOperation>
#include <osg/ImageUtils> #include <osg/ImageUtils>
@ -391,6 +390,11 @@ namespace MWRender
mWorkQueue = nullptr; mWorkQueue = nullptr;
} }
osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()
{
return mViewer->getIncrementalCompileOperation();
}
MWRender::Objects& RenderingManager::getObjects() MWRender::Objects& RenderingManager::getObjects()
{ {
return *mObjects.get(); return *mObjects.get();

View file

@ -7,6 +7,8 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <osgUtil/IncrementalCompileOperation>
#include "objects.hpp" #include "objects.hpp"
#include "renderinginterface.hpp" #include "renderinginterface.hpp"
@ -89,6 +91,8 @@ namespace MWRender
const std::string& resourcePath, DetourNavigator::Navigator& navigator); const std::string& resourcePath, DetourNavigator::Navigator& navigator);
~RenderingManager(); ~RenderingManager();
osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation();
MWRender::Objects& getObjects(); MWRender::Objects& getObjects();
Resource::ResourceSystem* getResourceSystem(); Resource::ResourceSystem* getResourceSystem();

View file

@ -23,7 +23,7 @@ class TextureOverrideVisitor : public osg::NodeVisitor
virtual void apply(osg::Node& node) virtual void apply(osg::Node& node)
{ {
int index; int index = 0;
osg::ref_ptr<osg::Node> nodePtr(&node); osg::ref_ptr<osg::Node> nodePtr(&node);
if (node.getUserValue("overrideFx", index)) if (node.getUserValue("overrideFx", index))
{ {

View file

@ -10,11 +10,13 @@
#include <components/interpreter/runtime.hpp> #include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp> #include <components/interpreter/opcodes.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/actionteleport.hpp" #include "../mwworld/actionteleport.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/player.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
@ -34,6 +36,52 @@ namespace MWScript
} }
}; };
class OpTestCells : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame)
{
runtime.getContext().report("Use TestCells from the main menu, when there is no active game session.");
return;
}
bool wasConsole = MWBase::Environment::get().getWindowManager()->isConsoleMode();
if (wasConsole)
MWBase::Environment::get().getWindowManager()->toggleConsole();
MWBase::Environment::get().getWorld()->testExteriorCells();
if (wasConsole)
MWBase::Environment::get().getWindowManager()->toggleConsole();
}
};
class OpTestInteriorCells : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame)
{
runtime.getContext().report("Use TestInteriorCells from the main menu, when there is no active game session.");
return;
}
bool wasConsole = MWBase::Environment::get().getWindowManager()->isConsoleMode();
if (wasConsole)
MWBase::Environment::get().getWindowManager()->toggleConsole();
MWBase::Environment::get().getWorld()->testInteriorCells();
if (wasConsole)
MWBase::Environment::get().getWindowManager()->toggleConsole();
}
};
class OpCOC : public Interpreter::Opcode0 class OpCOC : public Interpreter::Opcode0
{ {
public: public:
@ -204,6 +252,8 @@ namespace MWScript
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
interpreter.installSegment5 (Compiler::Cell::opcodeCellChanged, new OpCellChanged); interpreter.installSegment5 (Compiler::Cell::opcodeCellChanged, new OpCellChanged);
interpreter.installSegment5 (Compiler::Cell::opcodeTestCells, new OpTestCells);
interpreter.installSegment5 (Compiler::Cell::opcodeTestInteriorCells, new OpTestInteriorCells);
interpreter.installSegment5 (Compiler::Cell::opcodeCOC, new OpCOC); interpreter.installSegment5 (Compiler::Cell::opcodeCOC, new OpCOC);
interpreter.installSegment5 (Compiler::Cell::opcodeCOE, new OpCOE); interpreter.installSegment5 (Compiler::Cell::opcodeCOE, new OpCOE);
interpreter.installSegment5 (Compiler::Cell::opcodeGetInterior, new OpGetInterior); interpreter.installSegment5 (Compiler::Cell::opcodeGetInterior, new OpGetInterior);

View file

@ -461,5 +461,7 @@ op 0x200030a: SetNavMeshNumber
op 0x200030b: Journal, explicit op 0x200030b: Journal, explicit
op 0x200030c: RepairedOnMe op 0x200030c: RepairedOnMe
op 0x200030d: RepairedOnMe, explicit op 0x200030d: RepairedOnMe, explicit
op 0x200030e: TestCells
op 0x200030f: TestInteriorCells
opcodes 0x200030c-0x3ffffff unused opcodes 0x2000310-0x3ffffff unused

View file

@ -12,6 +12,7 @@
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/resource/bulletshape.hpp> #include <components/resource/bulletshape.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/detournavigator/navigator.hpp> #include <components/detournavigator/navigator.hpp>
#include <components/detournavigator/debug.hpp> #include <components/detournavigator/debug.hpp>
#include <components/misc/convert.hpp> #include <components/misc/convert.hpp>
@ -33,6 +34,8 @@
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwrender/renderingmanager.hpp" #include "../mwrender/renderingmanager.hpp"
#include "../mwrender/landmanager.hpp" #include "../mwrender/landmanager.hpp"
@ -216,10 +219,11 @@ namespace
{ {
MWWorld::CellStore& mCell; MWWorld::CellStore& mCell;
Loading::Listener& mLoadingListener; Loading::Listener& mLoadingListener;
bool mTest;
std::vector<MWWorld::Ptr> mToInsert; std::vector<MWWorld::Ptr> mToInsert;
InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener); InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool test);
bool operator() (const MWWorld::Ptr& ptr); bool operator() (const MWWorld::Ptr& ptr);
@ -227,8 +231,8 @@ namespace
void insert(AddObject&& addObject); void insert(AddObject&& addObject);
}; };
InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener) InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool test)
: mCell (cell), mLoadingListener (loadingListener) : mCell (cell), mLoadingListener (loadingListener), mTest(test)
{} {}
bool InsertVisitor::operator() (const MWWorld::Ptr& ptr) bool InsertVisitor::operator() (const MWWorld::Ptr& ptr)
@ -257,7 +261,8 @@ namespace
} }
} }
mLoadingListener.increaseProgress (1); if (!mTest)
mLoadingListener.increaseProgress (1);
} }
} }
@ -328,9 +333,10 @@ namespace MWWorld
mPreloader->updateCache(mRendering.getReferenceTime()); mPreloader->updateCache(mRendering.getReferenceTime());
} }
void Scene::unloadCell (CellStoreCollection::iterator iter) void Scene::unloadCell (CellStoreCollection::iterator iter, bool test)
{ {
Log(Debug::Info) << "Unloading cell " << (*iter)->getCell()->getDescription(); if (!test)
Log(Debug::Info) << "Unloading cell " << (*iter)->getCell()->getDescription();
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
ListAndResetObjectsVisitor visitor; ListAndResetObjectsVisitor visitor;
@ -405,13 +411,16 @@ namespace MWWorld
*/ */
} }
void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn) void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test)
{ {
std::pair<CellStoreCollection::iterator, bool> result = mActiveCells.insert(cell); std::pair<CellStoreCollection::iterator, bool> result = mActiveCells.insert(cell);
if(result.second) if(result.second)
{ {
Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription(); if (test)
Log(Debug::Info) << "Testing cell " << cell->getCell()->getDescription();
else
Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription();
float verts = ESM::Land::LAND_SIZE; float verts = ESM::Land::LAND_SIZE;
float worldsize = ESM::Land::REAL_SIZE; float worldsize = ESM::Land::REAL_SIZE;
@ -422,7 +431,7 @@ namespace MWWorld
const int cellY = cell->getCell()->getGridY(); const int cellY = cell->getCell()->getGridY();
// Load terrain physics first... // Load terrain physics first...
if (cell->getCell()->isExterior()) if (!test && cell->getCell()->isExterior())
{ {
osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY); osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY);
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0;
@ -450,48 +459,54 @@ namespace MWWorld
cell->respawn(); cell->respawn();
// ... then references. This is important for adjustPosition to work correctly. // ... then references. This is important for adjustPosition to work correctly.
insertCell (*cell, loadingListener); insertCell (*cell, loadingListener, test);
mRendering.addCell(cell); mRendering.addCell(cell);
MWBase::Environment::get().getWindowManager()->addCell(cell); if (!test)
bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior();
float waterLevel = cell->getWaterLevel();
mRendering.setWaterEnabled(waterEnabled);
if (waterEnabled)
{ {
mPhysics->enableWater(waterLevel); MWBase::Environment::get().getWindowManager()->addCell(cell);
mRendering.setWaterHeight(waterLevel); bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior();
float waterLevel = cell->getWaterLevel();
if (cell->getCell()->isExterior()) mRendering.setWaterEnabled(waterEnabled);
if (waterEnabled)
{ {
if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) mPhysics->enableWater(waterLevel);
navigator->addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE, mRendering.setWaterHeight(waterLevel);
cell->getWaterLevel(), heightField->getCollisionObject()->getWorldTransform());
if (cell->getCell()->isExterior())
{
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE,
cell->getWaterLevel(), heightField->getCollisionObject()->getWorldTransform());
}
else
{
navigator->addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),
cell->getWaterLevel(), btTransform::getIdentity());
}
} }
else else
mPhysics->disableWater();
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
navigator->update(player.getRefData().getPosition().asVec3());
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
{ {
navigator->addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),
cell->getWaterLevel(), btTransform::getIdentity()); mRendering.configureAmbient(cell->getCell());
} }
/*
Start of tes3mp addition
Store a cell load for the LocalPlayer
*/
mwmp::Main::get().getLocalPlayer()->storeCellState(*cell->getCell(), mwmp::CellState::LOAD);
/*
End of tes3mp addition
*/
} }
else
mPhysics->disableWater();
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
navigator->update(player.getRefData().getPosition().asVec3());
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
mRendering.configureAmbient(cell->getCell());
/*
Start of tes3mp addition
Store a cell load for the LocalPlayer
*/
mwmp::Main::get().getLocalPlayer()->storeCellState(*cell->getCell(), mwmp::CellState::LOAD);
/*
End of tes3mp addition
*/
} }
mPreloader->notifyLoaded(cell); mPreloader->notifyLoaded(cell);
@ -651,6 +666,101 @@ namespace MWWorld
mCellChanged = true; mCellChanged = true;
} }
void Scene::testExteriorCells()
{
// Note: temporary disable ICO to decrease memory usage
mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(nullptr);
mRendering.getResourceSystem()->setExpiryDelay(1.f);
const MWWorld::Store<ESM::Cell> &cells = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>();
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
loadingListener->setProgressRange(cells.getExtSize());
MWWorld::Store<ESM::Cell>::iterator it = cells.extBegin();
int i = 1;
for (; it != cells.extEnd(); ++it)
{
loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")...");
CellStoreCollection::iterator iter = mActiveCells.begin();
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY);
loadCell (cell, loadingListener, false, true);
iter = mActiveCells.begin();
while (iter != mActiveCells.end())
{
if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() &&
it->mData.mY == (*iter)->getCell()->getGridY())
{
unloadCell(iter, true);
break;
}
++iter;
}
mRendering.getResourceSystem()->updateCache(mRendering.getReferenceTime());
mRendering.getUnrefQueue()->flush(mRendering.getWorkQueue());
loadingListener->increaseProgress (1);
i++;
}
mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(mRendering.getIncrementalCompileOperation());
mRendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat("cache expiry delay", "Cells"));
}
void Scene::testInteriorCells()
{
// Note: temporary disable ICO to decrease memory usage
mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(nullptr);
mRendering.getResourceSystem()->setExpiryDelay(1.f);
const MWWorld::Store<ESM::Cell> &cells = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>();
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
loadingListener->setProgressRange(cells.getIntSize());
int i = 1;
MWWorld::Store<ESM::Cell>::iterator it = cells.intBegin();
for (; it != cells.intEnd(); ++it)
{
loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")...");
CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName);
loadCell (cell, loadingListener, false, true);
CellStoreCollection::iterator iter = mActiveCells.begin();
while (iter != mActiveCells.end())
{
assert (!(*iter)->getCell()->isExterior());
if (it->mName == (*iter)->getCell()->mName)
{
unloadCell(iter, true);
break;
}
++iter;
}
mRendering.getResourceSystem()->updateCache(mRendering.getReferenceTime());
mRendering.getUnrefQueue()->flush(mRendering.getWorkQueue());
loadingListener->increaseProgress (1);
i++;
}
mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(mRendering.getIncrementalCompileOperation());
mRendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat("cache expiry delay", "Cells"));
}
void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos)
{ {
mCurrentCell = cell; mCurrentCell = cell;
@ -831,9 +941,9 @@ namespace MWWorld
mCellChanged = false; mCellChanged = false;
} }
void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener) void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test)
{ {
InsertVisitor insertVisitor (cell, *loadingListener); InsertVisitor insertVisitor (cell, *loadingListener, test);
cell.forEach (insertVisitor); cell.forEach (insertVisitor);
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering); });
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); });

View file

@ -85,7 +85,7 @@ namespace MWWorld
osg::Vec3f mLastPlayerPos; osg::Vec3f mLastPlayerPos;
void insertCell (CellStore &cell, Loading::Listener* loadingListener); void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false);
// Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center
void changeCellGrid (int playerCellX, int playerCellY, bool changeEvent = true); void changeCellGrid (int playerCellX, int playerCellY, bool changeEvent = true);
@ -107,9 +107,9 @@ namespace MWWorld
void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false);
void preloadTerrain(const osg::Vec3f& pos); void preloadTerrain(const osg::Vec3f& pos);
void unloadCell (CellStoreCollection::iterator iter); void unloadCell (CellStoreCollection::iterator iter, bool test = false);
void loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn); void loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test = false);
void playerMoved (const osg::Vec3f& pos); void playerMoved (const osg::Vec3f& pos);
@ -151,6 +151,9 @@ namespace MWWorld
Ptr searchPtrViaActorId (int actorId); Ptr searchPtrViaActorId (int actorId);
void preload(const std::string& mesh, bool useAnim=false); void preload(const std::string& mesh, bool useAnim=false);
void testExteriorCells();
void testInteriorCells();
}; };
} }

View file

@ -792,6 +792,14 @@ namespace MWWorld
{ {
return mSharedInt.size() + mSharedExt.size(); return mSharedInt.size() + mSharedExt.size();
} }
size_t Store<ESM::Cell>::getExtSize() const
{
return mSharedExt.size();
}
size_t Store<ESM::Cell>::getIntSize() const
{
return mSharedInt.size();
}
void Store<ESM::Cell>::listIdentifier(std::vector<std::string> &list) const void Store<ESM::Cell>::listIdentifier(std::vector<std::string> &list) const
{ {
list.reserve(list.size() + mSharedInt.size()); list.reserve(list.size() + mSharedInt.size());

View file

@ -106,6 +106,11 @@ namespace MWWorld
return iter; return iter;
} }
SharedIterator &operator+=(int advance) {
mIter += advance;
return *this;
}
SharedIterator &operator--() { SharedIterator &operator--() {
--mIter; --mIter;
return *this; return *this;
@ -314,6 +319,8 @@ namespace MWWorld
const ESM::Cell *searchExtByRegion(const std::string &id) const; const ESM::Cell *searchExtByRegion(const std::string &id) const;
size_t getSize() const; size_t getSize() const;
size_t getExtSize() const;
size_t getIntSize() const;
void listIdentifier(std::vector<std::string> &list) const; void listIdentifier(std::vector<std::string> &list) const;

View file

@ -617,6 +617,16 @@ namespace MWWorld
return getInterior (id.mWorldspace); return getInterior (id.mWorldspace);
} }
void World::testExteriorCells()
{
mWorldScene->testExteriorCells();
}
void World::testInteriorCells()
{
mWorldScene->testInteriorCells();
}
void World::useDeathCamera() void World::useDeathCamera()
{ {
if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() ) if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() )

View file

@ -232,6 +232,9 @@ namespace MWWorld
CellStore *getCell (const ESM::CellId& id) override; CellStore *getCell (const ESM::CellId& id) override;
void testExteriorCells() override;
void testInteriorCells() override;
//switch to POV before showing player's death animation //switch to POV before showing player's death animation
void useDeathCamera() override; void useDeathCamera() override;

View file

@ -89,6 +89,8 @@ namespace Compiler
void registerExtensions (Extensions& extensions) void registerExtensions (Extensions& extensions)
{ {
extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged); extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged);
extensions.registerInstruction("testcells", "", opcodeTestCells);
extensions.registerInstruction("testinteriorcells", "", opcodeTestInteriorCells);
extensions.registerInstruction ("coc", "S", opcodeCOC); extensions.registerInstruction ("coc", "S", opcodeCOC);
extensions.registerInstruction ("centeroncell", "S", opcodeCOC); extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
extensions.registerInstruction ("coe", "ll", opcodeCOE); extensions.registerInstruction ("coe", "ll", opcodeCOE);

View file

@ -75,6 +75,8 @@ namespace Compiler
namespace Cell namespace Cell
{ {
const int opcodeCellChanged = 0x2000000; const int opcodeCellChanged = 0x2000000;
const int opcodeTestCells = 0x200030e;
const int opcodeTestInteriorCells = 0x200030f;
const int opcodeCOC = 0x2000026; const int opcodeCOC = 0x2000026;
const int opcodeCOE = 0x2000226; const int opcodeCOE = 0x2000226;
const int opcodeGetInterior = 0x2000131; const int opcodeGetInterior = 0x2000131;

View file

@ -30,7 +30,7 @@ namespace DetourNavigator
public: public:
struct Water struct Water
{ {
int mCellSize; int mCellSize = 0;
btTransform mTransform; btTransform mTransform;
}; };

View file

@ -168,9 +168,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool
if (mChargeInt != -1) if (mChargeInt != -1)
esm.writeHNT("INTV", mChargeInt); esm.writeHNT("INTV", mChargeInt);
if (mGoldValue != 1) { if (mGoldValue > 1)
esm.writeHNT("NAM9", mGoldValue); esm.writeHNT("NAM9", mGoldValue);
}
if (!inInventory && mTeleport) if (!inInventory && mTeleport)
{ {
@ -208,7 +207,7 @@ void ESM::CellRef::blank()
mChargeInt = -1; mChargeInt = -1;
mChargeIntRemainder = 0.0f; mChargeIntRemainder = 0.0f;
mEnchantmentCharge = -1; mEnchantmentCharge = -1;
mGoldValue = 0; mGoldValue = 1;
mDestCell.clear(); mDestCell.clear();
mLockLevel = 0; mLockLevel = 0;
mKey.clear(); mKey.clear();

View file

@ -3,6 +3,8 @@
#include "esmreader.hpp" #include "esmreader.hpp"
#include "esmwriter.hpp" #include "esmwriter.hpp"
#include <components/debug/debuglog.hpp>
namespace ESM namespace ESM
{ {
@ -12,12 +14,20 @@ namespace ESM
mDoorState = 0; mDoorState = 0;
esm.getHNOT (mDoorState, "ANIM"); esm.getHNOT (mDoorState, "ANIM");
if (mDoorState < 0 || mDoorState > 2)
Log(Debug::Warning) << "Dropping invalid door state (" << mDoorState << ") for door \"" << mRef.mRefID << "\"";
} }
void DoorState::save(ESMWriter &esm, bool inInventory) const void DoorState::save(ESMWriter &esm, bool inInventory) const
{ {
ObjectState::save(esm, inInventory); ObjectState::save(esm, inInventory);
if (mDoorState < 0 || mDoorState > 2)
{
Log(Debug::Warning) << "Dropping invalid door state (" << mDoorState << ") for door \"" << mRef.mRefID << "\"";
return;
}
if (mDoorState != 0) if (mDoorState != 0)
esm.writeHNT ("ANIM", mDoorState); esm.writeHNT ("ANIM", mDoorState);
} }

View file

@ -9,7 +9,7 @@ namespace ESM
struct DoorState : public ObjectState struct DoorState : public ObjectState
{ {
int mDoorState; int mDoorState = 0;
virtual void load (ESMReader &esm); virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const; virtual void save (ESMWriter &esm, bool inInventory = false) const;

View file

@ -261,7 +261,7 @@ void LowLevelFile::open (char const * filename)
if (handle == INVALID_HANDLE_VALUE) if (handle == INVALID_HANDLE_VALUE)
{ {
std::ostringstream os; std::ostringstream os;
os << "Failed to open '" << filename << "' for reading."; os << "Failed to open '" << filename << "' for reading: " << GetLastError();
throw std::runtime_error (os.str ()); throw std::runtime_error (os.str ());
} }

View file

@ -282,3 +282,19 @@ equivalent to the one introduced by the equivalent Morrowind Code Patch feature.
This makes the movement speed behavior more fair between different races. This makes the movement speed behavior more fair between different races.
This setting can be controlled in Advanced tab of the launcher. This setting can be controlled in Advanced tab of the launcher.
projectiles enchant multiplier
------------------------------
:Type: floating point
:Range: 0.0 to 1.0
:Default: 0.0
The value of this setting determines how many projectiles (thrown weapons, arrows and bolts) you can enchant at once according to the following formula:
count = (soul gem charge * projectiles enchant multiplier) / enchantment strength
A value of 0 means that you can only enchant one projectile.
If you want to have Morrowind Code Patch-like count of projectiles being enchanted at once, set this value to 0.25 (i.e. 25% of the charge).
This setting can only be configured by editing the settings configuration file.

View file

@ -306,6 +306,10 @@ struct dtMeshTile
int dataSize; ///< Size of the tile data. int dataSize; ///< Size of the tile data.
int flags; ///< Tile flags. (See: #dtTileFlags) int flags; ///< Tile flags. (See: #dtTileFlags)
dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid. dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid.
// OpenMW code - make dtMeshTile POD since R&D init it by memset
//private:
// dtMeshTile(const dtMeshTile&);
// dtMeshTile& operator=(const dtMeshTile&);
}; };
/// Get flags for edge in detail triangle. /// Get flags for edge in detail triangle.

View file

@ -267,6 +267,11 @@ use magic item animations = false
# Don't use race weight in NPC movement speed calculations # Don't use race weight in NPC movement speed calculations
normalise race speed = false normalise race speed = false
# Adjusts the number of projectiles you can enchant at once:
# count = (soul gem charge * projectiles enchant multiplier) / enchantment strength
# A value of 0 means that you can only enchant one projectile.
projectiles enchant multiplier = 0
[General] [General]
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).