1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-29 18:32:36 +00:00

Merge branch 'refactor_content_loader' into 'master'

Refactor MWWorld::ContentLoader and derived classes

See merge request OpenMW/openmw!1432
This commit is contained in:
psi29a 2021-11-30 15:36:19 +00:00
commit 8d1baa9ae0
9 changed files with 282 additions and 276 deletions

View File

@ -74,7 +74,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref weather projectilemanager
cellpreloader datetimemanager groundcoverstore
cellpreloader datetimemanager groundcoverstore magiceffects
)
add_openmw_dir (mwphysics

View File

@ -1,4 +1,5 @@
#include "cellstore.hpp"
#include "magiceffects.hpp"
#include <algorithm>

View File

@ -2,33 +2,20 @@
#define CONTENTLOADER_HPP
#include <boost/filesystem/path.hpp>
#include <MyGUI_TextIterator.h>
#include <components/debug/debuglog.hpp>
#include "components/loadinglistener/loadinglistener.hpp"
namespace Loading
{
class Listener;
}
namespace MWWorld
{
struct ContentLoader
{
ContentLoader(Loading::Listener& listener)
: mListener(listener)
{
}
virtual ~ContentLoader() = default;
virtual ~ContentLoader()
{
}
virtual void load(const boost::filesystem::path& filepath, int& index)
{
Log(Debug::Info) << "Loading content file " << filepath.string();
mListener.setLabel(MyGUI::TextIterator::toTagsString(filepath.string()));
}
protected:
Loading::Listener& mListener;
virtual void load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener) = 0;
};
} /* namespace MWWorld */

View File

@ -2,233 +2,27 @@
#include "esmstore.hpp"
#include <components/esm/esmreader.hpp>
#include <components/esm/npcstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/magiceffects.hpp"
namespace
{
template<class T>
void getEnchantedItem(const std::string& id, std::string& enchantment, std::string& itemName)
{
const T* item = MWBase::Environment::get().getWorld()->getStore().get<T>().search(id);
if(item)
{
enchantment = item->mEnchant;
itemName = item->mName;
}
}
}
namespace MWWorld
{
EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener)
: ContentLoader(listener)
, mEsm(readers)
, mStore(store)
, mEncoder(encoder)
ToUTF8::Utf8Encoder* encoder)
: mEsm(readers)
, mStore(store)
, mEncoder(encoder)
{
}
void EsmLoader::load(const boost::filesystem::path& filepath, int& index)
void EsmLoader::load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener)
{
ContentLoader::load(filepath.filename(), index);
ESM::ESMReader lEsm;
lEsm.setEncoder(mEncoder);
lEsm.setIndex(index);
lEsm.open(filepath.string());
lEsm.resolveParentFileIndices(mEsm);
mEsm[index] = lEsm;
mStore.load(mEsm[index], &mListener);
ESM::ESMReader lEsm;
lEsm.setEncoder(mEncoder);
lEsm.setIndex(index);
lEsm.open(filepath.string());
lEsm.resolveParentFileIndices(mEsm);
mEsm[index] = lEsm;
mStore.load(mEsm[index], listener);
}
void convertMagicEffects(ESM::CreatureStats& creatureStats, ESM::InventoryState& inventory, ESM::NpcStats* npcStats)
{
const auto& store = MWBase::Environment::get().getWorld()->getStore();
// Convert corprus to format 10
for (const auto& [id, oldStats] : creatureStats.mSpells.mCorprusSpells)
{
const ESM::Spell* spell = store.get<ESM::Spell>().search(id);
if (!spell)
continue;
ESM::CreatureStats::CorprusStats stats;
stats.mNextWorsening = oldStats.mNextWorsening;
for (int i=0; i<ESM::Attribute::Length; ++i)
stats.mWorsenings[i] = 0;
for (auto& effect : spell->mEffects.mList)
{
if (effect.mEffectID == ESM::MagicEffect::DrainAttribute)
stats.mWorsenings[effect.mAttribute] = oldStats.mWorsenings;
}
creatureStats.mCorprusSpells[id] = stats;
}
// Convert to format 17
for(const auto& [id, oldParams] : creatureStats.mSpells.mSpellParams)
{
const ESM::Spell* spell = store.get<ESM::Spell>().search(id);
if (!spell || spell->mData.mType == ESM::Spell::ST_Spell || spell->mData.mType == ESM::Spell::ST_Power)
continue;
ESM::ActiveSpells::ActiveSpellParams params;
params.mId = id;
params.mDisplayName = spell->mName;
params.mItem.unset();
params.mCasterActorId = creatureStats.mActorId;
if(spell->mData.mType == ESM::Spell::ST_Ability)
params.mType = ESM::ActiveSpells::Type_Ability;
else
params.mType = ESM::ActiveSpells::Type_Permanent;
params.mWorsenings = -1;
int effectIndex = 0;
for(const auto& enam : spell->mEffects.mList)
{
if(oldParams.mPurgedEffects.find(effectIndex) == oldParams.mPurgedEffects.end())
{
ESM::ActiveEffect effect;
effect.mEffectId = enam.mEffectID;
effect.mArg = MWMechanics::EffectKey(enam).mArg;
effect.mDuration = -1;
effect.mTimeLeft = -1;
effect.mEffectIndex = effectIndex;
auto rand = oldParams.mEffectRands.find(effectIndex);
if(rand != oldParams.mEffectRands.end())
{
float magnitude = (enam.mMagnMax - enam.mMagnMin) * rand->second + enam.mMagnMin;
effect.mMagnitude = magnitude;
effect.mMinMagnitude = magnitude;
effect.mMaxMagnitude = magnitude;
// Prevent recalculation of resistances and don't reflect or absorb the effect
effect.mFlags = ESM::ActiveEffect::Flag_Ignore_Resistances | ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption;
}
else
{
effect.mMagnitude = 0.f;
effect.mMinMagnitude = enam.mMagnMin;
effect.mMaxMagnitude = enam.mMagnMax;
effect.mFlags = ESM::ActiveEffect::Flag_None;
}
params.mEffects.emplace_back(effect);
}
effectIndex++;
}
creatureStats.mActiveSpells.mSpells.emplace_back(params);
}
std::multimap<std::string, int> equippedItems;
for(std::size_t i = 0; i < inventory.mItems.size(); ++i)
{
const ESM::ObjectState& item = inventory.mItems[i];
auto slot = inventory.mEquipmentSlots.find(i);
if(slot != inventory.mEquipmentSlots.end())
equippedItems.emplace(item.mRef.mRefID, slot->second);
}
for(const auto& [id, oldMagnitudes] : inventory.mPermanentMagicEffectMagnitudes)
{
std::string eId;
std::string name;
switch(store.find(id))
{
case ESM::REC_ARMO:
getEnchantedItem<ESM::Armor>(id, eId, name);
break;
case ESM::REC_CLOT:
getEnchantedItem<ESM::Clothing>(id, eId, name);
break;
case ESM::REC_WEAP:
getEnchantedItem<ESM::Weapon>(id, eId, name);
break;
}
if(eId.empty())
continue;
const ESM::Enchantment* enchantment = store.get<ESM::Enchantment>().search(eId);
if(!enchantment)
continue;
ESM::ActiveSpells::ActiveSpellParams params;
params.mId = id;
params.mDisplayName = name;
params.mCasterActorId = creatureStats.mActorId;
params.mType = ESM::ActiveSpells::Type_Enchantment;
params.mWorsenings = -1;
for(std::size_t effectIndex = 0; effectIndex < oldMagnitudes.size() && effectIndex < enchantment->mEffects.mList.size(); ++effectIndex)
{
const auto& enam = enchantment->mEffects.mList[effectIndex];
auto [random, multiplier] = oldMagnitudes[effectIndex];
float magnitude = (enam.mMagnMax - enam.mMagnMin) * random + enam.mMagnMin;
magnitude *= multiplier;
if(magnitude <= 0)
continue;
ESM::ActiveEffect effect;
effect.mEffectId = enam.mEffectID;
effect.mMagnitude = magnitude;
effect.mMinMagnitude = magnitude;
effect.mMaxMagnitude = magnitude;
effect.mArg = MWMechanics::EffectKey(enam).mArg;
effect.mDuration = -1;
effect.mTimeLeft = -1;
effect.mEffectIndex = static_cast<int>(effectIndex);
// Prevent recalculation of resistances and don't reflect or absorb the effect
effect.mFlags = ESM::ActiveEffect::Flag_Ignore_Resistances | ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption;
params.mEffects.emplace_back(effect);
}
auto [begin, end] = equippedItems.equal_range(id);
for(auto it = begin; it != end; ++it)
{
params.mItem = { static_cast<unsigned int>(it->second), 0 };
creatureStats.mActiveSpells.mSpells.emplace_back(params);
}
}
for(const auto& spell : creatureStats.mCorprusSpells)
{
auto it = std::find_if(creatureStats.mActiveSpells.mSpells.begin(), creatureStats.mActiveSpells.mSpells.end(), [&] (const auto& params) { return params.mId == spell.first; });
if(it != creatureStats.mActiveSpells.mSpells.end())
{
it->mNextWorsening = spell.second.mNextWorsening;
int worsenings = 0;
for(int i = 0; i < ESM::Attribute::Length; ++i)
worsenings = std::max(spell.second.mWorsenings[i], worsenings);
it->mWorsenings = worsenings;
}
}
for(const auto& [key, actorId] : creatureStats.mSummonedCreatureMap)
{
if(actorId == -1)
continue;
for(auto& params : creatureStats.mActiveSpells.mSpells)
{
if(params.mId == key.mSourceId)
{
bool found = false;
for(auto& effect : params.mEffects)
{
if(effect.mEffectId == key.mEffectId && effect.mEffectIndex == key.mEffectIndex)
{
effect.mArg = actorId;
found = true;
break;
}
}
if(found)
break;
}
}
}
// Reset modifiers that were previously recalculated each frame
for(std::size_t i = 0; i < ESM::Attribute::Length; ++i)
creatureStats.mAttributes[i].mMod = 0.f;
for(std::size_t i = 0; i < 3; ++i)
creatureStats.mDynamic[i].mMod = 0.f;
for(std::size_t i = 0; i < 4; ++i)
creatureStats.mAiSettings[i].mMod = 0.f;
if(npcStats)
{
for(std::size_t i = 0; i < ESM::Skill::Length; ++i)
npcStats->mSkills[i].mMod = 0.f;
}
}
} /* namespace MWWorld */

View File

@ -13,9 +13,6 @@ namespace ToUTF8
namespace ESM
{
class ESMReader;
struct CreatureStats;
struct InventoryState;
struct NpcStats;
}
namespace MWWorld
@ -26,18 +23,16 @@ class ESMStore;
struct EsmLoader : public ContentLoader
{
EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener);
ToUTF8::Utf8Encoder* encoder);
void load(const boost::filesystem::path& filepath, int& index) override;
void load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener) override;
private:
std::vector<ESM::ESMReader>& mEsm;
MWWorld::ESMStore& mStore;
ToUTF8::Utf8Encoder* mEncoder;
std::vector<ESM::ESMReader>& mEsm;
MWWorld::ESMStore& mStore;
ToUTF8::Utf8Encoder* mEncoder;
};
void convertMagicEffects(ESM::CreatureStats& creatureStats, ESM::InventoryState& inventory, ESM::NpcStats* npcStats = nullptr);
} /* namespace MWWorld */
#endif // ESMLOADER_HPP

View File

@ -0,0 +1,210 @@
#include "magiceffects.hpp"
#include "esmstore.hpp"
#include <components/esm/npcstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/magiceffects.hpp"
namespace
{
template<class T>
void getEnchantedItem(const std::string& id, std::string& enchantment, std::string& itemName)
{
const T* item = MWBase::Environment::get().getWorld()->getStore().get<T>().search(id);
if(item)
{
enchantment = item->mEnchant;
itemName = item->mName;
}
}
}
namespace MWWorld
{
void convertMagicEffects(ESM::CreatureStats& creatureStats, ESM::InventoryState& inventory, ESM::NpcStats* npcStats)
{
const auto& store = MWBase::Environment::get().getWorld()->getStore();
// Convert corprus to format 10
for (const auto& [id, oldStats] : creatureStats.mSpells.mCorprusSpells)
{
const ESM::Spell* spell = store.get<ESM::Spell>().search(id);
if (!spell)
continue;
ESM::CreatureStats::CorprusStats stats;
stats.mNextWorsening = oldStats.mNextWorsening;
for (int i=0; i<ESM::Attribute::Length; ++i)
stats.mWorsenings[i] = 0;
for (auto& effect : spell->mEffects.mList)
{
if (effect.mEffectID == ESM::MagicEffect::DrainAttribute)
stats.mWorsenings[effect.mAttribute] = oldStats.mWorsenings;
}
creatureStats.mCorprusSpells[id] = stats;
}
// Convert to format 17
for(const auto& [id, oldParams] : creatureStats.mSpells.mSpellParams)
{
const ESM::Spell* spell = store.get<ESM::Spell>().search(id);
if (!spell || spell->mData.mType == ESM::Spell::ST_Spell || spell->mData.mType == ESM::Spell::ST_Power)
continue;
ESM::ActiveSpells::ActiveSpellParams params;
params.mId = id;
params.mDisplayName = spell->mName;
params.mItem.unset();
params.mCasterActorId = creatureStats.mActorId;
if(spell->mData.mType == ESM::Spell::ST_Ability)
params.mType = ESM::ActiveSpells::Type_Ability;
else
params.mType = ESM::ActiveSpells::Type_Permanent;
params.mWorsenings = -1;
int effectIndex = 0;
for(const auto& enam : spell->mEffects.mList)
{
if(oldParams.mPurgedEffects.find(effectIndex) == oldParams.mPurgedEffects.end())
{
ESM::ActiveEffect effect;
effect.mEffectId = enam.mEffectID;
effect.mArg = MWMechanics::EffectKey(enam).mArg;
effect.mDuration = -1;
effect.mTimeLeft = -1;
effect.mEffectIndex = effectIndex;
auto rand = oldParams.mEffectRands.find(effectIndex);
if(rand != oldParams.mEffectRands.end())
{
float magnitude = (enam.mMagnMax - enam.mMagnMin) * rand->second + enam.mMagnMin;
effect.mMagnitude = magnitude;
effect.mMinMagnitude = magnitude;
effect.mMaxMagnitude = magnitude;
// Prevent recalculation of resistances and don't reflect or absorb the effect
effect.mFlags = ESM::ActiveEffect::Flag_Ignore_Resistances | ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption;
}
else
{
effect.mMagnitude = 0.f;
effect.mMinMagnitude = enam.mMagnMin;
effect.mMaxMagnitude = enam.mMagnMax;
effect.mFlags = ESM::ActiveEffect::Flag_None;
}
params.mEffects.emplace_back(effect);
}
effectIndex++;
}
creatureStats.mActiveSpells.mSpells.emplace_back(params);
}
std::multimap<std::string, int> equippedItems;
for(std::size_t i = 0; i < inventory.mItems.size(); ++i)
{
const ESM::ObjectState& item = inventory.mItems[i];
auto slot = inventory.mEquipmentSlots.find(i);
if(slot != inventory.mEquipmentSlots.end())
equippedItems.emplace(item.mRef.mRefID, slot->second);
}
for(const auto& [id, oldMagnitudes] : inventory.mPermanentMagicEffectMagnitudes)
{
std::string eId;
std::string name;
switch(store.find(id))
{
case ESM::REC_ARMO:
getEnchantedItem<ESM::Armor>(id, eId, name);
break;
case ESM::REC_CLOT:
getEnchantedItem<ESM::Clothing>(id, eId, name);
break;
case ESM::REC_WEAP:
getEnchantedItem<ESM::Weapon>(id, eId, name);
break;
}
if(eId.empty())
continue;
const ESM::Enchantment* enchantment = store.get<ESM::Enchantment>().search(eId);
if(!enchantment)
continue;
ESM::ActiveSpells::ActiveSpellParams params;
params.mId = id;
params.mDisplayName = name;
params.mCasterActorId = creatureStats.mActorId;
params.mType = ESM::ActiveSpells::Type_Enchantment;
params.mWorsenings = -1;
for(std::size_t effectIndex = 0; effectIndex < oldMagnitudes.size() && effectIndex < enchantment->mEffects.mList.size(); ++effectIndex)
{
const auto& enam = enchantment->mEffects.mList[effectIndex];
auto [random, multiplier] = oldMagnitudes[effectIndex];
float magnitude = (enam.mMagnMax - enam.mMagnMin) * random + enam.mMagnMin;
magnitude *= multiplier;
if(magnitude <= 0)
continue;
ESM::ActiveEffect effect;
effect.mEffectId = enam.mEffectID;
effect.mMagnitude = magnitude;
effect.mMinMagnitude = magnitude;
effect.mMaxMagnitude = magnitude;
effect.mArg = MWMechanics::EffectKey(enam).mArg;
effect.mDuration = -1;
effect.mTimeLeft = -1;
effect.mEffectIndex = static_cast<int>(effectIndex);
// Prevent recalculation of resistances and don't reflect or absorb the effect
effect.mFlags = ESM::ActiveEffect::Flag_Ignore_Resistances | ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption;
params.mEffects.emplace_back(effect);
}
auto [begin, end] = equippedItems.equal_range(id);
for(auto it = begin; it != end; ++it)
{
params.mItem = { static_cast<unsigned int>(it->second), 0 };
creatureStats.mActiveSpells.mSpells.emplace_back(params);
}
}
for(const auto& spell : creatureStats.mCorprusSpells)
{
auto it = std::find_if(creatureStats.mActiveSpells.mSpells.begin(), creatureStats.mActiveSpells.mSpells.end(), [&] (const auto& params) { return params.mId == spell.first; });
if(it != creatureStats.mActiveSpells.mSpells.end())
{
it->mNextWorsening = spell.second.mNextWorsening;
int worsenings = 0;
for(int i = 0; i < ESM::Attribute::Length; ++i)
worsenings = std::max(spell.second.mWorsenings[i], worsenings);
it->mWorsenings = worsenings;
}
}
for(const auto& [key, actorId] : creatureStats.mSummonedCreatureMap)
{
if(actorId == -1)
continue;
for(auto& params : creatureStats.mActiveSpells.mSpells)
{
if(params.mId == key.mSourceId)
{
bool found = false;
for(auto& effect : params.mEffects)
{
if(effect.mEffectId == key.mEffectId && effect.mEffectIndex == key.mEffectIndex)
{
effect.mArg = actorId;
found = true;
break;
}
}
if(found)
break;
}
}
}
// Reset modifiers that were previously recalculated each frame
for(std::size_t i = 0; i < ESM::Attribute::Length; ++i)
creatureStats.mAttributes[i].mMod = 0.f;
for(std::size_t i = 0; i < 3; ++i)
creatureStats.mDynamic[i].mMod = 0.f;
for(std::size_t i = 0; i < 4; ++i)
creatureStats.mAiSettings[i].mMod = 0.f;
if(npcStats)
{
for(std::size_t i = 0; i < ESM::Skill::Length; ++i)
npcStats->mSkills[i].mMod = 0.f;
}
}
}

View File

@ -0,0 +1,17 @@
#ifndef OPENMW_MWWORLD_MAGICEFFECTS_H
#define OPENMW_MWWORLD_MAGICEFFECTS_H
namespace ESM
{
struct CreatureStats;
struct InventoryState;
struct NpcStats;
}
namespace MWWorld
{
void convertMagicEffects(ESM::CreatureStats& creatureStats, ESM::InventoryState& inventory,
ESM::NpcStats* npcStats = nullptr);
}
#endif

View File

@ -12,6 +12,7 @@
#include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/magiceffects.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"

View File

@ -7,6 +7,8 @@
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
#include <MyGUI_TextIterator.h>
#include <components/debug/debuglog.hpp>
#include <components/esm/esmreader.hpp>
@ -30,6 +32,8 @@
#include <components/detournavigator/navigator.hpp>
#include <components/detournavigator/settings.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
@ -79,43 +83,40 @@ namespace MWWorld
{
struct GameContentLoader : public ContentLoader
{
GameContentLoader(Loading::Listener& listener)
: ContentLoader(listener)
void addLoader(std::string&& extension, ContentLoader& loader)
{
mLoaders.emplace(std::move(extension), &loader);
}
bool addLoader(const std::string& extension, ContentLoader* loader)
void load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener) override
{
return mLoaders.insert(std::make_pair(extension, loader)).second;
}
void load(const boost::filesystem::path& filepath, int& index) override
{
LoadersContainer::iterator it(mLoaders.find(Misc::StringUtils::lowerCase(filepath.extension().string())));
const auto it = mLoaders.find(Misc::StringUtils::lowerCase(filepath.extension().string()));
if (it != mLoaders.end())
{
it->second->load(filepath, index);
const std::string filename = filepath.filename().string();
Log(Debug::Info) << "Loading content file " << filename;
if (listener != nullptr)
listener->setLabel(MyGUI::TextIterator::toTagsString(filename));
it->second->load(filepath, index, listener);
}
else
{
std::string msg("Cannot load file: ");
msg += filepath.string();
throw std::runtime_error(msg.c_str());
std::string msg("Cannot load file: ");
msg += filepath.string();
throw std::runtime_error(msg.c_str());
}
}
private:
typedef std::map<std::string, ContentLoader*> LoadersContainer;
LoadersContainer mLoaders;
std::map<std::string, ContentLoader*> mLoaders;
};
struct OMWScriptsLoader : public ContentLoader
{
ESMStore& mStore;
OMWScriptsLoader(Loading::Listener& listener, ESMStore& store) : ContentLoader(listener), mStore(store) {}
void load(const boost::filesystem::path& filepath, int& index) override
OMWScriptsLoader(ESMStore& store) : mStore(store) {}
void load(const boost::filesystem::path& filepath, int& /*index*/, Loading::Listener* /*listener*/) override
{
ContentLoader::load(filepath.filename(), index);
mStore.addOMWScripts(filepath.string());
}
};
@ -2936,18 +2937,18 @@ namespace MWWorld
void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content, ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
{
GameContentLoader gameContentLoader(*listener);
EsmLoader esmLoader(store, readers, encoder, *listener);
GameContentLoader gameContentLoader;
EsmLoader esmLoader(store, readers, encoder);
validateMasterFiles(readers);
gameContentLoader.addLoader(".esm", &esmLoader);
gameContentLoader.addLoader(".esp", &esmLoader);
gameContentLoader.addLoader(".omwgame", &esmLoader);
gameContentLoader.addLoader(".omwaddon", &esmLoader);
gameContentLoader.addLoader(".project", &esmLoader);
gameContentLoader.addLoader(".esm", esmLoader);
gameContentLoader.addLoader(".esp", esmLoader);
gameContentLoader.addLoader(".omwgame", esmLoader);
gameContentLoader.addLoader(".omwaddon", esmLoader);
gameContentLoader.addLoader(".project", esmLoader);
OMWScriptsLoader omwScriptsLoader(*listener, store);
gameContentLoader.addLoader(".omwscripts", &omwScriptsLoader);
OMWScriptsLoader omwScriptsLoader(store);
gameContentLoader.addLoader(".omwscripts", omwScriptsLoader);
int idx = 0;
for (const std::string &file : content)
@ -2956,7 +2957,7 @@ namespace MWWorld
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
if (col.doesExist(file))
{
gameContentLoader.load(col.getPath(file), idx);
gameContentLoader.load(col.getPath(file), idx, listener);
}
else
{