diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index e16ade6e24..b7cbbc4eaf 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -439,7 +439,7 @@ void Record::print() template<> void Record::print() { - std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Race: " << mData.mRace << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Type: " << meshTypeLabel(mData.mData.mType) << " (" << (int)mData.mData.mType << ")" << std::endl; diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index eb93d71e78..0c93474da9 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -102,3 +102,9 @@ if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(omwlauncher gcov) endif() + +# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream +if (UNIX AND NOT APPLE) +target_link_libraries(omwlauncher dl Xt) +endif() + diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5c15938bda..efc3551fd3 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -22,7 +22,7 @@ opencs_units (model/world opencs_units_noqt (model/world - universalid data record idcollection commands columnbase scriptcontext + universalid data record idcollection commands columnbase scriptcontext cell ) opencs_hdrs_noqt (model/world diff --git a/apps/opencs/model/world/cell.cpp b/apps/opencs/model/world/cell.cpp new file mode 100644 index 0000000000..759468fa8f --- /dev/null +++ b/apps/opencs/model/world/cell.cpp @@ -0,0 +1,20 @@ + +#include "cell.hpp" + +#include + +void CSMWorld::Cell::load (ESM::ESMReader &esm) +{ + mName = mId; + + ESM::Cell::load (esm, true); /// \todo set this to false, once the bug in ESM::Cell::load is fixed + + if (!(mData.mFlags & Interior)) + { + std::ostringstream stream; + + stream << "#" << mData.mX << " " << mData.mY; + + mId = stream.str(); + } +} \ No newline at end of file diff --git a/apps/opencs/model/world/cell.hpp b/apps/opencs/model/world/cell.hpp new file mode 100644 index 0000000000..6a9676a559 --- /dev/null +++ b/apps/opencs/model/world/cell.hpp @@ -0,0 +1,17 @@ +#ifndef CSM_WOLRD_CELL_H +#define CSM_WOLRD_CELL_H + +#include + +namespace CSMWorld +{ + /// \brief Wrapper for Cell record + struct Cell : public ESM::Cell + { + std::string mId; + + void load (ESM::ESMReader &esm); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 9242e8a23b..f1d8d4ae62 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -748,6 +748,31 @@ namespace CSMWorld return true; } }; + + template + struct RegionColumn : public Column + { + RegionColumn() : Column ("Region", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mRegion.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mRegion = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 9f6b186c0c..dedbfc4e7d 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -119,6 +119,15 @@ CSMWorld::Data::Data() mSpells.addColumn (new FlagColumn ("Starter Spell", 0x2)); mSpells.addColumn (new FlagColumn ("Always Succeeds", 0x4)); + mCells.addColumn (new StringIdColumn); + mCells.addColumn (new RecordStateColumn); + mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); + mCells.addColumn (new NameColumn); + mCells.addColumn (new FlagColumn ("Sleep forbidden", ESM::Cell::NoSleep)); + mCells.addColumn (new FlagColumn ("Interior Water", ESM::Cell::HasWater)); + mCells.addColumn (new FlagColumn ("Interior Sky", ESM::Cell::QuasiEx)); + mCells.addColumn (new RegionColumn); + addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill); @@ -130,6 +139,7 @@ CSMWorld::Data::Data() addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); + addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); } CSMWorld::Data::~Data() @@ -248,6 +258,16 @@ CSMWorld::IdCollection& CSMWorld::Data::getSpells() return mSpells; } +const CSMWorld::IdCollection& CSMWorld::Data::getCells() const +{ + return mCells; +} + +CSMWorld::IdCollection& CSMWorld::Data::getCells() +{ + return mCells; +} + QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) { std::map::iterator iter = mModelIndex.find (id.getType()); @@ -293,6 +313,7 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) case ESM::REC_REGN: mRegions.load (reader, base); break; case ESM::REC_BSGN: mBirthsigns.load (reader, base); break; case ESM::REC_SPEL: mSpells.load (reader, base); break; + case ESM::REC_CELL: mCells.load (reader, base); break; default: diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index d7b69ba5e3..03a2448f11 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -20,6 +20,7 @@ #include "idcollection.hpp" #include "universalid.hpp" +#include "cell.hpp" class QAbstractItemModel; @@ -38,6 +39,7 @@ namespace CSMWorld IdCollection mRegions; IdCollection mBirthsigns; IdCollection mSpells; + IdCollection mCells; std::vector mModels; std::map mModelIndex; @@ -98,6 +100,10 @@ namespace CSMWorld IdCollection& getSpells(); + const IdCollection& getCells() const; + + IdCollection& getCells(); + QAbstractItemModel *getTableModel (const UniversalId& id); ///< If no table model is available for \a id, an exception is thrown. /// diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 3bf53349e6..4afe9cbaab 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -325,10 +325,10 @@ namespace CSMWorld { std::string id = reader.getHNOString ("NAME"); - int index = searchId (id); - if (reader.isNextSub ("DELE")) { + int index = searchId (id); + reader.skipRecord(); if (index==-1) @@ -354,6 +354,8 @@ namespace CSMWorld record.mId = id; record.load (reader); + int index = searchId (record.mId); + if (index==-1) { // new record diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 11f4877886..c0241bc383 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -28,6 +28,7 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -45,6 +46,7 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 5586b22e79..9b52aded1f 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -55,7 +55,9 @@ namespace CSMWorld Type_Birthsigns, Type_Birthsign, Type_Spells, - Type_Spell + Type_Spell, + Type_Cells, + Type_Cell }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index dfdcb10365..a684b85c5b 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -125,6 +125,10 @@ void CSVDoc::View::setupWorldMenu() QAction *spells = new QAction (tr ("Spells"), this); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); world->addAction (spells); + + QAction *cells = new QAction (tr ("Cells"), this); + connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); + world->addAction (cells); } void CSVDoc::View::setupUi() @@ -325,6 +329,11 @@ void CSVDoc::View::addSpellsSubView() addSubView (CSMWorld::UniversalId::Type_Spells); } +void CSVDoc::View::addCellsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Cells); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 9241efbb9a..a240d3b01d 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -133,6 +133,8 @@ namespace CSVDoc void addBirthsignsSubView(); void addSpellsSubView(); + + void addCellsSubView(); }; } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 8f7887f3b9..0ce3d3d6db 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -26,6 +26,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, + CSMWorld::UniversalId::Type_Cells, CSMWorld::UniversalId::Type_None // end marker }; diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 3334e7865c..f4fdcb390e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -122,6 +122,12 @@ if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) endif() +# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream +if (UNIX AND NOT APPLE) +target_link_libraries(openmw dl Xt) +endif() + + if(APPLE) find_library(CARBON_FRAMEWORK Carbon) find_library(COCOA_FRAMEWORK Cocoa) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 17610e479f..4472b205f7 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -161,7 +161,7 @@ void OMW::Engine::loadBSA() for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { // Last data dir has the highest priority - std::string groupName = "Data" + Ogre::StringConverter::toString(dataDirs.size()-i); + std::string groupName = "Data" + Ogre::StringConverter::toString(dataDirs.size()-i, 8, '0'); Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); std::string dataDirectory = iter->string(); @@ -176,7 +176,7 @@ void OMW::Engine::loadBSA() if (mFileCollections.doesExist(*archive)) { // Last BSA has the highest priority - std::string groupName = "DataBSA" + Ogre::StringConverter::toString(dataDirs.size()-i); + std::string groupName = "DataBSA" + Ogre::StringConverter::toString(mArchives.size()-i, 8, '0'); Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); @@ -368,6 +368,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap, mActivationDistanceOverride)); + MWBase::Environment::get().getWorld()->setupPlayer(mNewGame); //Load translation data mTranslationDataStorage.setEncoder(mEncoder); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 39e985890a..bee744386f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -321,6 +321,7 @@ namespace MWBase virtual void changeVanityModeScale(float factor) = 0; virtual bool vanityRotateCamera(float * rot) = 0; + virtual void setupPlayer(bool newGame) = 0; virtual void renderPlayer() = 0; virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 6791138d35..8a8f72c888 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -292,7 +292,7 @@ namespace MWClass ref->mBase = record; } - int Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + std::pair Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); @@ -317,10 +317,7 @@ namespace MWClass { if((*itr).mPart == ESM::PRT_Head) { - if(npc == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}"); - - return 0; + return std::make_pair(0, "#{sNotifyMessage13}"); } } } @@ -331,9 +328,7 @@ namespace MWClass { if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) { - if(npc == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}"); - return 0; + return std::make_pair(0, "#{sNotifyMessage14}"); } } } @@ -344,7 +339,7 @@ namespace MWClass MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(weapon == invStore.end()) - return 1; + return std::make_pair(1,""); if(weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || @@ -354,12 +349,12 @@ namespace MWClass weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { - return 3; + return std::make_pair(3,""); } - return 1; + return std::make_pair(1,""); } } - return 1; + return std::make_pair(1,""); } boost::shared_ptr Armor::use (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 703d3af1ed..9b8e9dd149 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -67,8 +67,9 @@ namespace MWClass virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; - virtual int canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; - ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \n + /// Second item in the pair specifies the error message virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 6c231e0c60..3072f852d5 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -238,7 +238,7 @@ namespace MWClass ref->mBase = record; } - int Clothing::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + std::pair Clothing::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { // slots that this item can be equipped in std::pair, bool> slots = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); @@ -260,12 +260,7 @@ namespace MWClass for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) { if((*itr).mPart == ESM::PRT_Head) - { - if(npc == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}"); - - return 0; - } + return std::make_pair(0, "#{sNotifyMessage13}"); } } @@ -274,19 +269,12 @@ namespace MWClass for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) { if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - { - if(npc == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}"); - } - - return 0; - } + return std::make_pair(0, "#{sNotifyMessage15}"); } } } } - return 1; + return std::make_pair (1, ""); } boost::shared_ptr Clothing::use (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 1be42adbd4..a6de0cb4f0 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -61,8 +61,9 @@ namespace MWClass virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; - virtual int canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index fbac6d89e8..6c6b3daa61 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -384,7 +384,7 @@ namespace MWClass ref->mBase = record; } - int Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + std::pair Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { std::pair, bool> slots = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); @@ -402,12 +402,12 @@ namespace MWClass ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { - return 2; + return std::make_pair (2, ""); } - return 1; + return std::make_pair (1, ""); } } - return 0; + return std::make_pair (0, ""); } boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 314f6bc212..05b1aee22b 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -67,8 +67,9 @@ namespace MWClass virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; - virtual int canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 0a7374085f..9065333f5c 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -21,36 +21,6 @@ namespace else return index; } - - int countParts(const std::string &part, const std::string &race, bool male) - { - /// \todo loop through the whole store for appropriate bodyparts instead of looking for fixed IDs - const MWWorld::Store &store = - MWBase::Environment::get().getWorld()->getStore().get(); - - std::string prefix = - "b_n_" + race + ((male) ? "_m_" : "_f_") + part; - - std::string suffix; - suffix.reserve(prefix.size() + 3); - - int count = -1; - do { - ++count; - suffix = "_" + (boost::format("%02d") % (count + 1)).str(); - } - while (store.search(prefix + suffix) != 0); - - if (count == 0 && part == "hair") { - count = -1; - do { - ++count; - suffix = (boost::format("%02d") % (count + 1)).str(); - } - while (store.search(prefix + suffix) != 0); - } - return count; - } } namespace MWGui @@ -61,8 +31,6 @@ namespace MWGui , mGenderIndex(0) , mFaceIndex(0) , mHairIndex(0) - , mFaceCount(10) - , mHairCount(14) , mCurrentAngle(0) { // Centre dialog @@ -227,67 +195,28 @@ namespace MWGui void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) { - do - mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); - while (!isFacePlayable()); + mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size()); updatePreview(); } void RaceDialog::onSelectNextFace(MyGUI::Widget*) { - do - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); - while (!isFacePlayable()); + mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size()); updatePreview(); } void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) { - do - mHairIndex = wrap(mHairIndex - 1, mHairCount); - while (!isHairPlayable()); + mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size()); updatePreview(); } void RaceDialog::onSelectNextHair(MyGUI::Widget*) { - do - mHairIndex = wrap(mHairIndex + 1, mHairCount); - while (!isHairPlayable()); + mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size()); updatePreview(); } - bool RaceDialog::isFacePlayable() - { - std::string prefix = - "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); - - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(prefix + "head_" + headIndex) == 0) - return !(parts.find(prefix + "head" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - else - return !(parts.find(prefix + "head_" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - } - - bool RaceDialog::isHairPlayable() - { - std::string prefix = - "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); - - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - if (parts.search(prefix + "hair_" + hairIndex) == 0) - return !(parts.find(prefix + "hair" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - else - return !(parts.find(prefix + "hair_" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - } - void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) @@ -308,18 +237,41 @@ namespace MWGui updateSpellPowers(); } + void RaceDialog::getBodyParts (int part, std::vector& out) + { + out.clear(); + const MWWorld::Store &store = + MWBase::Environment::get().getWorld()->getStore().get(); + + for (MWWorld::Store::iterator it = store.begin(); it != store.end(); ++it) + { + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) + continue; + if (bodypart.mData.mPart != static_cast(part)) + continue; + if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson) + continue; + if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId)) + out.push_back(bodypart.mId); + } + } + void RaceDialog::recountParts() { - mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); - mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); + getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs); + getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads); mFaceIndex = 0; mHairIndex = 0; - - while (!isHairPlayable()) - mHairIndex = wrap(mHairIndex + 1, mHairCount); - while (!isFacePlayable()) - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); } // update widget content @@ -330,21 +282,9 @@ namespace MWGui record.mRace = mCurrentRaceId; record.setIsMale(mGenderIndex == 0); - std::string prefix = - "b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); + record.mHead = mAvailableHeads[mFaceIndex]; + record.mHair = mAvailableHairs[mHairIndex]; - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - record.mHead = prefix + "head_" + headIndex; - record.mHair = prefix + "hair_" + hairIndex; - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(record.mHair) == 0) { - record.mHair = prefix + "hair" + hairIndex; - } mPreview->setPrototype(record); } @@ -442,5 +382,4 @@ namespace MWGui ++i; } } - } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 893c4c90b6..1d48c67cdf 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -76,8 +76,10 @@ namespace MWGui void updatePreview(); void recountParts(); - bool isHairPlayable(); - bool isFacePlayable(); + void getBodyParts (int part, std::vector& out); + + std::vector mAvailableHeads; + std::vector mAvailableHairs; MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; @@ -90,7 +92,6 @@ namespace MWGui std::vector mSpellPowerItems; int mGenderIndex, mFaceIndex, mHairIndex; - int mFaceCount, mHairCount; std::string mCurrentRaceId; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 780ffa0569..95b2bb8ca1 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -134,21 +134,22 @@ namespace MWGui int x,y; bool interior = _sender->getUserString("interior") == "y"; MWBase::Environment::get().getWorld()->positionToIndex(pos.pos[0],pos.pos[1],x,y); - MWWorld::CellStore* cell; - if(interior) cell = MWBase::Environment::get().getWorld()->getInterior(cellname); + if(interior) + MWBase::Environment::get().getWorld()->changeToInteriorCell(cellname, pos); else { - cell = MWBase::Environment::get().getWorld()->getExterior(x,y); - ESM::Position PlayerPos = player.getRefData().getPosition(); - float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); - int time = int(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); - for(int i = 0;i < time;i++) + ESM::Position playerPos = player.getRefData().getPosition(); + float d = Ogre::Vector3(pos.pos[0], pos.pos[1], 0).distance( + Ogre::Vector3(playerPos.pos[0], playerPos.pos[1], 0)); + int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); + for(int i = 0;i < hours;i++) { MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats (); } - MWBase::Environment::get().getWorld()->advanceTime(time); + MWBase::Environment::get().getWorld()->advanceTime(hours); + + MWBase::Environment::get().getWorld()->changeToExteriorCell(pos); } - MWBase::Environment::get().getWorld()->moveObject(player,*cell,pos.pos[0],pos.pos[1],pos.pos[2]); MWWorld::Class::get(player).adjustPosition(player); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 4a2a2ecc69..d34c4b97f5 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -155,6 +155,18 @@ namespace MWMechanics stat.setCurrent (stat.getModified()); creatureStats.setDynamic (i, stat); } + + // unequip any items that may not be equipped. we need this for when the race is changed to a beast race + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); + for (int i=0; igetStore().get().find (iter->first); - if (spell->mData.mFlags & ESM::Spell::ST_Disease) + if (spell->mData.mType == ESM::Spell::ST_Disease) return true; } @@ -94,7 +94,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mFlags & ESM::Spell::ST_Blight) + if (spell->mData.mType == ESM::Spell::ST_Blight) return true; } diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 644d3613bd..566b6fa81e 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -94,6 +94,9 @@ void Actors::insertActivator (const MWWorld::Ptr& ptr) bool Actors::deleteObject (const MWWorld::Ptr& ptr) { + if (mAllActors.find(ptr) == mAllActors.end()) + return false; + mRendering->removeWaterRippleEmitter (ptr); delete mAllActors[ptr]; @@ -139,6 +142,7 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store) Ogre::SceneNode *base = celliter->second; base->removeAndDestroyAllChildren(); mRend.getScene()->destroySceneNode(base); + mCellSceneNodes.erase(celliter); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 96220f47d0..ecc0869cc5 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -305,56 +305,83 @@ void NpcAnimation::updateParts(bool forceupdate) if(mViewMode == VM_HeadOnly) return; - static const struct { - ESM::PartReferenceType type; - const char name[2][12]; - } PartTypeList[] = { - { ESM::PRT_Neck, { "neck", "" } }, - { ESM::PRT_Cuirass, { "chest", "" } }, - { ESM::PRT_Groin, { "groin", "" } }, - { ESM::PRT_RHand, { "hand", "hands" } }, - { ESM::PRT_LHand, { "hand", "hands" } }, - { ESM::PRT_RWrist, { "wrist", "" } }, - { ESM::PRT_LWrist, { "wrist", "" } }, - { ESM::PRT_RForearm, { "forearm", "" } }, - { ESM::PRT_LForearm, { "forearm", "" } }, - { ESM::PRT_RUpperarm, { "upper arm", "" } }, - { ESM::PRT_LUpperarm, { "upper arm", "" } }, - { ESM::PRT_RFoot, { "foot", "feet" } }, - { ESM::PRT_LFoot, { "foot", "feet" } }, - { ESM::PRT_RAnkle, { "ankle", "" } }, - { ESM::PRT_LAnkle, { "ankle", "" } }, - { ESM::PRT_RKnee, { "knee", "" } }, - { ESM::PRT_LKnee, { "knee", "" } }, - { ESM::PRT_RLeg, { "upper leg", "" } }, - { ESM::PRT_LLeg, { "upper leg", "" } }, - { ESM::PRT_Tail, { "tail", "" } } - }; + std::map bodypartMap; + bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck; + bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest; + bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin; + bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail; - const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : ""; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++) + + const int Flag_Female = 0x01; + const int Flag_FirstPerson = 0x02; + + int flags = 0; + if (!mNpc->isMale()) + flags |= Flag_Female; + if (mViewMode == VM_FirstPerson) + flags |= Flag_FirstPerson; + + // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination + static std::map< std::pair , std::vector > sRaceMapping; + std::string race = Misc::StringUtils::lowerCase(mNpc->mRace); + std::pair thisCombination = std::make_pair(race, flags); + if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { - if(mPartPriorities[PartTypeList[i].type] < 1) + sRaceMapping[thisCombination].resize(ESM::PRT_Count); + for (int i=0; i &partStore = store.get(); + + for (MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) { - const ESM::BodyPart *part = NULL; - const MWWorld::Store &partStore = store.get(); - - if(!mNpc->isMale()) + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) { - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]+ext); - if(part == 0) - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]+ext); + continue; } - if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]+ext); - if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]+ext); + if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) + continue; - if(part) - addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson != (mViewMode == VM_FirstPerson)) + continue; + for (std::map::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt ) + if (bIt->second == bodypart.mData.mPart) + sRaceMapping[thisCombination][bIt->first] = &*it; } } + + for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) + { + const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part]; + if (mPartPriorities[part] < 1 && bodypart) + addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); + } } NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 2cbc85cd38..87ae8175de 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -788,8 +788,8 @@ void VideoState::decode_thread_loop(VideoState *self) // main decode loop while(!self->quit) { - if((self->audio_st >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || - (self->video_st >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) + if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE)) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 84d1a3d158..85e73a0560 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -21,7 +21,13 @@ namespace MWWorld MWWorld::Ptr object = getTarget(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); - switch(MWWorld::Class::get (object).canBeEquipped (object, actor)) + std::pair result = MWWorld::Class::get (object).canBeEquipped (object, actor); + + // display error message if the player tried to equip something + if (!result.second.empty() && actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox(result.second); + + switch(result.first) { case 0: return; @@ -48,8 +54,6 @@ namespace MWWorld assert(it != invStore.end()); - std::string npcRace = actor.get()->mBase->mRace; - bool equipped = false; // equip the item in the first free slot diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index 8bba898ce7..4b1f70096a 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -13,8 +13,8 @@ namespace ESM namespace MWWorld { - /// List all (Ogre-)handles. - struct ListHandles + /// List all (Ogre-)handles, then reset RefData::mBaseNode to 0. + struct ListAndResetHandles { std::vector mHandles; @@ -23,6 +23,8 @@ namespace MWWorld Ogre::SceneNode* handle = data.getBaseNode(); if (handle) mHandles.push_back (handle); + + data.setBaseNode(0); return true; } }; diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 4838cfefa5..87ac1c6d7f 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -165,6 +165,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce else return Ptr(); } + MWWorld::Ptr ptr; if (MWWorld::LiveCellRef *ref = cell.mActivators.find (name)) @@ -246,16 +247,16 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) } // Then check cells that are already listed - for (std::map::iterator iter = mInteriors.begin(); - iter!=mInteriors.end(); ++iter) + for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); + iter!=mExteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) - return ptr; + return ptr; } - for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); - iter!=mExteriors.end(); ++iter) + for (std::map::iterator iter = mInteriors.begin(); + iter!=mInteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) @@ -266,7 +267,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) const MWWorld::Store &cells = mStore.get(); MWWorld::Store::iterator iter; - for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter) + for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter) { Ptr::CellStore *cellStore = getCellStore (&(*iter)); @@ -276,7 +277,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) return ptr; } - for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter) + for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter) { Ptr::CellStore *cellStore = getCellStore (&(*iter)); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 4321fc46b7..451f0c5c10 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -264,9 +264,9 @@ namespace MWWorld throw std::runtime_error ("class can't be enchanted"); } - int Class::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + std::pair Class::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { - return 1; + return std::make_pair (1, ""); } void Class::adjustPosition(const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 32941c633d..b901950e31 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -245,8 +245,9 @@ namespace MWWorld virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; - virtual int canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 5495d6a021..ac5586266d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -186,7 +186,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) } } - switch(MWWorld::Class::get (test).canBeEquipped (test, npc)) + switch(MWWorld::Class::get (test).canBeEquipped (test, npc).first) { case 0: continue; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 439f761311..b9b0aa19c8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -76,27 +76,28 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; - ListHandles functor; + ListAndResetHandles functor; - (*iter)->forEach(functor); + (*iter)->forEach(functor); { // silence annoying g++ warning for (std::vector::const_iterator iter2 (functor.mHandles.begin()); - iter2!=functor.mHandles.end(); ++iter2){ - Ogre::SceneNode* node = *iter2; + iter2!=functor.mHandles.end(); ++iter2) + { + Ogre::SceneNode* node = *iter2; mPhysics->removeObject (node->getName()); } + } - if ((*iter)->mCell->isExterior()) - { - ESM::Land* land = - MWBase::Environment::get().getWorld()->getStore().get().search( - (*iter)->mCell->getGridX(), - (*iter)->mCell->getGridY() - ); - if (land) - mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); - } + if ((*iter)->mCell->isExterior()) + { + ESM::Land* land = + MWBase::Environment::get().getWorld()->getStore().get().search( + (*iter)->mCell->getGridX(), + (*iter)->mCell->getGridY() + ); + if (land) + mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); } mRendering.removeCell(*iter); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 11ccd8f2fc..424f6f5089 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -24,6 +24,7 @@ #include "manualref.hpp" #include "cellfunctors.hpp" #include "containerstore.hpp" +#include "inventorystore.hpp" using namespace Ogre; @@ -210,9 +211,6 @@ namespace MWWorld mStore.setUp(); - mPlayer = new MWWorld::Player (mStore.get().find ("player"), *this); - mRendering->attachCameraTo(mPlayer->getPlayer()); - // global variables mGlobalVariables = new Globals (mStore); @@ -801,8 +799,8 @@ namespace MWWorld void World::scaleObject (const Ptr& ptr, float scale) { - MWWorld::Class::get(ptr).adjustScale(ptr,scale); ptr.getCellRef().mScale = scale; + MWWorld::Class::get(ptr).adjustScale(ptr,scale); if(ptr.getRefData().getBaseNode() == 0) return; @@ -1369,6 +1367,18 @@ namespace MWWorld return mRendering->vanityRotateCamera(rot); } + void World::setupPlayer(bool newGame) + { + const ESM::NPC* player = mStore.get().find ("player"); + mPlayer = new MWWorld::Player (player, *this); + mRendering->attachCameraTo(mPlayer->getPlayer()); + if (newGame) + { + MWWorld::Class::get(mPlayer->getPlayer()).getContainerStore(mPlayer->getPlayer()).fill(player->mInventory, "", mStore); + MWWorld::Class::get(mPlayer->getPlayer()).getInventoryStore(mPlayer->getPlayer()).autoEquip (mPlayer->getPlayer()); + } + } + void World::renderPlayer() { mRendering->renderPlayer(mPlayer->getPlayer()); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7b12babee9..99e7cc79d9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -363,6 +363,7 @@ namespace MWWorld virtual bool vanityRotateCamera(float * rot); + virtual void setupPlayer(bool newGame); virtual void renderPlayer(); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index a94ae67353..c18b4486da 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -38,7 +38,9 @@ enum PartReferenceType PRT_RPauldron = 23, PRT_LPauldron = 24, PRT_Weapon = 25, - PRT_Tail = 26 + PRT_Tail = 26, + + PRT_Count = 27 }; // Reference to body parts diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index 831ad8b641..e95a8a8603 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -9,13 +9,13 @@ namespace ESM void BodyPart::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mRace = esm.getHNString("FNAM"); esm.getHNT(mData, "BYDT", 4); } void BodyPart::save(ESMWriter &esm) { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNCString("FNAM", mRace); esm.writeHNT("BYDT", mData, 4); } diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index c467b36251..3ad9b1b958 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -27,7 +27,9 @@ struct BodyPart MP_Knee = 11, MP_Upperleg = 12, MP_Clavicle = 13, - MP_Tail = 14 + MP_Tail = 14, + + MP_Count = 15 }; enum Flags @@ -52,7 +54,7 @@ struct BodyPart }; BYDTstruct mData; - std::string mId, mModel, mName; + std::string mId, mModel, mRace; void load(ESMReader &esm); void save(ESMWriter &esm); diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 5cbf1de2b7..77e4d3691c 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -356,4 +356,22 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) return true; } + void Cell::blank() + { + mName.clear(); + mRegion.clear(); + mWater = 0; + mWaterInt = false; + mMapColor = 0; + mNAM0 = 0; + + mData.mFlags = 0; + mData.mX = 0; + mData.mY = 0; + + mAmbi.mAmbient = 0; + mAmbi.mSunlight = 0; + mAmbi.mFog = 0; + mAmbi.mFogDensity = 0; + } } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 44412b5eb9..d7f64817f8 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -140,6 +140,9 @@ struct Cell * Since they are comparably rare, we use a separate method for this. */ static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 48893bf4a8..a26f431311 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -351,14 +351,19 @@ class NIFObjectLoader static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) { - Ogre::ParticleEmitter *emitter = partsys->addEmitter("Point"); - emitter->setDirection(Ogre::Vector3(0.0f, 0.0f, std::cos(partctrl->verticalDir))); - emitter->setAngle(Ogre::Radian(partctrl->verticalAngle)); + Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); emitter->setParticleVelocity(partctrl->velocity-partctrl->velocityRandom, partctrl->velocity+partctrl->velocityRandom); emitter->setEmissionRate(partctrl->emitRate); emitter->setTimeToLive(partctrl->lifetime-partctrl->lifetimeRandom, partctrl->lifetime+partctrl->lifetimeRandom); + emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); + emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); + emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); + emitter->setParameter("vertical_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalDir).valueDegrees())); + emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees())); + emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees())); + emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees())); Nif::ExtraPtr e = partctrl->extra; while(!e.empty()) @@ -366,21 +371,44 @@ class NIFObjectLoader if(e->recType == Nif::RC_NiParticleGrowFade) { const Nif::NiParticleGrowFade *gf = static_cast(e.getPtr()); + Ogre::ParticleAffector *affector = partsys->addAffector("GrowFade"); affector->setParameter("grow_time", Ogre::StringConverter::toString(gf->growTime)); affector->setParameter("fade_time", Ogre::StringConverter::toString(gf->fadeTime)); } - else if(e->recType == Nif::RC_NiParticleRotation) + else if(e->recType == Nif::RC_NiGravity) { - // TODO: Implement (Ogre::RotationAffector?) + const Nif::NiGravity *gr = static_cast(e.getPtr()); + + Ogre::ParticleAffector *affector = partsys->addAffector("Gravity"); + affector->setParameter("force", Ogre::StringConverter::toString(gr->mForce)); + affector->setParameter("force_type", (gr->mType==0) ? "wind" : "point"); + affector->setParameter("direction", Ogre::StringConverter::toString(gr->mDirection)); + affector->setParameter("position", Ogre::StringConverter::toString(gr->mPosition)); } else if(e->recType == Nif::RC_NiParticleColorModifier) { - // TODO: Implement (Ogre::ColourInterpolatorAffector?) + const Nif::NiParticleColorModifier *cl = static_cast(e.getPtr()); + const Nif::NiColorData *clrdata = cl->data.getPtr(); + + Ogre::ParticleAffector *affector = partsys->addAffector("ColourInterpolator"); + size_t num_colors = std::min(6, clrdata->mKeyList.mKeys.size()); + for(size_t i = 0;i < num_colors;i++) + { + Ogre::ColourValue color; + color.r = clrdata->mKeyList.mKeys[i].mValue[0]; + color.g = clrdata->mKeyList.mKeys[i].mValue[1]; + color.b = clrdata->mKeyList.mKeys[i].mValue[2]; + color.a = clrdata->mKeyList.mKeys[i].mValue[3]; + affector->setParameter("colour"+Ogre::StringConverter::toString(i), + Ogre::StringConverter::toString(color)); + affector->setParameter("time"+Ogre::StringConverter::toString(i), + Ogre::StringConverter::toString(clrdata->mKeyList.mKeys[i].mTime)); + } } - else if(e->recType == Nif::RC_NiGravity) + else if(e->recType == Nif::RC_NiParticleRotation) { - // TODO: Implement + // TODO: Implement (Ogre::RotationAffector?) } else warn("Unhandled particle modifier "+e->recName); @@ -424,6 +452,9 @@ class NIFObjectLoader particledata->particleRadius*2.0f); partsys->setCullIndividually(false); partsys->setParticleQuota(particledata->numParticles); + // TODO: There is probably a field or flag to specify this, as some + // particle effects have it and some don't. + partsys->setKeepParticlesInLocalSpace(true); Nif::ControllerPtr ctrl = partnode->controller; while(!ctrl.empty()) @@ -461,7 +492,7 @@ class NIFObjectLoader static void createObjects(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, const Nif::Node *node, - ObjectList &objectlist, int flags=0) + ObjectList &objectlist, int flags) { // Do not create objects for the collision shape (includes all children) if(node->recType == Nif::RC_RootCollisionNode) @@ -578,8 +609,8 @@ class NIFObjectLoader } } - if(node->recType == Nif::RC_NiAutoNormalParticles || - node->recType == Nif::RC_NiRotatingParticles) + if((node->recType == Nif::RC_NiAutoNormalParticles || + node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) { Ogre::ParticleSystem *partsys = createParticleSystem(name, group, sceneMgr, objectlist.mSkelBase, node); if(partsys != NULL) @@ -607,8 +638,7 @@ class NIFObjectLoader { /* This creates an empty mesh to which a skeleton gets attached. This * is to ensure we have an entity with a skeleton instance, even if all - * other meshes are hidden or entities attached to a specific node - * instead of skinned. */ + * other entities are attached to bones and not skinned. */ Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); if(meshMgr.getByName(name).isNull()) NIFMeshLoader::createMesh(name, name, group, ~(size_t)0); @@ -618,7 +648,7 @@ class NIFObjectLoader } public: - static void load(Ogre::SceneManager *sceneMgr, ObjectList &objectlist, const std::string &name, const std::string &group) + static void load(Ogre::SceneManager *sceneMgr, ObjectList &objectlist, const std::string &name, const std::string &group, int flags=0) { Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); if(nif->numRoots() < 1) @@ -644,7 +674,7 @@ public: // Create a base skeleton entity if this NIF needs one createSkelBase(name, group, sceneMgr, node, objectlist); } - createObjects(name, group, sceneMgr, node, objectlist); + createObjects(name, group, sceneMgr, node, objectlist, flags); } }; @@ -732,7 +762,7 @@ ObjectList Loader::createObjectBase(Ogre::SceneManager *sceneMgr, std::string na ObjectList objectlist; Misc::StringUtils::toLower(name); - NIFObjectLoader::load(sceneMgr, objectlist, name, group); + NIFObjectLoader::load(sceneMgr, objectlist, name, group, 0xC0000000); return objectlist; } diff --git a/libs/openengine/ogre/particles.cpp b/libs/openengine/ogre/particles.cpp index 3453b7f3d7..707bd75e08 100644 --- a/libs/openengine/ogre/particles.cpp +++ b/libs/openengine/ogre/particles.cpp @@ -2,9 +2,347 @@ #include #include +#include #include #include +/* FIXME: "Nif" isn't really an appropriate emitter name. */ +class NifEmitter : public Ogre::ParticleEmitter +{ +public: + /** Command object for the emitter width (see Ogre::ParamCommand).*/ + class CmdWidth : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getWidth()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setWidth(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter height (see Ogre::ParamCommand).*/ + class CmdHeight : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getHeight()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setHeight(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter depth (see Ogre::ParamCommand).*/ + class CmdDepth : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getDepth()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setDepth(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter vertical_direction (see Ogre::ParamCommand).*/ + class CmdVerticalDir : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getVerticalDirection().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setVerticalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter vertical_angle (see Ogre::ParamCommand).*/ + class CmdVerticalAngle : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getVerticalAngle().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setVerticalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter horizontal_direction (see Ogre::ParamCommand).*/ + class CmdHorizontalDir : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getHorizontalDirection().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setHorizontalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter horizontal_angle (see Ogre::ParamCommand).*/ + class CmdHorizontalAngle : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getHorizontalAngle().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setHorizontalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + + NifEmitter(Ogre::ParticleSystem *psys) + : Ogre::ParticleEmitter(psys) + { + initDefaults("Nif"); + } + + /** See Ogre::ParticleEmitter. */ + unsigned short _getEmissionCount(Ogre::Real timeElapsed) + { + // Use basic constant emission + return genConstantEmissionCount(timeElapsed); + } + + /** See Ogre::ParticleEmitter. */ + void _initParticle(Ogre::Particle *particle) + { + Ogre::Vector3 xOff, yOff, zOff; + + // Call superclass + ParticleEmitter::_initParticle(particle); + + xOff = Ogre::Math::SymmetricRandom() * mXRange; + yOff = Ogre::Math::SymmetricRandom() * mYRange; + zOff = Ogre::Math::SymmetricRandom() * mZRange; + + particle->position = mPosition + xOff + yOff + zOff; + + // Generate complex data by reference + genEmissionColour(particle->colour); + + // NOTE: We do not use mDirection/mAngle for the initial direction. + Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom(); + Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom(); + particle->direction = (Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * + Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * + Ogre::Vector3::UNIT_Z; + + genEmissionVelocity(particle->direction); + + // Generate simpler data + particle->timeToLive = particle->totalTimeToLive = genEmissionTTL(); + } + + /** Overloaded to update the trans. matrix */ + void setDirection(const Ogre::Vector3 &dir) + { + ParticleEmitter::setDirection(dir); + genAreaAxes(); + } + + /** Sets the size of the area from which particles are emitted. + @param + size Vector describing the size of the area. The area extends + around the center point by half the x, y and z components of + this vector. The box is aligned such that it's local Z axis points + along it's direction (see setDirection) + */ + void setSize(const Ogre::Vector3 &size) + { + mSize = size; + genAreaAxes(); + } + + /** Sets the size of the area from which particles are emitted. + @param x,y,z + Individual axis lengths describing the size of the area. The area + extends around the center point by half the x, y and z components + of this vector. The box is aligned such that it's local Z axis + points along it's direction (see setDirection) + */ + void setSize(Ogre::Real x, Ogre::Real y, Ogre::Real z) + { + mSize.x = x; + mSize.y = y; + mSize.z = z; + genAreaAxes(); + } + + /** Sets the width (local x size) of the emitter. */ + void setWidth(Ogre::Real width) + { + mSize.x = width; + genAreaAxes(); + } + /** Gets the width (local x size) of the emitter. */ + Ogre::Real getWidth(void) const + { return mSize.x; } + /** Sets the height (local y size) of the emitter. */ + void setHeight(Ogre::Real height) + { + mSize.y = height; + genAreaAxes(); + } + /** Gets the height (local y size) of the emitter. */ + Ogre::Real getHeight(void) const + { return mSize.y; } + /** Sets the depth (local y size) of the emitter. */ + void setDepth(Ogre::Real depth) + { + mSize.z = depth; + genAreaAxes(); + } + /** Gets the depth (local y size) of the emitter. */ + Ogre::Real getDepth(void) const + { return mSize.z; } + + void setVerticalDirection(Ogre::Radian vdir) + { mVerticalDir = vdir; } + Ogre::Radian getVerticalDirection(void) const + { return mVerticalDir; } + + void setVerticalAngle(Ogre::Radian vangle) + { mVerticalAngle = vangle; } + Ogre::Radian getVerticalAngle(void) const + { return mVerticalAngle; } + + void setHorizontalDirection(Ogre::Radian hdir) + { mHorizontalDir = hdir; } + Ogre::Radian getHorizontalDirection(void) const + { return mHorizontalDir; } + + void setHorizontalAngle(Ogre::Radian hangle) + { mHorizontalAngle = hangle; } + Ogre::Radian getHorizontalAngle(void) const + { return mHorizontalAngle; } + + +protected: + /// Size of the area + Ogre::Vector3 mSize; + + Ogre::Radian mVerticalDir; + Ogre::Radian mVerticalAngle; + Ogre::Radian mHorizontalDir; + Ogre::Radian mHorizontalAngle; + + /// Local axes, not normalised, their magnitude reflects area size + Ogre::Vector3 mXRange, mYRange, mZRange; + + /// Internal method for generating the area axes + void genAreaAxes(void) + { + Ogre::Vector3 mLeft = mUp.crossProduct(mDirection); + mXRange = mLeft * (mSize.x * 0.5f); + mYRange = mUp * (mSize.y * 0.5f); + mZRange = mDirection * (mSize.z * 0.5f); + } + + /** Internal for initializing some defaults and parameters + @return True if custom parameters need initialising + */ + bool initDefaults(const Ogre::String &t) + { + // Defaults + mDirection = Ogre::Vector3::UNIT_Z; + mUp = Ogre::Vector3::UNIT_Y; + setSize(100.0f, 100.0f, 100.0f); + mType = t; + + // Set up parameters + if(createParamDictionary(mType + "Emitter")) + { + addBaseParameters(); + Ogre::ParamDictionary *dict = getParamDictionary(); + + // Custom params + dict->addParameter(Ogre::ParameterDef("width", + "Width of the shape in world coordinates.", + Ogre::PT_REAL), + &msWidthCmd); + dict->addParameter(Ogre::ParameterDef("height", + "Height of the shape in world coordinates.", + Ogre::PT_REAL), + &msHeightCmd); + dict->addParameter(Ogre::ParameterDef("depth", + "Depth of the shape in world coordinates.", + Ogre::PT_REAL), + &msDepthCmd); + + dict->addParameter(Ogre::ParameterDef("vertical_direction", + "Vertical direction of emitted particles (in degrees).", + Ogre::PT_REAL), + &msVerticalDirCmd); + dict->addParameter(Ogre::ParameterDef("vertical_angle", + "Vertical direction variance of emitted particles (in degrees).", + Ogre::PT_REAL), + &msVerticalAngleCmd); + dict->addParameter(Ogre::ParameterDef("horizontal_direction", + "Horizontal direction of emitted particles (in degrees).", + Ogre::PT_REAL), + &msHorizontalDirCmd); + dict->addParameter(Ogre::ParameterDef("horizontal_angle", + "Horizontal direction variance of emitted particles (in degrees).", + Ogre::PT_REAL), + &msHorizontalAngleCmd); + + return true; + } + return false; + } + + /// Command objects + static CmdWidth msWidthCmd; + static CmdHeight msHeightCmd; + static CmdDepth msDepthCmd; + static CmdVerticalDir msVerticalDirCmd; + static CmdVerticalAngle msVerticalAngleCmd; + static CmdHorizontalDir msHorizontalDirCmd; + static CmdHorizontalAngle msHorizontalAngleCmd; +}; +NifEmitter::CmdWidth NifEmitter::msWidthCmd; +NifEmitter::CmdHeight NifEmitter::msHeightCmd; +NifEmitter::CmdDepth NifEmitter::msDepthCmd; +NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd; +NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd; +NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd; +NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd; + +Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys) +{ + Ogre::ParticleEmitter *emit = OGRE_NEW NifEmitter(psys); + mEmitters.push_back(emit); + return emit; +} + + class GrowFadeAffector : public Ogre::ParticleAffector { public: @@ -144,3 +482,212 @@ Ogre::ParticleAffector *GrowFadeAffectorFactory::createAffector(Ogre::ParticleSy mAffectors.push_back(p); return p; } + + +class GravityAffector : public Ogre::ParticleAffector +{ + enum ForceType { + Type_Wind, + Type_Point + }; + +public: + /** Command object for force (see Ogre::ParamCommand).*/ + class CmdForce : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getForce()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setForce(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for force_type (see Ogre::ParamCommand).*/ + class CmdForceType : public Ogre::ParamCommand + { + static ForceType getTypeFromString(const Ogre::String &type) + { + if(type == "wind") + return Type_Wind; + if(type == "point") + return Type_Point; + OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type string: "+type, + "CmdForceType::getTypeFromString"); + } + + static Ogre::String getStringFromType(ForceType type) + { + switch(type) + { + case Type_Wind: return "wind"; + case Type_Point: return "point"; + } + OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type enum: "+Ogre::StringConverter::toString(type), + "CmdForceType::getStringFromType"); + } + + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return getStringFromType(self->getForceType()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setForceType(getTypeFromString(val)); + } + }; + + /** Command object for direction (see Ogre::ParamCommand).*/ + class CmdDirection : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getDirection()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setDirection(Ogre::StringConverter::parseVector3(val)); + } + }; + + /** Command object for position (see Ogre::ParamCommand).*/ + class CmdPosition : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getPosition()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setPosition(Ogre::StringConverter::parseVector3(val)); + } + }; + + + /** Default constructor. */ + GravityAffector(Ogre::ParticleSystem *psys) + : ParticleAffector(psys) + , mForce(0.0f) + , mForceType(Type_Wind) + , mPosition(0.0f) + , mDirection(0.0f) + { + mType = "Gravity"; + + // Init parameters + if(createParamDictionary("GravityAffector")) + { + Ogre::ParamDictionary *dict = getParamDictionary(); + + Ogre::String force_title("force"); + Ogre::String force_descr("Amount of force applied to particles."); + Ogre::String force_type_title("force_type"); + Ogre::String force_type_descr("Type of force applied to particles (point or wind)."); + Ogre::String direction_title("direction"); + Ogre::String direction_descr("Direction of wind forces."); + Ogre::String position_title("position"); + Ogre::String position_descr("Position of point forces."); + + dict->addParameter(Ogre::ParameterDef(force_title, force_descr, Ogre::PT_REAL), &msForceCmd); + dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd); + dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd); + dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd); + } + } + + /** See Ogre::ParticleAffector. */ + void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + switch(mForceType) + { + case Type_Wind: + applyWindForce(psys, timeElapsed); + break; + case Type_Point: + applyPointForce(psys, timeElapsed); + break; + } + } + + void setForce(Ogre::Real force) + { mForce = force; } + Ogre::Real getForce() const + { return mForce; } + + void setForceType(ForceType type) + { mForceType = type; } + ForceType getForceType() const + { return mForceType; } + + void setDirection(const Ogre::Vector3 &dir) + { mDirection = dir; } + const Ogre::Vector3 &getDirection() const + { return mDirection; } + + void setPosition(const Ogre::Vector3 &pos) + { mPosition = pos; } + const Ogre::Vector3 &getPosition() const + { return mPosition; } + + static CmdForce msForceCmd; + static CmdForceType msForceTypeCmd; + static CmdDirection msDirectionCmd; + static CmdPosition msPositionCmd; + +protected: + void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + const Ogre::Vector3 vec = mDirection * mForce * timeElapsed; + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + p->direction += vec; + } + } + + void applyPointForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + const Ogre::Real force = mForce * timeElapsed; + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + const Ogre::Vector3 vec = (p->position - mPosition).normalisedCopy() * force; + p->direction += vec; + } + } + + + float mForce; + + ForceType mForceType; + + Ogre::Vector3 mPosition; + Ogre::Vector3 mDirection; +}; +GravityAffector::CmdForce GravityAffector::msForceCmd; +GravityAffector::CmdForceType GravityAffector::msForceTypeCmd; +GravityAffector::CmdDirection GravityAffector::msDirectionCmd; +GravityAffector::CmdPosition GravityAffector::msPositionCmd; + +Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys) +{ + Ogre::ParticleAffector *p = new GravityAffector(psys); + mAffectors.push_back(p); + return p; +} diff --git a/libs/openengine/ogre/particles.hpp b/libs/openengine/ogre/particles.hpp index d466bb0308..e1f3fd282c 100644 --- a/libs/openengine/ogre/particles.hpp +++ b/libs/openengine/ogre/particles.hpp @@ -1,8 +1,21 @@ #ifndef OENGINE_OGRE_PARTICLES_H #define OENGINE_OGRE_PARTICLES_H +#include #include +/** Factory class for NifEmitter. */ +class NifEmitterFactory : public Ogre::ParticleEmitterFactory +{ +public: + /** See ParticleEmitterFactory */ + Ogre::String getName() const + { return "Nif"; } + + /** See ParticleEmitterFactory */ + Ogre::ParticleEmitter* createEmitter(Ogre::ParticleSystem *psys); +}; + /** Factory class for GrowFadeAffector. */ class GrowFadeAffectorFactory : public Ogre::ParticleAffectorFactory { @@ -14,4 +27,15 @@ class GrowFadeAffectorFactory : public Ogre::ParticleAffectorFactory Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys); }; +/** Factory class for GravityAffector. */ +class GravityAffectorFactory : public Ogre::ParticleAffectorFactory +{ + /** See Ogre::ParticleAffectorFactory */ + Ogre::String getName() const + { return "Gravity"; } + + /** See Ogre::ParticleAffectorFactory */ + Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys); +}; + #endif /* OENGINE_OGRE_PARTICLES_H */ diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index c9e91968f5..7be7137969 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -110,6 +110,11 @@ void OgreRenderer::loadPlugins() void OgreRenderer::unloadPlugins() { + std::vector::iterator ei; + for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();ei++) + OGRE_DELETE (*ei); + mEmitterFactories.clear(); + std::vector::iterator ai; for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();ai++) OGRE_DELETE (*ai); @@ -206,11 +211,22 @@ void OgreRenderer::configure(const std::string &logPath, Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot); + + Ogre::ParticleEmitterFactory *emitter; + emitter = OGRE_NEW NifEmitterFactory(); + Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter); + mEmitterFactories.push_back(emitter); + + Ogre::ParticleAffectorFactory *affector; affector = OGRE_NEW GrowFadeAffectorFactory(); Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); mAffectorFactories.push_back(affector); + affector = OGRE_NEW GravityAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index ea46f5ae62..962ae4f2ec 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -40,6 +40,7 @@ namespace Ogre class SceneManager; class Camera; class Viewport; + class ParticleEmitterFactory; class ParticleAffectorFactory; } @@ -95,6 +96,7 @@ namespace OEngine Ogre::D3D9Plugin* mD3D9Plugin; #endif Fader* mFader; + std::vector mEmitterFactories; std::vector mAffectorFactories; bool logging;