diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 1eb6a88c1..19028de08 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -131,6 +131,8 @@ namespace CSMWorld Display_InfoCondComp, Display_String32, Display_LongString256, + Display_BookType, + Display_BloodType, Display_EffectSkill, // must display at least one, unlike Display_Skill Display_EffectAttribute, // must display at least one, unlike Display_Attribute diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 028a759dc..534d7da05 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -98,7 +98,7 @@ namespace CSMWorld { ColumnId_ArmorType, "Armor Type" }, { ColumnId_Health, "Health" }, { ColumnId_ArmorValue, "Armor Value" }, - { ColumnId_Scroll, "Scroll" }, + { ColumnId_BookType, "Book Type" }, { ColumnId_ClothingType, "Clothing Type" }, { ColumnId_WeightCapacity, "Weight Capacity" }, { ColumnId_OrganicContainer, "Organic Container" }, @@ -112,8 +112,8 @@ namespace CSMWorld { ColumnId_Flies, "Flies" }, { ColumnId_Walks, "Walks" }, { ColumnId_Essential, "Essential" }, - { ColumnId_SkeletonBlood, "Skeleton Blood" }, - { ColumnId_MetalBlood, "Metal Blood" }, + { ColumnId_BloodType, "Blood Type" }, + { ColumnId_OpenSound, "Open Sound" }, { ColumnId_CloseSound, "Close Sound" }, { ColumnId_Duration, "Duration" }, @@ -553,6 +553,16 @@ namespace "AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0 }; + static const char *sBookType[] = + { + "Book", "Scroll", 0 + }; + + static const char *sBloodType[] = + { + "Default (Red)", "Skeleton Blood (White)", "Metal Blood (Golden)", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -582,6 +592,8 @@ namespace case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType; case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings; case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings; + case CSMWorld::Columns::ColumnId_BookType: return sBookType; + case CSMWorld::Columns::ColumnId_BloodType: return sBloodType; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index e3899af73..2438d2de2 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -92,7 +92,7 @@ namespace CSMWorld ColumnId_ArmorType = 77, ColumnId_Health = 78, ColumnId_ArmorValue = 79, - ColumnId_Scroll = 80, + ColumnId_BookType = 80, ColumnId_ClothingType = 81, ColumnId_WeightCapacity = 82, ColumnId_OrganicContainer = 83, @@ -107,8 +107,8 @@ namespace CSMWorld ColumnId_Flies = 92, ColumnId_Walks = 93, ColumnId_Essential = 94, - ColumnId_SkeletonBlood = 95, - ColumnId_MetalBlood = 96, + ColumnId_BloodType = 95, + // unused ColumnId_OpenSound = 97, ColumnId_CloseSound = 98, ColumnId_Duration = 99, diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 0da4769a8..b80f1ac2f 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -301,9 +301,9 @@ void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& } CSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns, - const RefIdColumn *scroll, const RefIdColumn *skill, const RefIdColumn *text) + const RefIdColumn *bookType, const RefIdColumn *skill, const RefIdColumn *text) : EnchantableRefIdAdapter (UniversalId::Type_Book, columns), - mScroll (scroll), mSkill (skill), mText (text) + mBookType (bookType), mSkill (skill), mText (text) {} QVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column, @@ -312,8 +312,8 @@ QVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column, const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); - if (column==mScroll) - return record.get().mData.mIsScroll!=0; + if (column==mBookType) + return record.get().mData.mIsScroll; if (column==mSkill) return record.get().mData.mSkillId; @@ -332,7 +332,7 @@ void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& ESM::Book book = record.get(); - if (column==mScroll) + if (column==mBookType) book.mData.mIsScroll = value.toInt(); else if (column==mSkill) book.mData.mSkillId = value.toInt(); @@ -458,7 +458,8 @@ CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) mOriginal(NULL), mAttributes(NULL), mAttacks(NULL), - mMisc(NULL) + mMisc(NULL), + mBloodType(NULL) {} CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) @@ -489,6 +490,19 @@ QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, con if (column==mColumns.mMisc) return QVariant::fromValue(ColumnBase::TableEdit_Full); + if (column == mColumns.mBloodType) + { + int mask = ESM::Creature::Skeleton | ESM::Creature::Metal; + + if ((record.get().mFlags & mask) == ESM::Creature::Skeleton) + return 1; + + if ((record.get().mFlags & mask) == ESM::Creature::Metal) + return 2; + + return 0; + } + std::map::const_iterator iter = mColumns.mFlags.find (column); @@ -512,6 +526,17 @@ void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdDa creature.mScale = value.toFloat(); else if (column==mColumns.mOriginal) creature.mOriginal = value.toString().toUtf8().constData(); + else if (column == mColumns.mBloodType) + { + int mask = ~(ESM::Creature::Skeleton | ESM::Creature::Metal); + + if (value.toInt() == 1) + creature.mFlags = (creature.mFlags & mask) | ESM::Creature::Skeleton; + else if (value.toInt() == 2) + creature.mFlags = (creature.mFlags & mask) | ESM::Creature::Metal; + else + creature.mFlags = creature.mFlags & mask; + } else { std::map::const_iterator iter = @@ -696,7 +721,8 @@ CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) mHead(NULL), mAttributes(NULL), mSkills(NULL), - mMisc(NULL) + mMisc(NULL), + mBloodType(NULL) {} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) @@ -735,6 +761,19 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re if (column==mColumns.mMisc) return QVariant::fromValue(ColumnBase::TableEdit_Full); + if (column == mColumns.mBloodType) + { + int mask = ESM::NPC::Skeleton | ESM::NPC::Metal; + + if ((record.get().mFlags & mask) == ESM::NPC::Skeleton) + return 1; + + if ((record.get().mFlags & mask) == ESM::NPC::Metal) + return 2; + + return 0; + } + std::map::const_iterator iter = mColumns.mFlags.find (column); @@ -762,6 +801,17 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d npc.mHair = value.toString().toUtf8().constData(); else if (column==mColumns.mHead) npc.mHead = value.toString().toUtf8().constData(); + else if (column == mColumns.mBloodType) + { + int mask = ~(ESM::NPC::Skeleton | ESM::NPC::Metal); + + if (value.toInt() == 1) + npc.mFlags = (npc.mFlags & mask) | ESM::NPC::Skeleton; + else if (value.toInt() == 2) + npc.mFlags = (npc.mFlags & mask) | ESM::NPC::Metal; + else + npc.mFlags = npc.mFlags & mask; + } else { std::map::const_iterator iter = diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index 757a8ad77..b89b87ab1 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -694,13 +694,13 @@ namespace CSMWorld class BookRefIdAdapter : public EnchantableRefIdAdapter { - const RefIdColumn *mScroll; + const RefIdColumn *mBookType; const RefIdColumn *mSkill; const RefIdColumn *mText; public: - BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *scroll, + BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *bookType, const RefIdColumn *skill, const RefIdColumn *text); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) @@ -757,6 +757,7 @@ namespace CSMWorld const RefIdColumn *mAttributes; const RefIdColumn *mAttacks; const RefIdColumn *mMisc; + const RefIdColumn *mBloodType; CreatureColumns (const ActorColumns& actorColumns); }; @@ -849,6 +850,7 @@ namespace CSMWorld const RefIdColumn *mAttributes; // depends on npc type const RefIdColumn *mSkills; // depends on npc type const RefIdColumn *mMisc; // may depend on npc type, e.g. FactionID + const RefIdColumn *mBloodType; NpcColumns (const ActorColumns& actorColumns); }; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 5674ba327..a85ee5d25 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -291,8 +291,8 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorValue, ColumnBase::Display_Integer)); const RefIdColumn *armor = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Scroll, ColumnBase::Display_Boolean)); - const RefIdColumn *scroll = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_BookType, ColumnBase::Display_BookType)); + const RefIdColumn *bookType = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId)); const RefIdColumn *skill = &mColumns.back(); @@ -346,15 +346,11 @@ CSMWorld::RefIdCollection::RefIdCollection() { Columns::ColumnId_Flies, ESM::Creature::Flies }, { Columns::ColumnId_Walks, ESM::Creature::Walks }, { Columns::ColumnId_Essential, ESM::Creature::Essential }, - { Columns::ColumnId_SkeletonBlood, ESM::Creature::Skeleton }, - { Columns::ColumnId_MetalBlood, ESM::Creature::Metal }, { -1, 0 } }; // for re-use in NPC records const RefIdColumn *essential = 0; - const RefIdColumn *skeletonBlood = 0; - const RefIdColumn *metalBlood = 0; for (int i=0; sCreatureFlagTable[i].mName!=-1; ++i) { @@ -364,11 +360,14 @@ CSMWorld::RefIdCollection::RefIdCollection() switch (sCreatureFlagTable[i].mFlag) { case ESM::Creature::Essential: essential = &mColumns.back(); break; - case ESM::Creature::Skeleton: skeletonBlood = &mColumns.back(); break; - case ESM::Creature::Metal: metalBlood = &mColumns.back(); break; } } + mColumns.push_back(RefIdColumn(Columns::ColumnId_BloodType, ColumnBase::Display_BloodType)); + // For re-use in NPC records. + const RefIdColumn *bloodType = &mColumns.back(); + creatureColumns.mBloodType = bloodType; + creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn)); // Nested table @@ -497,9 +496,8 @@ CSMWorld::RefIdCollection::RefIdCollection() npcColumns.mFlags.insert (std::make_pair (autoCalc, ESM::NPC::Autocalc)); - npcColumns.mFlags.insert (std::make_pair (skeletonBlood, ESM::NPC::Skeleton)); - - npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal)); + // Re-used from Creature records. + npcColumns.mBloodType = bloodType; // Need a way to add a table of stats and values (rather than adding a long list of // entries in the dialogue subview) E.g. attributes+stats(health, mana, fatigue), skills @@ -659,7 +657,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mAdapters.insert (std::make_pair (UniversalId::Type_Armor, new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor, partRef))); mAdapters.insert (std::make_pair (UniversalId::Type_Book, - new BookRefIdAdapter (enchantableColumns, scroll, skill, text))); + new BookRefIdAdapter (enchantableColumns, bookType, skill, text))); mAdapters.insert (std::make_pair (UniversalId::Type_Clothing, new ClothingRefIdAdapter (enchantableColumns, clothingType, partRef))); mAdapters.insert (std::make_pair (UniversalId::Type_Container, diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 024636f34..b3a8d03d4 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -107,6 +107,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_IngredEffectId, CSMWorld::Columns::ColumnId_EffectId, true }, { CSMWorld::ColumnBase::Display_EffectSkill, CSMWorld::Columns::ColumnId_Skill, false }, { CSMWorld::ColumnBase::Display_EffectAttribute, CSMWorld::Columns::ColumnId_Attribute, false }, + { CSMWorld::ColumnBase::Display_BookType, CSMWorld::Columns::ColumnId_BookType, false}, + { CSMWorld::ColumnBase::Display_BloodType, CSMWorld::Columns::ColumnId_BloodType, false} }; for (std::size_t i=0; isetSelectedSpell(spellId, successChancePercent); const ESM::Spell* spell = mStore->get().find(spellId); @@ -1295,6 +1296,7 @@ namespace MWGui void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { + mSelectedEnchantItem = item; mSelectedSpell = ""; const ESM::Enchantment* ench = mStore->get() .find(item.getClass().getEnchantment(item)); @@ -1305,17 +1307,29 @@ namespace MWGui mSpellWindow->setTitle(item.getClass().getName(item)); } + const MWWorld::Ptr &WindowManager::getSelectedEnchantItem() const + { + return mSelectedEnchantItem; + } + void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { + mSelectedWeapon = item; int durabilityPercent = static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } + const MWWorld::Ptr &WindowManager::getSelectedWeapon() const + { + return mSelectedWeapon; + } + void WindowManager::unsetSelectedSpell() { mSelectedSpell = ""; + mSelectedEnchantItem = MWWorld::Ptr(); mHud->unsetSelectedSpell(); MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer(); @@ -1327,6 +1341,7 @@ namespace MWGui void WindowManager::unsetSelectedWeapon() { + mSelectedWeapon = MWWorld::Ptr(); mHud->unsetSelectedWeapon(); mInventoryWindow->setTitle("#{sSkillHandtohand}"); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 4e2cef8af..a8f6263c2 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -13,6 +13,8 @@ #include "../mwbase/windowmanager.hpp" +#include "../mwworld/ptr.hpp" + #include #include @@ -244,7 +246,9 @@ namespace MWGui virtual std::string getSelectedSpell() { return mSelectedSpell; } virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); + virtual const MWWorld::Ptr& getSelectedEnchantItem() const; virtual void setSelectedWeapon(const MWWorld::Ptr& item); + virtual const MWWorld::Ptr& getSelectedWeapon() const; virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); @@ -403,6 +407,8 @@ namespace MWGui void onWindowChangeCoord(MyGUI::Window* _sender); std::string mSelectedSpell; + MWWorld::Ptr mSelectedEnchantItem; + MWWorld::Ptr mSelectedWeapon; std::stack mCurrentModals; diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 437aae277..cc1434d52 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -426,6 +426,9 @@ namespace MWMechanics MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); inv.setSelectedEnchantItem(inv.end()); } + + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(mSpellId); + MWBase::Environment::get().getWorld()->preloadEffects(&spell->mEffects); } float ActionSpell::getCombatRange (bool& isRanged) const diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 551e20888..33bb84345 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -202,7 +203,8 @@ namespace MWRender mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get())); - mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); + if (getenv("OPENMW_DONT_PRECOMPILE") == NULL) + mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c2145c389..94beccf79 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -679,6 +680,35 @@ namespace MWWorld return Ptr(); } + class PreloadMeshItem : public SceneUtil::WorkItem + { + public: + PreloadMeshItem(const std::string& mesh, Resource::SceneManager* sceneManager) + : mMesh(mesh), mSceneManager(sceneManager) + { + } + + virtual void doWork() + { + try + { + mSceneManager->getTemplate(mMesh); + } + catch (std::exception& e) + { + } + } + private: + std::string mMesh; + Resource::SceneManager* mSceneManager; + }; + + void Scene::preload(const std::string &mesh) + { + if (!mRendering.getResourceSystem()->getSceneManager()->checkLoaded(mesh, mRendering.getReferenceTime())) + mRendering.getWorkQueue()->addWorkItem(new PreloadMeshItem(mesh, mRendering.getResourceSystem()->getSceneManager())); + } + void Scene::preloadCells(float dt) { const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 328538076..cbceb14f5 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -132,6 +132,8 @@ namespace MWWorld bool isCellActive(const CellStore &cell); Ptr searchPtrViaActorId (int actorId); + + void preload(const std::string& mesh); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c91d446db..a126e7e88 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -159,7 +159,7 @@ namespace MWWorld mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath), mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), - mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0) + mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), mSpellPreloadTimer(0.f) { mPhysics = new MWPhysics::PhysicsSystem(resourceSystem, rootNode); mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath); @@ -1653,6 +1653,13 @@ namespace MWWorld updateSoundListener(); updatePlayer(paused); + + mSpellPreloadTimer -= duration; + if (mSpellPreloadTimer <= 0.f) + { + mSpellPreloadTimer = 0.1f; + preloadSpells(); + } } void World::updatePlayer(bool paused) @@ -1709,7 +1716,39 @@ namespace MWWorld if (result.mHit) mRendering->getCamera()->setCameraDistance((result.mHitPos - focal).length() - radius, false, false); } + } + void World::preloadSpells() + { + std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell(); + if (!selectedSpell.empty()) + { + const ESM::Spell* spell = mStore.get().search(selectedSpell); + if (spell) + preloadEffects(&spell->mEffects); + } + const MWWorld::Ptr& selectedEnchantItem = MWBase::Environment::get().getWindowManager()->getSelectedEnchantItem(); + if (!selectedEnchantItem.isEmpty()) + { + std::string enchantId = selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem); + if (!enchantId.empty()) + { + const ESM::Enchantment* ench = mStore.get().search(selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem)); + if (ench) + preloadEffects(&ench->mEffects); + } + } + const MWWorld::Ptr& selectedWeapon = MWBase::Environment::get().getWindowManager()->getSelectedWeapon(); + if (!selectedWeapon.isEmpty()) + { + std::string enchantId = selectedWeapon.getClass().getEnchantment(selectedWeapon); + if (!enchantId.empty()) + { + const ESM::Enchantment* ench = mStore.get().search(enchantId); + if (ench && ench->mData.mType == ESM::Enchantment::WhenStrikes) + preloadEffects(&ench->mEffects); + } + } } void World::updateSoundListener() @@ -2803,7 +2842,6 @@ namespace MWWorld if (!selectedSpell.empty()) { const ESM::Spell* spell = getStore().get().find(selectedSpell); - cast.cast(spell); } else if (actor.getClass().hasInventoryStore(actor)) @@ -3439,4 +3477,30 @@ namespace MWWorld return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y(); } + void preload(MWWorld::Scene* scene, const ESMStore& store, const std::string& obj) + { + if (obj.empty()) + return; + MWWorld::ManualRef ref(store, obj); + std::string model = ref.getPtr().getClass().getModel(ref.getPtr()); + if (!model.empty()) + scene->preload(model); + } + + void World::preloadEffects(const ESM::EffectList *effectList) + { + for (std::vector::const_iterator it = effectList->mList.begin(); it != effectList->mList.end(); ++it) + { + const ESM::MagicEffect *effect = mStore.get().find(it->mEffectID); + + preload(mWorldScene, mStore, effect->mCasting); + preload(mWorldScene, mStore, effect->mHit); + + if (it->mArea > 0) + preload(mWorldScene, mStore, effect->mArea); + if (it->mRange == ESM::RT_Target) + preload(mWorldScene, mStore, effect->mBolt); + } + } + } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e1ef388d3..0cd4c6746 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -135,6 +135,8 @@ namespace MWWorld void updateWindowManager (); void updatePlayer(bool paused); + void preloadSpells(); + MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true); public: // FIXME @@ -174,6 +176,8 @@ namespace MWWorld bool mGoToJail; int mDaysInPrison; + float mSpellPreloadTimer; + float feetToGameUnits(float feet); float getActivationDistancePlusTelekinesis(); @@ -680,6 +684,9 @@ namespace MWWorld /// Export scene graph to a file and return the filename. /// \param ptr object to export scene graph for (if empty, export entire scene graph) virtual std::string exportSceneGraph(const MWWorld::Ptr& ptr); + + /// Preload VFX associated with this effect list + virtual void preloadEffects(const ESM::EffectList* effectList); }; } diff --git a/components/resource/objectcache.cpp b/components/resource/objectcache.cpp index 7caf5366c..d80561c24 100644 --- a/components/resource/objectcache.cpp +++ b/components/resource/objectcache.cpp @@ -49,6 +49,18 @@ osg::ref_ptr ObjectCache::getRefFromObjectCache(const std::string& else return 0; } +bool ObjectCache::checkInObjectCache(const std::string &fileName, double timeStamp) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + ObjectCacheMap::iterator itr = _objectCache.find(fileName); + if (itr!=_objectCache.end()) + { + itr->second.second = timeStamp; + return true; + } + else return false; +} + void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime) { OpenThreads::ScopedLock lock(_objectCacheMutex); diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index 82aca76f0..3d65df546 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -64,6 +64,9 @@ class ObjectCache : public osg::Referenced /** Get an ref_ptr from the object cache*/ osg::ref_ptr getRefFromObjectCache(const std::string& fileName); + /** Check if an object is in the cache, and if it is, update its usage time stamp. */ + bool checkInObjectCache(const std::string& fileName, double timeStamp); + /** call releaseGLObjects on all objects attached to the object cache.*/ void releaseGLObjects(osg::State* state); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index c975286ce..9cda7d755 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -272,6 +272,14 @@ namespace Resource mShaderManager->setShaderPath(path); } + bool SceneManager::checkLoaded(const std::string &name, double timeStamp) + { + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + return mCache->checkInObjectCache(normalized, timeStamp); + } + /// @brief Callback to read image files from the VFS. class ImageReadCallback : public osgDB::ReadFileCallback { diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index ee4a453a3..97e5b43be 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -77,6 +77,9 @@ namespace Resource void setShaderPath(const std::string& path); + /// Check if a given scene is loaded and if so, update its usage timestamp to prevent it from being unloaded + bool checkLoaded(const std::string& name, double referenceTime); + /// Get a read-only copy of this scene "template" /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead. /// If even the error marker mesh can not be found, an exception is thrown.