diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..374b38ce08 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,40 @@ +language: cpp +compiler: + - gcc +branches: + only: + - master + - next +before_install: + - pwd + - git submodule update --init --recursive + - echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" + - echo "yes" | sudo apt-add-repository ppa:openmw/deps + - sudo apt-get update -qq + - sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock libzzip-dev + - sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev + - sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev + - sudo apt-get install -qq libcg nvidia-cg-toolkit + - sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev + - sudo apt-get install -qq libois-dev libbullet-dev libogre-static-dev libmygui-static-dev + - sudo mkdir /usr/src/gtest/build + - cd /usr/src/gtest/build + - sudo cmake .. -DBUILD_SHARED_LIBS=1 + - sudo make -j4 + - sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so + - sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so +before_script: + - cd - + - mkdir build + - cd build + - cmake .. -DOGRE_STATIC=1 -DMYGUI_STATIC=1 -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 +script: + - make -j4 +after_script: + - ./openmw_test_suite +notifications: + recipients: + - lgromanowski+travis.ci@gmail.com + email: + on_success: change + on_failure: always diff --git a/CMakeLists.txt b/CMakeLists.txt index e1b8e32e5e..b6a1017906 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/fader.cpp + ${LIBDIR}/openengine/ogre/particles.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ) set(OENGINE_GUI diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index de3a175106..e16ade6e24 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -679,14 +679,14 @@ void Record::print() std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl; if (mData.mData.mUnknown != -1) std::cout << " Unknown: " << mData.mData.mUnknown << std::endl; - std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute1) - << " (" << mData.mData.mAttribute1 << ")" << std::endl; - std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute2) - << " (" << mData.mData.mAttribute2 << ")" << std::endl; + std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0]) + << " (" << mData.mData.mAttribute[0] << ")" << std::endl; + std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) + << " (" << mData.mData.mAttribute[1] << ")" << std::endl; for (int i = 0; i != 6; i++) - if (mData.mData.mSkillID[i] != -1) - std::cout << " Skill: " << skillLabel(mData.mData.mSkillID[i]) - << " (" << mData.mData.mSkillID[i] << ")" << std::endl; + if (mData.mData.mSkills[i] != -1) + std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) + << " (" << mData.mData.mSkills[i] << ")" << std::endl; for (int i = 0; i != 10; i++) if (mData.mRanks[i] != "") { @@ -1099,53 +1099,29 @@ void Record::print() template<> void Record::print() { + static const char *sAttributeNames[8] = + { + "Strength", "Intelligence", "Willpower", "Agility", + "Speed", "Endurance", "Personality", "Luck" + }; + std::cout << " Name: " << mData.mName << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl; - std::cout << " Male:" << std::endl; - std::cout << " Strength: " - << mData.mData.mStrength.mMale << std::endl; - std::cout << " Intelligence: " - << mData.mData.mIntelligence.mMale << std::endl; - std::cout << " Willpower: " - << mData.mData.mWillpower.mMale << std::endl; - std::cout << " Agility: " - << mData.mData.mAgility.mMale << std::endl; - std::cout << " Speed: " - << mData.mData.mSpeed.mMale << std::endl; - std::cout << " Endurance: " - << mData.mData.mEndurance.mMale << std::endl; - std::cout << " Personality: " - << mData.mData.mPersonality.mMale << std::endl; - std::cout << " Luck: " - << mData.mData.mLuck.mMale << std::endl; - std::cout << " Height: " - << mData.mData.mHeight.mMale << std::endl; - std::cout << " Weight: " - << mData.mData.mWeight.mMale << std::endl; + for (int i=0; i<2; ++i) + { + bool male = i==0; - std::cout << " Female:" << std::endl; - std::cout << " Strength: " - << mData.mData.mStrength.mFemale << std::endl; - std::cout << " Intelligence: " - << mData.mData.mIntelligence.mFemale << std::endl; - std::cout << " Willpower: " - << mData.mData.mWillpower.mFemale << std::endl; - std::cout << " Agility: " - << mData.mData.mAgility.mFemale << std::endl; - std::cout << " Speed: " - << mData.mData.mSpeed.mFemale << std::endl; - std::cout << " Endurance: " - << mData.mData.mEndurance.mFemale << std::endl; - std::cout << " Personality: " - << mData.mData.mPersonality.mFemale << std::endl; - std::cout << " Luck: " - << mData.mData.mLuck.mFemale << std::endl; - std::cout << " Height: " - << mData.mData.mHeight.mFemale << std::endl; - std::cout << " Weight: " - << mData.mData.mWeight.mFemale << std::endl; + std::cout << (male ? " Male:" : " Female:") << std::endl; + + for (int i=0; i<8; ++i) + std::cout << " " << sAttributeNames[i] << ": " + << mData.mData.mAttributeValues[i].getValue (male) << std::endl; + + std::cout << " Height: " << mData.mData.mHeight.getValue (male) << std::endl; + std::cout << " Weight: " << mData.mData.mWeight.getValue (male) << std::endl; + } for (int i = 0; i != 7; i++) // Not all races have 7 skills. @@ -1206,7 +1182,7 @@ void Record::print() std::cout << " Variable: " << *vit << std::endl; std::cout << " ByteCode: "; - std::vector::iterator cit; + std::vector::iterator cit; for (cit = mData.mScriptData.begin(); cit != mData.mScriptData.end(); cit++) std::cout << boost::format("%02X") % (int)(*cit); std::cout << std::endl; diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 7e818a74ab..824dff6e82 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -2,9 +2,9 @@ #define MAINDIALOG_H #include - +#ifndef Q_MOC_RUN #include - +#endif #include "settings/gamesettings.hpp" #include "settings/graphicssettings.hpp" #include "settings/launchersettings.hpp" diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index a305d90d93..5c15938bda 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 + universalid data record idcollection commands columnbase scriptcontext ) opencs_hdrs_noqt (model/world @@ -35,7 +35,8 @@ opencs_units (model/tools ) opencs_units_noqt (model/tools - stage verifier mandatoryid skillcheck + stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck + birthsigncheck spellcheck ) @@ -54,11 +55,11 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world - table tablesubview + table tablesubview scriptsubview ) opencs_units_noqt (view/world - dialoguesubview util subviews enumdelegate vartypedelegate + dialoguesubview util subviews enumdelegate vartypedelegate scripthighlighter ) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp new file mode 100644 index 0000000000..b673c93ded --- /dev/null +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -0,0 +1,39 @@ + +#include "birthsigncheck.hpp" + +#include +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns) +: mBirthsigns (birthsigns) +{} + +int CSMTools::BirthsignCheckStage::setup() +{ + return mBirthsigns.getSize(); +} + +void CSMTools::BirthsignCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::BirthSign& birthsign = mBirthsigns.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); + + // test for empty name, description and texture + if (birthsign.mName.empty()) + messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty name"); + + if (birthsign.mDescription.empty()) + messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty description"); + + if (birthsign.mTexture.empty()) + messages.push_back (id.toString() + "|" + birthsign.mId + " is missing a texture"); + + /// \todo test if the texture exists + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp new file mode 100644 index 0000000000..42b5a6b244 --- /dev/null +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_BIRTHSIGNCHECK_H +#define CSM_TOOLS_BIRTHSIGNCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that birthsign records are internally consistent + class BirthsignCheckStage : public Stage + { + const CSMWorld::IdCollection& mBirthsigns; + + public: + + BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp new file mode 100644 index 0000000000..da2e9f19a6 --- /dev/null +++ b/apps/opencs/model/tools/classcheck.cpp @@ -0,0 +1,72 @@ + +#include "classcheck.hpp" + +#include +#include + +#include +#include + +#include "../world/universalid.hpp" + +CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection& classes) +: mClasses (classes) +{} + +int CSMTools::ClassCheckStage::setup() +{ + return mClasses.getSize(); +} + +void CSMTools::ClassCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Class& class_= mClasses.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId); + + // test for empty name and description + if (class_.mName.empty()) + messages.push_back (id.toString() + "|" + class_.mId + " has an empty name"); + + if (class_.mDescription.empty()) + messages.push_back (id.toString() + "|" + class_.mId + " has an empty description"); + + // test for invalid attributes + for (int i=0; i<2; ++i) + if (class_.mData.mAttribute[i]==-1) + { + std::ostringstream stream; + + stream << id.toString() << "|Attribute #" << i << " of " << class_.mId << " is not set"; + + messages.push_back (stream.str()); + } + + if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) + { + std::ostringstream stream; + + stream << id.toString() << "|Class lists same attribute twice"; + + messages.push_back (stream.str()); + } + + // test for non-unique skill + std::map skills; // ID, number of occurrences + + for (int i=0; i<5; ++i) + for (int i2=0; i2<2; ++i2) + ++skills[class_.mData.mSkills[i][i2]]; + + for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) + if (iter->second>1) + { + std::ostringstream stream; + + stream + << id.toString() << "|" + << ESM::Skill::indexToId (iter->first) << " is listed more than once"; + + messages.push_back (stream.str()); + } +} \ No newline at end of file diff --git a/apps/opencs/model/tools/classcheck.hpp b/apps/opencs/model/tools/classcheck.hpp new file mode 100644 index 0000000000..a29d7c8b78 --- /dev/null +++ b/apps/opencs/model/tools/classcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_CLASSCHECK_H +#define CSM_TOOLS_CLASSCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that class records are internally consistent + class ClassCheckStage : public Stage + { + const CSMWorld::IdCollection& mClasses; + + public: + + ClassCheckStage (const CSMWorld::IdCollection& classes); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp new file mode 100644 index 0000000000..af26904efa --- /dev/null +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -0,0 +1,61 @@ + +#include "factioncheck.hpp" + +#include +#include + +#include +#include + +#include "../world/universalid.hpp" + +CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection& factions) +: mFactions (factions) +{} + +int CSMTools::FactionCheckStage::setup() +{ + return mFactions.getSize(); +} + +void CSMTools::FactionCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Faction& faction = mFactions.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId); + + // test for empty name + if (faction.mName.empty()) + messages.push_back (id.toString() + "|" + faction.mId + " has an empty name"); + + // test for invalid attributes + if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) + { + std::ostringstream stream; + + stream << id.toString() << "|Faction lists same attribute twice"; + + messages.push_back (stream.str()); + } + + // test for non-unique skill + std::map skills; // ID, number of occurrences + + for (int i=0; i<6; ++i) + if (faction.mData.mSkills[i]!=-1) + ++skills[faction.mData.mSkills[i]]; + + for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) + if (iter->second>1) + { + std::ostringstream stream; + + stream + << id.toString() << "|" + << ESM::Skill::indexToId (iter->first) << " is listed more than once"; + + messages.push_back (stream.str()); + } + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/factioncheck.hpp b/apps/opencs/model/tools/factioncheck.hpp new file mode 100644 index 0000000000..8686505727 --- /dev/null +++ b/apps/opencs/model/tools/factioncheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_FACTIONCHECK_H +#define CSM_TOOLS_FACTIONCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that faction records are internally consistent + class FactionCheckStage : public Stage + { + const CSMWorld::IdCollection& mFactions; + + public: + + FactionCheckStage (const CSMWorld::IdCollection& factions); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp new file mode 100644 index 0000000000..1e7a4cab45 --- /dev/null +++ b/apps/opencs/model/tools/racecheck.cpp @@ -0,0 +1,68 @@ + +#include "racecheck.hpp" + +#include + +#include + +#include "../world/universalid.hpp" + +void CSMTools::RaceCheckStage::performPerRecord (int stage, std::vector& messages) +{ + const ESM::Race& race = mRaces.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); + + // test for empty name and description + if (race.mName.empty()) + messages.push_back (id.toString() + "|" + race.mId + " has an empty name"); + + if (race.mDescription.empty()) + messages.push_back (id.toString() + "|" + race.mId + " has an empty description"); + + // test for positive height + if (race.mData.mHeight.mMale<=0) + messages.push_back (id.toString() + "|male " + race.mId + " has non-positive height"); + + if (race.mData.mHeight.mFemale<=0) + messages.push_back (id.toString() + "|female " + race.mId + " has non-positive height"); + + // test for non-negative weight + if (race.mData.mWeight.mMale<0) + messages.push_back (id.toString() + "|male " + race.mId + " has negative weight"); + + if (race.mData.mWeight.mFemale<0) + messages.push_back (id.toString() + "|female " + race.mId + " has negative weight"); + + // remember playable flag + if (race.mData.mFlags & 0x1) + mPlayable = true; + + /// \todo check data members that can't be edited in the table view +} + +void CSMTools::RaceCheckStage::performFinal (std::vector& messages) +{ + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races); + + if (!mPlayable) + messages.push_back (id.toString() + "|No playable race"); +} + +CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection& races) +: mRaces (races), mPlayable (false) +{} + +int CSMTools::RaceCheckStage::setup() +{ + mPlayable = false; + return mRaces.getSize()+1; +} + +void CSMTools::RaceCheckStage::perform (int stage, std::vector& messages) +{ + if (stage==mRaces.getSize()) + performFinal (messages); + else + performPerRecord (stage, messages); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/racecheck.hpp b/apps/opencs/model/tools/racecheck.hpp new file mode 100644 index 0000000000..155f799021 --- /dev/null +++ b/apps/opencs/model/tools/racecheck.hpp @@ -0,0 +1,34 @@ +#ifndef CSM_TOOLS_RACECHECK_H +#define CSM_TOOLS_RACECHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that race records are internally consistent + class RaceCheckStage : public Stage + { + const CSMWorld::IdCollection& mRaces; + bool mPlayable; + + void performPerRecord (int stage, std::vector& messages); + + void performFinal (std::vector& messages); + + public: + + RaceCheckStage (const CSMWorld::IdCollection& races); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/regioncheck.cpp b/apps/opencs/model/tools/regioncheck.cpp new file mode 100644 index 0000000000..ac64ac0271 --- /dev/null +++ b/apps/opencs/model/tools/regioncheck.cpp @@ -0,0 +1,33 @@ + +#include "regioncheck.hpp" + +#include +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection& regions) +: mRegions (regions) +{} + +int CSMTools::RegionCheckStage::setup() +{ + return mRegions.getSize(); +} + +void CSMTools::RegionCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Region& region = mRegions.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId); + + // test for empty name + if (region.mName.empty()) + messages.push_back (id.toString() + "|" + region.mId + " has an empty name"); + + /// \todo test that the ID in mSleeplist exists + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/regioncheck.hpp b/apps/opencs/model/tools/regioncheck.hpp new file mode 100644 index 0000000000..b421356514 --- /dev/null +++ b/apps/opencs/model/tools/regioncheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_REGIONCHECK_H +#define CSM_TOOLS_REGIONCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that region records are internally consistent + class RegionCheckStage : public Stage + { + const CSMWorld::IdCollection& mRegions; + + public: + + RegionCheckStage (const CSMWorld::IdCollection& regions); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp new file mode 100644 index 0000000000..52834e6594 --- /dev/null +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -0,0 +1,29 @@ + +#include "soundcheck.hpp" + +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection& sounds) +: mSounds (sounds) +{} + +int CSMTools::SoundCheckStage::setup() +{ + return mSounds.getSize(); +} + +void CSMTools::SoundCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Sound& sound = mSounds.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); + + if (sound.mData.mMinRange>sound.mData.mMaxRange) + messages.push_back (id.toString() + "|Maximum range larger than minimum range"); + + /// \todo check, if the sound file exists +} \ No newline at end of file diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp new file mode 100644 index 0000000000..a309763a12 --- /dev/null +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_SOUNDCHECK_H +#define CSM_TOOLS_SOUNDCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that sound records are internally consistent + class SoundCheckStage : public Stage + { + const CSMWorld::IdCollection& mSounds; + + public: + + SoundCheckStage (const CSMWorld::IdCollection& sounds); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp new file mode 100644 index 0000000000..3adee0a4ef --- /dev/null +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -0,0 +1,35 @@ + +#include "spellcheck.hpp" + +#include +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection& spells) +: mSpells (spells) +{} + +int CSMTools::SpellCheckStage::setup() +{ + return mSpells.getSize(); +} + +void CSMTools::SpellCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Spell& spell = mSpells.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); + + // test for empty name and description + if (spell.mName.empty()) + messages.push_back (id.toString() + "|" + spell.mId + " has an empty name"); + + // test for invalid cost values + if (spell.mData.mCost<0) + messages.push_back (id.toString() + "|" + spell.mId + " has a negative spell costs"); + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/spellcheck.hpp b/apps/opencs/model/tools/spellcheck.hpp new file mode 100644 index 0000000000..0566392193 --- /dev/null +++ b/apps/opencs/model/tools/spellcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_SPELLCHECK_H +#define CSM_TOOLS_SPELLCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that spell records are internally consistent + class SpellCheckStage : public Stage + { + const CSMWorld::IdCollection& mSpells; + + public: + + SpellCheckStage (const CSMWorld::IdCollection& spells); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 33cc3cc61b..803861203c 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -13,6 +13,13 @@ #include "reportmodel.hpp" #include "mandatoryid.hpp" #include "skillcheck.hpp" +#include "classcheck.hpp" +#include "factioncheck.hpp" +#include "racecheck.hpp" +#include "soundcheck.hpp" +#include "regioncheck.hpp" +#include "birthsigncheck.hpp" +#include "spellcheck.hpp" CSMTools::Operation *CSMTools::Tools::get (int type) { @@ -54,6 +61,20 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier() CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); mVerifier->appendStage (new SkillCheckStage (mData.getSkills())); + + mVerifier->appendStage (new ClassCheckStage (mData.getClasses())); + + mVerifier->appendStage (new FactionCheckStage (mData.getFactions())); + + mVerifier->appendStage (new RaceCheckStage (mData.getRaces())); + + mVerifier->appendStage (new SoundCheckStage (mData.getSounds())); + + mVerifier->appendStage (new RegionCheckStage (mData.getRegions())); + + mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); + + mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); } return mVerifier; diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 5c2ce8a676..fea618037c 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -34,7 +34,9 @@ namespace CSMWorld Display_GlobalVarType, Display_Specialisation, Display_Attribute, - Display_Boolean + Display_Boolean, + Display_SpellType, + Display_Script }; std::string mTitle; diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index cbcddd972d..9242e8a23b 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -5,6 +5,8 @@ #include +#include + #include "columnbase.hpp" namespace CSMWorld @@ -347,15 +349,15 @@ namespace CSMWorld int mIndex; bool mMajor; - SkillsColumn (int index, bool major) - : Column ((major ? "Major Skill #" : "Minor Skill #")+ + SkillsColumn (int index, bool typePrefix = false, bool major = false) + : Column ((typePrefix ? (major ? "Major Skill #" : "Minor Skill #") : "Skill #")+ boost::lexical_cast (index), ColumnBase::Display_String), mIndex (index), mMajor (major) {} virtual QVariant get (const Record& record) const { - int skill = record.get().mData.mSkills[mIndex][mMajor ? 1 : 0]; + int skill = record.get().mData.getSkill (mIndex, mMajor); return QString::fromUtf8 (ESM::Skill::indexToId (skill).c_str()); } @@ -373,7 +375,7 @@ namespace CSMWorld { ESXRecordT record2 = record.get(); - record2.mData.mSkills[mIndex][mMajor ? 1 : 0] = index; + record2.mData.getSkill (mIndex, mMajor) = index; record.setModified (record2); } @@ -409,6 +411,343 @@ namespace CSMWorld return true; } }; + + template + struct HiddenColumn : public Column + { + HiddenColumn() : Column ("Hidden", ColumnBase::Display_Boolean) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mIsHidden!=0; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mIsHidden = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct FlagColumn : public Column + { + int mMask; + + FlagColumn (const std::string& name, int mask) + : Column (name, ColumnBase::Display_Boolean), mMask (mask) + {} + + virtual QVariant get (const Record& record) const + { + return (record.get().mData.mFlags & mMask)!=0; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + int flags = record2.mData.mFlags & ~mMask; + + if (data.toInt()) + flags |= mMask; + + record2.mData.mFlags = flags; + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct WeightHeightColumn : public Column + { + bool mMale; + bool mWeight; + + WeightHeightColumn (bool male, bool weight) + : Column (male ? (weight ? "Male Weight" : "Male Height") : + (weight ? "Female Weight" : "Female Height"), ColumnBase::Display_Float), + mMale (male), mWeight (weight) + {} + + virtual QVariant get (const Record& record) const + { + const ESM::Race::MaleFemaleF& value = + mWeight ? record.get().mData.mWeight : record.get().mData.mHeight; + + return mMale ? value.mMale : value.mFemale; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + ESM::Race::MaleFemaleF& value = + mWeight ? record2.mData.mWeight : record2.mData.mHeight; + + (mMale ? value.mMale : value.mFemale) = data.toFloat(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SoundParamColumn : public Column + { + enum Type + { + Type_Volume, + Type_MinRange, + Type_MaxRange + }; + + Type mType; + + SoundParamColumn (Type type) + : Column ( + type==Type_Volume ? "Volume" : (type==Type_MinRange ? "Min Range" : "Max Range"), + ColumnBase::Display_Integer), + mType (type) + {} + + virtual QVariant get (const Record& record) const + { + int value = 0; + + switch (mType) + { + case Type_Volume: value = record.get().mData.mVolume; break; + case Type_MinRange: value = record.get().mData.mMinRange; break; + case Type_MaxRange: value = record.get().mData.mMaxRange; break; + } + + return value; + } + + virtual void set (Record& record, const QVariant& data) + { + int value = data.toInt(); + + if (value<0) + value = 0; + else if (value>255) + value = 255; + + ESXRecordT record2 = record.get(); + + switch (mType) + { + case Type_Volume: record2.mData.mVolume = static_cast (value); break; + case Type_MinRange: record2.mData.mMinRange = static_cast (value); break; + case Type_MaxRange: record2.mData.mMaxRange = static_cast (value); break; + } + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SoundFileColumn : public Column + { + SoundFileColumn() : Column ("Sound File", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mSound.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mSound = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + /// \todo QColor is a GUI class and should not be in model. Need to think of an alternative + /// solution. + template + struct MapColourColumn : public Column + { + /// \todo Replace Display_Integer with something that displays the colour value more directly. + MapColourColumn() : Column ("Map Colour", ColumnBase::Display_Integer) {} + + virtual QVariant get (const Record& record) const + { + int colour = record.get().mMapColor; + + return QColor (colour & 0xff, (colour>>8) & 0xff, (colour>>16) & 0xff); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + QColor colour = data.value(); + + record2.mMapColor = colour.rgb() & 0xffffff; + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SleepListColumn : public Column + { + SleepListColumn() : Column ("Sleep Encounter", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mSleepList.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mSleepList = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct TextureColumn : public Column + { + TextureColumn() : Column ("Texture", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mTexture.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mTexture = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SpellTypeColumn : public Column + { + SpellTypeColumn() : Column ("Type", ColumnBase::Display_SpellType) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mType; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mType = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct CostColumn : public Column + { + CostColumn() : Column ("Cost", ColumnBase::Display_Integer) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mCost; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mCost = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct ScriptColumn : public Column + { + ScriptColumn() : Column ("Script", ColumnBase::Display_Script, 0) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mScriptText.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mScriptText = 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 7b3e667715..9f6b186c0c 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -33,11 +33,13 @@ CSMWorld::Data::Data() mGmsts.addColumn (new StringIdColumn); mGmsts.addColumn (new RecordStateColumn); mGmsts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Gmst)); + mGmsts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Gmst)); mGmsts.addColumn (new VarTypeColumn (ColumnBase::Display_GmstVarType)); mGmsts.addColumn (new VarValueColumn); mSkills.addColumn (new StringIdColumn); mSkills.addColumn (new RecordStateColumn); + mSkills.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Skill)); mSkills.addColumn (new AttributeColumn); mSkills.addColumn (new SpecialisationColumn); for (int i=0; i<4; ++i) @@ -46,21 +48,88 @@ CSMWorld::Data::Data() mClasses.addColumn (new StringIdColumn); mClasses.addColumn (new RecordStateColumn); + mClasses.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Class)); mClasses.addColumn (new NameColumn); mClasses.addColumn (new AttributesColumn (0)); mClasses.addColumn (new AttributesColumn (1)); mClasses.addColumn (new SpecialisationColumn); for (int i=0; i<5; ++i) - mClasses.addColumn (new SkillsColumn (i, true)); + mClasses.addColumn (new SkillsColumn (i, true, true)); for (int i=0; i<5; ++i) - mClasses.addColumn (new SkillsColumn (i, false)); + mClasses.addColumn (new SkillsColumn (i, true, false)); mClasses.addColumn (new PlayableColumn); mClasses.addColumn (new DescriptionColumn); + mFactions.addColumn (new StringIdColumn); + mFactions.addColumn (new RecordStateColumn); + mFactions.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Faction)); + mFactions.addColumn (new NameColumn); + mFactions.addColumn (new AttributesColumn (0)); + mFactions.addColumn (new AttributesColumn (1)); + mFactions.addColumn (new HiddenColumn); + for (int i=0; i<6; ++i) + mFactions.addColumn (new SkillsColumn (i)); + + mRaces.addColumn (new StringIdColumn); + mRaces.addColumn (new RecordStateColumn); + mRaces.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Race)); + mRaces.addColumn (new NameColumn); + mRaces.addColumn (new DescriptionColumn); + mRaces.addColumn (new FlagColumn ("Playable", 0x1)); + mRaces.addColumn (new FlagColumn ("Beast Race", 0x2)); + mRaces.addColumn (new WeightHeightColumn (true, true)); + mRaces.addColumn (new WeightHeightColumn (true, false)); + mRaces.addColumn (new WeightHeightColumn (false, true)); + mRaces.addColumn (new WeightHeightColumn (false, false)); + + mSounds.addColumn (new StringIdColumn); + mSounds.addColumn (new RecordStateColumn); + mSounds.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Sound)); + mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_Volume)); + mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_MinRange)); + mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_MaxRange)); + mSounds.addColumn (new SoundFileColumn); + + mScripts.addColumn (new StringIdColumn); + mScripts.addColumn (new RecordStateColumn); + mScripts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Script)); + mScripts.addColumn (new ScriptColumn); + + mRegions.addColumn (new StringIdColumn); + mRegions.addColumn (new RecordStateColumn); + mRegions.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Region)); + mRegions.addColumn (new NameColumn); + mRegions.addColumn (new MapColourColumn); + mRegions.addColumn (new SleepListColumn); + + mBirthsigns.addColumn (new StringIdColumn); + mBirthsigns.addColumn (new RecordStateColumn); + mBirthsigns.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Birthsign)); + mBirthsigns.addColumn (new NameColumn); + mBirthsigns.addColumn (new TextureColumn); + mBirthsigns.addColumn (new DescriptionColumn); + + mSpells.addColumn (new StringIdColumn); + mSpells.addColumn (new RecordStateColumn); + mSpells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Spell)); + mSpells.addColumn (new NameColumn); + mSpells.addColumn (new SpellTypeColumn); + mSpells.addColumn (new CostColumn); + mSpells.addColumn (new FlagColumn ("Autocalc", 0x1)); + mSpells.addColumn (new FlagColumn ("Starter Spell", 0x2)); + mSpells.addColumn (new FlagColumn ("Always Succeeds", 0x4)); + 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); addModel (new IdTable (&mClasses), UniversalId::Type_Classes, UniversalId::Type_Class); + addModel (new IdTable (&mFactions), UniversalId::Type_Factions, UniversalId::Type_Faction); + addModel (new IdTable (&mRaces), UniversalId::Type_Races, UniversalId::Type_Race); + addModel (new IdTable (&mSounds), UniversalId::Type_Sounds, UniversalId::Type_Sound); + addModel (new IdTable (&mScripts), UniversalId::Type_Scripts, UniversalId::Type_Script); + 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); } CSMWorld::Data::~Data() @@ -99,6 +168,86 @@ CSMWorld::IdCollection& CSMWorld::Data::getSkills() return mSkills; } +const CSMWorld::IdCollection& CSMWorld::Data::getClasses() const +{ + return mClasses; +} + +CSMWorld::IdCollection& CSMWorld::Data::getClasses() +{ + return mClasses; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getFactions() const +{ + return mFactions; +} + +CSMWorld::IdCollection& CSMWorld::Data::getFactions() +{ + return mFactions; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getRaces() const +{ + return mRaces; +} + +CSMWorld::IdCollection& CSMWorld::Data::getRaces() +{ + return mRaces; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getSounds() const +{ + return mSounds; +} + +CSMWorld::IdCollection& CSMWorld::Data::getSounds() +{ + return mSounds; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getScripts() const +{ + return mScripts; +} + +CSMWorld::IdCollection& CSMWorld::Data::getScripts() +{ + return mScripts; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getRegions() const +{ + return mRegions; +} + +CSMWorld::IdCollection& CSMWorld::Data::getRegions() +{ + return mRegions; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getBirthsigns() const +{ + return mBirthsigns; +} + +CSMWorld::IdCollection& CSMWorld::Data::getBirthsigns() +{ + return mBirthsigns; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getSpells() const +{ + return mSpells; +} + +CSMWorld::IdCollection& CSMWorld::Data::getSpells() +{ + return mSpells; +} + QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) { std::map::iterator iter = mModelIndex.find (id.getType()); @@ -137,6 +286,13 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) case ESM::REC_GMST: mGmsts.load (reader, base); break; case ESM::REC_SKIL: mSkills.load (reader, base); break; case ESM::REC_CLAS: mClasses.load (reader, base); break; + case ESM::REC_FACT: mFactions.load (reader, base); break; + case ESM::REC_RACE: mRaces.load (reader, base); break; + case ESM::REC_SOUN: mSounds.load (reader, base); break; + case ESM::REC_SCPT: mScripts.load (reader, base); break; + 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; default: diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 7baf03259e..d7b69ba5e3 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -10,6 +10,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include "idcollection.hpp" #include "universalid.hpp" @@ -24,6 +31,13 @@ namespace CSMWorld IdCollection mGmsts; IdCollection mSkills; IdCollection mClasses; + IdCollection mFactions; + IdCollection mRaces; + IdCollection mSounds; + IdCollection mScripts; + IdCollection mRegions; + IdCollection mBirthsigns; + IdCollection mSpells; std::vector mModels; std::map mModelIndex; @@ -52,6 +66,38 @@ namespace CSMWorld IdCollection& getSkills(); + const IdCollection& getClasses() const; + + IdCollection& getClasses(); + + const IdCollection& getFactions() const; + + IdCollection& getFactions(); + + const IdCollection& getRaces() const; + + IdCollection& getRaces(); + + const IdCollection& getSounds() const; + + IdCollection& getSounds(); + + const IdCollection& getScripts() const; + + IdCollection& getScripts(); + + const IdCollection& getRegions() const; + + IdCollection& getRegions(); + + const IdCollection& getBirthsigns() const; + + IdCollection& getBirthsigns(); + + const IdCollection& getSpells() const; + + IdCollection& getSpells(); + 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/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp new file mode 100644 index 0000000000..69b72abf26 --- /dev/null +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -0,0 +1,22 @@ + +#include "scriptcontext.hpp" + +bool CSMWorld::ScriptContext::canDeclareLocals() const +{ + return false; +} + +char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const +{ + return ' '; +} + +char CSMWorld::ScriptContext::getMemberType (const std::string& name, const std::string& id) const +{ + return ' '; +} + +bool CSMWorld::ScriptContext::isId (const std::string& name) const +{ + return false; +} \ No newline at end of file diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp new file mode 100644 index 0000000000..1231aea649 --- /dev/null +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -0,0 +1,26 @@ +#ifndef CSM_WORLD_SCRIPTCONTEXT_H +#define CSM_WORLD_SCRIPTCONTEXT_H + +#include + +namespace CSMWorld +{ + class ScriptContext : public Compiler::Context + { + public: + + virtual bool canDeclareLocals() const; + ///< Is the compiler allowed to declare local variables? + + virtual char getGlobalType (const std::string& name) const; + ///< 'l: long, 's': short, 'f': float, ' ': does not exist. + + virtual char getMemberType (const std::string& name, const std::string& id) const; + ///< 'l: long, 's': short, 'f': float, ' ': does not exist. + + virtual bool isId (const std::string& name) const; + ///< Does \a name match an ID, that can be referenced? + }; +} + +#endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index a85b30c2a8..11f4877886 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -21,6 +21,13 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts" }, + { 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_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -31,6 +38,13 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script" }, + { 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_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -51,44 +65,41 @@ CSMWorld::UniversalId::UniversalId (const std::string& universalId) { std::string type = universalId.substr (0, index); - if (index==std::string::npos) - { - for (int i=0; sNoArg[i].mName; ++i) - if (type==sNoArg[i].mName) - { - mArgumentType = ArgumentType_None; - mType = sNoArg[i].mType; - mClass = sNoArg[i].mClass; + for (int i=0; sIdArg[i].mName; ++i) + if (type==sIdArg[i].mName) + { + mArgumentType = ArgumentType_Id; + mType = sIdArg[i].mType; + mClass = sIdArg[i].mClass; + mId = universalId.substr (index+2); + return; + } + + for (int i=0; sIndexArg[i].mName; ++i) + if (type==sIndexArg[i].mName) + { + mArgumentType = ArgumentType_Index; + mType = sIndexArg[i].mType; + mClass = sIndexArg[i].mClass; + + std::istringstream stream (universalId.substr (index+2)); + + if (stream >> mIndex) return; - } - } - else - { - for (int i=0; sIdArg[i].mName; ++i) - if (type==sIdArg[i].mName) - { - mArgumentType = ArgumentType_Id; - mType = sIdArg[i].mType; - mClass = sIdArg[i].mClass; - mId = universalId.substr (0, index); - return; - } - for (int i=0; sIndexArg[i].mName; ++i) - if (type==sIndexArg[i].mName) - { - mArgumentType = ArgumentType_Index; - mType = sIndexArg[i].mType; - mClass = sIndexArg[i].mClass; - - std::istringstream stream (universalId.substr (0, index)); - - if (stream >> mIndex) - return; - - break; - } - } + break; + } + } + else + { + for (int i=0; sNoArg[i].mName; ++i) + if (universalId==sNoArg[i].mName) + { + mArgumentType = ArgumentType_None; + mType = sNoArg[i].mType; + mClass = sNoArg[i].mClass; + return; + } } throw std::runtime_error ("invalid UniversalId: " + universalId); diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 4c4d95654b..5586b22e79 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -41,7 +41,21 @@ namespace CSMWorld Type_Skills, Type_Skill, Type_Classes, - Type_Class + Type_Class, + Type_Factions, + Type_Faction, + Type_Races, + Type_Race, + Type_Sounds, + Type_Sound, + Type_Scripts, + Type_Script, + Type_Regions, + Type_Region, + Type_Birthsigns, + Type_Birthsign, + Type_Spells, + Type_Spell }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 2ef66593f7..dfdcb10365 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -41,6 +41,10 @@ void CSVDoc::View::setupFileMenu() connect (mSave, SIGNAL (triggered()), this, SLOT (save())); file->addAction (mSave); + mVerify = new QAction (tr ("&Verify"), this); + connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); + file->addAction (mVerify); + QAction *close = new QAction (tr ("&Close"), this); connect (close, SIGNAL (triggered()), this, SLOT (close())); file->addAction(close); @@ -94,9 +98,33 @@ void CSVDoc::View::setupWorldMenu() connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); world->addAction (classes); - mVerify = new QAction (tr ("&Verify"), this); - connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); - world->addAction (mVerify); + QAction *factions = new QAction (tr ("Factions"), this); + connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); + world->addAction (factions); + + QAction *races = new QAction (tr ("Races"), this); + connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); + world->addAction (races); + + QAction *sounds = new QAction (tr ("Sounds"), this); + connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); + world->addAction (sounds); + + QAction *scripts = new QAction (tr ("Scripts"), this); + connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); + world->addAction (scripts); + + QAction *regions = new QAction (tr ("Regions"), this); + connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView())); + world->addAction (regions); + + QAction *birthsigns = new QAction (tr ("Birthsigns"), this); + connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); + world->addAction (birthsigns); + + QAction *spells = new QAction (tr ("Spells"), this); + connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); + world->addAction (spells); } void CSVDoc::View::setupUi() @@ -262,6 +290,41 @@ void CSVDoc::View::addClassesSubView() addSubView (CSMWorld::UniversalId::Type_Classes); } +void CSVDoc::View::addFactionsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Factions); +} + +void CSVDoc::View::addRacesSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Races); +} + +void CSVDoc::View::addSoundsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Sounds); +} + +void CSVDoc::View::addScriptsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Scripts); +} + +void CSVDoc::View::addRegionsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Regions); +} + +void CSVDoc::View::addBirthsignsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Birthsigns); +} + +void CSVDoc::View::addSpellsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Spells); +} + 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 bc8e8fc26d..9241efbb9a 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -119,6 +119,20 @@ namespace CSVDoc void addSkillsSubView(); void addClassesSubView(); + + void addFactionsSubView(); + + void addRacesSubView(); + + void addSoundsSubView(); + + void addScriptsSubView(); + + void addRegionsSubView(); + + void addBirthsignsSubView(); + + void addSpellsSubView(); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index bc87728945..050bd51fe2 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -49,6 +49,11 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) "Luck", 0 }; + static const char *sSpellTypes[] = + { + "Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0 + }; + mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType, @@ -61,7 +66,10 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) new CSVWorld::EnumDelegateFactory (sSpecialisations)); mDelegateFactories->add (CSMWorld::ColumnBase::Display_Attribute, - new CSVWorld::EnumDelegateFactory (sAttributes)); + new CSVWorld::EnumDelegateFactory (sAttributes, true)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_SpellType, + new CSVWorld::EnumDelegateFactory (sSpellTypes)); } CSVDoc::ViewManager::~ViewManager() diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 0dd0a1d594..b1e9f72865 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -92,10 +92,13 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte CSVWorld::EnumDelegateFactory::EnumDelegateFactory() {} -CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names) +CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names, bool allowNone) { assert (names); + if (allowNone) + add (-1, ""); + for (int i=0; names[i]; ++i) add (i, names[i]); } diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index 752ed5be72..58f19ff782 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -47,8 +47,9 @@ namespace CSVWorld EnumDelegateFactory(); - EnumDelegateFactory (const char **names); + EnumDelegateFactory (const char **names, bool allowNone = false); ///< \param names Array of char pointer with a 0-pointer as end mark + /// \param allowNone Use value of -1 for "none selected" (empty string) virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. diff --git a/apps/opencs/view/world/scripthighlighter.cpp b/apps/opencs/view/world/scripthighlighter.cpp new file mode 100644 index 0000000000..288a3d12ac --- /dev/null +++ b/apps/opencs/view/world/scripthighlighter.cpp @@ -0,0 +1,118 @@ + +#include "scripthighlighter.hpp" + +#include + +#include + +bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Int); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseFloat (float value, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Float); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseName (const std::string& name, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Name); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseKeyword (int keyword, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Keyword); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseSpecial (int code, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Special); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseComment (const std::string& comment, + const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) +{ + highlight (loc, Type_Comment); + return true; +} + +void CSVWorld::ScriptHighlighter::parseEOF (Compiler::Scanner& scanner) +{} + +void CSVWorld::ScriptHighlighter::highlight (const Compiler::TokenLoc& loc, Type type) +{ + int length = static_cast (loc.mLiteral.size()); + + int index = loc.mColumn; + + // compensate for bug in Compiler::Scanner (position of token is the character after the token) + index -= length; + + setFormat (index, length, mScheme[type]); +} + +CSVWorld::ScriptHighlighter::ScriptHighlighter (QTextDocument *parent) +: QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext) +{ + /// \ŧodo replace this with user settings + { + QTextCharFormat format; + format.setForeground (Qt::darkMagenta); + mScheme.insert (std::make_pair (Type_Int, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::magenta); + mScheme.insert (std::make_pair (Type_Float, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::gray); + mScheme.insert (std::make_pair (Type_Name, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::red); + mScheme.insert (std::make_pair (Type_Keyword, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::darkYellow); + mScheme.insert (std::make_pair (Type_Special, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::green); + mScheme.insert (std::make_pair (Type_Comment, format)); + } +} + +void CSVWorld::ScriptHighlighter::highlightBlock (const QString& text) +{ + std::istringstream stream (text.toUtf8().constData()); + + Compiler::Scanner scanner (mErrorHandler, stream, mContext.getExtensions()); + + try + { + scanner.scan (*this); + } + catch (...) {} // ignore syntax errors + +} \ No newline at end of file diff --git a/apps/opencs/view/world/scripthighlighter.hpp b/apps/opencs/view/world/scripthighlighter.hpp new file mode 100644 index 0000000000..3ef6978097 --- /dev/null +++ b/apps/opencs/view/world/scripthighlighter.hpp @@ -0,0 +1,80 @@ +#ifndef CSV_WORLD_SCRIPTHIGHLIGHTER_H +#define CSV_WORLD_SCRIPTHIGHLIGHTER_H + +#include + +#include + +#include +#include + +#include "../../model/world/scriptcontext.hpp" + +namespace CSVWorld +{ + class ScriptHighlighter : public QSyntaxHighlighter, private Compiler::Parser + { + public: + + enum Type + { + Type_Int, + Type_Float, + Type_Name, + Type_Keyword, + Type_Special, + Type_Comment + }; + + private: + + Compiler::NullErrorHandler mErrorHandler; + CSMWorld::ScriptContext mContext; + std::map mScheme; + + private: + + virtual bool parseInt (int value, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle an int token. + /// \return fetch another token? + + virtual bool parseFloat (float value, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle a float token. + /// \return fetch another token? + + virtual bool parseName (const std::string& name, + const Compiler::TokenLoc& loc, Compiler::Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + + virtual bool parseKeyword (int keyword, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle a keyword token. + /// \return fetch another token? + + virtual bool parseSpecial (int code, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + + virtual bool parseComment (const std::string& comment, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle comment token. + /// \return fetch another token? + + virtual void parseEOF (Compiler::Scanner& scanner); + ///< Handle EOF token. + + void highlight (const Compiler::TokenLoc& loc, Type type); + + public: + + ScriptHighlighter (QTextDocument *parent); + + virtual void highlightBlock (const QString& text); + }; +} + +#endif diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp new file mode 100644 index 0000000000..ab1c2d57c6 --- /dev/null +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -0,0 +1,99 @@ + +#include "scriptsubview.hpp" + +#include + +#include + +#include "../../model/doc/document.hpp" +#include "../../model/world/universalid.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/columnbase.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtable.hpp" + +#include "scripthighlighter.hpp" + +CSVWorld::ScriptSubView::ChangeLock::ChangeLock (ScriptSubView& view) : mView (view) +{ + ++mView.mChangeLocked; +} + +CSVWorld::ScriptSubView::ChangeLock::~ChangeLock() +{ + --mView.mChangeLocked; +} + +CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) +: SubView (id), mDocument (document), mColumn (-1), mChangeLocked (0) +{ + setWidget (mEditor = new QTextEdit (this)); + + mEditor->setAcceptRichText (false); + mEditor->setLineWrapMode (QTextEdit::NoWrap); + mEditor->setTabStopWidth (4); + mEditor->setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead + + mModel = &dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); + + for (int i=0; icolumnCount(); ++i) + if (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display)== + CSMWorld::ColumnBase::Display_Script) + { + mColumn = i; + break; + } + + if (mColumn==-1) + throw std::logic_error ("Can't find script column"); + + mEditor->setPlainText (mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString()); + + connect (mEditor, SIGNAL (textChanged()), this, SLOT (textChanged())); + + connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&))); + + connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (rowsAboutToBeRemoved (const QModelIndex&, int, int))); + + new ScriptHighlighter (mEditor->document()); +} + +void CSVWorld::ScriptSubView::setEditLock (bool locked) +{ + mEditor->setReadOnly (locked); +} + +void CSVWorld::ScriptSubView::textChanged() +{ + ChangeLock lock (*this); + + mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel, + mModel->getModelIndex (getUniversalId().getId(), mColumn), mEditor->toPlainText())); +} + +void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + if (mChangeLocked) + return; + + QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); + + if (index.row()>=topLeft.row() && index.row()<=bottomRight.row() && + index.column()>=topLeft.column() && index.column()<=bottomRight.column()) + { + QTextCursor cursor = mEditor->textCursor(); + mEditor->setPlainText (mModel->data (index).toString()); + mEditor->setTextCursor (cursor); + } +} + +void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); + + if (!parent.isValid() && index.row()>=start && index.row()<=end) + deleteLater(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/scriptsubview.hpp b/apps/opencs/view/world/scriptsubview.hpp new file mode 100644 index 0000000000..07d87d9476 --- /dev/null +++ b/apps/opencs/view/world/scriptsubview.hpp @@ -0,0 +1,62 @@ +#ifndef CSV_WORLD_SCRIPTSUBVIEW_H +#define CSV_WORLD_SCRIPTSUBVIEW_H + +#include "../doc/subview.hpp" + +class QTextEdit; +class QModelIndex; + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSVWorld +{ + class ScriptSubView : public CSVDoc::SubView + { + Q_OBJECT + + QTextEdit *mEditor; + CSMDoc::Document& mDocument; + CSMWorld::IdTable *mModel; + int mColumn; + int mChangeLocked; + + class ChangeLock + { + ScriptSubView& mView; + + ChangeLock (const ChangeLock&); + ChangeLock& operator= (const ChangeLock&); + + public: + + ChangeLock (ScriptSubView& view); + ~ChangeLock(); + }; + + friend class ChangeLock; + + public: + + ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); + + virtual void setEditLock (bool locked); + + private slots: + + void textChanged(); + + void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 5d715ea21e..8f7887f3b9 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -5,20 +5,35 @@ #include "tablesubview.hpp" #include "dialoguesubview.hpp" +#include "scriptsubview.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { - manager.add (CSMWorld::UniversalId::Type_Globals, - new CSVDoc::SubViewFactoryWithCreateFlag (true)); - manager.add (CSMWorld::UniversalId::Type_Gmsts, new CSVDoc::SubViewFactoryWithCreateFlag (false)); manager.add (CSMWorld::UniversalId::Type_Skills, new CSVDoc::SubViewFactoryWithCreateFlag (false)); - manager.add (CSMWorld::UniversalId::Type_Classes, - new CSVDoc::SubViewFactoryWithCreateFlag (true)); + static const CSMWorld::UniversalId::Type sTableTypes[] = + { + CSMWorld::UniversalId::Type_Globals, + CSMWorld::UniversalId::Type_Classes, + CSMWorld::UniversalId::Type_Factions, + CSMWorld::UniversalId::Type_Races, + CSMWorld::UniversalId::Type_Sounds, + CSMWorld::UniversalId::Type_Scripts, + CSMWorld::UniversalId::Type_Regions, + CSMWorld::UniversalId::Type_Birthsigns, + CSMWorld::UniversalId::Type_Spells, + + CSMWorld::UniversalId::Type_None // end marker + }; + + for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i) + manager.add (sTableTypes[i], new CSVDoc::SubViewFactoryWithCreateFlag (true)); + + manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); // manager.add (CSMWorld::UniversalId::Type_Global, // new CSVDoc::SubViewFactoryWithCreateFlag (true)); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index f4deceb490..bb4bb76c61 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -21,6 +21,5 @@ void CSVWorld::TableSubView::setEditLock (bool locked) void CSVWorld::TableSubView::rowActivated (const QModelIndex& index) { - /// \todo re-enable, after dialogue sub views have been fixed up -// focusId (mTable->getUniversalId (index.row())); + focusId (mTable->getUniversalId (index.row())); } \ No newline at end of file diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index c122d886dc..3334e7865c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -24,9 +24,9 @@ add_openmw_dir (mwinput ) add_openmw_dir (mwgui - text_input widgets race class birth review windowmanagerimp console dialogue - dialogue_history window_base stats_window messagebox journalwindow charactercreation - map_window window_pinnable_base tooltips scrollwindow bookwindow list + textinput widgets race class birth review windowmanagerimp console dialogue + dialoguehistory windowbase statswindow messagebox journalwindow charactercreation + mapwindow windowpinnablebase tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog @@ -54,7 +54,7 @@ add_openmw_dir (mwworld containerstore actiontalk actiontake manualref player cellfunctors failedaction cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat - esmstore store recordcmp fallback actionrepair actionsoulgem + esmstore store recordcmp fallback actionrepair actionsoulgem livecellref ) add_openmw_dir (mwclass diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index ce84b8dfe1..17610e479f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -66,6 +66,7 @@ bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt) { if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame); + MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame); return true; } @@ -153,28 +154,42 @@ OMW::Engine::~Engine() void OMW::Engine::loadBSA() { + // We use separate resource groups to handle location priority. + const Files::PathContainer& dataDirs = mFileCollections.getPaths(); + + int i=0; + 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); + Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); + + std::string dataDirectory = iter->string(); + std::cout << "Data dir " << dataDirectory << std::endl; + Bsa::addDir(dataDirectory, mFSStrict, groupName); + ++i; + } + + i=0; for (std::vector::const_iterator archive = mArchives.begin(); archive != mArchives.end(); ++archive) { if (mFileCollections.doesExist(*archive)) { + // Last BSA has the highest priority + std::string groupName = "DataBSA" + Ogre::StringConverter::toString(dataDirs.size()-i); + + Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); + const std::string archivePath = mFileCollections.getPath(*archive).string(); std::cout << "Adding BSA archive " << archivePath << std::endl; - Bsa::addBSA(archivePath); + Bsa::addBSA(archivePath, groupName); + ++i; } else { std::cout << "Archive " << *archive << " not found" << std::endl; } } - - const Files::PathContainer& dataDirs = mFileCollections.getPaths(); - std::string dataDirectory; - for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) - { - dataDirectory = iter->string(); - std::cout << "Data dir " << dataDirectory << std::endl; - Bsa::addDir(dataDirectory, mFSStrict); - } } // add resources directory diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 4d66a77423..976d7d84c1 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -182,8 +182,8 @@ namespace MWBase virtual void activateQuickKey (int index) = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; - virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) = 0; - virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) = 0; + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0; + virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; @@ -246,6 +246,8 @@ namespace MWBase virtual void showSoulgemDialog (MWWorld::Ptr item) = 0; + virtual void frameStarted(float dt) = 0; + virtual void changePointer (const std::string& name) = 0; virtual const Translation::Storage& getTranslationDataStorage() const = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8eea383eb4..39e985890a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -216,6 +216,9 @@ namespace MWBase virtual MWWorld::Ptr getFacedObject() = 0; ///< Return pointer to the object the player is looking at, if it is within activation range + virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0; + ///< Adjust position after load to be on ground. Must be called after model load. + virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; @@ -316,6 +319,7 @@ namespace MWBase virtual void allowVanityMode(bool allow) = 0; virtual void togglePlayerLooking(bool enable) = 0; virtual void changeVanityModeScale(float factor) = 0; + virtual bool vanityRotateCamera(float * rot) = 0; virtual void renderPlayer() = 0; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 851a5ae360..32c1277318 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -159,4 +159,9 @@ namespace MWClass return MWWorld::Ptr(&cell.mAppas.insert(*ref), &cell); } + + bool Apparatus::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Apparatus; + } } diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 7045f62d6f..d4917c6186 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -54,6 +54,8 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 281dd1f42c..6791138d35 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -16,6 +16,8 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" @@ -260,6 +262,8 @@ namespace MWClass } info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; info.text = text; @@ -274,7 +278,7 @@ namespace MWClass return ref->mBase->mEnchant; } - std::string Armor::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + void Armor::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { MWWorld::LiveCellRef *ref = ptr.get(); @@ -285,7 +289,77 @@ namespace MWClass newItem.mData.mEnchant=enchCharge; newItem.mEnchant=enchId; const ESM::Armor *record = MWBase::Environment::get().getWorld()->createRecord (newItem); - return record->mId; + ref->mBase = record; + } + + int Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + + // slots that this item can be equipped in + std::pair, bool> slots = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + + std::string npcRace = npc.get()->mBase->mRace; + + for (std::vector::const_iterator slot=slots.first.begin(); + slot!=slots.first.end(); ++slot) + { + + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) + { + std::vector parts = ptr.get()->mBase->mParts.mParts; + + if(*slot == MWWorld::InventoryStore::Slot_Helmet) + { + 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; + } + } + } + + if (*slot == MWWorld::InventoryStore::Slot_Boots) + { + 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 ("#{sNotifyMessage14}"); + return 0; + } + } + } + } + + if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) + { + MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if(weapon == invStore.end()) + return 1; + + if(weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + { + return 3; + } + return 1; + } + } + return 1; } boost::shared_ptr Armor::use (const MWWorld::Ptr& ptr) const @@ -313,4 +387,9 @@ namespace MWClass return ref->mBase->mData.mEnchant; } + + bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Armor; + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 0c32015a35..703d3af1ed 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -65,7 +65,10 @@ namespace MWClass virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + 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 boost::shared_ptr use (const MWWorld::Ptr& ptr) const; @@ -74,6 +77,8 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 644561e52b..b658295f85 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -147,7 +147,7 @@ namespace MWClass return ref->mBase->mEnchant; } - std::string Book::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + void Book::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { MWWorld::LiveCellRef *ref = ptr.get(); @@ -159,7 +159,7 @@ namespace MWClass newItem.mData.mEnchant=enchCharge; newItem.mEnchant=enchId; const ESM::Book *record = MWBase::Environment::get().getWorld()->createRecord (newItem); - return record->mId; + ref->mBase = record; } boost::shared_ptr Book::use (const MWWorld::Ptr& ptr) const @@ -183,4 +183,9 @@ namespace MWClass return ref->mBase->mData.mEnchant; } + + bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Books; + } } diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index c17d4255b1..3f083f5525 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -51,7 +51,7 @@ namespace MWClass virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu @@ -59,6 +59,8 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 8f29a2084d..6c231e0c60 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -14,6 +14,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/player.hpp" #include "../mwgui/tooltips.hpp" @@ -207,6 +208,8 @@ namespace MWClass } info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; info.text = text; @@ -221,7 +224,7 @@ namespace MWClass return ref->mBase->mEnchant; } - std::string Clothing::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + void Clothing::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { MWWorld::LiveCellRef *ref = ptr.get(); @@ -232,7 +235,58 @@ namespace MWClass newItem.mData.mEnchant=enchCharge; newItem.mEnchant=enchId; const ESM::Clothing *record = MWBase::Environment::get().getWorld()->createRecord (newItem); - return record->mId; + ref->mBase = record; + } + + int 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); + + std::string npcRace = npc.get()->mBase->mRace; + + for (std::vector::const_iterator slot=slots.first.begin(); + slot!=slots.first.end(); ++slot) + { + + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) + { + std::vector parts = ptr.get()->mBase->mParts.mParts; + + if(*slot == MWWorld::InventoryStore::Slot_Helmet) + { + 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; + } + } + } + + if (*slot == MWWorld::InventoryStore::Slot_Boots) + { + 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 1; } boost::shared_ptr Clothing::use (const MWWorld::Ptr& ptr) const @@ -260,4 +314,9 @@ namespace MWClass return ref->mBase->mData.mEnchant; } + + bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Clothing; + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 4457e79fb6..1be42adbd4 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -59,7 +59,10 @@ namespace MWClass virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + 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 boost::shared_ptr use (const MWWorld::Ptr& ptr) const; @@ -68,6 +71,8 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1097f8f29c..04889360f3 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -9,6 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -86,6 +87,11 @@ namespace MWClass return ref->mBase->mId; } + void Creature::adjustPosition(const MWWorld::Ptr& ptr) const + { + MWBase::Environment::get().getWorld()->adjustPosition(ptr); + } + void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { MWRender::Actors& actors = renderingInterface.getActors(); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index ea356165eb..297c2ea61b 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -22,6 +22,8 @@ namespace MWClass virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void adjustPosition(const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 14cf6ff6f9..0afe60e0e2 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -197,4 +197,9 @@ namespace MWClass return MWWorld::Ptr(&cell.mIngreds.insert(*ref), &cell); } + + bool Ingredient::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Ingredients; + } } diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 0afd202fb4..d27a7cbb0e 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -56,6 +56,8 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 7466657725..306c6bbb6a 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -203,4 +203,9 @@ namespace MWClass return MWWorld::Ptr(&cell.mLights.insert(*ref), &cell); } + + bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Lights; + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 640e1705bd..7d919f75df 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -57,6 +57,8 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 2015726968..bfbf107565 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -176,4 +176,9 @@ namespace MWClass return MWWorld::Ptr(&cell.mLockpicks.insert(*ref), &cell); } + + bool Lockpick::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Picks; + } } diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 0961b55b24..edd884a3e0 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -57,6 +57,8 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 8cfac1a686..02307cc021 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -246,4 +246,12 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionSoulgem(ptr)); } + bool Miscellaneous::canSell (const MWWorld::Ptr& item, int npcServices) const + { + MWWorld::LiveCellRef *ref = + item.get(); + + return !ref->mBase->mData.mIsKey && (npcServices & ESM::NPC::Misc); + } + } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 12a50af19d..ba00900bd0 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -53,6 +53,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index cd6b0def11..7cda87bb15 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -160,6 +160,11 @@ namespace MWClass return ref->mBase->mId; } + void Npc::adjustPosition(const MWWorld::Ptr& ptr) const + { + MWBase::Environment::get().getWorld()->adjustPosition(ptr); + } + void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { renderingInterface.getActors().insertNPC(ptr, getInventoryStore(ptr)); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 1a10bce6c1..d1a9158fdf 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -44,6 +44,8 @@ namespace MWClass virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void adjustPosition(const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 37461ed905..ad2826ea83 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -194,4 +194,9 @@ namespace MWClass return MWWorld::Ptr(&cell.mPotions.insert(*ref), &cell); } + + bool Potion::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Potions; + } } diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index d595f7e694..845795d0e8 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -52,6 +52,8 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index e4533af655..4ec6a8340b 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -175,4 +175,9 @@ namespace MWClass return MWWorld::Ptr(&cell.mProbes.insert(*ref), &cell); } + + bool Probe::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Probes; + } } diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index d9f90baf6d..75ebaa01cb 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -57,6 +57,8 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index bafedee88b..36e7068252 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -175,4 +175,9 @@ namespace MWClass { return boost::shared_ptr(new MWWorld::ActionRepair(ptr)); } + + bool Repair::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::RepairItem; + } } diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 3083c97e35..a545cf4d60 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -61,6 +61,8 @@ namespace MWClass virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exceoption) + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 894b38acc9..fbac6d89e8 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -349,6 +349,9 @@ namespace MWClass info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; + if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); @@ -367,7 +370,7 @@ namespace MWClass return ref->mBase->mEnchant; } - std::string Weapon::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + void Weapon::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { MWWorld::LiveCellRef *ref = ptr.get(); @@ -378,7 +381,33 @@ namespace MWClass newItem.mData.mEnchant=enchCharge; newItem.mEnchant=enchId; const ESM::Weapon *record = MWBase::Environment::get().getWorld()->createRecord (newItem); - return record->mId; + ref->mBase = record; + } + + int Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + std::pair, bool> slots = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + + // equip the item in the first free slot + for (std::vector::const_iterator slot=slots.first.begin(); + slot!=slots.first.end(); ++slot) + { + if(*slot == MWWorld::InventoryStore::Slot_CarriedRight) + { + if(ptr.get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || + ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + ptr.get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || + ptr.get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + { + return 2; + } + return 1; + } + } + return 0; } boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const @@ -406,4 +435,9 @@ namespace MWClass return ref->mBase->mData.mEnchant; } + + bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Weapon; + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 533f32f592..314f6bc212 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -65,7 +65,10 @@ namespace MWClass virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + 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 boost::shared_ptr use (const MWWorld::Ptr& ptr) const; @@ -73,6 +76,8 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index f7e9529568..78969ffd0b 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -569,8 +569,8 @@ bool MWDialogue::Filter::hasFactionRankSkillRequirements (const MWWorld::Ptr& ac MWMechanics::CreatureStats& stats = MWWorld::Class::get (actor).getCreatureStats (actor); - return stats.getAttribute (faction.mData.mAttribute1).getBase()>=faction.mData.mRankData[rank].mAttribute1 && - stats.getAttribute (faction.mData.mAttribute2).getBase()>=faction.mData.mRankData[rank].mAttribute2; + return stats.getAttribute (faction.mData.mAttribute[0]).getBase()>=faction.mData.mRankData[rank].mAttribute1 && + stats.getAttribute (faction.mData.mAttribute[1]).getBase()>=faction.mData.mRankData[rank].mAttribute2; } bool MWDialogue::Filter::hasFactionRankReputationRequirements (const MWWorld::Ptr& actor, diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index ca7f1b913a..a6121c234c 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -26,8 +26,8 @@ namespace namespace MWGui { - AlchemyWindow::AlchemyWindow(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_alchemy_window.layout", parWindowManager) + AlchemyWindow::AlchemyWindow() + : WindowBase("openmw_alchemy_window.layout") , ContainerBase(0), mApparatus (4), mIngredients (4) { getWidget(mCreateButton, "CreateButton"); @@ -64,8 +64,8 @@ namespace MWGui { mAlchemy.clear(); - mWindowManager.removeGuiMode(GM_Alchemy); - mWindowManager.removeGuiMode(GM_Inventory); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Inventory); } void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) @@ -77,40 +77,40 @@ namespace MWGui if (result == MWMechanics::Alchemy::Result_NoName) { - mWindowManager.messageBox("#{sNotifyMessage37}"); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage37}"); return; } // check if mortar & pestle is available (always needed) if (result == MWMechanics::Alchemy::Result_NoMortarAndPestle) { - mWindowManager.messageBox("#{sNotifyMessage45}"); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage45}"); return; } // make sure 2 or more ingredients were selected if (result == MWMechanics::Alchemy::Result_LessThanTwoIngredients) { - mWindowManager.messageBox("#{sNotifyMessage6a}"); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage6a}"); return; } if (result == MWMechanics::Alchemy::Result_NoEffects) { - mWindowManager.messageBox("#{sNotifyMessage8}"); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); return; } if (result == MWMechanics::Alchemy::Result_Success) { - mWindowManager.messageBox("#{sPotionSuccess}"); + MWBase::Environment::get().getWindowManager()->messageBox("#{sPotionSuccess}"); MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f); } else if (result == MWMechanics::Alchemy::Result_RandomFailure) { // potion failed - mWindowManager.messageBox("#{sNotifyMessage8}"); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); } @@ -232,7 +232,6 @@ namespace MWGui MyGUI::IntCoord coord(0, 0, mEffectsBox->getWidth(), 24); Widgets::MWEffectListPtr effectsWidget = mEffectsBox->createWidget ("MW_StatName", coord, MyGUI::Align::Left | MyGUI::Align::Top); - effectsWidget->setWindowManager(&mWindowManager); Widgets::SpellEffectList _list = Widgets::MWEffectList::effectListFromESM(&list); effectsWidget->setEffectList(_list); diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 5f84e73e9b..655a832c17 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -5,16 +5,16 @@ #include "../mwmechanics/alchemy.hpp" -#include "window_base.hpp" #include "container.hpp" #include "widgets.hpp" namespace MWGui { + class AlchemyWindow : public WindowBase, public ContainerBase { public: - AlchemyWindow(MWBase::WindowManager& parWindowManager); + AlchemyWindow(); virtual void open(); @@ -38,7 +38,7 @@ namespace MWGui virtual void onReferenceUnavailable() { ; } void update(); - + private: MWMechanics::Alchemy mAlchemy; diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 4b07dd698c..133bbd32fb 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -24,8 +24,8 @@ bool sortBirthSigns(const std::pair& left, c } -BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_birth.layout", parWindowManager) +BirthDialog::BirthDialog() + : WindowModal("openmw_chargen_birth.layout") { // Centre dialog center(); @@ -46,7 +46,7 @@ BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) MyGUI::Button* okButton; getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); updateBirths(); @@ -59,9 +59,9 @@ void BirthDialog::setNextButtonShow(bool shown) getWidget(okButton, "OKButton"); if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } void BirthDialog::open() @@ -221,7 +221,7 @@ void BirthDialog::updateSpells() if (!categories[category].spells.empty()) { MyGUI::TextBox* label = mSpellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); - label->setCaption(mWindowManager.getGameSettingString(categories[category].label, "")); + label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, "")); mSpellItems.push_back(label); coord.top += lineHeight; @@ -230,7 +230,6 @@ void BirthDialog::updateSpells() { const std::string &spellId = *it; spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); - spellWidget->setWindowManager(&mWindowManager); spellWidget->setSpellId(spellId); mSpellItems.push_back(spellWidget); diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index d3f82dace4..cc958ddcaa 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_BIRTH_H #define MWGUI_BIRTH_H -#include "window_base.hpp" +#include "windowbase.hpp" /* This file contains the dialog for choosing a birth sign. @@ -13,7 +13,7 @@ namespace MWGui class BirthDialog : public WindowModal { public: - BirthDialog(MWBase::WindowManager& parWindowManager); + BirthDialog(); enum Gender { diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 777751069b..ce74c48593 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -14,8 +14,8 @@ using namespace MWGui; -BookWindow::BookWindow (MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_book.layout", parWindowManager) +BookWindow::BookWindow () + : WindowBase("openmw_book.layout") , mTakeButtonShow(true) , mTakeButtonAllowed(true) { @@ -102,7 +102,7 @@ void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) // no 3d sounds because the object could be in a container. MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); - mWindowManager.removeGuiMode(GM_Book); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); } void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) @@ -112,7 +112,7 @@ void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) MWWorld::ActionTake take(mBook); take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - mWindowManager.removeGuiMode(GM_Book); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); } void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index a509f131fe..9123969e09 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_BOOKWINDOW_H #define MWGUI_BOOKWINDOW_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "../mwworld/ptr.hpp" @@ -12,7 +12,7 @@ namespace MWGui class BookWindow : public WindowBase { public: - BookWindow(MWBase::WindowManager& parWindowManager); + BookWindow(); void open(MWWorld::Ptr book); void setTakeButtonShow(bool show); diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index bcf3c335d3..0dd6502c6c 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -1,6 +1,6 @@ #include "charactercreation.hpp" -#include "text_input.hpp" +#include "textinput.hpp" #include "race.hpp" #include "class.hpp" #include "birth.hpp" @@ -47,7 +47,7 @@ namespace using namespace MWGui; -CharacterCreation::CharacterCreation(MWBase::WindowManager* _wm) +CharacterCreation::CharacterCreation() : mNameDialog(0) , mRaceDialog(0) , mClassChoiceDialog(0) @@ -58,7 +58,6 @@ CharacterCreation::CharacterCreation(MWBase::WindowManager* _wm) , mBirthSignDialog(0) , mReviewDialog(0) , mGenerateClassStep(0) - , mWM(_wm) { mCreationStage = CSE_NotStarted; } @@ -118,10 +117,10 @@ void CharacterCreation::spawnDialog(const char id) switch (id) { case GM_Name: - mWM->removeDialog(mNameDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); mNameDialog = 0; - mNameDialog = new TextInputDialog(*mWM); - mNameDialog->setTextLabel(mWM->getGameSettingString("sName", "Name")); + mNameDialog = new TextInputDialog(); + mNameDialog->setTextLabel(MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "Name")); mNameDialog->setTextInput(mPlayerName); mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); @@ -129,9 +128,9 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Race: - mWM->removeDialog(mRaceDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); mRaceDialog = 0; - mRaceDialog = new RaceDialog(*mWM); + mRaceDialog = new RaceDialog(); mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); mRaceDialog->setRaceId(mPlayerRaceId); mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); @@ -142,9 +141,9 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Class: - mWM->removeDialog(mClassChoiceDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); mClassChoiceDialog = 0; - mClassChoiceDialog = new ClassChoiceDialog(*mWM); + mClassChoiceDialog = new ClassChoiceDialog(); mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); mClassChoiceDialog->setVisible(true); if (mCreationStage < CSE_RaceChosen) @@ -152,9 +151,9 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_ClassPick: - mWM->removeDialog(mPickClassDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); mPickClassDialog = 0; - mPickClassDialog = new PickClassDialog(*mWM); + mPickClassDialog = new PickClassDialog(); mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mPickClassDialog->setClassId(mPlayerClass.mName); mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); @@ -165,9 +164,9 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Birth: - mWM->removeDialog(mBirthSignDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); mBirthSignDialog = 0; - mBirthSignDialog = new BirthDialog(*mWM); + mBirthSignDialog = new BirthDialog(); mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); mBirthSignDialog->setBirthId(mPlayerBirthSignId); mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); @@ -178,9 +177,9 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_ClassCreate: - mWM->removeDialog(mCreateClassDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); mCreateClassDialog = 0; - mCreateClassDialog = new CreateClassDialog(*mWM); + mCreateClassDialog = new CreateClassDialog(); mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); @@ -199,9 +198,9 @@ void CharacterCreation::spawnDialog(const char id) mCreationStage = CSE_RaceChosen; break; case GM_Review: - mWM->removeDialog(mReviewDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; - mReviewDialog = new ReviewDialog(*mWM); + mReviewDialog = new ReviewDialog(); mReviewDialog->setPlayerName(mPlayerName); mReviewDialog->setRace(mPlayerRaceId); mReviewDialog->setClass(mPlayerClass); @@ -212,7 +211,7 @@ void CharacterCreation::spawnDialog(const char id) mReviewDialog->setFatigue(mPlayerFatigue); { - std::map > attributes = mWM->getPlayerAttributeValues(); + std::map > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); for (std::map >::iterator it = attributes.begin(); it != attributes.end(); ++it) { @@ -221,13 +220,13 @@ void CharacterCreation::spawnDialog(const char id) } { - std::map > skills = mWM->getPlayerSkillValues(); + std::map > skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); for (std::map >::iterator it = skills.begin(); it != skills.end(); ++it) { mReviewDialog->setSkillValue(static_cast (it->first), it->second); } - mReviewDialog->configureSkills(mWM->getPlayerMajorSkills(), mWM->getPlayerMinorSkills()); + mReviewDialog->configureSkills(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills()); } mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); @@ -257,41 +256,41 @@ void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { - mWM->removeDialog(mReviewDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); } void CharacterCreation::onReviewDialogBack() { - mWM->removeDialog(mReviewDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; - mWM->pushGuiMode(GM_Birth); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } void CharacterCreation::onReviewActivateDialog(int parDialog) { - mWM->removeDialog(mReviewDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; mCreationStage = CSE_ReviewNext; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); switch(parDialog) { case ReviewDialog::NAME_DIALOG: - mWM->pushGuiMode(GM_Name); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); break; case ReviewDialog::RACE_DIALOG: - mWM->pushGuiMode(GM_Race); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); break; case ReviewDialog::CLASS_DIALOG: - mWM->pushGuiMode(GM_Class); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); break; case ReviewDialog::BIRTHSIGN_DIALOG: - mWM->pushGuiMode(GM_Birth); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); }; } @@ -308,27 +307,27 @@ void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) if (klass) { mPlayerClass = *klass; - mWM->setPlayerClass(mPlayerClass); + MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); } - mWM->removeDialog(mPickClassDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); mPickClassDialog = 0; } //TODO This bit gets repeated a few times; wrap it in a function if (mCreationStage == CSE_ReviewNext) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); } } @@ -339,34 +338,34 @@ void CharacterCreation::onPickClassDialogBack() const std::string classId = mPickClassDialog->getClassId(); if (!classId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); - mWM->removeDialog(mPickClassDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); mPickClassDialog = 0; } - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } void CharacterCreation::onClassChoice(int _index) { - mWM->removeDialog(mClassChoiceDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); mClassChoiceDialog = 0; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); switch(_index) { case ClassChoiceDialog::Class_Generate: - mWM->pushGuiMode(GM_ClassGenerate); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassGenerate); break; case ClassChoiceDialog::Class_Pick: - mWM->pushGuiMode(GM_ClassPick); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassPick); break; case ClassChoiceDialog::Class_Create: - mWM->pushGuiMode(GM_ClassCreate); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassCreate); break; case ClassChoiceDialog::Class_Back: - mWM->pushGuiMode(GM_Race); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); break; }; @@ -377,26 +376,26 @@ void CharacterCreation::onNameDialogDone(WindowBase* parWindow) if (mNameDialog) { mPlayerName = mNameDialog->getTextInput(); - mWM->setValue("name", mPlayerName); + MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName); MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); - mWM->removeDialog(mNameDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); mNameDialog = 0; } if (mCreationStage == CSE_ReviewNext) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_NameChosen) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Race); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); } else { mCreationStage = CSE_NameChosen; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); } } @@ -414,12 +413,12 @@ void CharacterCreation::onRaceDialogBack() data.mHair ); } - mWM->removeDialog(mRaceDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); mRaceDialog = 0; } - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Name); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); } void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) @@ -436,26 +435,26 @@ void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) data.mHair ); } - mWM->getInventoryWindow()->rebuildAvatar(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); - mWM->removeDialog(mRaceDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); mRaceDialog = 0; } if (mCreationStage == CSE_ReviewNext) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_RaceChosen) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } else { mCreationStage = CSE_RaceChosen; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); } } @@ -466,19 +465,19 @@ void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) mPlayerBirthSignId = mBirthSignDialog->getBirthId(); if (!mPlayerBirthSignId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); - mWM->removeDialog(mBirthSignDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); mBirthSignDialog = 0; } if (mCreationStage >= CSE_BirthSignChosen) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else { mCreationStage = CSE_BirthSignChosen; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); } } @@ -487,12 +486,12 @@ void CharacterCreation::onBirthSignDialogBack() if (mBirthSignDialog) { MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); - mWM->removeDialog(mBirthSignDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); mBirthSignDialog = 0; } - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) @@ -522,49 +521,49 @@ void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); mPlayerClass = klass; - mWM->setPlayerClass(klass); + MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); - mWM->removeDialog(mCreateClassDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); mCreateClassDialog = 0; } if (mCreationStage == CSE_ReviewNext) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); } } void CharacterCreation::onCreateClassDialogBack() { - mWM->removeDialog(mCreateClassDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); mCreateClassDialog = 0; - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } void CharacterCreation::onClassQuestionChosen(int _index) { MWBase::Environment::get().getSoundManager()->stopSay(); - mWM->removeDialog(mGenerateClassQuestionDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); mGenerateClassQuestionDialog = 0; if (_index < 0 || _index >= 3) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); return; } @@ -637,10 +636,10 @@ void CharacterCreation::showClassQuestionDialog() } } - mWM->removeDialog(mGenerateClassResultDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); mGenerateClassResultDialog = 0; - mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM); + mGenerateClassResultDialog = new GenerateClassResultDialog(); mGenerateClassResultDialog->setClassId(mGenerateClass); mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); @@ -650,15 +649,15 @@ void CharacterCreation::showClassQuestionDialog() if (mGenerateClassStep > 10) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); return; } - mWM->removeDialog(mGenerateClassQuestionDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); mGenerateClassQuestionDialog = 0; - mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); + mGenerateClassQuestionDialog = new InfoBoxDialog(); InfoBoxDialog::ButtonList buttons; mGenerateClassQuestionDialog->setText(sGenerateClassSteps(mGenerateClassStep).mText); @@ -674,18 +673,18 @@ void CharacterCreation::showClassQuestionDialog() void CharacterCreation::onGenerateClassBack() { - mWM->removeDialog(mGenerateClassResultDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); mGenerateClassResultDialog = 0; MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) { - mWM->removeDialog(mGenerateClassResultDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); mGenerateClassResultDialog = 0; MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); @@ -694,22 +693,22 @@ void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); mPlayerClass = *klass; - mWM->setPlayerClass(mPlayerClass); + MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); if (mCreationStage == CSE_ReviewNext) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); } } diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 9653aeede5..586faf966e 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -1,13 +1,9 @@ #ifndef CHARACTER_CREATION_HPP #define CHARACTER_CREATION_HPP -#include "../mwworld/esmstore.hpp" - #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwmechanics/stat.hpp" - namespace MWGui { class WindowBase; @@ -29,7 +25,7 @@ namespace MWGui public: typedef std::vector SkillList; - CharacterCreation(MWBase::WindowManager* _wm); + CharacterCreation(); ~CharacterCreation(); //Show a dialog @@ -58,8 +54,6 @@ namespace MWGui BirthDialog* mBirthSignDialog; ReviewDialog* mReviewDialog; - MWBase::WindowManager* mWM; - //Player data std::string mPlayerName; std::string mPlayerRaceId; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index a2f09096a1..6edad9e83b 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -20,13 +20,13 @@ using namespace MWGui; /* GenerateClassResultDialog */ -GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_generate_class_result.layout", parWindowManager) +GenerateClassResultDialog::GenerateClassResultDialog() + : WindowModal("openmw_chargen_generate_class_result.layout") { // Centre dialog center(); - setText("ReflectT", mWindowManager.getGameSettingString("sMessageQuestionAnswer1", "")); + setText("ReflectT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sMessageQuestionAnswer1", "")); getWidget(mClassImage, "ClassImage"); getWidget(mClassName, "ClassName"); @@ -37,7 +37,7 @@ GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parW MyGUI::Button* okButton; getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); } @@ -67,8 +67,8 @@ void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) /* PickClassDialog */ -PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_class.layout", parWindowManager) +PickClassDialog::PickClassDialog() + : WindowModal("openmw_chargen_class.layout") { // Centre dialog center(); @@ -77,16 +77,12 @@ PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); - mFavoriteAttribute[0]->setWindowManager(&mWindowManager); - mFavoriteAttribute[1]->setWindowManager(&mWindowManager); for(int i = 0; i < 5; i++) { char theIndex = '0'+i; getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); - mMajorSkill[i]->setWindowManager(&mWindowManager); - mMinorSkill[i]->setWindowManager(&mWindowManager); } getWidget(mClassList, "ClassList"); @@ -115,9 +111,9 @@ void PickClassDialog::setNextButtonShow(bool shown) getWidget(okButton, "OKButton"); if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } void PickClassDialog::open() @@ -224,7 +220,7 @@ void PickClassDialog::updateStats() "sSpecializationMagic", "sSpecializationStealth" }; - std::string specName = mWindowManager.getGameSettingString(specIds[specialization], specIds[specialization]); + std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[specialization], specIds[specialization]); mSpecializationName->setCaption(specName); ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); @@ -276,8 +272,8 @@ void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin) widget->setSize(width, pos); } -InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_infobox.layout", parWindowManager) +InfoBoxDialog::InfoBoxDialog() + : WindowModal("openmw_infobox.layout") , mCurrentButton(-1) { getWidget(mTextBox, "TextBox"); @@ -360,22 +356,22 @@ void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender) /* ClassChoiceDialog */ -ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager) - : InfoBoxDialog(parWindowManager) +ClassChoiceDialog::ClassChoiceDialog() + : InfoBoxDialog() { setText(""); ButtonList buttons; - buttons.push_back(mWindowManager.getGameSettingString("sClassChoiceMenu1", "")); - buttons.push_back(mWindowManager.getGameSettingString("sClassChoiceMenu2", "")); - buttons.push_back(mWindowManager.getGameSettingString("sClassChoiceMenu3", "")); - buttons.push_back(mWindowManager.getGameSettingString("sBack", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu1", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu2", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu3", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBack", "")); setButtons(buttons); } /* CreateClassDialog */ -CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_create_class.layout", parWindowManager) +CreateClassDialog::CreateClassDialog() + : WindowModal("openmw_chargen_create_class.layout") , mSpecDialog(NULL) , mAttribDialog(NULL) , mSkillDialog(NULL) @@ -384,20 +380,18 @@ CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) // Centre dialog center(); - setText("SpecializationT", mWindowManager.getGameSettingString("sChooseClassMenu1", "Specialization")); + setText("SpecializationT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu1", "Specialization")); getWidget(mSpecializationName, "SpecializationName"); mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); - setText("FavoriteAttributesT", mWindowManager.getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); + setText("FavoriteAttributesT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); getWidget(mFavoriteAttribute0, "FavoriteAttribute0"); getWidget(mFavoriteAttribute1, "FavoriteAttribute1"); - mFavoriteAttribute0->setWindowManager(&mWindowManager); - mFavoriteAttribute1->setWindowManager(&mWindowManager); mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); - setText("MajorSkillT", mWindowManager.getGameSettingString("sSkillClassMajor", "")); - setText("MinorSkillT", mWindowManager.getGameSettingString("sSkillClassMinor", "")); + setText("MajorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMajor", "")); + setText("MinorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMinor", "")); for(int i = 0; i < 5; i++) { char theIndex = '0'+i; @@ -410,11 +404,10 @@ CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) std::vector::const_iterator end = mSkills.end(); for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) { - (*it)->setWindowManager(&mWindowManager); (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); } - setText("LabelT", mWindowManager.getGameSettingString("sName", "")); + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "")); getWidget(mEditName, "EditName"); // Make sure the edit box has focus @@ -522,32 +515,32 @@ void CreateClassDialog::setNextButtonShow(bool shown) getWidget(okButton, "OKButton"); if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } // widget controls void CreateClassDialog::onDialogCancel() { - mWindowManager.removeDialog(mSpecDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); mSpecDialog = 0; - mWindowManager.removeDialog(mAttribDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); mAttribDialog = 0; - mWindowManager.removeDialog(mSkillDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); mSkillDialog = 0; - mWindowManager.removeDialog(mDescDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); mDescDialog = 0; } void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender) { delete mSpecDialog; - mSpecDialog = new SelectSpecializationDialog(mWindowManager); + mSpecDialog = new SelectSpecializationDialog(); mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); mSpecDialog->setVisible(true); @@ -558,7 +551,7 @@ void CreateClassDialog::onSpecializationSelected() mSpecializationId = mSpecDialog->getSpecializationId(); setSpecialization(mSpecializationId); - mWindowManager.removeDialog(mSpecDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); mSpecDialog = 0; } @@ -570,7 +563,7 @@ void CreateClassDialog::setSpecialization(int id) "sSpecializationMagic", "sSpecializationStealth" }; - std::string specName = mWindowManager.getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); + std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); mSpecializationName->setCaption(specName); ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); } @@ -578,7 +571,7 @@ void CreateClassDialog::setSpecialization(int id) void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) { delete mAttribDialog; - mAttribDialog = new SelectAttributeDialog(mWindowManager); + mAttribDialog = new SelectAttributeDialog(); mAffectedAttribute = _sender; mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); @@ -599,7 +592,7 @@ void CreateClassDialog::onAttributeSelected() mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); } mAffectedAttribute->setAttributeId(id); - mWindowManager.removeDialog(mAttribDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); mAttribDialog = 0; update(); @@ -608,7 +601,7 @@ void CreateClassDialog::onAttributeSelected() void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) { delete mSkillDialog; - mSkillDialog = new SelectSkillDialog(mWindowManager); + mSkillDialog = new SelectSkillDialog(); mAffectedSkill = _sender; mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); @@ -633,14 +626,14 @@ void CreateClassDialog::onSkillSelected() } mAffectedSkill->setSkillId(mSkillDialog->getSkillId()); - mWindowManager.removeDialog(mSkillDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); mSkillDialog = 0; update(); } void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) { - mDescDialog = new DescriptionDialog(mWindowManager); + mDescDialog = new DescriptionDialog(); mDescDialog->setTextInput(mDescription); mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); mDescDialog->setVisible(true); @@ -649,7 +642,7 @@ void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) { mDescription = mDescDialog->getTextInput(); - mWindowManager.removeDialog(mDescDialog); + MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); mDescDialog = 0; } @@ -667,20 +660,20 @@ void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) /* SelectSpecializationDialog */ -SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_specialization.layout", parWindowManager) +SelectSpecializationDialog::SelectSpecializationDialog() + : WindowModal("openmw_chargen_select_specialization.layout") { // Centre dialog center(); - setText("LabelT", mWindowManager.getGameSettingString("sSpecializationMenu1", "")); + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMenu1", "")); getWidget(mSpecialization0, "Specialization0"); getWidget(mSpecialization1, "Specialization1"); getWidget(mSpecialization2, "Specialization2"); - std::string combat = mWindowManager.getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Combat], ""); - std::string magic = mWindowManager.getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Magic], ""); - std::string stealth = mWindowManager.getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Stealth], ""); + std::string combat = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Combat], ""); + std::string magic = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Magic], ""); + std::string stealth = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Stealth], ""); mSpecialization0->setCaption(combat); mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); @@ -696,7 +689,7 @@ SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& pa MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); } @@ -727,13 +720,13 @@ void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectAttributeDialog */ -SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_attribute.layout", parWindowManager) +SelectAttributeDialog::SelectAttributeDialog() + : WindowModal("openmw_chargen_select_attribute.layout") { // Centre dialog center(); - setText("LabelT", mWindowManager.getGameSettingString("sAttributesMenu1", "")); + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sAttributesMenu1", "")); for (int i = 0; i < 8; ++i) { @@ -741,7 +734,6 @@ SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowMan char theIndex = '0'+i; getWidget(attribute, std::string("Attribute").append(1, theIndex)); - attribute->setWindowManager(&parWindowManager); attribute->setAttributeId(ESM::Attribute::sAttributeIds[i]); attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId()); @@ -749,7 +741,7 @@ SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowMan MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); } @@ -774,16 +766,16 @@ void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectSkillDialog */ -SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_skill.layout", parWindowManager) +SelectSkillDialog::SelectSkillDialog() + : WindowModal("openmw_chargen_select_skill.layout") { // Centre dialog center(); - setText("LabelT", mWindowManager.getGameSettingString("sSkillsMenu1", "")); - setText("CombatLabelT", mWindowManager.getGameSettingString("sSpecializationCombat", "")); - setText("MagicLabelT", mWindowManager.getGameSettingString("sSpecializationMagic", "")); - setText("StealthLabelT", mWindowManager.getGameSettingString("sSpecializationStealth", "")); + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillsMenu1", "")); + setText("CombatLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationCombat", "")); + setText("MagicLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMagic", "")); + setText("StealthLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationStealth", "")); for(int i = 0; i < 9; i++) { @@ -833,7 +825,6 @@ SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) { for (int i = 0; i < 9; ++i) { - mSkills[spec][i].widget->setWindowManager(&mWindowManager); mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId); mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId()); @@ -842,7 +833,7 @@ SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); } @@ -865,8 +856,8 @@ void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) /* DescriptionDialog */ -DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_class_description.layout", parWindowManager) +DescriptionDialog::DescriptionDialog() + : WindowModal("openmw_chargen_class_description.layout") { // Centre dialog center(); @@ -876,7 +867,7 @@ DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); - okButton->setCaption(mWindowManager.getGameSettingString("sInputMenu1", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sInputMenu1", "")); // Make sure the edit box has focus MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 8c60331d87..15fc89658f 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -3,7 +3,7 @@ #include "widgets.hpp" -#include "window_base.hpp" +#include "windowbase.hpp" /* This file contains the dialogs for choosing a class. @@ -15,7 +15,7 @@ namespace MWGui class InfoBoxDialog : public WindowModal { public: - InfoBoxDialog(MWBase::WindowManager& parWindowManager); + InfoBoxDialog(); typedef std::vector ButtonList; @@ -60,13 +60,13 @@ namespace MWGui Class_Create = 2, Class_Back = 3 }; - ClassChoiceDialog(MWBase::WindowManager& parWindowManager); + ClassChoiceDialog(); }; class GenerateClassResultDialog : public WindowModal { public: - GenerateClassResultDialog(MWBase::WindowManager& parWindowManager); + GenerateClassResultDialog(); std::string getClassId() const; void setClassId(const std::string &classId); @@ -93,7 +93,7 @@ namespace MWGui class PickClassDialog : public WindowModal { public: - PickClassDialog(MWBase::WindowManager& parWindowManager); + PickClassDialog(); const std::string &getClassId() const { return mCurrentClassId; } void setClassId(const std::string &classId); @@ -132,7 +132,7 @@ namespace MWGui class SelectSpecializationDialog : public WindowModal { public: - SelectSpecializationDialog(MWBase::WindowManager& parWindowManager); + SelectSpecializationDialog(); ~SelectSpecializationDialog(); ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } @@ -163,7 +163,7 @@ namespace MWGui class SelectAttributeDialog : public WindowModal { public: - SelectAttributeDialog(MWBase::WindowManager& parWindowManager); + SelectAttributeDialog(); ~SelectAttributeDialog(); ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } @@ -192,7 +192,7 @@ namespace MWGui class SelectSkillDialog : public WindowModal { public: - SelectSkillDialog(MWBase::WindowManager& parWindowManager); + SelectSkillDialog(); ~SelectSkillDialog(); ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } @@ -225,7 +225,7 @@ namespace MWGui class DescriptionDialog : public WindowModal { public: - DescriptionDialog(MWBase::WindowManager& parWindowManager); + DescriptionDialog(); ~DescriptionDialog(); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } @@ -241,7 +241,7 @@ namespace MWGui class CreateClassDialog : public WindowModal { public: - CreateClassDialog(MWBase::WindowManager& parWindowManager); + CreateClassDialog(); virtual ~CreateClassDialog(); std::string getName() const; diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 643cdf4c65..0a20c471ab 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -13,9 +13,9 @@ namespace MWGui { -CompanionWindow::CompanionWindow(MWBase::WindowManager &parWindowManager, DragAndDrop *dragAndDrop, MessageBoxManager* manager) +CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* manager) : ContainerBase(dragAndDrop) - , WindowBase("openmw_companion_window.layout", parWindowManager) + , WindowBase("openmw_companion_window.layout") , mMessageBoxManager(manager) { MyGUI::ScrollView* itemView; diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 1b64a34d50..073a77a916 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -11,7 +11,7 @@ namespace MWGui class CompanionWindow : public ContainerBase, public WindowBase { public: - CompanionWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop, MessageBoxManager* manager); + CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager); virtual ~CompanionWindow() {} void open(MWWorld::Ptr npc); diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 31f4989e07..904468f886 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -7,8 +7,8 @@ namespace MWGui { - ConfirmationDialog::ConfirmationDialog(MWBase::WindowManager& parWindowManager) : - WindowModal("openmw_confirmation_dialog.layout", parWindowManager) + ConfirmationDialog::ConfirmationDialog() : + WindowModal("openmw_confirmation_dialog.layout") { getWidget(mMessage, "Message"); getWidget(mOkButton, "OkButton"); diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index 45941f2ad0..47b256017f 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -1,14 +1,14 @@ #ifndef MWGUI_CONFIRMATIONDIALOG_H #define MWGUI_CONFIRMATIONDIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { class ConfirmationDialog : public WindowModal { public: - ConfirmationDialog(MWBase::WindowManager& parWindowManager); + ConfirmationDialog(); void open(const std::string& message); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 7e6a0b0885..8ee4754a32 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -649,9 +649,9 @@ MWWorld::ContainerStore& ContainerBase::getContainerStore() // ------------------------------------------------------------------------------------------------ -ContainerWindow::ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) +ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) - , WindowBase("openmw_container_window.layout", parWindowManager) + , WindowBase("openmw_container_window.layout") { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); getWidget(mTakeButton, "TakeButton"); @@ -751,7 +751,7 @@ void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) /// \todo I don't think this is the correct flag to check if (MWWorld::Class::get(mPtr).isEssential(mPtr)) - mWindowManager.messageBox("#{sDisposeCorpseFail}"); + MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); else MWBase::Environment::get().getWorld()->deleteObject(mPtr); diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 03bd519f7d..8440c6444e 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -1,13 +1,10 @@ #ifndef MGUI_CONTAINER_H #define MGUI_CONTAINER_H -#include "../mwworld/esmstore.hpp" - -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" #include "../mwclass/container.hpp" -#include "../mwworld/ptr.hpp" #include "../mwworld/containerstore.hpp" @@ -134,7 +131,7 @@ namespace MWGui class ContainerWindow : public ContainerBase, public WindowBase { public: - ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + ContainerWindow(DragAndDrop* dragAndDrop); virtual ~ContainerWindow(); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 61c3c358a2..d017cc1986 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -7,8 +7,8 @@ namespace MWGui { - CountDialog::CountDialog(MWBase::WindowManager& parWindowManager) : - WindowModal("openmw_count_window.layout", parWindowManager) + CountDialog::CountDialog() : + WindowModal("openmw_count_window.layout") { getWidget(mSlider, "CountSlider"); getWidget(mItemEdit, "ItemEdit"); diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 80da6eea01..9d6d8b263f 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -1,14 +1,14 @@ #ifndef MWGUI_COUNTDIALOG_H #define MWGUI_COUNTDIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { class CountDialog : public WindowModal { public: - CountDialog(MWBase::WindowManager& parWindowManager); + CountDialog(); void open(const std::string& item, const std::string& message, const int maxCount); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; diff --git a/apps/openmw/mwgui/cursor.hpp b/apps/openmw/mwgui/cursor.hpp index 3a4a05f4ca..badf82262b 100644 --- a/apps/openmw/mwgui/cursor.hpp +++ b/apps/openmw/mwgui/cursor.hpp @@ -3,7 +3,6 @@ #include #include -#include namespace MWGui { diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index b3aa27617c..f3c11752e8 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -18,7 +18,7 @@ #include "../mwdialogue/dialoguemanagerimp.hpp" -#include "dialogue_history.hpp" +#include "dialoguehistory.hpp" #include "widgets.hpp" #include "list.hpp" #include "tradewindow.hpp" @@ -55,8 +55,8 @@ bool sortByLength (const std::string& left, const std::string& right) -PersuasionDialog::PersuasionDialog(MWBase::WindowManager &parWindowManager) - : WindowModal("openmw_persuasion_dialog.layout", parWindowManager) +PersuasionDialog::PersuasionDialog() + : WindowModal("openmw_persuasion_dialog.layout") { getWidget(mCancelButton, "CancelButton"); getWidget(mAdmireButton, "AdmireButton"); @@ -89,17 +89,17 @@ void PersuasionDialog::onPersuade(MyGUI::Widget *sender) else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; else if (sender == mBribe10Button) { - mWindowManager.getTradeWindow()->addOrRemoveGold(-10); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-10); type = MWBase::MechanicsManager::PT_Bribe10; } else if (sender == mBribe100Button) { - mWindowManager.getTradeWindow()->addOrRemoveGold(-100); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-100); type = MWBase::MechanicsManager::PT_Bribe100; } else /*if (sender == mBribe1000Button)*/ { - mWindowManager.getTradeWindow()->addOrRemoveGold(-1000); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-1000); type = MWBase::MechanicsManager::PT_Bribe1000; } @@ -113,7 +113,7 @@ void PersuasionDialog::open() WindowModal::open(); center(); - int playerGold = mWindowManager.getInventoryWindow()->getPlayerGold(); + int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold(); mBribe10Button->setEnabled (playerGold >= 10); mBribe100Button->setEnabled (playerGold >= 100); @@ -124,9 +124,9 @@ void PersuasionDialog::open() // -------------------------------------------------------------------------------------------------- -DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_dialogue_window.layout", parWindowManager) - , mPersuasionDialog(parWindowManager) +DialogueWindow::DialogueWindow() + : WindowBase("openmw_dialogue_window.layout") + , mPersuasionDialog() , mEnabled(false) , mServices(0) { @@ -251,45 +251,45 @@ void DialogueWindow::onSelectTopic(const std::string& topic, int id) } else if (topic == gmst.find("sCompanionShare")->getString()) { - mWindowManager.pushGuiMode(GM_Companion); - mWindowManager.showCompanionWindow(mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion); + MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr); } else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused()) { if (topic == gmst.find("sBarter")->getString()) { - mWindowManager.pushGuiMode(GM_Barter); - mWindowManager.getTradeWindow()->startTrade(mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->startTrade(mPtr); } else if (topic == gmst.find("sSpells")->getString()) { - mWindowManager.pushGuiMode(GM_SpellBuying); - mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying); + MWBase::Environment::get().getWindowManager()->getSpellBuyingWindow()->startSpellBuying(mPtr); } else if (topic == gmst.find("sTravel")->getString()) { - mWindowManager.pushGuiMode(GM_Travel); - mWindowManager.getTravelWindow()->startTravel(mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->getTravelWindow()->startTravel(mPtr); } else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) { - mWindowManager.pushGuiMode(GM_SpellCreation); - mWindowManager.startSpellMaking (mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr); } else if (topic == gmst.find("sEnchanting")->getString()) { - mWindowManager.pushGuiMode(GM_Enchanting); - mWindowManager.startEnchanting (mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); + MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr); } else if (topic == gmst.find("sServiceTrainingTitle")->getString()) { - mWindowManager.pushGuiMode(GM_Training); - mWindowManager.startTraining (mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training); + MWBase::Environment::get().getWindowManager()->startTraining (mPtr); } else if (topic == gmst.find("sRepair")->getString()) { - mWindowManager.pushGuiMode(GM_MerchantRepair); - mWindowManager.startRepair (mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair); + MWBase::Environment::get().getWindowManager()->startRepair (mPtr); } } } @@ -456,7 +456,7 @@ std::string DialogueWindow::parseText(const std::string& text) } else { - if( !mWindowManager.getTranslationDataStorage().hasTranslation() ) + if( !MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation() ) { for(std::vector::const_iterator it = topics.begin(); it != topics.end(); ++it) { @@ -528,7 +528,7 @@ void DialogueWindow::goodbye() void DialogueWindow::onReferenceUnavailable() { - mWindowManager.removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void DialogueWindow::onFrame() diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 187731fc77..74fa8051c5 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -1,11 +1,8 @@ #ifndef MWGUI_DIALOGE_H #define MWGUI_DIALOGE_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" -#include - -#include "../mwworld/ptr.hpp" namespace MWGui { @@ -29,7 +26,7 @@ namespace MWGui class PersuasionDialog : public WindowModal { public: - PersuasionDialog(MWBase::WindowManager& parWindowManager); + PersuasionDialog(); virtual void open(); @@ -50,7 +47,7 @@ namespace MWGui class DialogueWindow: public WindowBase, public ReferenceInterface { public: - DialogueWindow(MWBase::WindowManager& parWindowManager); + DialogueWindow(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialoguehistory.cpp similarity index 98% rename from apps/openmw/mwgui/dialogue_history.cpp rename to apps/openmw/mwgui/dialoguehistory.cpp index 13f72545e2..a122a78916 100644 --- a/apps/openmw/mwgui/dialogue_history.cpp +++ b/apps/openmw/mwgui/dialoguehistory.cpp @@ -1,4 +1,4 @@ -#include "dialogue_history.hpp" +#include "dialoguehistory.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/dialogue_history.hpp b/apps/openmw/mwgui/dialoguehistory.hpp similarity index 99% rename from apps/openmw/mwgui/dialogue_history.hpp rename to apps/openmw/mwgui/dialoguehistory.hpp index c37504af77..4cf6de621d 100644 --- a/apps/openmw/mwgui/dialogue_history.hpp +++ b/apps/openmw/mwgui/dialoguehistory.hpp @@ -1,5 +1,6 @@ #ifndef MWGUI_DIALOGE_HISTORY_H #define MWGUI_DIALOGE_HISTORY_H + #include namespace MWGui diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index ef124bb430..2ae98f358f 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -15,11 +15,10 @@ namespace MWGui { - EnchantingDialog::EnchantingDialog(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_enchanting_dialog.layout", parWindowManager) - , EffectEditorBase(parWindowManager) + EnchantingDialog::EnchantingDialog() + : WindowBase("openmw_enchanting_dialog.layout") + , EffectEditorBase() , mItemSelectionDialog(NULL) - , mEnchanting(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) { getWidget(mName, "NameEdit"); getWidget(mCancelButton, "CancelButton"); @@ -33,6 +32,7 @@ namespace MWGui getWidget(mTypeButton, "TypeButton"); getWidget(mBuyButton, "BuyButton"); getWidget(mPrice, "PriceLabel"); + getWidget(mPriceText, "PriceTextLabel"); setWidgets(mAvailableEffectsList, mUsedEffectsView); @@ -64,22 +64,24 @@ namespace MWGui mCastCost->setCaption(boost::lexical_cast(mEnchanting.getEnchantCost())); + mPrice->setCaption(boost::lexical_cast(mEnchanting.getEnchantPrice())); + switch(mEnchanting.getEnchantType()) { case 0: - mTypeButton->setCaption(mWindowManager.getGameSettingString("sItemCastOnce","Cast Once")); + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once")); mAddEffectDialog.constantEffect=false; break; case 1: - mTypeButton->setCaption(mWindowManager.getGameSettingString("sItemCastWhenStrikes", "When Strikes")); + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes")); mAddEffectDialog.constantEffect=false; break; case 2: - mTypeButton->setCaption(mWindowManager.getGameSettingString("sItemCastWhenUsed", "When Used")); + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used")); mAddEffectDialog.constantEffect=false; break; case 3: - mTypeButton->setCaption(mWindowManager.getGameSettingString("sItemCastConstant", "Cast Constant")); + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant")); mAddEffectDialog.constantEffect=true; break; } @@ -87,6 +89,9 @@ namespace MWGui void EnchantingDialog::startEnchanting (MWWorld::Ptr actor) { + mEnchanting.setSelfEnchanting(false); + mEnchanting.setEnchanter(actor); + mPtr = actor; startEditing (); @@ -94,25 +99,47 @@ namespace MWGui void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) { - /// \todo + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + mEnchanting.setSelfEnchanting(true); + mEnchanting.setEnchanter(player); + + mPtr = player; + startEditing(); + mEnchanting.setSoulGem(soulgem); + + MyGUI::ImageBox* image = mSoulBox->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(soulgem).getInventoryIcon(soulgem); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->setUserString ("ToolTipType", "ItemPtr"); + image->setUserData(soulgem); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveSoul); + + mPrice->setVisible(false); + mPriceText->setVisible(false); + updateLabels(); } void EnchantingDialog::onReferenceUnavailable () { - mWindowManager.removeGuiMode (GM_Dialogue); - mWindowManager.removeGuiMode (GM_Enchanting); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); } void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender) { - mWindowManager.removeGuiMode (GM_Enchanting); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); } void EnchantingDialog::onSelectItem(MyGUI::Widget *sender) { delete mItemSelectionDialog; mItemSelectionDialog = new ItemSelectionDialog("#{sEnchantItems}", - ContainerBase::Filter_Apparel|ContainerBase::Filter_Weapon|ContainerBase::Filter_NoMagic, mWindowManager); + ContainerBase::Filter_Apparel|ContainerBase::Filter_Weapon|ContainerBase::Filter_NoMagic); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onItemSelected); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onItemCancel); mItemSelectionDialog->setVisible(true); @@ -163,7 +190,7 @@ namespace MWGui if(mEnchanting.getGemCharge()==0) { - mWindowManager.messageBox ("#{sNotifyMessage32}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage32}"); return; } @@ -200,14 +227,14 @@ namespace MWGui { delete mItemSelectionDialog; mItemSelectionDialog = new ItemSelectionDialog("#{sSoulGemsWithSouls}", - ContainerBase::Filter_Misc|ContainerBase::Filter_ChargedSoulstones, mWindowManager); + ContainerBase::Filter_Misc|ContainerBase::Filter_ChargedSoulstones); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onSoulSelected); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onSoulCancel); mItemSelectionDialog->setVisible(true); mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); mItemSelectionDialog->drawItems (); - //mWindowManager.messageBox("#{sInventorySelectNoSoul}"); + //MWBase::Environment::get().getWindowManager()->messageBox("#{sInventorySelectNoSoul}"); } void EnchantingDialog::notifyEffectsChanged () @@ -227,45 +254,50 @@ namespace MWGui { if (mEffects.size() <= 0) { - mWindowManager.messageBox ("#{sNotifyMessage30}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); return; } if (mName->getCaption ().empty()) { - mWindowManager.messageBox ("#{sNotifyMessage10}"); - return; - } - - if (boost::lexical_cast(mPrice->getCaption()) > mWindowManager.getInventoryWindow()->getPlayerGold()) - { - mWindowManager.messageBox ("#{sNotifyMessage18}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage10}"); return; } if (mEnchanting.soulEmpty()) { - mWindowManager.messageBox ("#{sNotifyMessage52}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage52}"); return; } if (mEnchanting.itemEmpty()) { - mWindowManager.messageBox ("#{sNotifyMessage11}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage11}"); return; } if (mEnchanting.getEnchantCost() > mEnchanting.getMaxEnchantValue()) { - mWindowManager.messageBox ("#{sNotifyMessage29}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}"); return; } mEnchanting.setNewItemName(mName->getCaption()); mEnchanting.setEffect(mEffectList); - mEnchanting.create(); - mWindowManager.messageBox ("#{sEnchantmentMenu12}"); - mWindowManager.removeGuiMode (GM_Enchanting); + if (mEnchanting.getEnchantPrice() > MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); + return; + } + + int result = mEnchanting.create(); + + if(result==1) + MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu12}"); + else + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage34}"); + + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); } } diff --git a/apps/openmw/mwgui/enchantingdialog.hpp b/apps/openmw/mwgui/enchantingdialog.hpp index 347b37e908..8bad60c8e6 100644 --- a/apps/openmw/mwgui/enchantingdialog.hpp +++ b/apps/openmw/mwgui/enchantingdialog.hpp @@ -1,8 +1,6 @@ #ifndef MWGUI_ENCHANTINGDIALOG_H #define MWGUI_ENCHANTINGDIALOG_H -#include "window_base.hpp" -#include "referenceinterface.hpp" #include "spellcreationdialog.hpp" #include "../mwbase/windowmanager.hpp" @@ -17,7 +15,7 @@ namespace MWGui class EnchantingDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase { public: - EnchantingDialog(MWBase::WindowManager& parWindowManager); + EnchantingDialog(); virtual ~EnchantingDialog(); virtual void open(); @@ -56,6 +54,7 @@ namespace MWGui MyGUI::TextBox* mCastCost; MyGUI::TextBox* mCharge; MyGUI::TextBox* mPrice; + MyGUI::TextBox* mPriceText; MWMechanics::Enchanting mEnchanting; ESM::EffectList mEffectList; diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index aab9e62a4f..1dd53683b8 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -1,6 +1,4 @@ -#include "map_window.hpp" - -#include +#include "mapwindow.hpp" #include "../mwmechanics/stat.hpp" #include "../mwworld/ptr.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 1943ff773a..40c228fd5a 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -30,13 +30,14 @@ namespace MWGui { - InventoryWindow::InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) + InventoryWindow::InventoryWindow(DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) - , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) + , WindowPinnableBase("openmw_inventory_window.layout") , mTrading(false) , mLastXSize(0) , mLastYSize(0) , mPreview(MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()) + , mPreviewDirty(true) { static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); @@ -129,7 +130,7 @@ namespace MWGui void InventoryWindow::onPinToggled() { - mWindowManager.setWeaponVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned); } void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender) @@ -149,6 +150,7 @@ namespace MWGui it = invStore.add(ptr); (*it).getRefData().setCount(mDragAndDrop->mDraggedCount); ptr = *it; + mDragAndDrop->mDraggedFrom->notifyItemDragged(ptr, -mDragAndDrop->mDraggedCount); } /// \todo scripts @@ -161,13 +163,13 @@ namespace MWGui // the "Take" button should not be visible. // NOTE: the take button is "reset" when the window opens, so we can safely do the following // without screwing up future book windows - mWindowManager.getBookWindow()->setTakeButtonShow(false); - mWindowManager.getScrollWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); mDragAndDrop->mIsOnDragAndDrop = false; MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - mWindowManager.setDragDrop(false); + MWBase::Environment::get().getWindowManager()->setDragDrop(false); drawItems(); @@ -223,11 +225,11 @@ namespace MWGui { invStore.equip(slot, invStore.end()); std::string script = MWWorld::Class::get(*it).getScript(*it); - + // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared if(script != "") (*it).mRefData->getLocals().setVarByInt(script, "onpcequip", 0); - + return; } } @@ -268,25 +270,34 @@ namespace MWGui mTrading = true; } + void InventoryWindow::doRenderUpdate () + { + if (mPreviewDirty) + { + mPreviewDirty = false; + MyGUI::IntSize size = mAvatar->getSize(); + + mPreview.update (size.width, size.height); + mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); + mAvatarImage->setImageTexture("CharacterPreview"); + } + } + void InventoryWindow::notifyContentChanged() { // update the spell window just in case new enchanted items were added to inventory - if (mWindowManager.getSpellWindow()) - mWindowManager.getSpellWindow()->updateSpells(); + if (MWBase::Environment::get().getWindowManager()->getSpellWindow()) + MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells(); // update selected weapon icon MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weaponSlot = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weaponSlot == invStore.end()) - mWindowManager.unsetSelectedWeapon(); + MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); else - mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(*weaponSlot); /// \todo track weapon durability - MyGUI::IntSize size = mAvatar->getSize(); - - mPreview.update (size.width, size.height); - mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); - mAvatarImage->setImageTexture("CharacterPreview"); + mPreviewDirty = true; mArmorRating->setCaptionWithReplacing ("#{sArmor}: " + boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 95657672d3..a4ed89d401 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -4,7 +4,7 @@ #include "../mwrender/characterpreview.hpp" #include "container.hpp" -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" #include "widgets.hpp" namespace MWGui @@ -12,10 +12,12 @@ namespace MWGui class InventoryWindow : public ContainerBase, public WindowPinnableBase { public: - InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + InventoryWindow(DragAndDrop* dragAndDrop); virtual void open(); + void doRenderUpdate(); + /// start trading, disables item drag&drop void startTrade(); @@ -34,6 +36,8 @@ namespace MWGui } protected: + bool mPreviewDirty; + MyGUI::Widget* mAvatar; MyGUI::ImageBox* mAvatarImage; MyGUI::TextBox* mArmorRating; diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 4b8adcef4f..ed8b47b086 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -3,9 +3,9 @@ namespace MWGui { - ItemSelectionDialog::ItemSelectionDialog(const std::string &label, int filter, MWBase::WindowManager& parWindowManager) + ItemSelectionDialog::ItemSelectionDialog(const std::string &label, int filter) : ContainerBase(NULL) - , WindowModal("openmw_itemselection_dialog.layout", parWindowManager) + , WindowModal("openmw_itemselection_dialog.layout") { mFilter = filter; diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp index 733daa91ae..19007de6b0 100644 --- a/apps/openmw/mwgui/itemselection.hpp +++ b/apps/openmw/mwgui/itemselection.hpp @@ -1,14 +1,12 @@ #include "container.hpp" -#include "../mwworld/ptr.hpp" - namespace MWGui { class ItemSelectionDialog : public ContainerBase, public WindowModal { public: - ItemSelectionDialog(const std::string& label, int filter, MWBase::WindowManager& parWindowManager); + ItemSelectionDialog(const std::string& label, int filter); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Item; diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index ba39b01012..35a687cc4a 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -81,8 +81,8 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) } -MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_journal.layout", parWindowManager) +MWGui::JournalWindow::JournalWindow () + : WindowBase("openmw_journal.layout") , mPageNumber(0) { mMainWidget->setVisible(false); @@ -114,15 +114,9 @@ MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) //displayLeftText(list.front()); } -void MWGui::JournalWindow::close() -{ - MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); -} - void MWGui::JournalWindow::open() { mPageNumber = 0; - MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); if(MWBase::Environment::get().getJournal()->begin()!=MWBase::Environment::get().getJournal()->end()) { book journal; diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index cd1ff7ebbd..27bf608e18 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -1,12 +1,9 @@ #ifndef MWGUI_JOURNAL_H #define MWGUI_JOURNAL_H -#include -#include #include -#include -#include "window_base.hpp" +#include "windowbase.hpp" #include "imagebutton.hpp" namespace MWGui @@ -14,9 +11,8 @@ namespace MWGui class JournalWindow : public WindowBase { public: - JournalWindow(MWBase::WindowManager& parWindowManager); + JournalWindow(); virtual void open(); - virtual void close(); private: void displayLeftText(std::string text); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 67620d49da..fc1317e908 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -18,8 +18,8 @@ namespace MWGui { - LevelupDialog::LevelupDialog(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_levelup_dialog.layout", parWindowManager) + LevelupDialog::LevelupDialog() + : WindowBase("openmw_levelup_dialog.layout") { getWidget(mOkButton, "OkButton"); getWidget(mClassImage, "ClassImage"); @@ -183,7 +183,7 @@ namespace MWGui creatureStats.setLevel (creatureStats.getLevel()+1); pcStats.levelUp (); - mWindowManager.removeGuiMode (GM_Levelup); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup); } } diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index 3c8b74800b..69afbf0897 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_LEVELUPDIALOG_H #define MWGUI_LEVELUPDIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -9,7 +9,7 @@ namespace MWGui class LevelupDialog : public WindowBase { public: - LevelupDialog(MWBase::WindowManager& parWindowManager); + LevelupDialog(); virtual void open(); diff --git a/apps/openmw/mwgui/list.hpp b/apps/openmw/mwgui/list.hpp index 09e42e865e..956523c0dc 100644 --- a/apps/openmw/mwgui/list.hpp +++ b/apps/openmw/mwgui/list.hpp @@ -1,7 +1,6 @@ #ifndef MWGUI_LIST_HPP #define MWGUI_LIST_HPP -#include #include namespace MWGui diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index e7c7acb533..858c3f36ec 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -23,10 +23,10 @@ namespace MWGui { - LoadingScreen::LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw, MWBase::WindowManager& parWindowManager) + LoadingScreen::LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw) : mSceneMgr(sceneMgr) , mWindow(rw) - , WindowBase("openmw_loading_screen.layout", parWindowManager) + , WindowBase("openmw_loading_screen.layout") , mLoadingOn(false) , mLastRenderTime(0.f) , mLastWallpaperChangeTime(0.f) @@ -195,12 +195,12 @@ namespace MWGui { changeWallpaper(); - mWindowManager.pushGuiMode(GM_LoadingWallpaper); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_LoadingWallpaper); } else { mBackgroundImage->setImageTexture(""); - mWindowManager.pushGuiMode(GM_Loading); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Loading); } } @@ -211,21 +211,27 @@ namespace MWGui mLoadingOn = false; mFirstLoad = false; - mWindowManager.removeGuiMode(GM_Loading); - mWindowManager.removeGuiMode(GM_LoadingWallpaper); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); } void LoadingScreen::changeWallpaper () { - if (mResources.isNull ()) - mResources = Ogre::ResourceGroupManager::getSingleton ().findResourceNames ("General", "Splash_*.tga"); - - - if (mResources->size()) + if (mResources.empty()) { - std::string const & randomSplash = mResources->at (rand() % mResources->size()); + Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); + for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) + { + Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "Splash_*.tga"); + mResources.insert(mResources.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end()); + } + } - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); + if (!mResources.empty()) + { + std::string const & randomSplash = mResources.at (rand() % mResources.size()); + + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); mBackgroundImage->setImageTexture (randomSplash); } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 24b3850710..87cedaa98c 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -2,16 +2,15 @@ #define MWGUI_LOADINGSCREEN_H #include -#include -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { class LoadingScreen : public WindowBase { public: - LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw, MWBase::WindowManager& parWindowManager); + LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw); virtual ~LoadingScreen(); void setLoadingProgress (const std::string& stage, int depth, int current, int total); @@ -44,7 +43,7 @@ namespace MWGui Ogre::Rectangle2D* mRectangle; Ogre::MaterialPtr mBackgroundMaterial; - Ogre::StringVectorPtr mResources; + Ogre::StringVector mResources; bool mLoadingOn; diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/mapwindow.cpp similarity index 98% rename from apps/openmw/mwgui/map_window.cpp rename to apps/openmw/mwgui/mapwindow.cpp index d0f3f921e5..17834be937 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1,4 +1,4 @@ -#include "map_window.hpp" +#include "mapwindow.hpp" #include @@ -262,8 +262,8 @@ void LocalMapBase::setPlayerDir(const float x, const float y) // ------------------------------------------------------------------------------------------ -MapWindow::MapWindow(MWBase::WindowManager& parWindowManager, const std::string& cacheDir) - : MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager) +MapWindow::MapWindow(const std::string& cacheDir) + : MWGui::WindowPinnableBase("openmw_map_window.layout") , mGlobal(false) { setCoord(500,0,320,300); @@ -376,7 +376,7 @@ void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) void MapWindow::onPinToggled() { - mWindowManager.setMinimapVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned); } void MapWindow::open() diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/mapwindow.hpp similarity index 95% rename from apps/openmw/mwgui/map_window.hpp rename to apps/openmw/mwgui/mapwindow.hpp index 39770a7a26..18c81a0608 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" namespace MWRender { @@ -65,7 +65,7 @@ namespace MWGui class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase { public: - MapWindow(MWBase::WindowManager& parWindowManager, const std::string& cacheDir); + MapWindow(const std::string& cacheDir); virtual ~MapWindow(); void setCellName(const std::string& cellName); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 0a65326050..53148cb3f0 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -19,8 +19,8 @@ namespace MWGui { -MerchantRepair::MerchantRepair(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_merchantrepair.layout", parWindowManager) +MerchantRepair::MerchantRepair() + : WindowBase("openmw_merchantrepair.layout") { getWidget(mList, "RepairView"); getWidget(mOkButton, "OkButton"); @@ -40,7 +40,8 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); - for (MWWorld::ContainerStoreIterator iter (store.begin()); + int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; + for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); iter!=store.end(); ++iter) { if (MWWorld::Class::get(*iter).hasItemHealth(*iter)) @@ -71,7 +72,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) MyGUI::Button* button = mList->createWidget( - (price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, currentY, 0, @@ -81,7 +82,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) currentY += 18; - button->setEnabled(price<=mWindowManager.getInventoryWindow()->getPlayerGold()); + button->setEnabled(price<=MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()); button->setUserString("Price", boost::lexical_cast(price)); button->setUserData(*iter); button->setCaptionWithReplacing(name); @@ -94,7 +95,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY))); mGoldLabel->setCaptionWithReplacing("#{sGold}: " - + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); } void MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel) @@ -119,14 +120,14 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); int price = boost::lexical_cast(sender->getUserString("Price")); - mWindowManager.getTradeWindow()->addOrRemoveGold(-price); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-price); startRepair(mActor); } void MerchantRepair::onOkButtonClick(MyGUI::Widget *sender) { - mWindowManager.removeGuiMode(GM_MerchantRepair); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_MerchantRepair); } } diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index 4b7e2b8fbd..4cb39fe012 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_MWGUI_MERCHANTREPAIR_H #define OPENMW_MWGUI_MERCHANTREPAIR_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "../mwworld/ptr.hpp" @@ -12,7 +12,7 @@ namespace MWGui class MerchantRepair : public WindowBase { public: - MerchantRepair(MWBase::WindowManager &parWindowManager); + MerchantRepair(); virtual void open(); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 46663b67a5..8e53380bde 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -7,9 +7,8 @@ using namespace MWGui; -MessageBoxManager::MessageBoxManager (MWBase::WindowManager *windowManager) +MessageBoxManager::MessageBoxManager () { - mWindowManager = windowManager; // defines mMessageBoxSpeed = 0.1; mInterMessageBoxe = NULL; @@ -213,7 +212,7 @@ int MessageBox::getHeight () InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) - : WindowModal("openmw_interactive_messagebox.layout", *MWBase::Environment::get().getWindowManager()) + : WindowModal("openmw_interactive_messagebox.layout") , mMessageBoxManager(parMessageBoxManager) , mButtonPressed(-1) { @@ -371,7 +370,7 @@ InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxMan void InteractiveMessageBox::enterPressed() { - + std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 859e1806a7..0df6f3544b 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -1,9 +1,7 @@ #ifndef MWGUI_MESSAGE_BOX_H #define MWGUI_MESSAGE_BOX_H -#include - -#include "window_base.hpp" +#include "windowbase.hpp" #include "../mwbase/windowmanager.hpp" @@ -31,7 +29,7 @@ namespace MWGui class MessageBoxManager { public: - MessageBoxManager (MWBase::WindowManager* windowManager); + MessageBoxManager (); void onFrame (float frameDuration); void createMessageBox (const std::string& message); bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); @@ -40,7 +38,7 @@ namespace MWGui void removeMessageBox (float time, MessageBox *msgbox); bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - + void enterPressed(); int readPressedButton (); @@ -51,8 +49,6 @@ namespace MWGui void onButtonPressed(int button) { eventButtonPressed(button); eventButtonPressed.clear(); } - MWBase::WindowManager *mWindowManager; - private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; @@ -92,7 +88,7 @@ namespace MWGui private: void buttonActivated (MyGUI::Widget* _widget); - + MessageBoxManager& mMessageBoxManager; MyGUI::EditBox* mMessageWidget; MyGUI::Widget* mButtonsWidget; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 5ea13fb0d7..2deb37d301 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -43,8 +43,8 @@ namespace namespace MWGui { - QuickKeysMenu::QuickKeysMenu(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_quickkeys_menu.layout", parWindowManager) + QuickKeysMenu::QuickKeysMenu() + : WindowBase("openmw_quickkeys_menu.layout") , mAssignDialog(0) , mItemSelectionDialog(0) , mMagicSelectionDialog(0) @@ -109,14 +109,14 @@ namespace MWGui { // open assign dialog if (!mAssignDialog) - mAssignDialog = new QuickKeysMenuAssign(mWindowManager, this); + mAssignDialog = new QuickKeysMenuAssign(this); mAssignDialog->setVisible (true); } } void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender) { - mWindowManager.removeGuiMode(GM_QuickKeysMenu); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu); } @@ -124,7 +124,7 @@ namespace MWGui { if (!mItemSelectionDialog ) { - mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}", ContainerBase::Filter_All, mWindowManager); + mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}", ContainerBase::Filter_All); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel); } @@ -139,7 +139,7 @@ namespace MWGui { if (!mMagicSelectionDialog ) { - mMagicSelectionDialog = new MagicSelectionDialog(mWindowManager, this); + mMagicSelectionDialog = new MagicSelectionDialog(this); } mMagicSelectionDialog->setVisible(true); @@ -281,7 +281,7 @@ namespace MWGui std::string spellId = button->getChildAt(0)->getUserString("Spell"); spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); - mWindowManager.setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); + MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } else if (type == Type_Item) { @@ -303,11 +303,11 @@ namespace MWGui // the "Take" button should not be visible. // NOTE: the take button is "reset" when the window opens, so we can safely do the following // without screwing up future book windows - mWindowManager.getBookWindow()->setTakeButtonShow(false); - mWindowManager.getScrollWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); // since we changed equipping status, update the inventory window - mWindowManager.getInventoryWindow()->drawItems(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); } else if (type == Type_MagicItem) { @@ -341,19 +341,19 @@ namespace MWGui action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); // since we changed equipping status, update the inventory window - mWindowManager.getInventoryWindow()->drawItems(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); } store.setSelectedEnchantItem(it); spells.setSelectedSpell(""); - mWindowManager.setSelectedEnchantItem(item, 100); /// \todo track charge % + MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); } } // --------------------------------------------------------------------------------------------------------- - QuickKeysMenuAssign::QuickKeysMenuAssign (MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) - : WindowModal("openmw_quickkeys_menu_assign.layout", parWindowManager) + QuickKeysMenuAssign::QuickKeysMenuAssign (QuickKeysMenu* parent) + : WindowModal("openmw_quickkeys_menu_assign.layout") , mParent(parent) { getWidget(mLabel, "Label"); @@ -399,8 +399,8 @@ namespace MWGui // --------------------------------------------------------------------------------------------------------- - MagicSelectionDialog::MagicSelectionDialog(MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) - : WindowModal("openmw_magicselection_dialog.layout", parWindowManager) + MagicSelectionDialog::MagicSelectionDialog(QuickKeysMenu* parent) + : WindowModal("openmw_magicselection_dialog.layout") , mParent(parent) , mWidth(0) , mHeight(0) diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 345ffa0c87..058519ece4 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -1,10 +1,9 @@ #ifndef MWGUI_QUICKKEYS_H #define MWGUI_QUICKKEYS_H - #include "../mwworld/ptr.hpp" -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -16,7 +15,7 @@ namespace MWGui class QuickKeysMenu : public WindowBase { public: - QuickKeysMenu(MWBase::WindowManager& parWindowManager); + QuickKeysMenu(); ~QuickKeysMenu(); @@ -64,7 +63,7 @@ namespace MWGui class QuickKeysMenuAssign : public WindowModal { public: - QuickKeysMenuAssign(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + QuickKeysMenuAssign(QuickKeysMenu* parent); private: MyGUI::TextBox* mLabel; @@ -79,7 +78,7 @@ namespace MWGui class MagicSelectionDialog : public WindowModal { public: - MagicSelectionDialog(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + MagicSelectionDialog(QuickKeysMenu* parent); virtual void open(); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index be693eb2ba..9a0d8a029f 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -62,8 +62,8 @@ int countParts(const std::string &part, const std::string &race, bool male) } } -RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_race.layout", parWindowManager) +RaceDialog::RaceDialog() + : WindowModal("openmw_chargen_race.layout") , mGenderIndex(0) , mFaceIndex(0) , mHairIndex(0) @@ -74,7 +74,7 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) // Centre dialog center(); - setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); + setText("AppearanceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu1", "Appearance")); getWidget(mPreviewImage, "PreviewImage"); getWidget(mHeadRotate, "HeadRotate"); @@ -86,34 +86,34 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) // Set up next/previous buttons MyGUI::Button *prevButton, *nextButton; - setText("GenderChoiceT", mWindowManager.getGameSettingString("sRaceMenu2", "Change Sex")); + setText("GenderChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu2", "Change Sex")); getWidget(prevButton, "PrevGenderButton"); getWidget(nextButton, "NextGenderButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender); - setText("FaceChoiceT", mWindowManager.getGameSettingString("sRaceMenu3", "Change Face")); + setText("FaceChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu3", "Change Face")); getWidget(prevButton, "PrevFaceButton"); getWidget(nextButton, "NextFaceButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace); - setText("HairChoiceT", mWindowManager.getGameSettingString("sRaceMenu4", "Change Hair")); + setText("HairChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu4", "Change Hair")); getWidget(prevButton, "PrevHairButton"); getWidget(nextButton, "NextHairButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); - setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu5", "Race")); + setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race")); getWidget(mRaceList, "RaceList"); mRaceList->setScrollVisible(true); mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - setText("SkillsT", mWindowManager.getGameSettingString("sBonusSkillTitle", "Skill Bonus")); + setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); getWidget(mSkillList, "SkillList"); - setText("SpellPowerT", mWindowManager.getGameSettingString("sRaceMenu7", "Specials")); + setText("SpellPowerT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials")); getWidget(mSpellPowerList, "SpellPowerList"); MyGUI::Button* backButton; @@ -122,7 +122,7 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) MyGUI::Button* okButton; getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); updateRaces(); @@ -136,9 +136,9 @@ void RaceDialog::setNextButtonShow(bool shown) getWidget(okButton, "OKButton"); if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } void RaceDialog::open() @@ -156,7 +156,7 @@ void RaceDialog::open() const ESM::NPC proto = mPreview->getPrototype(); setRaceId(proto.mRace); recountParts(); - + std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2); mFaceIndex = boost::lexical_cast(index) - 1; @@ -361,7 +361,7 @@ void RaceDialog::updateRaces() const MWWorld::Store &races = MWBase::Environment::get().getWorld()->getStore().get(); - + int index = 0; MWWorld::Store::iterator it = races.begin(); for (; it != races.end(); ++it) @@ -403,7 +403,6 @@ void RaceDialog::updateSkills() skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, std::string("Skill") + boost::lexical_cast(i)); - skillWidget->setWindowManager(&mWindowManager); skillWidget->setSkillNumber(skillId); skillWidget->setSkillValue(MWSkill::SkillValue(race->mData.mBonus[i].mBonus)); ToolTips::createSkillToolTip(skillWidget, skillId); @@ -439,7 +438,6 @@ void RaceDialog::updateSpellPowers() { const std::string &spellpower = *it; spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); - spellPowerWidget->setWindowManager(&mWindowManager); spellPowerWidget->setSpellId(spellpower); spellPowerWidget->setUserString("ToolTipType", "Spell"); spellPowerWidget->setUserString("Spell", spellpower); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 0ca440ad53..893c4c90b6 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -1,14 +1,9 @@ #ifndef MWGUI_RACE_H #define MWGUI_RACE_H - -#include - -#include "../mwworld/esmstore.hpp" - #include "../mwrender/characterpreview.hpp" -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui @@ -26,7 +21,7 @@ namespace MWGui class RaceDialog : public WindowModal { public: - RaceDialog(MWBase::WindowManager& parWindowManager); + RaceDialog(); enum Gender { diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index f53ddc4305..0bd4b0995f 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -15,8 +15,8 @@ namespace MWGui { -Repair::Repair(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_repair.layout", parWindowManager) +Repair::Repair() + : WindowBase("openmw_repair.layout") { getWidget(mRepairBox, "RepairBox"); getWidget(mRepairView, "RepairView"); @@ -133,7 +133,7 @@ void Repair::updateRepairView() void Repair::onCancel(MyGUI::Widget *sender) { - mWindowManager.removeGuiMode(GM_Repair); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Repair); } void Repair::onRepairItem(MyGUI::Widget *sender) diff --git a/apps/openmw/mwgui/repair.hpp b/apps/openmw/mwgui/repair.hpp index c14b1955bd..d0f5c54c4b 100644 --- a/apps/openmw/mwgui/repair.hpp +++ b/apps/openmw/mwgui/repair.hpp @@ -1,9 +1,8 @@ #ifndef OPENMW_MWGUI_REPAIR_H #define OPENMW_MWGUI_REPAIR_H -#include "window_base.hpp" +#include "windowbase.hpp" -#include "../mwworld/ptr.hpp" #include "../mwmechanics/repair.hpp" namespace MWGui @@ -12,7 +11,7 @@ namespace MWGui class Repair : public WindowBase { public: - Repair(MWBase::WindowManager &parWindowManager); + Repair(); virtual void open(); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 50508cc5f0..562bf97748 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -22,8 +22,8 @@ using namespace Widgets; const int ReviewDialog::sLineHeight = 18; -ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_review.layout", parWindowManager) +ReviewDialog::ReviewDialog() + : WindowModal("openmw_chargen_review.layout") , mLastPos(0) { // Centre dialog @@ -53,15 +53,15 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) // Setup dynamic stats getWidget(mHealth, "Health"); - mHealth->setTitle(mWindowManager.getGameSettingString("sHealth", "")); + mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sHealth", "")); mHealth->setValue(45, 45); getWidget(mMagicka, "Magicka"); - mMagicka->setTitle(mWindowManager.getGameSettingString("sMagic", "")); + mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sMagic", "")); mMagicka->setValue(50, 50); getWidget(mFatigue, "Fatigue"); - mFatigue->setTitle(mWindowManager.getGameSettingString("sFatigue", "")); + mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFatigue", "")); mFatigue->setValue(160, 160); // Setup attributes @@ -71,7 +71,6 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) { getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); - attribute->setWindowManager(&mWindowManager); attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); attribute->setAttributeValue(MWAttribute::AttributeValue(0, 0)); } @@ -277,7 +276,7 @@ void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId addSeparator(coord1, coord2); } - addGroup(mWindowManager.getGameSettingString(titleId, titleDefault), coord1, coord2); + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); SkillList::const_iterator end = skills.end(); for (SkillList::const_iterator it = skills.begin(); it != end; ++it) @@ -296,7 +295,7 @@ void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId state = "increased"; else if (modified < base) state = "decreased"; - MyGUI::TextBox* widget = addValueItem(mWindowManager.getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), state, coord1, coord2); + MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), state, coord1, coord2); for (int i=0; i<2; ++i) { diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 4f41ec42d6..87d6fedfa7 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -1,8 +1,7 @@ #ifndef MWGUI_REVIEW_H #define MWGUI_REVIEW_H -#include "window_base.hpp" -#include "../mwmechanics/stat.hpp" +#include "windowbase.hpp" #include "widgets.hpp" namespace MWGui @@ -28,7 +27,7 @@ namespace MWGui }; typedef std::vector SkillList; - ReviewDialog(MWBase::WindowManager& parWindowManager); + ReviewDialog(); void setPlayerName(const std::string &name); void setRace(const std::string &raceId); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 3bd3a47439..1935f7a9b6 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -12,8 +12,8 @@ using namespace MWGui; -ScrollWindow::ScrollWindow (MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_scroll.layout", parWindowManager) +ScrollWindow::ScrollWindow () + : WindowBase("openmw_scroll.layout") , mTakeButtonShow(true) , mTakeButtonAllowed(true) { @@ -66,7 +66,7 @@ void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) { MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); - mWindowManager.removeGuiMode(GM_Scroll); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); } void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) @@ -76,5 +76,5 @@ void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) MWWorld::ActionTake take(mScroll); take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - mWindowManager.removeGuiMode(GM_Scroll); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); } diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index 42b6395a9a..5feaff7bf8 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SCROLLWINDOW_H #define MWGUI_SCROLLWINDOW_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "imagebutton.hpp" #include "../mwworld/ptr.hpp" @@ -11,7 +11,7 @@ namespace MWGui class ScrollWindow : public WindowBase { public: - ScrollWindow (MWBase::WindowManager& parWindowManager); + ScrollWindow (); void open (MWWorld::Ptr scroll); void setTakeButtonShow(bool show); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 58754472dd..93b82e3745 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -93,8 +93,8 @@ namespace namespace MWGui { - SettingsWindow::SettingsWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_settings_window.layout", parWindowManager) + SettingsWindow::SettingsWindow() : + WindowBase("openmw_settings_window.layout") { getWidget(mOkButton, "OkButton"); getWidget(mSubtitlesButton, "SubtitlesButton"); @@ -236,9 +236,7 @@ namespace MWGui mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); - //mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); - mShadowsLargeDistance->setCaptionWithReplacing("#{sOff}"); - mShadowsLargeDistance->setEnabled (false); + mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); mShadowsEnabledButton->setCaptionWithReplacing(Settings::Manager::getBool("enabled", "Shadows") ? "#{sOn}" : "#{sOff}"); mActorShadows->setCaptionWithReplacing(Settings::Manager::getBool("actor shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); @@ -274,7 +272,7 @@ namespace MWGui void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) { - mWindowManager.removeGuiMode(GM_Settings); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Settings); } void SettingsWindow::onResolutionSelected(MyGUI::ListBox* _sender, size_t index) @@ -282,7 +280,7 @@ namespace MWGui if (index == MyGUI::ITEM_NONE) return; - ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->open("#{sNotifyMessage67}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept); @@ -329,8 +327,8 @@ namespace MWGui void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender) { - std::string on = mWindowManager.getGameSettingString("sOn", "On"); - std::string off = mWindowManager.getGameSettingString("sOff", "On"); + std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On"); + std::string off = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOff", "On"); bool newState; if (_sender->castType()->getCaption() == on) { @@ -437,8 +435,8 @@ namespace MWGui void SettingsWindow::onShadersToggled(MyGUI::Widget* _sender) { - std::string on = mWindowManager.getGameSettingString("sOn", "On"); - std::string off = mWindowManager.getGameSettingString("sOff", "On"); + std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On"); + std::string off = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOff", "On"); std::string val = static_cast(_sender)->getCaption(); if (val == off) @@ -610,7 +608,7 @@ namespace MWGui void SettingsWindow::onResetDefaultBindings(MyGUI::Widget* _sender) { - ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->open("#{sNotifyMessage66}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindingsAccept); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index fc1ec9e365..6dcef2422e 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SETTINGS_H #define MWGUI_SETTINGS_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -13,7 +13,7 @@ namespace MWGui class SettingsWindow : public WindowBase { public: - SettingsWindow(MWBase::WindowManager& parWindowManager); + SettingsWindow(); virtual void open(); diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index d39ad6a5ae..7d634df8d6 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -23,8 +23,8 @@ namespace MWGui { const int SpellBuyingWindow::sLineHeight = 18; - SpellBuyingWindow::SpellBuyingWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_spell_buying_window.layout", parWindowManager) + SpellBuyingWindow::SpellBuyingWindow() : + WindowBase("openmw_spell_buying_window.layout") , mCurrentY(0) , mLastPos(0) { @@ -48,7 +48,7 @@ namespace MWGui MyGUI::Button* toAdd = mSpellsView->createWidget( - (price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, @@ -120,13 +120,13 @@ namespace MWGui { int price = *_sender->getUserData(); - if (mWindowManager.getInventoryWindow()->getPlayerGold()>=price) + if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()>=price) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); - mWindowManager.getTradeWindow()->addOrRemoveGold(-price); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-price); startSpellBuying(mPtr); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); @@ -135,12 +135,12 @@ namespace MWGui void SpellBuyingWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - mWindowManager.removeGuiMode(GM_SpellBuying); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying); } void SpellBuyingWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, @@ -150,8 +150,8 @@ namespace MWGui void SpellBuyingWindow::onReferenceUnavailable() { // remove both Spells and Dialogue (since you always trade with the NPC/creature that you have previously talked to) - mWindowManager.removeGuiMode(GM_SpellBuying); - mWindowManager.removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void SpellBuyingWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index f9cda35df0..f7ea54c89c 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SpellBuyingWINDOW_H #define MWGUI_SpellBuyingWINDOW_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" namespace MyGUI @@ -21,7 +21,7 @@ namespace MWGui class SpellBuyingWindow : public ReferenceInterface, public WindowBase { public: - SpellBuyingWindow(MWBase::WindowManager& parWindowManager); + SpellBuyingWindow(); void startSpellBuying(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 592063a761..e1df9f9cd1 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -40,8 +40,8 @@ namespace namespace MWGui { - EditEffectDialog::EditEffectDialog(MWBase::WindowManager &parWindowManager) - : WindowModal("openmw_edit_effect.layout", parWindowManager) + EditEffectDialog::EditEffectDialog() + : WindowModal("openmw_edit_effect.layout") , mEditing(false) { getWidget(mCancelButton, "CancelButton"); @@ -274,9 +274,9 @@ namespace MWGui // ------------------------------------------------------------------------------------------------ - SpellCreationDialog::SpellCreationDialog(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_spellcreation_dialog.layout", parWindowManager) - , EffectEditorBase(parWindowManager) + SpellCreationDialog::SpellCreationDialog() + : WindowBase("openmw_spellcreation_dialog.layout") + , EffectEditorBase() { getWidget(mNameEdit, "NameEdit"); getWidget(mMagickaCost, "MagickaCost"); @@ -303,38 +303,38 @@ namespace MWGui void SpellCreationDialog::onCancelButtonClicked (MyGUI::Widget* sender) { - mWindowManager.removeGuiMode (MWGui::GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_SpellCreation); } void SpellCreationDialog::onBuyButtonClicked (MyGUI::Widget* sender) { if (mEffects.size() <= 0) { - mWindowManager.messageBox ("#{sNotifyMessage30}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); return; } if (mNameEdit->getCaption () == "") { - mWindowManager.messageBox ("#{sNotifyMessage10}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage10}"); return; } if (mMagickaCost->getCaption() == "0") { - mWindowManager.messageBox ("#{sEnchantmentMenu8}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu8}"); return; } - if (boost::lexical_cast(mPriceLabel->getCaption()) > mWindowManager.getInventoryWindow()->getPlayerGold()) + if (boost::lexical_cast(mPriceLabel->getCaption()) > MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) { - mWindowManager.messageBox ("#{sNotifyMessage18}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; } mSpell.mName = mNameEdit->getCaption(); - mWindowManager.getTradeWindow()->addOrRemoveGold(-boost::lexical_cast(mPriceLabel->getCaption())); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-boost::lexical_cast(mPriceLabel->getCaption())); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); @@ -347,7 +347,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); - mWindowManager.removeGuiMode (GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation); } void SpellCreationDialog::open() @@ -357,8 +357,8 @@ namespace MWGui void SpellCreationDialog::onReferenceUnavailable () { - mWindowManager.removeGuiMode (GM_Dialogue); - mWindowManager.removeGuiMode (GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation); } void SpellCreationDialog::notifyEffectsChanged () @@ -412,8 +412,8 @@ namespace MWGui // ------------------------------------------------------------------------------------------------ - EffectEditorBase::EffectEditorBase(MWBase::WindowManager& parWindowManager) - : mAddEffectDialog(parWindowManager) + EffectEditorBase::EffectEditorBase() + : mAddEffectDialog() , mSelectAttributeDialog(NULL) , mSelectSkillDialog(NULL) { @@ -541,7 +541,7 @@ namespace MWGui if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) { delete mSelectSkillDialog; - mSelectSkillDialog = new SelectSkillDialog(*MWBase::Environment::get().getWindowManager ()); + mSelectSkillDialog = new SelectSkillDialog(); mSelectSkillDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); mSelectSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectSkill); mSelectSkillDialog->setVisible (true); @@ -549,7 +549,7 @@ namespace MWGui else if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) { delete mSelectAttributeDialog; - mSelectAttributeDialog = new SelectAttributeDialog(*MWBase::Environment::get().getWindowManager ()); + mSelectAttributeDialog = new SelectAttributeDialog(); mSelectAttributeDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); mSelectAttributeDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectAttribute); mSelectAttributeDialog->setVisible (true); @@ -601,7 +601,6 @@ namespace MWGui Widgets::MWSpellEffectPtr effect = button->createWidget("MW_EffectImage", MyGUI::IntCoord(0,0,0,24), MyGUI::Align::Default); effect->setNeedMouseFocus (false); - effect->setWindowManager (MWBase::Environment::get().getWindowManager ()); effect->setSpellEffect (params); effect->setSize(effect->getRequestedWidth (), 24); diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 8f1c071804..5ad306fbea 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SPELLCREATION_H #define MWGUI_SPELLCREATION_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" #include "list.hpp" #include "widgets.hpp" @@ -15,7 +15,7 @@ namespace MWGui class EditEffectDialog : public WindowModal { public: - EditEffectDialog(MWBase::WindowManager& parWindowManager); + EditEffectDialog(); virtual void open(); @@ -83,7 +83,7 @@ namespace MWGui class EffectEditorBase { public: - EffectEditorBase(MWBase::WindowManager& parWindowManager); + EffectEditorBase(); protected: @@ -123,7 +123,7 @@ namespace MWGui class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase { public: - SpellCreationDialog(MWBase::WindowManager& parWindowManager); + SpellCreationDialog(); virtual void open(); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index d7fb0e1bcd..d07f9ca542 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -47,8 +47,8 @@ namespace namespace MWGui { - SpellWindow::SpellWindow(MWBase::WindowManager& parWindowManager) - : WindowPinnableBase("openmw_spell_window.layout", parWindowManager) + SpellWindow::SpellWindow() + : WindowPinnableBase("openmw_spell_window.layout") , mHeight(0) , mWidth(0) { @@ -71,7 +71,7 @@ namespace MWGui void SpellWindow::onPinToggled() { - mWindowManager.setSpellVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned); } void SpellWindow::open() @@ -140,7 +140,7 @@ namespace MWGui { store.setSelectedEnchantItem(store.end()); spells.setSelectedSpell(""); - mWindowManager.unsetSelectedSpell(); + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); selectedItem = MWWorld::Ptr(); } } @@ -377,12 +377,12 @@ namespace MWGui action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); // since we changed equipping status, update the inventory window - mWindowManager.getInventoryWindow()->drawItems(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); } store.setSelectedEnchantItem(it); spells.setSelectedSpell(""); - mWindowManager.setSelectedEnchantItem(item, 100); /// \todo track charge % + MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); updateSpells(); } @@ -404,14 +404,14 @@ namespace MWGui if (spell->mData.mFlags & ESM::Spell::F_Always || spell->mData.mType == ESM::Spell::ST_Power) { - mWindowManager.messageBox("#{sDeleteSpellError}"); + MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}"); } else { // ask for confirmation mSpellToDelete = spellId; - ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); - std::string question = mWindowManager.getGameSettingString("sQuestionDeleteSpell", "Delete %s?"); + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); + std::string question = MWBase::Environment::get().getWindowManager()->getGameSettingString("sQuestionDeleteSpell", "Delete %s?"); question = boost::str(boost::format(question) % spell->mName); dialog->open(question); dialog->eventOkClicked.clear(); @@ -423,7 +423,7 @@ namespace MWGui { spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); - mWindowManager.setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); + MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } updateSpells(); @@ -454,7 +454,7 @@ namespace MWGui if (spells.getSelectedSpell() == mSpellToDelete) { spells.setSelectedSpell(""); - mWindowManager.unsetSelectedSpell(); + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); } spells.remove(mSpellToDelete); diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 1963d43463..521e73d767 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SPELLWINDOW_H #define MWGUI_SPELLWINDOW_H -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" namespace MWGui { @@ -10,7 +10,7 @@ namespace MWGui class SpellWindow : public WindowPinnableBase { public: - SpellWindow(MWBase::WindowManager& parWindowManager); + SpellWindow(); virtual ~SpellWindow(); void updateSpells(); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/statswindow.cpp similarity index 95% rename from apps/openmw/mwgui/stats_window.cpp rename to apps/openmw/mwgui/statswindow.cpp index 0fa4127b55..a00ec167da 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -1,4 +1,4 @@ -#include "stats_window.hpp" +#include "statswindow.hpp" #include #include @@ -22,8 +22,8 @@ using namespace MWGui; const int StatsWindow::sLineHeight = 18; -StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) - : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) +StatsWindow::StatsWindow () + : WindowPinnableBase("openmw_stats_window.layout") , mSkillView(NULL) , mClientHeight(0) , mMajorSkills() @@ -363,7 +363,7 @@ void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, addSeparator(coord1, coord2); } - addGroup(mWindowManager.getGameSettingString(titleId, titleDefault), coord1, coord2); + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); SkillList::const_iterator end = skills.end(); for (SkillList::const_iterator it = skills.begin(); it != end; ++it) @@ -395,7 +395,7 @@ void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, state = "increased"; else if (modified < base) state = "decreased"; - MyGUI::TextBox* widget = addValueItem(mWindowManager.getGameSettingString(skillNameId, skillNameId), + MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), state, coord1, coord2); for (int i=0; i<2; ++i) @@ -476,7 +476,7 @@ void StatsWindow::updateSkillArea() MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); std::set& expelled = PCstats.getExpelled (); - addGroup(mWindowManager.getGameSettingString("sFaction", "Faction"), coord1, coord2); + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFaction", "Faction"), coord1, coord2); FactionList::const_iterator end = mFactions.end(); for (FactionList::const_iterator it = mFactions.begin(); it != end; ++it) { @@ -500,8 +500,8 @@ void StatsWindow::updateSkillArea() text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; ESM::RankData rankData = faction->mData.mRankData[it->second+1]; - const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute1); - const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute2); + const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); + const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); assert(attr1 && attr2); text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) @@ -511,7 +511,7 @@ void StatsWindow::updateSkillArea() text += "\n#BF9959"; for (int i=0; i<6; ++i) { - text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkillID[i]]+"}"; + text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+"}"; if (i<5) text += ", "; } @@ -537,7 +537,7 @@ void StatsWindow::updateSkillArea() if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); - addGroup(mWindowManager.getGameSettingString("sBirthSign", "Sign"), coord1, coord2); + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBirthSign", "Sign"), coord1, coord2); const ESM::BirthSign *sign = store.get().find(mBirthSignId); MyGUI::Widget* w = addItem(sign->mName, coord1, coord2); @@ -549,7 +549,7 @@ void StatsWindow::updateSkillArea() if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); - addValueItem(mWindowManager.getGameSettingString("sReputation", "Reputation"), + addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sReputation", "Reputation"), boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); for (int i=0; i<2; ++i) @@ -559,7 +559,7 @@ void StatsWindow::updateSkillArea() mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); } - addValueItem(mWindowManager.getGameSettingString("sBounty", "Bounty"), + addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBounty", "Bounty"), boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); for (int i=0; i<2; ++i) @@ -576,5 +576,5 @@ void StatsWindow::updateSkillArea() void StatsWindow::onPinToggled() { - mWindowManager.setHMSVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned); } diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/statswindow.hpp similarity index 94% rename from apps/openmw/mwgui/stats_window.hpp rename to apps/openmw/mwgui/statswindow.hpp index 3befc1f002..bec42d029b 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -3,13 +3,8 @@ #include "../mwworld/esmstore.hpp" -#include -#include -#include -#include - #include "../mwmechanics/stat.hpp" -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" namespace MWGui { @@ -22,7 +17,7 @@ namespace MWGui typedef std::vector SkillList; - StatsWindow(MWBase::WindowManager& parWindowManager); + StatsWindow(); /// automatically updates all the data in the stats window, but only if it has changed. void onFrame(); diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/textinput.cpp similarity index 71% rename from apps/openmw/mwgui/text_input.cpp rename to apps/openmw/mwgui/textinput.cpp index ee9144be68..ab2936cfce 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -1,11 +1,12 @@ -#include "text_input.hpp" +#include "textinput.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" using namespace MWGui; -TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_text_input.layout", parWindowManager) +TextInputDialog::TextInputDialog() + : WindowModal("openmw_text_input.layout") { // Centre dialog center(); @@ -27,9 +28,9 @@ void TextInputDialog::setNextButtonShow(bool shown) getWidget(okButton, "OKButton"); if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } void TextInputDialog::setTextLabel(const std::string &label) @@ -50,7 +51,7 @@ void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) { if (mTextEdit->getCaption() == "") { - mWindowManager.messageBox ("#{sNotifyMessage37}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); } else @@ -61,7 +62,7 @@ void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) { if (mTextEdit->getCaption() == "") { - mWindowManager.messageBox ("#{sNotifyMessage37}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); } else diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/textinput.hpp similarity index 87% rename from apps/openmw/mwgui/text_input.hpp rename to apps/openmw/mwgui/textinput.hpp index 29de7388b2..1f53263ecd 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -1,22 +1,19 @@ #ifndef MWGUI_TEXT_INPUT_H #define MWGUI_TEXT_INPUT_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { class WindowManager; } -/* - */ - namespace MWGui { class TextInputDialog : public WindowModal { public: - TextInputDialog(MWBase::WindowManager& parWindowManager); + TextInputDialog(); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index af3e146bba..2250ffd0b5 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -12,17 +12,16 @@ #include "../mwworld/class.hpp" -#include "map_window.hpp" +#include "mapwindow.hpp" #include "widgets.hpp" #include "inventorywindow.hpp" using namespace MWGui; using namespace MyGUI; -ToolTips::ToolTips(MWBase::WindowManager* windowManager) : +ToolTips::ToolTips() : Layout("openmw_tooltips.layout") , mGameMode(true) - , mWindowManager(windowManager) , mFullHelp(false) , mEnabled(true) , mFocusToolTipX(0.0) @@ -81,9 +80,9 @@ void ToolTips::onFrame(float frameDuration) { const MyGUI::IntPoint& mousePos = InputManager::getInstance().getMousePosition(); - if (mWindowManager->getWorldMouseOver() && ((mWindowManager->getMode() == GM_Console) - || (mWindowManager->getMode() == GM_Container) - || (mWindowManager->getMode() == GM_Inventory))) + if (MWBase::Environment::get().getWindowManager()->getWorldMouseOver() && ((MWBase::Environment::get().getWindowManager()->getMode() == GM_Console) + || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Container) + || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Inventory))) { mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); @@ -93,7 +92,7 @@ void ToolTips::onFrame(float frameDuration) const MWWorld::Class& objectclass = MWWorld::Class::get (mFocusObject); IntSize tooltipSize; - if ((!objectclass.hasToolTip(mFocusObject))&&(mWindowManager->getMode() == GM_Console)) + if ((!objectclass.hasToolTip(mFocusObject))&&(MWBase::Environment::get().getWindowManager()->getMode() == GM_Console)) { setCoord(0, 0, 300, 300); mDynamicToolTipBox->setVisible(true); @@ -139,7 +138,7 @@ void ToolTips::onFrame(float frameDuration) mLastMouseX = mousePos.left; mLastMouseY = mousePos.top; - + if (mRemainingDelay > 0) return; @@ -167,8 +166,8 @@ void ToolTips::onFrame(float frameDuration) { return; } - - + + // special handling for markers on the local map: the tooltip should only be visible // if the marker is not hidden due to the fog of war. if (focus->getUserString ("IsMarker") == "true") @@ -190,11 +189,11 @@ void ToolTips::onFrame(float frameDuration) } else if (type == "AvatarItemSelection") { - MyGUI::IntCoord avatarPos = mWindowManager->getInventoryWindow ()->getAvatarScreenCoord (); + MyGUI::IntCoord avatarPos = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarScreenCoord (); MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f ); int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f ); - MWWorld::Ptr item = mWindowManager->getInventoryWindow ()->getAvatarSelectedItem (realX, realY); + MWWorld::Ptr item = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarSelectedItem (realX, realY); mFocusObject = item; if (!mFocusObject.isEmpty ()) @@ -346,7 +345,7 @@ void ToolTips::findImageExtension(std::string& image) } IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) -{ +{ mDynamicToolTipBox->setVisible(true); std::string caption = info.caption; @@ -380,7 +379,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) setCoord(0, 0, 300, 300); const IntPoint padding(8, 8); - + const int maximumWidth = 500; const int imageCaptionHPadding = (caption != "" ? 8 : 0); @@ -424,7 +423,6 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget ("MW_StatName", coord, Align::Default, "ToolTipEffectsWidget"); - effectsWidget->setWindowManager(mWindowManager); effectsWidget->setEffectList(info.effects); std::vector effectItems; @@ -444,7 +442,6 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget ("MW_StatName", coord, Align::Default, "ToolTipEnchantWidget"); - enchantWidget->setWindowManager(mWindowManager); enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); std::vector enchantEffectItems; @@ -456,8 +453,8 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) if (enchant->mData.mType == ESM::Enchantment::WhenStrikes || enchant->mData.mType == ESM::Enchantment::WhenUsed) { - /// \todo store the current enchantment charge somewhere - int charge = enchant->mData.mCharge; + int maxCharge = enchant->mData.mCharge; + int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge; const int chargeWidth = 204; @@ -493,7 +490,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) (captionHeight-captionSize.height)/2, captionSize.width-imageSize, captionSize.height); - + //if its too long we do hscroll with the caption if (captionSize.width > maximumWidth) { diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index ba94915cc7..f8f256167b 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -17,6 +17,7 @@ namespace MWGui : isPotion(false) , imageSize(32) , wordWrap(true) + , remainingEnchantCharge(-1) {} std::string caption; @@ -26,6 +27,7 @@ namespace MWGui // enchantment (for cloth, armor, weapons) std::string enchant; + int remainingEnchantCharge; // effects (for potions, ingredients) Widgets::SpellEffectList effects; @@ -37,7 +39,7 @@ namespace MWGui class ToolTips : public OEngine::GUI::Layout { public: - ToolTips(MWBase::WindowManager* windowManager); + ToolTips(); void onFrame(float frameDuration); @@ -80,8 +82,6 @@ namespace MWGui private: MyGUI::Widget* mDynamicToolTipBox; - MWBase::WindowManager* mWindowManager; - MWWorld::Ptr mFocusObject; void findImageExtension(std::string& image); @@ -94,9 +94,9 @@ namespace MWGui float mFocusToolTipX; float mFocusToolTipY; - + int mHorizontalScrollIndex; - + float mDelay; float mRemainingDelay; // remaining time until tooltip will show diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 81a29e69bd..f3f226974e 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -24,8 +24,8 @@ namespace MWGui const float TradeWindow::sBalanceChangeInitialPause = 0.5; const float TradeWindow::sBalanceChangeInterval = 0.1; - TradeWindow::TradeWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_trade_window.layout", parWindowManager) + TradeWindow::TradeWindow() : + WindowBase("openmw_trade_window.layout") , ContainerBase(NULL) // no drag&drop , mCurrentBalance(0) , mBalanceButtonsState(BBS_None) @@ -85,7 +85,7 @@ namespace MWGui mCurrentBalance = 0; mCurrentMerchantOffer = 0; - mWindowManager.getInventoryWindow()->startTrade(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->startTrade(); mBoughtItems.clear(); @@ -127,7 +127,7 @@ namespace MWGui { bool goldFound = false; MWWorld::Ptr gold; - MWWorld::ContainerStore& playerStore = mWindowManager.getInventoryWindow()->getContainerStore(); + MWWorld::ContainerStore& playerStore = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getContainerStore(); for (MWWorld::ContainerStoreIterator it = playerStore.begin(); it != playerStore.end(); ++it) @@ -172,7 +172,7 @@ namespace MWGui MWBase::Environment::get().getWorld()->getStore().get(); // were there any items traded at all? - MWWorld::ContainerStore& playerBought = mWindowManager.getInventoryWindow()->getBoughtItems(); + MWWorld::ContainerStore& playerBought = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getBoughtItems(); MWWorld::ContainerStore& merchantBought = getBoughtItems(); if (playerBought.begin() == playerBought.end() && merchantBought.begin() == merchantBought.end()) { @@ -183,7 +183,7 @@ namespace MWGui } // check if the player can afford this - if (mCurrentBalance < 0 && mWindowManager.getInventoryWindow()->getPlayerGold() < std::abs(mCurrentBalance)) + if (mCurrentBalance < 0 && MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold() < std::abs(mCurrentBalance)) { // user notification MWBase::Environment::get().getWindowManager()-> @@ -257,8 +257,15 @@ namespace MWGui MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterSuccessDisposition); // success! make the item transfer. + MWWorld::ContainerStore& playerBoughtItems = + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getBoughtItems(); + for (MWWorld::ContainerStoreIterator it = playerBoughtItems.begin(); it != playerBoughtItems.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->getCellRef().mOwner, MWWorld::Class::get(mPtr).getId(mPtr))) + it->getCellRef().mOwner = ""; + } transferBoughtItems(); - mWindowManager.getInventoryWindow()->transferBoughtItems(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->transferBoughtItems(); // add or remove gold from the player. if (mCurrentBalance != 0) @@ -267,17 +274,17 @@ namespace MWGui std::string sound = "Item Gold Up"; MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - mWindowManager.removeGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { // i give you back your stuff! - returnBoughtItems(mWindowManager.getInventoryWindow()->getContainerStore()); + returnBoughtItems(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getContainerStore()); // now gimme back my stuff! - mWindowManager.getInventoryWindow()->returnBoughtItems(MWWorld::Class::get(mPtr).getContainerStore(mPtr)); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->returnBoughtItems(MWWorld::Class::get(mPtr).getContainerStore(mPtr)); - mWindowManager.removeGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onMaxSaleButtonClicked(MyGUI::Widget* _sender) @@ -321,7 +328,7 @@ namespace MWGui void TradeWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); if (mCurrentBalance > 0) { @@ -356,32 +363,7 @@ namespace MWGui services = ref->mBase->mAiData.mServices; } - /// \todo what about potions, there doesn't seem to be a flag for them?? - - if (item.getTypeName() == typeid(ESM::Weapon).name()) - return services & ESM::NPC::Weapon; - else if (item.getTypeName() == typeid(ESM::Armor).name()) - return services & ESM::NPC::Armor; - else if (item.getTypeName() == typeid(ESM::Clothing).name()) - return services & ESM::NPC::Clothing; - else if (item.getTypeName() == typeid(ESM::Book).name()) - return services & ESM::NPC::Books; - else if (item.getTypeName() == typeid(ESM::Ingredient).name()) - return services & ESM::NPC::Ingredients; - else if (item.getTypeName() == typeid(ESM::Lockpick).name()) - return services & ESM::NPC::Picks; - else if (item.getTypeName() == typeid(ESM::Probe).name()) - return services & ESM::NPC::Probes; - else if (item.getTypeName() == typeid(ESM::Light).name()) - return services & ESM::NPC::Lights; - else if (item.getTypeName() == typeid(ESM::Apparatus).name()) - return services & ESM::NPC::Apparatus; - else if (item.getTypeName() == typeid(ESM::Repair).name()) - return services & ESM::NPC::RepairItem; - else if (item.getTypeName() == typeid(ESM::Miscellaneous).name()) - return services & ESM::NPC::Misc; - - return false; + return MWWorld::Class::get(item).canSell(item, services); } std::vector TradeWindow::itemsToIgnore() @@ -422,8 +404,8 @@ namespace MWGui void TradeWindow::onReferenceUnavailable() { // remove both Trade and Dialogue (since you always trade with the NPC/creature that you have previously talked to) - mWindowManager.removeGuiMode(GM_Barter); - mWindowManager.removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } int TradeWindow::getMerchantGold() diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 2e05d03d51..892ce0297c 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -2,9 +2,6 @@ #define MWGUI_TRADEWINDOW_H #include "container.hpp" -#include "window_base.hpp" - -#include "../mwworld/ptr.hpp" namespace MyGUI { @@ -23,7 +20,7 @@ namespace MWGui class TradeWindow : public ContainerBase, public WindowBase { public: - TradeWindow(MWBase::WindowManager& parWindowManager); + TradeWindow(); void startTrade(MWWorld::Ptr actor); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 601b44d6c9..f85a8be594 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -20,8 +20,8 @@ namespace MWGui { - TrainingWindow::TrainingWindow(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_trainingwindow.layout", parWindowManager) + TrainingWindow::TrainingWindow() + : WindowBase("openmw_trainingwindow.layout") , mFadeTimeRemaining(0) { getWidget(mTrainingOptions, "TrainingOptions"); @@ -40,7 +40,7 @@ namespace MWGui { mPtr = actor; - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats (actor); @@ -82,7 +82,7 @@ namespace MWGui int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer (mPtr,pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); - std::string skin = (price > mWindowManager.getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton"; + std::string skin = (price > MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton"; MyGUI::Button* button = mTrainingOptions->createWidget(skin, MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); @@ -102,12 +102,12 @@ namespace MWGui void TrainingWindow::onReferenceUnavailable () { - mWindowManager.removeGuiMode(GM_Training); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training); } void TrainingWindow::onCancelButtonClicked (MyGUI::Widget *sender) { - mWindowManager.removeGuiMode (GM_Training); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); } void TrainingWindow::onTrainingSelected (MyGUI::Widget *sender) @@ -123,13 +123,13 @@ namespace MWGui int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - if (mWindowManager.getInventoryWindow()->getPlayerGold()getInventoryWindow()->getPlayerGold()messageBox ("#{sServiceTrainingWords}"); return; } @@ -141,11 +141,11 @@ namespace MWGui pcStats.increaseSkill (skillId, *class_, true); // remove gold - mWindowManager.getTradeWindow()->addOrRemoveGold(-price); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-price); // go back to game mode - mWindowManager.removeGuiMode (GM_Training); - mWindowManager.removeGuiMode (GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); // advance time MWBase::Environment::get().getWorld ()->advanceTime (2); diff --git a/apps/openmw/mwgui/trainingwindow.hpp b/apps/openmw/mwgui/trainingwindow.hpp index f2ef1714ee..740115cdfc 100644 --- a/apps/openmw/mwgui/trainingwindow.hpp +++ b/apps/openmw/mwgui/trainingwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_TRAININGWINDOW_H #define MWGUI_TRAININGWINDOW_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" namespace MWGui @@ -10,7 +10,7 @@ namespace MWGui class TrainingWindow : public WindowBase, public ReferenceInterface { public: - TrainingWindow(MWBase::WindowManager& parWindowManager); + TrainingWindow(); virtual void open(); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 465f588b8a..8eaa0d8c62 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -25,8 +25,8 @@ namespace MWGui { const int TravelWindow::sLineHeight = 18; - TravelWindow::TravelWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_travel_window.layout", parWindowManager) + TravelWindow::TravelWindow() : + WindowBase("openmw_travel_window.layout") , mCurrentY(0) , mLastPos(0) { @@ -71,7 +71,7 @@ namespace MWGui price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - MyGUI::Button* toAdd = mDestinationsView->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + MyGUI::Button* toAdd = mDestinationsView->createWidget((price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); mCurrentY += sLineHeight; if(interior) toAdd->setUserString("interior","y"); @@ -129,10 +129,10 @@ namespace MWGui int price; iss >> price; - if (mWindowManager.getInventoryWindow()->getPlayerGold()getInventoryWindow()->getPlayerGold()addOrRemoveGold (-price); + MWBase::Environment::get().getWindowManager()->getTradeWindow ()->addOrRemoveGold (-price); MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -156,20 +156,22 @@ namespace MWGui MWBase::Environment::get().getWorld()->advanceTime(time); } MWBase::Environment::get().getWorld()->moveObject(player,*cell,pos.pos[0],pos.pos[1],pos.pos[2]); - mWindowManager.removeGuiMode(GM_Travel); - mWindowManager.removeGuiMode(GM_Dialogue); + + MWWorld::Class::get(player).adjustPosition(player); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0); MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(1); } void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - mWindowManager.removeGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); } void TravelWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, @@ -178,8 +180,8 @@ namespace MWGui void TravelWindow::onReferenceUnavailable() { - mWindowManager.removeGuiMode(GM_Travel); - mWindowManager.removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void TravelWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index cc3d6a31f7..f2a23b0486 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -2,9 +2,6 @@ #define MWGUI_TravelWINDOW_H #include "container.hpp" -#include "window_base.hpp" - -#include "../mwworld/ptr.hpp" namespace MyGUI { @@ -23,7 +20,7 @@ namespace MWGui class TravelWindow : public ReferenceInterface, public WindowBase { public: - TravelWindow(MWBase::WindowManager& parWindowManager); + TravelWindow(); void startTravel(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 09eb5c914e..a84b9203a9 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -25,8 +25,8 @@ namespace MWGui { - WaitDialogProgressBar::WaitDialogProgressBar(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_wait_dialog_progressbar.layout", parWindowManager) + WaitDialogProgressBar::WaitDialogProgressBar() + : WindowBase("openmw_wait_dialog_progressbar.layout") { getWidget(mProgressBar, "ProgressBar"); getWidget(mProgressText, "ProgressText"); @@ -46,9 +46,9 @@ namespace MWGui // --------------------------------------------------------------------------------------------------------- - WaitDialog::WaitDialog(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_wait_dialog.layout", parWindowManager) - , mProgressBar(parWindowManager) + WaitDialog::WaitDialog() + : WindowBase("openmw_wait_dialog.layout") + , mProgressBar() , mWaiting(false) , mSleeping(false) , mHours(1) @@ -75,7 +75,7 @@ namespace MWGui { if (!MWBase::Environment::get().getWindowManager ()->getRestEnabled ()) { - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } int canRest = MWBase::Environment::get().getWorld ()->canRest (); @@ -83,8 +83,8 @@ namespace MWGui if (canRest == 2) { // resting underwater or mid-air not allowed - mWindowManager.messageBox ("#{sNotifyMessage1}"); - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } setCanRest(canRest == 0); @@ -212,7 +212,7 @@ namespace MWGui void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) { - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position) @@ -263,8 +263,8 @@ namespace MWGui { MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.2); mProgressBar.setVisible (false); - mWindowManager.removeGuiMode (GM_Rest); - mWindowManager.removeGuiMode (GM_RestBed); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed); mWaiting = false; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -273,7 +273,7 @@ namespace MWGui // trigger levelup if possible if (mSleeping && pcstats.getLevelProgress () >= 10) { - mWindowManager.pushGuiMode (GM_Levelup); + MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } } diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index c102d0fc67..d06d7d1128 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_WAIT_DIALOG_H #define MWGUI_WAIT_DIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -9,7 +9,7 @@ namespace MWGui class WaitDialogProgressBar : public WindowBase { public: - WaitDialogProgressBar(MWBase::WindowManager& parWindowManager); + WaitDialogProgressBar(); virtual void open(); @@ -23,7 +23,7 @@ namespace MWGui class WaitDialog : public WindowBase { public: - WaitDialog(MWBase::WindowManager& parWindowManager); + WaitDialog(); virtual void open(); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index e822e047ea..dd3206ed65 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -34,8 +34,7 @@ void MWGui::Widgets::fixTexturePath(std::string &path) /* MWSkill */ MWSkill::MWSkill() - : mManager(NULL) - , mSkillId(ESM::Skill::Length) + : mSkillId(ESM::Skill::Length) , mSkillNameWidget(NULL) , mSkillValueWidget(NULL) { @@ -65,7 +64,7 @@ void MWSkill::setSkillValue(const SkillValue& value) void MWSkill::updateWidgets() { - if (mSkillNameWidget && mManager) + if (mSkillNameWidget) { if (mSkillId == ESM::Skill::Length) { @@ -73,7 +72,7 @@ void MWSkill::updateWidgets() } else { - const std::string &name = mManager->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); + const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); static_cast(mSkillNameWidget)->setCaption(name); } } @@ -126,8 +125,7 @@ void MWSkill::initialiseOverride() /* MWAttribute */ MWAttribute::MWAttribute() - : mManager(NULL) - , mId(-1) + : mId(-1) , mAttributeNameWidget(NULL) , mAttributeValueWidget(NULL) { @@ -152,7 +150,7 @@ void MWAttribute::onClicked(MyGUI::Widget* _sender) void MWAttribute::updateWidgets() { - if (mAttributeNameWidget && mManager) + if (mAttributeNameWidget) { if (mId < 0 || mId >= 8) { @@ -170,7 +168,7 @@ void MWAttribute::updateWidgets() "sAttributePersonality", "sAttributeLuck" }; - const std::string &name = mManager->getGameSettingString(attributes[mId], ""); + const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(attributes[mId], ""); static_cast(mAttributeNameWidget)->setCaption(name); } } @@ -218,8 +216,7 @@ void MWAttribute::initialiseOverride() /* MWSpell */ MWSpell::MWSpell() - : mWindowManager(NULL) - , mSpellNameWidget(NULL) + : mSpellNameWidget(NULL) { } @@ -242,7 +239,6 @@ void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::W for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) { effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); - effect->setWindowManager(mWindowManager); SpellEffectParams params; params.mEffectID = it->mEffectID; params.mSkill = it->mSkill; @@ -262,7 +258,7 @@ void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::W void MWSpell::updateWidgets() { - if (mSpellNameWidget && mWindowManager) + if (mSpellNameWidget && MWBase::Environment::get().getWindowManager()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -289,8 +285,7 @@ MWSpell::~MWSpell() /* MWEffectList */ MWEffectList::MWEffectList() - : mWindowManager(NULL) - , mEffectList(0) + : mEffectList(0) { } @@ -311,7 +306,6 @@ void MWEffectList::createEffectWidgets(std::vector &effects, MyG it != mEffectList.end(); ++it) { effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); - effect->setWindowManager(mWindowManager); it->mIsConstant = (flags & EF_Constant) || it->mIsConstant; it->mNoTarget = (flags & EF_NoTarget) || it->mNoTarget; effect->setSpellEffect(*it); @@ -378,8 +372,7 @@ SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) /* MWSpellEffect */ MWSpellEffect::MWSpellEffect() - : mWindowManager(NULL) - , mImageWidget(NULL) + : mImageWidget(NULL) , mTextWidget(NULL) , mRequestedWidth(0) { @@ -409,22 +402,22 @@ void MWSpellEffect::updateWidgets() assert(magicEffect); - std::string pt = mWindowManager->getGameSettingString("spoint", ""); - std::string pts = mWindowManager->getGameSettingString("spoints", ""); - std::string to = " " + mWindowManager->getGameSettingString("sTo", "") + " "; - std::string sec = " " + mWindowManager->getGameSettingString("ssecond", ""); - std::string secs = " " + mWindowManager->getGameSettingString("sseconds", ""); + std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", ""); + std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", ""); + std::string to = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "") + " "; + std::string sec = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("ssecond", ""); + std::string secs = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sseconds", ""); std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); - std::string spellLine = mWindowManager->getGameSettingString(effectIDStr, ""); + std::string spellLine = MWBase::Environment::get().getWindowManager()->getGameSettingString(effectIDStr, ""); if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) { - spellLine += " " + mWindowManager->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); } if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) { - spellLine += " " + mWindowManager->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); } if ((mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) @@ -442,7 +435,7 @@ void MWSpellEffect::updateWidgets() { if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) { - spellLine += " " + mWindowManager->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); } if (mEffectParams.mArea > 0) @@ -453,13 +446,13 @@ void MWSpellEffect::updateWidgets() // potions have no target if (!mEffectParams.mNoTarget) { - std::string on = mWindowManager->getGameSettingString("sonword", ""); + std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sonword", ""); if (mEffectParams.mRange == ESM::RT_Self) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeSelf", ""); + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeSelf", ""); else if (mEffectParams.mRange == ESM::RT_Touch) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeTouch", ""); + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTouch", ""); else if (mEffectParams.mRange == ESM::RT_Target) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeTarget", ""); + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTarget", ""); } } diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 784537c42a..1567946913 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -4,8 +4,6 @@ #include "../mwworld/esmstore.hpp" #include "../mwmechanics/stat.hpp" -#include -#include #include #include @@ -94,12 +92,10 @@ namespace MWGui typedef MWMechanics::Stat SkillValue; - void setWindowManager(MWBase::WindowManager *m) { mManager = m; } /// \todo remove void setSkillId(ESM::Skill::SkillEnum skillId); void setSkillNumber(int skillId); void setSkillValue(const SkillValue& value); - MWBase::WindowManager *getWindowManager() const { return mManager; } ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } const SkillValue& getSkillValue() const { return mValue; } @@ -122,7 +118,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager *mManager; ESM::Skill::SkillEnum mSkillId; SkillValue mValue; MyGUI::Widget* mSkillNameWidget; @@ -138,11 +133,9 @@ namespace MWGui typedef MWMechanics::Stat AttributeValue; - void setWindowManager(MWBase::WindowManager *m) { mManager = m; } void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); - MWBase::WindowManager *getWindowManager() const { return mManager; } int getAttributeId() const { return mId; } const AttributeValue& getAttributeValue() const { return mValue; } @@ -165,7 +158,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager *mManager; int mId; AttributeValue mValue; MyGUI::Widget* mAttributeNameWidget; @@ -185,7 +177,6 @@ namespace MWGui typedef MWMechanics::Stat SpellValue; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellId(const std::string &id); /** @@ -207,7 +198,6 @@ namespace MWGui private: void updateWidgets(); - MWBase::WindowManager* mWindowManager; std::string mId; MyGUI::TextBox* mSpellNameWidget; }; @@ -227,7 +217,6 @@ namespace MWGui EF_Constant = 0x02 // constant effect means that duration will not be displayed }; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setEffectList(const SpellEffectList& list); static SpellEffectList effectListFromESM(const ESM::EffectList* effects); @@ -249,7 +238,6 @@ namespace MWGui private: void updateWidgets(); - MWBase::WindowManager* mWindowManager; SpellEffectList mEffectList; }; typedef MWEffectList* MWEffectListPtr; @@ -262,7 +250,6 @@ namespace MWGui typedef ESM::ENAMstruct SpellEffectValue; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellEffect(const SpellEffectParams& params); int getRequestedWidth() const { return mRequestedWidth; } @@ -276,7 +263,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager* mWindowManager; SpellEffectParams mEffectParams; MyGUI::ImageBox* mImageWidget; MyGUI::TextBox* mTextWidget; diff --git a/apps/openmw/mwgui/window_base.cpp b/apps/openmw/mwgui/windowbase.cpp similarity index 80% rename from apps/openmw/mwgui/window_base.cpp rename to apps/openmw/mwgui/windowbase.cpp index 38bee9ea30..c41bcb7ce6 100644 --- a/apps/openmw/mwgui/window_base.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -1,4 +1,4 @@ -#include "window_base.hpp" +#include "windowbase.hpp" #include @@ -6,9 +6,8 @@ using namespace MWGui; -WindowBase::WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) +WindowBase::WindowBase(const std::string& parLayout) : Layout(parLayout) - , mWindowManager(parWindowManager) { } @@ -39,8 +38,8 @@ void WindowBase::center() mMainWidget->setCoord(coord); } -WindowModal::WindowModal(const std::string& parLayout, MWBase::WindowManager& parWindowManager) - : WindowBase(parLayout, parWindowManager) +WindowModal::WindowModal(const std::string& parLayout) + : WindowBase(parLayout) { } diff --git a/apps/openmw/mwgui/window_base.hpp b/apps/openmw/mwgui/windowbase.hpp similarity index 76% rename from apps/openmw/mwgui/window_base.hpp rename to apps/openmw/mwgui/windowbase.hpp index afdf4d065b..2c014baf0b 100644 --- a/apps/openmw/mwgui/window_base.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -15,7 +15,7 @@ namespace MWGui class WindowBase: public OEngine::GUI::Layout { public: - WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); + WindowBase(const std::string& parLayout); // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; @@ -29,10 +29,6 @@ namespace MWGui signature : void method()\n */ EventHandle_WindowBase eventDone; - - protected: - /// \todo remove - MWBase::WindowManager& mWindowManager; }; @@ -42,7 +38,7 @@ namespace MWGui class WindowModal : public WindowBase { public: - WindowModal(const std::string& parLayout, MWBase::WindowManager& parWindowManager); + WindowModal(const std::string& parLayout); virtual void open(); virtual void close(); }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index cf14c1f514..f2ded8813e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -21,12 +21,12 @@ #include "console.hpp" #include "journalwindow.hpp" #include "charactercreation.hpp" -#include "text_input.hpp" +#include "textinput.hpp" #include "review.hpp" #include "dialogue.hpp" -#include "dialogue_history.hpp" -#include "map_window.hpp" -#include "stats_window.hpp" +#include "dialoguehistory.hpp" +#include "mapwindow.hpp" +#include "statswindow.hpp" #include "messagebox.hpp" #include "container.hpp" #include "inventorywindow.hpp" @@ -162,38 +162,38 @@ WindowManager::WindowManager( mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; mMenu = new MainMenu(w,h); - mMap = new MapWindow(*this, cacheDir); - mStatsWindow = new StatsWindow(*this); + mMap = new MapWindow(cacheDir); + mStatsWindow = new StatsWindow(); mConsole = new Console(w,h, consoleOnlyScripts); - mJournal = new JournalWindow(*this); - mMessageBoxManager = new MessageBoxManager(this); - mInventoryWindow = new InventoryWindow(*this,mDragAndDrop); - mTradeWindow = new TradeWindow(*this); - mSpellBuyingWindow = new SpellBuyingWindow(*this); - mTravelWindow = new TravelWindow(*this); - mDialogueWindow = new DialogueWindow(*this); - mContainerWindow = new ContainerWindow(*this,mDragAndDrop); + mJournal = new JournalWindow(); + mMessageBoxManager = new MessageBoxManager(); + mInventoryWindow = new InventoryWindow(mDragAndDrop); + mTradeWindow = new TradeWindow(); + mSpellBuyingWindow = new SpellBuyingWindow(); + mTravelWindow = new TravelWindow(); + mDialogueWindow = new DialogueWindow(); + mContainerWindow = new ContainerWindow(mDragAndDrop); mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); - mToolTips = new ToolTips(this); - mScrollWindow = new ScrollWindow(*this); - mBookWindow = new BookWindow(*this); - mCountDialog = new CountDialog(*this); - mSettingsWindow = new SettingsWindow(*this); - mConfirmationDialog = new ConfirmationDialog(*this); - mAlchemyWindow = new AlchemyWindow(*this); - mSpellWindow = new SpellWindow(*this); - mQuickKeysMenu = new QuickKeysMenu(*this); - mLevelupDialog = new LevelupDialog(*this); - mWaitDialog = new WaitDialog(*this); - mSpellCreationDialog = new SpellCreationDialog(*this); - mEnchantingDialog = new EnchantingDialog(*this); - mTrainingWindow = new TrainingWindow(*this); - mMerchantRepair = new MerchantRepair(*this); - mRepair = new Repair(*this); + mToolTips = new ToolTips(); + mScrollWindow = new ScrollWindow(); + mBookWindow = new BookWindow(); + mCountDialog = new CountDialog(); + mSettingsWindow = new SettingsWindow(); + mConfirmationDialog = new ConfirmationDialog(); + mAlchemyWindow = new AlchemyWindow(); + mSpellWindow = new SpellWindow(); + mQuickKeysMenu = new QuickKeysMenu(); + mLevelupDialog = new LevelupDialog(); + mWaitDialog = new WaitDialog(); + mSpellCreationDialog = new SpellCreationDialog(); + mEnchantingDialog = new EnchantingDialog(); + mTrainingWindow = new TrainingWindow(); + mMerchantRepair = new MerchantRepair(); + mRepair = new Repair(); mSoulgemDialog = new SoulgemDialog(mMessageBoxManager); - mCompanionWindow = new CompanionWindow(*this, mDragAndDrop, mMessageBoxManager); + mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); - mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow (), *this); + mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ()); mLoadingScreen->onResChange (w,h); mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); @@ -202,7 +202,7 @@ WindowManager::WindowManager( mHud->setVisible(mHudEnabled); - mCharGen = new CharacterCreation(this); + mCharGen = new CharacterCreation(); // Setup player stats for (int i = 0; i < ESM::Attribute::Length; ++i) @@ -933,14 +933,19 @@ void WindowManager::setSelectedSpell(const std::string& spellId, int successChan mSpellWindow->setTitle(spell->mName); } -void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) +void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { + const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() + .find(MWWorld::Class::get(item).getEnchantment(item)); + + int chargePercent = item.getCellRef().mEnchantmentCharge / static_cast(ench->mData.mCharge) * 100; mHud->setSelectedEnchantItem(item, chargePercent); mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); } -void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) +void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { + int durabilityPercent = item.getCellRef().mCharge / static_cast(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100; mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); } @@ -1190,3 +1195,8 @@ void WindowManager::showSoulgemDialog(MWWorld::Ptr item) { mSoulgemDialog->show(item); } + +void WindowManager::frameStarted (float dt) +{ + mInventoryWindow->doRenderUpdate (); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 7a7adec27d..652ad870f3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -10,11 +10,6 @@ this class. **/ -#include -#include - -#include - #include "../mwbase/windowmanager.hpp" namespace MyGUI @@ -179,8 +174,8 @@ namespace MWGui virtual void activateQuickKey (int index); virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); - virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); - virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); + virtual void setSelectedWeapon(const MWWorld::Ptr& item); virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); @@ -238,6 +233,8 @@ namespace MWGui virtual void startRepair(MWWorld::Ptr actor); virtual void startRepairItem(MWWorld::Ptr item); + virtual void frameStarted(float dt); + virtual void showSoulgemDialog (MWWorld::Ptr item); virtual void changePointer (const std::string& name); diff --git a/apps/openmw/mwgui/window_pinnable_base.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp similarity index 80% rename from apps/openmw/mwgui/window_pinnable_base.cpp rename to apps/openmw/mwgui/windowpinnablebase.cpp index 651b3a1e98..56868306ac 100644 --- a/apps/openmw/mwgui/window_pinnable_base.cpp +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -1,4 +1,4 @@ -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" #include "../mwbase/windowmanager.hpp" @@ -6,8 +6,8 @@ using namespace MWGui; -WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) - : WindowBase(parLayout, parWindowManager), mPinned(false), mVisible(false) +WindowPinnableBase::WindowPinnableBase(const std::string& parLayout) + : WindowBase(parLayout), mPinned(false), mVisible(false) { ExposedWindow* window = static_cast(mMainWidget); mPinButton = window->getSkinWidget ("Button"); diff --git a/apps/openmw/mwgui/window_pinnable_base.hpp b/apps/openmw/mwgui/windowpinnablebase.hpp similarity index 78% rename from apps/openmw/mwgui/window_pinnable_base.hpp rename to apps/openmw/mwgui/windowpinnablebase.hpp index 50259858e2..1ab6294328 100644 --- a/apps/openmw/mwgui/window_pinnable_base.hpp +++ b/apps/openmw/mwgui/windowpinnablebase.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_WINDOW_PINNABLE_BASE_H #define MWGUI_WINDOW_PINNABLE_BASE_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -10,7 +10,7 @@ namespace MWGui class WindowPinnableBase: public WindowBase { public: - WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); + WindowPinnableBase(const std::string& parLayout); bool pinned() { return mPinned; } private: diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 74d581b811..6a34161e08 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -301,6 +301,12 @@ namespace MWInput mPlayer.setForwardBackward (-1); } + else if(mPlayer.getAutoMove()) + { + triedToMove = true; + mPlayer.setForwardBackward (1); + } + mPlayer.setSneak(actionIsActive(A_Sneak)); if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"]) @@ -321,6 +327,7 @@ namespace MWInput mOverencumberedMessageDelay -= dt; if (MWWorld::Class::get(player).getEncumbrance(player) >= MWWorld::Class::get(player).getCapacity(player)) { + mPlayer.setAutoMove (false); if (mOverencumberedMessageDelay <= 0) { MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}"); @@ -526,8 +533,17 @@ namespace MWInput float scale = MWBase::Environment::get().getFrameDuration(); if(scale <= 0.0f) scale = 1.0f; - mPlayer.setYaw(x/scale); - mPlayer.setPitch(-y/scale); + float rot[3]; + rot[0] = -y; + rot[1] = 0.0f; + rot[2] = x; + + // Only actually turn player when we're not in vanity mode + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) + { + mPlayer.setYaw(x/scale); + mPlayer.setPitch(-y/scale); + } if (arg.state.Z.rel) MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.state.Z.rel); @@ -649,9 +665,15 @@ namespace MWInput bool gameMode = !mWindows.isGuiMode(); if(gameMode) + { + MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); mWindows.pushGuiMode(MWGui::GM_Journal); + } else if(mWindows.getMode() == MWGui::GM_Journal) + { + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); mWindows.popGuiMode(); + } // .. but don't touch any other mode. } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f60567c6c4..89671ee086 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -36,8 +36,7 @@ namespace MWMechanics void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) { if (!paused && ptr.getRefData().getHandle()!="player") - MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip ( - MWWorld::Class::get (ptr).getNpcStats (ptr)); + MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip (ptr); } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index e9db6d212a..3cd78a9338 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -13,7 +13,7 @@ MWMechanics::AiPackage * MWMechanics::AiWander::clone() const bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) { - std::cout << "AiWadner completed.\n"; + // Return completed return true; } diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 4ef20a5e52..a38cb0037b 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -3,10 +3,15 @@ #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include "creaturestats.hpp" +#include "npcstats.hpp" +#include + namespace MWMechanics { - Enchanting::Enchanting(MWWorld::Ptr enchanter): - mEnchanter(enchanter), + Enchanting::Enchanting(): mEnchantType(0) {} @@ -17,6 +22,7 @@ namespace MWMechanics { mObjectType = mOldItemPtr.getTypeName(); mOldItemId = mOldItemPtr.getCellRef().mRefID; + mOldItemCount = mOldItemPtr.getRefData().getCount(); } else { @@ -25,7 +31,7 @@ namespace MWMechanics } } - void Enchanting::setNewItemName(std::string s) + void Enchanting::setNewItemName(const std::string& s) { mNewItemName=s; } @@ -35,7 +41,7 @@ namespace MWMechanics mEffectList=effectList; } - int Enchanting::getEnchantType() + int Enchanting::getEnchantType() const { return mEnchantType; } @@ -45,28 +51,51 @@ namespace MWMechanics mSoulGemPtr=soulGem; } - void Enchanting::create() + bool Enchanting::create() { - mOldItemPtr.getRefData().setCount(mOldItemPtr.getRefData().getCount()-1); - mSoulGemPtr.getRefData().setCount(mSoulGemPtr.getRefData().getCount()-1); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + ESM::Enchantment enchantment; + enchantment.mData.mCharge = getGemCharge(); + + mSoulGemPtr.getRefData().setCount (mSoulGemPtr.getRefData().getCount()-1); + + //Exception for Azura Star, new one will be added after enchanting + if(boost::iequals(mSoulGemPtr.get()->mBase->mId, "Misc_SoulGem_Azura")) + { + MWWorld::ManualRef azura (MWBase::Environment::get().getWorld()->getStore(), "Misc_SoulGem_Azura"); + MWWorld::Class::get (player).getContainerStore (player).add (azura.getPtr()); + } + + if(mSelfEnchanting) + { + if(getEnchantChance() (RAND_MAX)*100) + return false; + + MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1); + } - mEnchantment.mData.mCharge = getGemCharge(); if(mEnchantType==3) { - mEnchantment.mData.mCharge=0; + enchantment.mData.mCharge=0; } - mEnchantment.mData.mType = mEnchantType; - mEnchantment.mData.mCost = getEnchantCost(); - mEnchantment.mEffects = mEffectList; - const ESM::Enchantment *enchantment = MWBase::Environment::get().getWorld()->createRecord (mEnchantment); + enchantment.mData.mType = mEnchantType; + enchantment.mData.mCost = getEnchantCost(); + enchantment.mEffects = mEffectList; - std::string newobjId = MWWorld::Class::get(mOldItemPtr).applyEnchantment(mOldItemPtr, enchantment->mId, getGemCharge(), mNewItemName); + const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); - MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), newobjId); - MWWorld::Ptr newobjPtr = ref.getPtr(); - MWWorld::Ptr result = mOldItemPtr; - result.mPtr = newobjPtr.mPtr; - MWWorld::Class::get (mEnchanter).getContainerStore (mEnchanter).add (result); + MWWorld::Class::get(mOldItemPtr).applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName); + + mOldItemPtr.getRefData().setCount(1); + + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), mOldItemId); + ref.getPtr().getRefData().setCount (mOldItemCount-1); + + MWWorld::Class::get (player).getContainerStore (player).add (ref.getPtr()); + if(!mSelfEnchanting) + payForEnchantment(); + + return true; } void Enchanting::nextEnchantType() @@ -79,12 +108,13 @@ namespace MWMechanics } if ((mObjectType == typeid(ESM::Armor).name())||(mObjectType == typeid(ESM::Clothing).name())) { + int soulConstAmount = MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->getInt(); switch(mEnchantType) { case 1: mEnchantType = 2; case 3: - if(getGemCharge()<400) + if(getGemCharge()getStore(); float cost = 0; std::vector mEffects = mEffectList.mList; int i=mEffects.size(); + if(i<=0) + return 0; + + /* + Formula from http://www.uesp.net/wiki/Morrowind:Enchant + */ for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { const ESM::MagicEffect* effect = store.get().find(it->mEffectID); @@ -120,7 +156,8 @@ namespace MWMechanics if(mEnchantType==3) { - cost1 *= 100; + int constDurationMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentConstantDurationMult")->getFloat(); + cost1 *= constDurationMultipler; cost2 *= 2; } if(effect->mData.mFlags & ESM::MagicEffect::CastTarget) @@ -134,7 +171,18 @@ namespace MWMechanics } return cost; } - int Enchanting::getGemCharge() + + int Enchanting::getEnchantPrice() const + { + if(mEnchanter.isEmpty()) + return 0; + + float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->getFloat(); + int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, (getEnchantCost() * priceMultipler), true); + return price; + } + + int Enchanting::getGemCharge() const { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); if(soulEmpty()) @@ -145,23 +193,73 @@ namespace MWMechanics return soul->mData.mSoul; } - int Enchanting::getMaxEnchantValue() + int Enchanting::getMaxEnchantValue() const { if (itemEmpty()) return 0; return MWWorld::Class::get(mOldItemPtr).getEnchantmentPoints(mOldItemPtr); } - bool Enchanting::soulEmpty() + bool Enchanting::soulEmpty() const { if (mSoulGemPtr.isEmpty()) return true; return false; } - bool Enchanting::itemEmpty() + bool Enchanting::itemEmpty() const { if(mOldItemPtr.isEmpty()) return true; return false; } + + void Enchanting::setSelfEnchanting(bool selfEnchanting) + { + mSelfEnchanting = selfEnchanting; + } + + void Enchanting::setEnchanter(MWWorld::Ptr enchanter) + { + mEnchanter = enchanter; + } + + float Enchanting::getEnchantChance() const + { + /* + Formula from http://www.uesp.net/wiki/Morrowind:Enchant + */ + const CreatureStats& creatureStats = MWWorld::Class::get (mEnchanter).getCreatureStats (mEnchanter); + const NpcStats& npcStats = MWWorld::Class::get (mEnchanter).getNpcStats (mEnchanter); + + float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() + + (0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()) + + (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())); + + float chance2 = 2.5 * getEnchantCost(); + if(mEnchantType==3) + { + float constantChance = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentConstantChanceMult")->getFloat(); + chance2 /= constantChance; + } + return (chance1-chance2); + } + + void Enchanting::payForEnchantment() const + { + MWWorld::Ptr gold; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + + for (MWWorld::ContainerStoreIterator it = store.begin(); + it != store.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) + { + gold = *it; + } + } + + gold.getRefData().setCount(gold.getRefData().getCount() - getEnchantPrice()); + } } diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index 1daf34c6d1..d8a6342ac9 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -9,33 +9,40 @@ namespace MWMechanics { class Enchanting { - MWWorld::Ptr mOldItemPtr; MWWorld::Ptr mSoulGemPtr; MWWorld::Ptr mEnchanter; - const MWWorld::Ptr *mNewItemPtr; + int mEnchantType; + bool mSelfEnchanting; + ESM::EffectList mEffectList; - ESM::Enchantment mEnchantment; std::string mNewItemName; std::string mObjectType; std::string mOldItemId; + int mOldItemCount; + public: - Enchanting(MWWorld::Ptr enchanter); + Enchanting(); + void setEnchanter(MWWorld::Ptr enchanter); + void setSelfEnchanting(bool selfEnchanting); void setOldItem(MWWorld::Ptr oldItem); - void setNewItemName(std::string s); + void setNewItemName(const std::string& s); void setEffect(ESM::EffectList effectList); void setSoulGem(MWWorld::Ptr soulGem); - void create(); - void nextEnchantType(); - int getEnchantType(); - int getEnchantCost(); - int getMaxEnchantValue(); - int getGemCharge(); - bool soulEmpty(); - bool itemEmpty(); + bool create(); //Return true if created, false if failed. + void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object) + int getEnchantType() const; + int getEnchantCost() const; + int getEnchantPrice() const; + int getMaxEnchantValue() const; + int getGemCharge() const; + float getEnchantChance() const; + bool soulEmpty() const; //Return true if empty + bool itemEmpty() const; //Return true if empty + void payForEnchantment() const; }; } #endif diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c8d8279210..4a2a2ecc69 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -53,21 +53,9 @@ namespace MWMechanics for (int i=0; i<8; ++i) { - const ESM::Race::MaleFemale *attribute = 0; - switch (i) - { - case 0: attribute = &race->mData.mStrength; break; - case 1: attribute = &race->mData.mIntelligence; break; - case 2: attribute = &race->mData.mWillpower; break; - case 3: attribute = &race->mData.mAgility; break; - case 4: attribute = &race->mData.mSpeed; break; - case 5: attribute = &race->mData.mEndurance; break; - case 6: attribute = &race->mData.mPersonality; break; - case 7: attribute = &race->mData.mLuck; break; - } + const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; - creatureStats.getAttribute(i).setBase ( - static_cast (male ? attribute->mMale : attribute->mFemale)); + creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); } for (int i=0; i<27; ++i) @@ -161,7 +149,7 @@ namespace MWMechanics // forced update and current value adjustments mActors.updateActor (ptr, 0); - for (int i=0; i<2; ++i) + for (int i=0; i<3; ++i) { DynamicStat stat = creatureStats.getDynamic (i); stat.setCurrent (stat.getModified()); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index b9aee6abf3..7216e8fe0e 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -334,7 +334,7 @@ bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int std::vector skills; for (int i=0; i<6; ++i) - skills.push_back (static_cast (getSkill (faction.mData.mSkillID[i]).getModified())); + skills.push_back (static_cast (getSkill (faction.mData.mSkills[i]).getModified())); std::sort (skills.begin(), skills.end()); diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 3a2e51644e..7b1185a29a 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -1,5 +1,5 @@ #ifndef GAME_MWMECHANICS_ACTIVATORS_H -#define GAME_MWMECHANICS_ACTOVATORS_H +#define GAME_MWMECHANICS_ACTIVATORS_H #include #include diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 961c070038..4630208b4b 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -1,6 +1,7 @@ #include "activatoranimation.hpp" #include +#include #include #include @@ -25,18 +26,24 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) { std::string mesh = "meshes\\" + ref->mBase->mModel; - createEntityList(mPtr.getRefData().getBaseNode(), mesh); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) + createObjectList(mPtr.getRefData().getBaseNode(), mesh); + for(size_t i = 0;i < mObjectList.mEntities.size();i++) { - Ogre::Entity *ent = mEntityList.mEntities[i]; + Ogre::Entity *ent = mObjectList.mEntities[i]; + ent->setVisibilityFlags(RV_Misc); for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) { Ogre::SubEntity* subEnt = ent->getSubEntity(j); subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } + } + for(size_t i = 0;i < mObjectList.mParticles.size();i++) + { + Ogre::ParticleSystem *part = mObjectList.mParticles[i]; + part->setVisibilityFlags(RV_Misc); - ent->setVisibilityFlags(RV_Misc); + part->setRenderQueueGroup(RQG_Alpha); } setAnimationSource(mesh); } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index cc926e685d..a849b20efe 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -16,6 +16,19 @@ namespace MWRender { +void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) +{ + for(size_t i = 0;i < objects.mParticles.size();i++) + sceneMgr->destroyParticleSystem(objects.mParticles[i]); + for(size_t i = 0;i < objects.mEntities.size();i++) + sceneMgr->destroyEntity(objects.mEntities[i]); + objects.mControllers.clear(); + objects.mCameras.clear(); + objects.mParticles.clear(); + objects.mEntities.clear(); + objects.mSkelBase = NULL; +} + Animation::Animation(const MWWorld::Ptr &ptr) : mPtr(ptr) , mController(NULL) @@ -24,6 +37,7 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mNonAccumRoot(NULL) , mAccumulate(Ogre::Vector3::ZERO) , mLastPosition(0.0f) + , mCurrentControllers(NULL) , mCurrentKeys(NULL) , mCurrentAnim(NULL) , mCurrentTime(0.0f) @@ -40,37 +54,67 @@ Animation::~Animation() if(mInsert) { Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - sceneMgr->destroyEntity(mEntityList.mEntities[i]); + destroyObjectList(sceneMgr, mObjectList); + + for(size_t i = 0;i < mAnimationSources.size();i++) + destroyObjectList(sceneMgr, mAnimationSources[i]); + mAnimationSources.clear(); } - mEntityList.mEntities.clear(); - mEntityList.mSkelBase = NULL; } void Animation::setAnimationSources(const std::vector &names) { - if(!mEntityList.mSkelBase) + if(!mObjectList.mSkelBase) return; + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + mCurrentControllers = &mObjectList.mControllers; mCurrentAnim = NULL; mCurrentKeys = NULL; mAnimVelocity = 0.0f; mAccumRoot = NULL; mNonAccumRoot = NULL; - mSkeletonSources.clear(); + mTextKeys.clear(); + for(size_t i = 0;i < mAnimationSources.size();i++) + destroyObjectList(sceneMgr, mAnimationSources[i]); + mAnimationSources.clear(); + Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); + Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton(); std::vector::const_iterator nameiter; for(nameiter = names.begin();nameiter != names.end();nameiter++) { - Ogre::SkeletonPtr skel = NifOgre::Loader::getSkeleton(*nameiter); - if(skel.isNull()) + mAnimationSources.push_back(NifOgre::Loader::createObjectBase(sceneMgr, *nameiter)); + if(!mAnimationSources.back().mSkelBase) { std::cerr<< "Failed to get skeleton source "<<*nameiter <touch(); + NifOgre::ObjectList &objects = mAnimationSources.back(); + for(size_t i = 0;i < objects.mControllers.size();i++) + { + NifOgre::NodeTargetValue *dstval = dynamic_cast*>(objects.mControllers[i].getDestination().getPointer()); + if(!dstval) continue; + + const Ogre::String &trgtname = dstval->getNode()->getName(); + if(!skelinst->hasBone(trgtname)) continue; + + Ogre::Bone *bone = skelinst->getBone(trgtname); + dstval->setNode(bone); + } + + for(size_t i = 0;i < objects.mControllers.size();i++) + { + if(objects.mControllers[i].getSource().isNull()) + objects.mControllers[i].setSource(ctrlval); + } + + Ogre::Entity *ent = objects.mSkelBase; + Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(ent->getSkeleton()->getName()); Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); while(boneiter.hasMoreElements()) { @@ -83,15 +127,14 @@ void Animation::setAnimationSources(const std::vector &names) if(!mNonAccumRoot) { mAccumRoot = mInsert; - mNonAccumRoot = mEntityList.mSkelBase->getSkeleton()->getBone(bone->getName()); + mNonAccumRoot = mObjectList.mSkelBase->getSkeleton()->getBone(bone->getName()); } - mSkeletonSources.push_back(skel); for(int i = 0;i < skel->getNumAnimations();i++) { Ogre::Animation *anim = skel->getAnimation(i); const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+ - "@"+anim->getName()); + "@"+anim->getName()); if(!groupdata.isEmpty()) mTextKeys[anim->getName()] = Ogre::any_cast(groupdata); } @@ -101,15 +144,15 @@ void Animation::setAnimationSources(const std::vector &names) } } -void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model) +void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model) { mInsert = node->createChildSceneNode(); assert(mInsert); - mEntityList = NifOgre::Loader::createEntities(mInsert, model); - if(mEntityList.mSkelBase) + mObjectList = NifOgre::Loader::createObjects(mInsert, model); + if(mObjectList.mSkelBase) { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateSet *aset = mObjectList.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -121,19 +164,39 @@ void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model // Set the bones as manually controlled since we're applying the // transformations manually (needed if we want to apply an animation // from one skeleton onto another). - Ogre::SkeletonInstance *skelinst = mEntityList.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); } + + Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); + for(size_t i = 0;i < mObjectList.mControllers.size();i++) + { + if(mObjectList.mControllers[i].getSource().isNull()) + mObjectList.mControllers[i].setSource(ctrlval); + } + mCurrentControllers = &mObjectList.mControllers; +} + + +Ogre::Node *Animation::getNode(const std::string &name) +{ + if(mObjectList.mSkelBase) + { + Ogre::SkeletonInstance *skel = mObjectList.mSkelBase->getSkeleton(); + if(skel->hasBone(name)) + return skel->getBone(name); + } + return NULL; } bool Animation::hasAnimation(const std::string &anim) { - for(std::vector::const_iterator iter(mSkeletonSources.begin());iter != mSkeletonSources.end();iter++) + for(std::vector::const_iterator iter(mAnimationSources.begin());iter != mAnimationSources.end();iter++) { - if((*iter)->hasAnimation(anim)) + if(iter->mSkelBase->hasAnimationState(anim)) return true; } return false; @@ -212,28 +275,6 @@ void Animation::calcAnimVelocity() } } -void Animation::applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel) -{ - Ogre::TimeIndex timeindex = anim->_getTimeIndex(time); - Ogre::Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator(); - while(tracks.hasMoreElements()) - { - Ogre::NodeAnimationTrack *track = tracks.getNext(); - const Ogre::String &targetname = track->getAssociatedNode()->getName(); - if(!skel->hasBone(targetname)) - continue; - Ogre::Bone *bone = skel->getBone(targetname); - bone->setOrientation(Ogre::Quaternion::IDENTITY); - bone->setPosition(Ogre::Vector3::ZERO); - bone->setScale(Ogre::Vector3::UNIT_SCALE); - track->applyToNode(bone, timeindex); - } - - // HACK: Dirty the animation state set so that Ogre will apply the - // transformations to entities this skeleton instance is shared with. - mEntityList.mSkelBase->getAllAnimationStates()->_notifyDirty(); -} - static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) { if(skelsrc->hasBone(bone->getName())) @@ -272,24 +313,29 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og } -Ogre::Vector3 Animation::updatePosition(float time) +Ogre::Vector3 Animation::updatePosition() { - if(mLooping) - mCurrentTime = std::fmod(std::max(time, 0.0f), mCurrentAnim->getLength()); - else - mCurrentTime = std::min(mCurrentAnim->getLength(), std::max(time, 0.0f)); - applyAnimation(mCurrentAnim, mCurrentTime, mEntityList.mSkelBase->getSkeleton()); + Ogre::Vector3 posdiff; - Ogre::Vector3 posdiff = Ogre::Vector3::ZERO; - if(mNonAccumRoot) + Ogre::TransformKeyFrame kf(0, mCurrentTime); + Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); + while(trackiter.hasMoreElements()) { - /* Get the non-accumulation root's difference from the last update. */ - posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate; - - /* Translate the accumulation root back to compensate for the move. */ - mLastPosition += posdiff; - mAccumRoot->setPosition(-mLastPosition); + const Ogre::NodeAnimationTrack *track = trackiter.getNext(); + if(track->getAssociatedNode()->getName() == mNonAccumRoot->getName()) + { + track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf); + break; + } } + + /* Get the non-accumulation root's difference from the last update. */ + posdiff = (kf.getTranslate() - mLastPosition) * mAccumulate; + + /* Translate the accumulation root back to compensate for the move. */ + mLastPosition += posdiff; + mAccumRoot->setPosition(-mLastPosition); + return posdiff; } @@ -304,7 +350,15 @@ void Animation::reset(const std::string &start, const std::string &stop) else { mNextKey = mCurrentKeys->begin(); - mCurrentTime = 0.0f; + while(mNextKey != mCurrentKeys->end() && mNextKey->second != "start") + mNextKey++; + if(mNextKey != mCurrentKeys->end()) + mCurrentTime = mNextKey->first; + else + { + mNextKey = mCurrentKeys->begin(); + mCurrentTime = 0.0f; + } } if(stop.length() > 0) @@ -392,12 +446,13 @@ void Animation::play(const std::string &groupname, const std::string &start, con try { bool found = false; /* Look in reverse; last-inserted source has priority. */ - for(std::vector::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++) + for(std::vector::reverse_iterator iter(mAnimationSources.rbegin());iter != mAnimationSources.rend();iter++) { - if((*iter)->hasAnimation(groupname)) + if(iter->mSkelBase->hasAnimationState(groupname)) { - mCurrentAnim = (*iter)->getAnimation(groupname); + mCurrentAnim = iter->mSkelBase->getSkeleton()->getAnimation(groupname); mCurrentKeys = &mTextKeys[groupname]; + mCurrentControllers = &iter->mControllers; mAnimVelocity = 0.0f; if(mNonAccumRoot) @@ -421,16 +476,19 @@ void Animation::play(const std::string &groupname, const std::string &start, con Ogre::Vector3 Animation::runAnimation(float timepassed) { - Ogre::Vector3 movement = Ogre::Vector3::ZERO; + Ogre::Vector3 movement(0.0f); timepassed *= mAnimSpeedMult; while(mCurrentAnim && mPlaying) { - float targetTime = std::min(mStopTime, mCurrentTime+timepassed); + float targetTime = mCurrentTime + timepassed; if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime) { - movement += updatePosition(targetTime); - mPlaying = (mLooping || mStopTime > targetTime); + mCurrentTime = std::min(mStopTime, targetTime); + if(mNonAccumRoot) + movement += updatePosition(); + mPlaying = (mLooping || mStopTime > mCurrentTime); + timepassed = targetTime - mCurrentTime; break; } @@ -438,14 +496,24 @@ Ogre::Vector3 Animation::runAnimation(float timepassed) const std::string &evt = mNextKey->second; mNextKey++; - movement += updatePosition(time); - mPlaying = (mLooping || mStopTime > time); - - timepassed = targetTime - time; + mCurrentTime = time; + if(mNonAccumRoot) + movement += updatePosition(); + mPlaying = (mLooping || mStopTime > mCurrentTime); + timepassed = targetTime - mCurrentTime; if(!handleEvent(time, evt)) break; } + for(size_t i = 0;i < mCurrentControllers->size();i++) + (*mCurrentControllers)[i].update(); + + if(mObjectList.mSkelBase) + { + // HACK: Dirty the animation state set so that Ogre will apply the + // transformations to entities this skeleton instance is shared with. + mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty(); + } return movement; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 7caf351694..029c56523d 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,6 +1,9 @@ #ifndef _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H +#include +#include + #include #include "../mwworld/ptr.hpp" @@ -16,19 +19,40 @@ namespace MWRender class Animation { protected: + class AnimationValue : public Ogre::ControllerValue + { + private: + Animation *mAnimation; + + public: + AnimationValue(Animation *anim) : mAnimation(anim) + { } + + virtual Ogre::Real getValue() const + { + return mAnimation->mCurrentTime; + } + + virtual void setValue(Ogre::Real value) + { + mAnimation->mCurrentTime = value; + } + }; + MWWorld::Ptr mPtr; MWMechanics::CharacterController *mController; Ogre::SceneNode* mInsert; - NifOgre::EntityList mEntityList; + NifOgre::ObjectList mObjectList; std::map mTextKeys; Ogre::Node *mAccumRoot; Ogre::Bone *mNonAccumRoot; Ogre::Vector3 mAccumulate; Ogre::Vector3 mLastPosition; - std::vector mSkeletonSources; + std::vector mAnimationSources; + std::vector > *mCurrentControllers; NifOgre::TextKeyMap *mCurrentKeys; NifOgre::TextKeyMap::const_iterator mNextKey; Ogre::Animation *mCurrentAnim; @@ -42,16 +66,13 @@ protected: void calcAnimVelocity(); - /* Applies the given animation to the given skeleton instance, using the specified time. */ - void applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel); - /* Updates a skeleton instance so that all bones matching the source skeleton (based on * bone names) are positioned identically. */ void updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel); - /* Updates the animation to the specified time, and returns the movement - * vector since the last update or reset. */ - Ogre::Vector3 updatePosition(float time); + /* Updates the position of the accum root node for the current time, and + * returns the wanted movement vector from the previous update. */ + Ogre::Vector3 updatePosition(); /* Resets the animation to the time of the specified start marker, without * moving anything, and set the end time to the specified stop marker. If @@ -71,7 +92,8 @@ protected: setAnimationSources(names); } - void createEntityList(Ogre::SceneNode *node, const std::string &model); + void createObjectList(Ogre::SceneNode *node, const std::string &model); + static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); public: Animation(const MWWorld::Ptr &ptr); @@ -94,6 +116,8 @@ public: void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); virtual Ogre::Vector3 runAnimation(float timepassed); + + Ogre::Node *getNode(const std::string &name); }; } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 843bcf007b..8396acaea8 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -59,10 +59,8 @@ namespace MWRender mNode = renderRoot->createChildSceneNode(); - mAnimation = new NpcAnimation(mCharacter, mNode, - MWWorld::Class::get(mCharacter).getInventoryStore (mCharacter), 0, renderHeadOnly()); - - mNode->setVisible (false); + mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter), + 0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); Ogre::Vector3 scale = mNode->getScale(); mCamera->setPosition(mPosition * scale); @@ -101,15 +99,13 @@ namespace MWRender assert(mAnimation); delete mAnimation; - mAnimation = new NpcAnimation(mCharacter, mNode, - MWWorld::Class::get(mCharacter).getInventoryStore (mCharacter), 0, renderHeadOnly()); + mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter), + 0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); float scale=1.f; MWWorld::Class::get(mCharacter).adjustScale(mCharacter, scale); mNode->setScale(Ogre::Vector3(scale)); - mNode->setVisible (false); - mCamera->setPosition(mPosition * mNode->getScale()); mCamera->lookAt(mLookAt * mNode->getScale()); @@ -139,12 +135,8 @@ namespace MWRender mNode->setOrientation (Ogre::Quaternion::IDENTITY); - mNode->setVisible (true); - mRenderTarget->update(); mSelectionBuffer->update(); - - mNode->setVisible (false); } int InventoryPreview::getSlotSelected (int posX, int posY) @@ -178,9 +170,7 @@ namespace MWRender updateCamera(); - mNode->setVisible (true); mRenderTarget->update(); - mNode->setVisible (false); } void RaceSelectionPreview::setPrototype(const ESM::NPC &proto) @@ -201,7 +191,7 @@ namespace MWRender void RaceSelectionPreview::updateCamera() { Ogre::Vector3 scale = mNode->getScale(); - Ogre::Vector3 headOffset = mAnimation->getHeadNode()->_getDerivedPosition(); + Ogre::Vector3 headOffset = mAnimation->getNode("Bip01 Head")->_getDerivedPosition(); headOffset = mNode->convertLocalToWorldPosition(headOffset); mCamera->setPosition(headOffset + mPosition * scale); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 22f84ee018..c714a2372a 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,6 +1,7 @@ #include "creatureanimation.hpp" #include +#include #include #include @@ -25,10 +26,10 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) { std::string model = "meshes\\"+ref->mBase->mModel; - createEntityList(mPtr.getRefData().getBaseNode(), model); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) + createObjectList(mPtr.getRefData().getBaseNode(), model); + for(size_t i = 0;i < mObjectList.mEntities.size();i++) { - Ogre::Entity *ent = mEntityList.mEntities[i]; + Ogre::Entity *ent = mObjectList.mEntities[i]; ent->setVisibilityFlags(RV_Actors); for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) @@ -37,6 +38,13 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } } + for(size_t i = 0;i < mObjectList.mParticles.size();i++) + { + Ogre::ParticleSystem *part = mObjectList.mParticles[i]; + part->setVisibilityFlags(RV_Actors); + + part->setRenderQueueGroup(RQG_Alpha); + } std::vector names; if((ref->mBase->mFlags&ESM::Creature::Biped)) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b76a38c469..96220f47d0 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include "../mwworld/esmstore.hpp" @@ -49,12 +50,13 @@ const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize NpcAnimation::~NpcAnimation() { + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); for(size_t i = 0;i < sPartListSize;i++) - removeEntities(mEntityParts[i]); + destroyObjectList(sceneMgr, mObjectParts[i]); } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, bool headOnly) +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, ViewMode viewMode) : Animation(ptr), mStateID(-1), mTimeToChange(0), @@ -71,7 +73,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor mGloveL(inv.end()), mGloveR(inv.end()), mSkirtIter(inv.end()), - mHeadOnly(headOnly) + mViewMode(viewMode) { mNpc = mPtr.get()->mBase; @@ -94,10 +96,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - createEntityList(node, smodel); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) + createObjectList(node, smodel); + for(size_t i = 0;i < mObjectList.mEntities.size();i++) { - Ogre::Entity *base = mEntityList.mEntities[i]; + Ogre::Entity *base = mObjectList.mEntities[i]; base->getUserObjectBindings().setUserAny(Ogre::Any(-1)); if (mVisibilityFlags != 0) @@ -109,6 +111,15 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } } + for(size_t i = 0;i < mObjectList.mParticles.size();i++) + { + Ogre::ParticleSystem *part = mObjectList.mParticles[i]; + + part->getUserObjectBindings().setUserAny(Ogre::Any(-1)); + if(mVisibilityFlags != 0) + part->setVisibilityFlags(mVisibilityFlags); + part->setRenderQueueGroup(RQG_Alpha); + } std::vector skelnames(1, smodel); if(!mNpc->isMale() && !isBeast) @@ -119,7 +130,40 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); setAnimationSources(skelnames); - updateParts(true); + forceUpdate(); +} + +void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) +{ + assert(viewMode != VM_HeadOnly); + mViewMode = viewMode; + + /* FIXME: Enable this once first-person animations work. */ +#if 0 + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mNpc->mRace); + + bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; + std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); + + std::vector skelnames(1, smodel); + if(!mNpc->isMale() && !isBeast) + skelnames.push_back("meshes\\base_anim_female.nif"); + else if(mBodyPrefix.find("argonian") != std::string::npos) + skelnames.push_back("meshes\\argonian_swimkna.nif"); + if(mNpc->mModel.length() > 0) + skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); + if(mViewMode == VM_FirstPerson) + { + smodel = (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif"); + skelnames.push_back(smodel); + } + setAnimationSources(skelnames); +#endif + + for(size_t i = 0;i < sPartListSize;i++) + removeIndividualPart(i); + forceUpdate(); } void NpcAnimation::updateParts(bool forceupdate) @@ -211,7 +255,15 @@ void NpcAnimation::updateParts(bool forceupdate) if(!forceupdate) return; - for(size_t i = 0;i < slotlistsize && !mHeadOnly;i++) + /* FIXME: Remove this once we figure out how to show what in first-person */ + if(mViewMode == VM_FirstPerson) + { + for(size_t i = 0;i < slotlistsize;i++) + this->*slotlist[i].part = inv.getSlot(slotlist[i].slot); + return; + } + + for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) { MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); @@ -243,12 +295,14 @@ void NpcAnimation::updateParts(bool forceupdate) reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio); } - if(mPartPriorities[ESM::PRT_Head] < 1) - addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel); - if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1) - addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel); - - if (mHeadOnly) + if(mViewMode != VM_FirstPerson) + { + if(mPartPriorities[ESM::PRT_Head] < 1) + addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel); + if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1) + addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel); + } + if(mViewMode == VM_HeadOnly) return; static const struct { @@ -277,6 +331,7 @@ void NpcAnimation::updateParts(bool forceupdate) { ESM::PRT_Tail, { "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++) { @@ -287,14 +342,14 @@ void NpcAnimation::updateParts(bool forceupdate) if(!mNpc->isMale()) { - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]); + part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]+ext); if(part == 0) - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]); + part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]+ext); } if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]); + part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]+ext); if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]); + part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]+ext); if(part) addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); @@ -302,26 +357,32 @@ void NpcAnimation::updateParts(bool forceupdate) } } -NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename) +NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) { - NifOgre::EntityList entities = NifOgre::Loader::createEntities(mEntityList.mSkelBase, bonename, - mInsert, mesh); - std::vector &parts = entities.mEntities; - for(size_t i = 0;i < parts.size();i++) + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mObjectList.mSkelBase, bonename, + mInsert, model); + for(size_t i = 0;i < objects.mEntities.size();i++) { - parts[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); - if (mVisibilityFlags != 0) - parts[i]->setVisibilityFlags(mVisibilityFlags); + objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + if(mVisibilityFlags != 0) + objects.mEntities[i]->setVisibilityFlags(mVisibilityFlags); - for(unsigned int j=0; j < parts[i]->getNumSubEntities(); ++j) + for(unsigned int j=0; j < objects.mEntities[i]->getNumSubEntities(); ++j) { - Ogre::SubEntity* subEnt = parts[i]->getSubEntity(j); + Ogre::SubEntity* subEnt = objects.mEntities[i]->getSubEntity(j); subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } } - if(entities.mSkelBase) + for(size_t i = 0;i < objects.mParticles.size();i++) { - Ogre::AnimationStateSet *aset = entities.mSkelBase->getAllAnimationStates(); + objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + if(mVisibilityFlags != 0) + objects.mParticles[i]->setVisibilityFlags(mVisibilityFlags); + objects.mParticles[i]->setRenderQueueGroup(RQG_Alpha); + } + if(objects.mSkelBase) + { + Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -329,12 +390,12 @@ NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int state->setEnabled(false); state->setLoop(false); } - Ogre::SkeletonInstance *skelinst = entities.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skelinst = objects.mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); } - return entities; + return objects; } Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) @@ -347,10 +408,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) mTimeToChange -= timepassed; Ogre::Vector3 ret = Animation::runAnimation(timepassed); - const Ogre::SkeletonInstance *skelsrc = mEntityList.mSkelBase->getSkeleton(); + const Ogre::SkeletonInstance *skelsrc = mObjectList.mSkelBase->getSkeleton(); for(size_t i = 0;i < sPartListSize;i++) { - Ogre::Entity *ent = mEntityParts[i].mSkelBase; + Ogre::Entity *ent = mObjectParts[i].mSkelBase; if(!ent) continue; updateSkeletonInstance(skelsrc, ent->getSkeleton()); ent->getAllAnimationStates()->_notifyDirty(); @@ -358,20 +419,6 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) return ret; } -void NpcAnimation::removeEntities(NifOgre::EntityList &entities) -{ - assert(&entities != &mEntityList); - - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < entities.mEntities.size();i++) - { - entities.mEntities[i]->detachFromParent(); - sceneMgr->destroyEntity(entities.mEntities[i]); - } - entities.mEntities.clear(); - entities.mSkelBase = NULL; -} - void NpcAnimation::removeIndividualPart(int type) { mPartPriorities[type] = 0; @@ -381,7 +428,7 @@ void NpcAnimation::removeIndividualPart(int type) { if(type == sPartList[i].type) { - removeEntities(mEntityParts[i]); + destroyObjectList(mInsert->getCreator(), mObjectParts[i]); break; } } @@ -419,7 +466,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, { if(type == sPartList[i].type) { - mEntityParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); + mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); break; } } @@ -428,6 +475,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts) { + const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : ""; for(std::size_t i = 0; i < parts.size(); i++) { const ESM::PartReference &part = parts[i]; @@ -437,9 +485,9 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectorisMale()) - bodypart = partStore.search(part.mFemale); + bodypart = partStore.search(part.mFemale+ext); if(!bodypart) - bodypart = partStore.search(part.mMale); + bodypart = partStore.search(part.mMale+ext); if(bodypart) addOrReplaceIndividualPart(part.mPart, group, priority, "meshes\\"+bodypart->mModel); @@ -448,9 +496,4 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectorgetSkeleton()->getBone("Bip01 Head"); -} - } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 5da4afef8a..f2f4c66697 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -26,6 +26,12 @@ struct PartInfo { const char name[32]; }; +enum ViewMode { + VM_Normal, + VM_FirstPerson, + VM_HeadOnly +}; + private: static const size_t sPartListSize = 27; static const PartInfo sPartList[sPartListSize]; @@ -33,13 +39,13 @@ private: int mStateID; // Bounded Parts - NifOgre::EntityList mEntityParts[sPartListSize]; + NifOgre::ObjectList mObjectParts[sPartListSize]; const ESM::NPC *mNpc; std::string mHeadModel; std::string mHairModel; std::string mBodyPrefix; - bool mHeadOnly; + ViewMode mViewMode; float mTimeToChange; MWWorld::ContainerStoreIterator mRobe; @@ -60,11 +66,10 @@ private: int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty int mPartPriorities[sPartListSize]; - NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename); + NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename); void updateParts(bool forceupdate = false); - void removeEntities(NifOgre::EntityList &entities); void removeIndividualPart(int type); void reserveIndividualPart(int type, int group, int priority); @@ -74,12 +79,13 @@ private: public: NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, - MWWorld::InventoryStore& inv, int visibilityFlags, bool headOnly=false); + MWWorld::InventoryStore& inv, int visibilityFlags, + ViewMode viewMode=VM_Normal); virtual ~NpcAnimation(); virtual Ogre::Vector3 runAnimation(float timepassed); - Ogre::Node* getHeadNode(); + void setViewMode(ViewMode viewMode); void forceUpdate() { updateParts(true); } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 50c0210649..3456e1c16a 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -128,9 +130,9 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool assert(insert); Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - NifOgre::EntityList entities = NifOgre::Loader::createEntities(insert, mesh); - for(size_t i = 0;i < entities.mEntities.size();i++) - bounds.merge(entities.mEntities[i]->getWorldBoundingBox(true)); + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(insert, mesh); + for(size_t i = 0;i < objects.mEntities.size();i++) + bounds.merge(objects.mEntities[i]->getWorldBoundingBox(true)); Ogre::Vector3 extents = bounds.getSize(); extents *= insert->getScale(); @@ -147,20 +149,21 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool mBounds[ptr.getCell()].merge(bounds); bool anyTransparency = false; - for(size_t i = 0;!anyTransparency && i < entities.mEntities.size();i++) + for(size_t i = 0;!anyTransparency && i < objects.mEntities.size();i++) { - Ogre::Entity *ent = entities.mEntities[i]; + Ogre::Entity *ent = objects.mEntities[i]; for(unsigned int i=0;!anyTransparency && i < ent->getNumSubEntities(); ++i) { anyTransparency = ent->getSubEntity(i)->getMaterial()->isTransparent(); } } - if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || anyTransparency) + if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || + anyTransparency || objects.mParticles.size() > 0) { - for(size_t i = 0;i < entities.mEntities.size();i++) + for(size_t i = 0;i < objects.mEntities.size();i++) { - Ogre::Entity *ent = entities.mEntities[i]; + Ogre::Entity *ent = objects.mEntities[i]; for(unsigned int i=0; i < ent->getNumSubEntities(); ++i) { Ogre::SubEntity* subEnt = ent->getSubEntity(i); @@ -169,6 +172,14 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); } + for(size_t i = 0;i < objects.mParticles.size();i++) + { + Ogre::ParticleSystem *part = objects.mParticles[i]; + // TODO: Check the particle system's material for actual transparency + part->setRenderQueueGroup(RQG_Alpha); + part->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); + part->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); + } } else { @@ -214,8 +225,8 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool sg->setRenderQueueGroup(RQG_Main); - std::vector::reverse_iterator iter = entities.mEntities.rbegin(); - while(iter != entities.mEntities.rend()) + std::vector::reverse_iterator iter = objects.mEntities.rbegin(); + while(iter != objects.mEntities.rend()) { Ogre::Node *node = (*iter)->getParentNode(); sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); @@ -228,7 +239,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool if (light) { - insertLight(ptr, entities.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition()); + insertLight(ptr, objects.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition()); } } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 63396378d0..439264f3af 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -124,8 +124,6 @@ namespace MWRender MWBase::Environment::get().getWindowManager ()->showCrosshair (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); - /// \fixme We shouldn't hide the whole model, just certain components of the character (head, chest, feet, etc) - mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView); if (mFirstPersonView && !mVanity.enabled) { return; } @@ -139,6 +137,8 @@ namespace MWRender void Player::toggleViewMode() { mFirstPersonView = !mFirstPersonView; + mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? + NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); setLowHeight(false); @@ -168,6 +168,9 @@ namespace MWRender mVanity.enabled = enable; mVanity.forced = force && enable; + mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? + NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); + float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { @@ -194,6 +197,8 @@ namespace MWRender return; } mPreviewMode = enable; + mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? + NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); float offset = mCamera->getPosition().z; if (mPreviewMode) { mMainCam.offset = offset; @@ -242,14 +247,15 @@ namespace MWRender void Player::setPitch(float angle) { - float limit = Ogre::Math::HALF_PI; + const float epsilon = 0.000001; + float limit = Ogre::Math::HALF_PI - epsilon; if (mVanity.forced || mPreviewMode) { limit /= 2; } if (angle > limit) { - angle = limit - 0.01; + angle = limit; } else if (angle < -limit) { - angle = -limit + 0.01; + angle = -limit; } if (mVanity.enabled || mPreviewMode) { mPreviewCam.pitch = angle; @@ -304,7 +310,8 @@ namespace MWRender delete mAnimation; mAnimation = anim; - mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView); + mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? + NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); } void Player::setHeight(float height) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7c442c6868..029cf394b4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -235,7 +235,7 @@ void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) mObjects.buildStaticGeometry (*store); mDebugging->cellAdded(store); if (store->mCell->isExterior()) - mTerrainManager->cellAdded(store); + mTerrainManager->cellAdded(store); waterAdded(store); } @@ -529,25 +529,28 @@ void RenderingManager::applyFog (bool underwater) void RenderingManager::setAmbientMode() { - switch (mAmbientMode) - { + switch (mAmbientMode) + { case 0: - - setAmbientColour(mAmbientColor); - break; + setAmbientColour(mAmbientColor); + break; case 1: - - setAmbientColour(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1)); - break; + setAmbientColour(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1)); + break; case 2: - - setAmbientColour(ColourValue(1,1,1)); - break; - } + setAmbientColour(ColourValue(1,1,1)); + break; + } } +float RenderingManager::getTerrainHeightAt(Ogre::Vector3 worldPos) +{ + return mTerrainManager->getTerrainHeightAt(worldPos); +} + + void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell) { mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient); @@ -888,6 +891,16 @@ void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float mPlayer->getSightAngles(pitch, yaw); } +bool RenderingManager::vanityRotateCamera(float* rot) +{ + if(!mPlayer->isVanityOrPreviewModeEnabled()) + return false; + + Ogre::Vector3 vRot(rot); + mPlayer->rotateCamera(vRot, true); + return true; +} + void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) { return mLocalMap->getInteriorMapPosition (position, nX, nY, x, y); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index fd43438ca5..cece7a95f0 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -88,6 +88,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->setCameraDistance(-factor/120.f*10, true, true); } + bool vanityRotateCamera(float* rot); + void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); void attachCameraTo(const MWWorld::Ptr &ptr); @@ -158,6 +160,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); + float getTerrainHeightAt (Ogre::Vector3 worldPos); + void setGlare(bool glare); void skyEnable (); void skyDisable (); diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 595a82294b..0d066a0ecb 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -28,10 +28,7 @@ void Shadows::recreate() { bool enabled = Settings::Manager::getBool("enabled", "Shadows"); - // Split shadow maps are currently disabled because the terrain cannot cope with them - // (Too many texture units) Solution would be a multi-pass terrain material - //bool split = Settings::Manager::getBool("split", "Shadows"); - const bool split = false; + bool split = Settings::Manager::getBool("split", "Shadows"); sh::Factory::getInstance ().setGlobalSetting ("shadows", enabled && !split ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("shadows_pssm", enabled && split ? "true" : "false"); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 94af3521b3..7f3abce701 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -288,10 +288,10 @@ void SkyManager::create() // Stars mAtmosphereNight = mRootNode->createChildSceneNode(); - NifOgre::EntityList entities = NifOgre::Loader::createEntities(mAtmosphereNight, "meshes\\sky_night_01.nif"); - for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++) + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_01.nif"); + for(size_t i = 0, matidx = 0;i < objects.mEntities.size();i++) { - Entity* night1_ent = entities.mEntities[i]; + Entity* night1_ent = objects.mEntities[i]; night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); night1_ent->setVisibilityFlags(RV_Sky); night1_ent->setCastShadows(false); @@ -314,10 +314,10 @@ void SkyManager::create() // Atmosphere (day) mAtmosphereDay = mRootNode->createChildSceneNode(); - entities = NifOgre::Loader::createEntities(mAtmosphereDay, "meshes\\sky_atmosphere.nif"); - for(size_t i = 0;i < entities.mEntities.size();i++) + objects = NifOgre::Loader::createObjects(mAtmosphereDay, "meshes\\sky_atmosphere.nif"); + for(size_t i = 0;i < objects.mEntities.size();i++) { - Entity* atmosphere_ent = entities.mEntities[i]; + Entity* atmosphere_ent = objects.mEntities[i]; atmosphere_ent->setCastShadows(false); atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); atmosphere_ent->setVisibilityFlags(RV_Sky); @@ -332,10 +332,10 @@ void SkyManager::create() // Clouds SceneNode* clouds_node = mRootNode->createChildSceneNode(); - entities = NifOgre::Loader::createEntities(clouds_node, "meshes\\sky_clouds_01.nif"); - for(size_t i = 0;i < entities.mEntities.size();i++) + objects = NifOgre::Loader::createObjects(clouds_node, "meshes\\sky_clouds_01.nif"); + for(size_t i = 0;i < objects.mEntities.size();i++) { - Entity* clouds_ent = entities.mEntities[i]; + Entity* clouds_ent = objects.mEntities[i]; clouds_ent->setVisibilityFlags(RV_Sky); clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); for(unsigned int j = 0;j < clouds_ent->getNumSubEntities();j++) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 438366873c..c27dce6cad 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -71,6 +71,17 @@ namespace MWRender //---------------------------------------------------------------------------------------------- + float TerrainManager::getTerrainHeightAt(Vector3 worldPos) + { + Ogre::Terrain* terrain = NULL; + float height = mTerrainGroup.getHeightAtWorldPosition(worldPos, &terrain); + if (terrain == NULL) + return std::numeric_limits().min(); + return height; + } + + //---------------------------------------------------------------------------------------------- + TerrainManager::~TerrainManager() { OGRE_DELETE mTerrainGlobals; diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 484a0dbe3a..45c56390e8 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -40,6 +40,9 @@ namespace MWRender{ void cellAdded(MWWorld::CellStore* store); void cellRemoved(MWWorld::CellStore* store); + + float getTerrainHeightAt (Ogre::Vector3 worldPos); + private: Ogre::TerrainGlobalOptions* mTerrainGlobals; Ogre::TerrainGroup mTerrainGroup; diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 8a568883df..dd74254be6 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -68,59 +68,113 @@ namespace MWRender Ogre::MaterialManager::getSingleton().remove(matName); mMaterial = sh::Factory::getInstance().createMaterialInstance (matName); - mMaterial->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); - sh::MaterialInstancePass* p = mMaterial->createPass (); + int numPasses = getRequiredPasses(terrain); + int maxLayersInOnePass = getMaxLayersPerPass(terrain); - p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); - p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); - - p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap))); - - // global colour map - sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap"); - colourMap->setProperty ("texture_alias", sh::makeProperty (new sh::StringValue(mMaterial->getName() + "_colourMap"))); - colourMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - - // global normal map - sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap"); - normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); - normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - - Ogre::uint maxLayers = getMaxLayers(terrain); - Ogre::uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); - Ogre::uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - - p->mShaderProperties.setProperty ("num_layers", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numLayers)))); - p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); - - // blend maps - for (Ogre::uint i = 0; i < numBlendTextures; ++i) + for (int pass=0; passcreateTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); - blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(i)))); - blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - } + int layerOffset = maxLayersInOnePass * pass; + int blendmapOffset = (pass == 0) ? 1 : 0; // the first layer of the first pass is the base layer and does not need a blend map - // layer maps - for (Ogre::uint i = 0; i < numLayers; ++i) - { - sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); - diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(i, 0)))); - p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), - sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(int((i-1) / 4)) + "." + getComponent(int((i-1) % 4))))); - } + sh::MaterialInstancePass* p = mMaterial->createPass (); - // shadow - for (Ogre::uint i = 0; i < 3; ++i) - { - sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); - shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); - } + p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); + p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); + if (pass != 0) + { + p->setProperty ("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend"))); + // Only write if depth is equal to the depth value written by the previous pass. + p->setProperty ("depth_func", sh::makeProperty(new sh::StringValue("equal"))); + } - p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty(new sh::StringValue( - Ogre::StringConverter::toString(numBlendTextures + numLayers + 2)))); + p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap))); + p->mShaderProperties.setProperty ("is_first_pass", sh::makeProperty(new sh::BooleanValue(pass == 0))); + + // global colour map + sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap"); + colourMap->setProperty ("texture_alias", sh::makeProperty (new sh::StringValue(mMaterial->getName() + "_colourMap"))); + colourMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + // global normal map + sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap"); + normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); + normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + Ogre::uint numLayersInThisPass = std::min(maxLayersInOnePass, terrain->getLayerCount()-layerOffset); + + // HACK: Terrain::getLayerBlendTextureIndex should be const, but it is not. + // Remove this once ogre got fixed. + Ogre::Terrain* nonconstTerrain = const_cast(terrain); + + // a blend map might be shared between two passes + // so we can't just use terrain->getBlendTextureCount() + Ogre::uint numBlendTextures=0; + std::vector blendTextures; + for (unsigned int layer=blendmapOffset; layergetBlendTextureName(nonconstTerrain->getLayerBlendTextureIndex( + static_cast(layerOffset+layer)).first); + if (std::find(blendTextures.begin(), blendTextures.end(), blendTextureName) == blendTextures.end()) + { + blendTextures.push_back(blendTextureName); + ++numBlendTextures; + } + } + + p->mShaderProperties.setProperty ("num_layers", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numLayersInThisPass)))); + p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); + + // blend maps + // the index of the first blend map used in this pass + int blendmapStart; + if (terrain->getLayerCount() == 1) // special case. if there's only one layer, we don't need blend maps at all + blendmapStart = 0; + else + blendmapStart = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+blendmapOffset)).first; + for (Ogre::uint i = 0; i < numBlendTextures; ++i) + { + sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); + blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(blendmapStart+i)))); + blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + } + + // layer maps + for (Ogre::uint i = 0; i < numLayersInThisPass; ++i) + { + sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); + diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(layerOffset+i, 0)))); + + if (i+layerOffset > 0) + { + int blendTextureIndex = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+i)).first; + int blendTextureComponent = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+i)).second; + p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(blendTextureIndex-blendmapStart) + "." + getComponent(blendTextureComponent)))); + } + else + { + // just to make it shut up about blendmap_component_0 not existing in the first pass. + // it might be retrieved, but will never survive the preprocessing step. + p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::StringValue(""))); + } + } + + // shadow + for (Ogre::uint i = 0; i < 3; ++i) + { + sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); + shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); + } + + p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty (new sh::StringValue( + Ogre::StringConverter::toString(numBlendTextures + numLayersInThisPass + 2)))); + + // make sure the pass index is fed to the permutation handler, because blendmap components may be different + p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(pass))); + } return Ogre::MaterialManager::getSingleton().getByName(matName); } @@ -142,6 +196,11 @@ namespace MWRender } Ogre::uint8 TerrainMaterial::Profile::getMaxLayers(const Ogre::Terrain* terrain) const + { + return 255; + } + + int TerrainMaterial::Profile::getMaxLayersPerPass (const Ogre::Terrain* terrain) { // count the texture units free Ogre::uint8 freeTextureUnits = 16; @@ -151,11 +210,21 @@ namespace MWRender --freeTextureUnits; // shadow --freeTextureUnits; + --freeTextureUnits; + --freeTextureUnits; // each layer needs 1.25 units (1xdiffusespec, 0.25xblend) return static_cast(freeTextureUnits / (1.25f)); } + int TerrainMaterial::Profile::getRequiredPasses (const Ogre::Terrain* terrain) + { + int maxLayersPerPass = getMaxLayersPerPass(terrain); + assert(terrain->getLayerCount()); + assert(maxLayersPerPass); + return std::ceil(static_cast(terrain->getLayerCount()) / maxLayersPerPass); + } + void TerrainMaterial::Profile::updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) { } diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index fe1214cf5e..c90499baeb 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -72,6 +72,9 @@ namespace MWRender private: sh::MaterialInstance* mMaterial; + int getRequiredPasses (const Ogre::Terrain* terrain); + int getMaxLayersPerPass (const Ogre::Terrain* terrain); + bool mGlobalColourMap; }; diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 8402a5b4cd..1ff6fbbf1f 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -196,8 +196,6 @@ namespace MWScript MWMechanics::AiWander wanderPackage(range, duration, time, idleList); MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(wanderPackage); - - std::cout << "AiWander" << std::endl; } }; diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 8d65dfdd5b..ac53b8ee25 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -12,6 +12,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 2cf2a97c1d..180a2791bc 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -1,8 +1,11 @@ #include "locals.hpp" +#include + +#include + #include "../mwbase/environment.hpp" #include "../mwbase/scriptmanager.hpp" -#include namespace MWScript { @@ -39,9 +42,9 @@ namespace MWScript } return 0; } - + bool Locals::setVarByInt(const std::string& script, const std::string& var, int val) - { + { Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); int index = locals.getIndex(var); char type = locals.getType(var); @@ -51,10 +54,10 @@ namespace MWScript { case 's': mShorts.at (index) = val; break; - + case 'l': mLongs.at (index) = val; break; - + case 'f': mFloats.at (index) = val; break; } diff --git a/apps/openmw/mwscript/locals.hpp b/apps/openmw/mwscript/locals.hpp index 1d9b9c3e4f..deae0d44ea 100644 --- a/apps/openmw/mwscript/locals.hpp +++ b/apps/openmw/mwscript/locals.hpp @@ -3,9 +3,13 @@ #include -#include #include +namespace ESM +{ + struct Script; +} + namespace MWScript { class Locals @@ -14,11 +18,11 @@ namespace MWScript std::vector mShorts; std::vector mLongs; std::vector mFloats; - + void configure (const ESM::Script& script); bool setVarByInt(const std::string& script, const std::string& var, int val); int getIntVar (const std::string& script, const std::string& var); ///< if var does not exist, returns 0 - + }; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index f329e30d90..489f6bd3db 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -399,6 +399,13 @@ namespace MWScript Interpreter::Type_Integer amount = runtime[0].mInteger; runtime.pop(); + if (amount<0) + throw std::runtime_error ("amount must be non-negative"); + + // no-op + if (amount == 0) + return; + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index d86a6e3486..49688efb5d 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -303,6 +303,8 @@ namespace MWScript zRot = zRot/60.; } MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + + MWWorld::Class::get(ptr).adjustPosition(ptr); } else { @@ -341,6 +343,7 @@ namespace MWScript zRot = zRot/60.; } MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + MWWorld::Class::get(ptr).adjustPosition(ptr); } }; @@ -457,6 +460,13 @@ namespace MWScript Interpreter::Type_Integer direction = runtime[0].mInteger; runtime.pop(); + if (count<0) + throw std::runtime_error ("count must be non-negative"); + + // no-op + if (count == 0) + return; + ESM::Position ipos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); @@ -500,6 +510,13 @@ namespace MWScript Interpreter::Type_Integer direction = runtime[0].mInteger; runtime.pop(); + if (count<0) + throw std::runtime_error ("count must be non-negative"); + + // no-op + if (count == 0) + return; + ESM::Position ipos = me.getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 1b07dfe627..69e3016767 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -208,14 +208,21 @@ namespace MWSound void SoundManager::startRandomTitle() { - Ogre::StringVectorPtr filelist; - filelist = mResourceMgr.findResourceNames(Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "Music/"+mCurrentPlaylist+"/*"); - if(!filelist->size()) + Ogre::StringVector filelist; + + Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); + for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) + { + Ogre::StringVectorPtr resourcesInThisGroup = mResourceMgr.findResourceNames(*it, + "Music/"+mCurrentPlaylist+"/*"); + filelist.insert(filelist.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end()); + } + + if(!filelist.size()) return; - int i = rand()%filelist->size(); - streamMusicFull((*filelist)[i]); + int i = rand()%filelist.size(); + streamMusicFull(filelist[i]); } bool SoundManager::isMusicPlaying() diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index afbb505f22..84d1a3d158 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -18,8 +18,21 @@ namespace MWWorld void ActionEquip::executeImp (const Ptr& actor) { + MWWorld::Ptr object = getTarget(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); + switch(MWWorld::Class::get (object).canBeEquipped (object, actor)) + { + case 0: + return; + case 2: + invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); + break; + case 3: + invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); + break; + } + // slots that this item can be equipped in std::pair, bool> slots = MWWorld::Class::get(getTarget()).getEquipmentSlots(getTarget()); @@ -27,7 +40,7 @@ namespace MWWorld MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { - if (*it == getTarget()) + if (*it == object) { break; } @@ -44,70 +57,6 @@ namespace MWWorld slot!=slots.first.end(); ++slot) { - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) - { - if(*slot == MWWorld::InventoryStore::Slot_Helmet) - { - std::vector parts; - if(it.getType() == MWWorld::ContainerStore::Type_Clothing) - parts = it->get()->mBase->mParts.mParts; - else - parts = it->get()->mBase->mParts.mParts; - - bool allow = true; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_Head) - { - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}"); - - allow = false; - break; - } - } - - if(!allow) - break; - } - - if (*slot == MWWorld::InventoryStore::Slot_Boots) - { - bool allow = true; - std::vector parts; - if(it.getType() == MWWorld::ContainerStore::Type_Clothing) - parts = it->get()->mBase->mParts.mParts; - else - parts = it->get()->mBase->mParts.mParts; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - { - allow = false; - // Only notify the player, not npcs - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - { - if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}"); - } - - else // It's boots - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}"); - } - } - break; - } - } - - if(!allow) - break; - } - - } - // if all slots are occupied, replace the last slot if (slot == --slots.first.end()) { @@ -125,10 +74,10 @@ namespace MWWorld } } - std::string script = MWWorld::Class::get(*it).getScript(*it); + std::string script = MWWorld::Class::get(object).getScript(object); /* Set OnPCEquip Variable on item's script, if the player is equipping it, and it has a script with that variable declared */ if(equipped && actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && script != "") - (*it).mRefData->getLocals().setVarByInt(script, "onpcequip", 1); + (object).mRefData->getLocals().setVarByInt(script, "onpcequip", 1); } } diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 5f771be475..4838cfefa5 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -45,7 +45,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) Ptr container (&*iter, &cellStore); Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); + iter->mBase->mInventory, container.getCellRef().mOwner, mStore); } for (CellRefList::List::iterator iter ( @@ -55,7 +55,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) Ptr container (&*iter, &cellStore); Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); + iter->mBase->mInventory, Class::get(container).getId(container), mStore); } for (CellRefList::List::iterator iter ( @@ -65,7 +65,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) Ptr container (&*iter, &cellStore); Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); + iter->mBase->mInventory, Class::get(container).getId(container), mStore); } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 2e6b45bb7e..f8467c84f6 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -1,50 +1,14 @@ #ifndef GAME_MWWORLD_CELLSTORE_H #define GAME_MWWORLD_CELLSTORE_H -#include - #include #include -#include "refdata.hpp" +#include "livecellref.hpp" #include "esmstore.hpp" -struct C; namespace MWWorld { - class Ptr; - class ESMStore; - - /// A reference to one object (of any type) in a cell. - /// - /// Constructing this with a CellRef instance in the constructor means that - /// in practice (where D is RefData) the possibly mutable data is copied - /// across to mData. If later adding data (such as position) to CellRef - /// this would have to be manually copied across. - template - struct LiveCellRef - { - LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) - : mBase(b), mRef(cref), mData(mRef) - {} - - LiveCellRef(const X* b = NULL) - : mBase(b), mData(mRef) - {} - - // The object that this instance is based on. - const X* mBase; - - /* Information about this instance, such as 3D location and - rotation and individual type-dependent data. - */ - ESM::CellRef mRef; - - /// runtime-data - RefData mData; - }; - - template bool operator==(const LiveCellRef& ref, int pRefnum); /// A list of cell references template diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index f6357f7e90..4321fc46b7 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -47,6 +47,11 @@ namespace MWWorld throw std::runtime_error ("class does not represent an actor"); } + bool Class::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return false; + } + MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const { throw std::runtime_error ("class does not have creature stats"); @@ -254,11 +259,20 @@ namespace MWWorld return ""; } - std::string Class::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + void Class::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { throw std::runtime_error ("class can't be enchanted"); } + int Class::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + return 1; + } + + void Class::adjustPosition(const MWWorld::Ptr& ptr) const + { + } + MWWorld::Ptr Class::copyToCellImpl(const Ptr &ptr, CellStore &cell) const { diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 36d7e97db0..32941c633d 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -81,6 +81,9 @@ namespace MWWorld ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. + virtual void adjustPosition(const MWWorld::Ptr& ptr) const; + ///< Adjust position to stand on ground. Must be called post model load + virtual MWMechanics::CreatureStats& getCreatureStats (const Ptr& ptr) const; ///< Return creature stats or throw an exception, if class does not have creature stats /// (default implementation: throw an exceoption) @@ -235,9 +238,15 @@ namespace MWWorld virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + ///< Determine whether or not \a item can be sold to an npc with the given \a npcServices + virtual std::string getModel(const MWWorld::Ptr &ptr) const; - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + 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 Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index a377f2bbbf..05026a98ba 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -14,6 +14,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "manualref.hpp" #include "refdata.hpp" #include "class.hpp" @@ -175,26 +177,78 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr return it; } -void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const MWWorld::ESMStore& store) +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); ++iter) { - ManualRef ref (store, iter->mItem.toString()); - - if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) - { - /// \todo implement leveled item lists - continue; - } - - ref.getPtr().getRefData().setCount (std::abs(iter->mCount)); /// \todo implement item restocking (indicated by negative count) - addImp (ref.getPtr()); + std::string id = iter->mItem.toString(); + addInitialItem(id, owner, iter->mCount); } flagAsModified(); } +void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count, unsigned char failChance, bool topLevel) +{ + count = std::abs(count); /// \todo implement item restocking (indicated by negative count) + + ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); + + if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + { + const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; + const std::vector& items = levItem->mList; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); + + failChance += levItem->mChanceNone; + + if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) + { + for (int i=0; i (std::rand()) / RAND_MAX; + if (random >= failChance/100.f) + { + std::vector candidates; + int highestLevel = 0; + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + if (it->mLevel > highestLevel) + highestLevel = it->mLevel; + } + + std::pair highest = std::make_pair(-1, ""); + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + if (playerLevel >= it->mLevel + && (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel)) + { + candidates.push_back(it->mId); + if (it->mLevel >= highest.first) + highest = std::make_pair(it->mLevel, it->mId); + } + + } + if (!candidates.size()) + return; + std::string item = candidates[std::rand()%candidates.size()]; + addInitialItem(item, owner, count, failChance, false); + } + } + else + { + ref.getPtr().getRefData().setCount (count); + ref.getPtr().getCellRef().mOwner = owner; + addImp (ref.getPtr()); + } +} + void MWWorld::ContainerStore::clear() { for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 9d315d27f0..edd09411cf 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -53,6 +53,7 @@ namespace MWWorld mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr); + void addInitialItem (const std::string& id, const std::string& owner, int count, unsigned char failChance=0, bool topLevel=true); public: @@ -84,7 +85,7 @@ namespace MWWorld public: - void fill (const ESM::InventoryList& items, const MWWorld::ESMStore& store); + void fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store); ///< Insert items into *this. void clear(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index dd518ff6a4..5495d6a021 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -128,8 +128,11 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) return mSlots[slot]; } -void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) +void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) { + const MWMechanics::NpcStats& stats = MWWorld::Class::get(npc).getNpcStats(npc); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + TSlots slots; initSlots (slots); @@ -183,6 +186,18 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) } } + switch(MWWorld::Class::get (test).canBeEquipped (test, npc)) + { + case 0: + continue; + case 2: + invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); + break; + case 3: + invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); + break; + } + if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped { // unstack item pointed to by iterator if required diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e55eca0ae7..cc28a0a0c0 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -78,7 +78,7 @@ namespace MWWorld ContainerStoreIterator getSlot (int slot); - void autoEquip (const MWMechanics::NpcStats& stats); + void autoEquip (const MWWorld::Ptr& npc); ///< Auto equip items according to stats and item value. const MWMechanics::MagicEffects& getMagicEffects(); diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp new file mode 100644 index 0000000000..28c1bb5c27 --- /dev/null +++ b/apps/openmw/mwworld/livecellref.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_MWWORLD_LIVECELLREF_H +#define GAME_MWWORLD_LIVECELLREF_H + +#include + +#include "refdata.hpp" + +namespace MWWorld +{ + class Ptr; + class ESMStore; + + /// A reference to one object (of any type) in a cell. + /// + /// Constructing this with a CellRef instance in the constructor means that + /// in practice (where D is RefData) the possibly mutable data is copied + /// across to mData. If later adding data (such as position) to CellRef + /// this would have to be manually copied across. + template + struct LiveCellRef + { + LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) + : mBase(b), mRef(cref), mData(mRef) + {} + + LiveCellRef(const X* b = NULL) + : mBase(b), mData(mRef) + {} + + // The object that this instance is based on. + const X* mBase; + + /* Information about this instance, such as 3D location and + rotation and individual type-dependent data. + */ + ESM::CellRef mRef; + + /// runtime-data + RefData mData; + }; + +// template bool operator==(const LiveCellRef& ref, int pRefnum); +} + +#endif diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 28f3317065..19ee2e517b 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -88,6 +88,50 @@ namespace MWWorld } public: + static Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, OEngine::Physic::PhysicEngine *engine) + { + const ESM::Position &refpos = ptr.getRefData().getPosition(); + Ogre::Vector3 position(refpos.pos); + + bool hit=false; + bool isInterior = !ptr.getCell()->isExterior(); + + OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + if (!physicActor) + return position; + + bool wasCollisionMode = physicActor->getCollisionMode(); + + physicActor->enableCollisions(false); + + Ogre::Vector3 halfExtents = physicActor->getHalfExtents();// + Vector3(1,1,1); + + Ogre::Vector3 newPosition = position; + + traceResults trace; //no initialization needed + + int maxHeight = 400.f; + int steps = 100; + for (int i=0; isetOnGround(hit); + physicActor->enableCollisions(wasCollisionMode); + + if (hit) + return newPosition; + else + return position; + } + + static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity, OEngine::Physic::PhysicEngine *engine) { @@ -318,7 +362,7 @@ namespace MWWorld btVector3 btTo = btVector3(to.x, to.y, to.z); std::pair test = mEngine->rayTest(btFrom, btTo); - if (test.first == "") { + if (test.second == -1) { return std::make_pair(false, Ogre::Vector3()); } return std::make_pair(true, ray.getPoint(len * test.second)); @@ -351,6 +395,10 @@ namespace MWWorld return MovementSolver::move(ptr, movement, time, gravity, mEngine); } + Ogre::Vector3 PhysicsSystem::traceDown(const MWWorld::Ptr &ptr) + { + return MovementSolver::traceDown(ptr, mEngine); + } void PhysicsSystem::addHeightField (float* heights, int x, int y, float yoffset, diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 2e48be4079..4eec9367cb 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -51,6 +51,7 @@ namespace MWWorld bool toggleCollisionMode(); Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity); + Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); std::pair getFacedHandle (MWWorld::World& world, float queryDistance); std::vector < std::pair > getFacedHandles (float queryDistance); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index ea8a02dee9..3338f08edf 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,14 +1,13 @@ #include "player.hpp" - #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" + +#include "../mwworld/ptr.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" -#include "esmstore.hpp" #include "class.hpp" namespace MWWorld @@ -25,12 +24,38 @@ namespace MWWorld playerPos[0] = playerPos[1] = playerPos[2] = 0; } + void Player::setCell (MWWorld::CellStore *cellStore) + { + mCellStore = cellStore; + } + + MWWorld::Ptr Player::getPlayer() + { + MWWorld::Ptr ptr (&mPlayer, mCellStore); + return ptr; + } + + void Player::setBirthSign (const std::string &sign) + { + mSign = sign; + } + + const std::string& Player::getBirthSign() const + { + return mSign; + } + void Player::setDrawState (MWMechanics::DrawState_ state) { MWWorld::Ptr ptr = getPlayer(); MWWorld::Class::get(ptr).getNpcStats(ptr).setDrawState (state); } + bool Player::getAutoMove() const + { + return mAutoMove; + } + void Player::setAutoMove (bool enable) { MWWorld::Ptr ptr = getPlayer(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index c985510917..dfaddf66a9 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -1,15 +1,20 @@ #ifndef GAME_MWWORLD_PLAYER_H #define GAME_MWWORLD_PLAYER_H -#include "../mwworld/cellstore.hpp" #include "../mwworld/refdata.hpp" -#include "../mwworld/ptr.hpp" +#include "../mwworld/livecellref.hpp" #include "../mwmechanics/drawstate.hpp" +namespace ESM +{ + struct NPC; +} + namespace MWBase { class World; + class Ptr; } namespace MWWorld @@ -30,31 +35,17 @@ namespace MWWorld Player(const ESM::NPC *player, const MWBase::World& world); - void setCell (MWWorld::CellStore *cellStore) - { - mCellStore = cellStore; - } + void setCell (MWWorld::CellStore *cellStore); - MWWorld::Ptr getPlayer() - { - MWWorld::Ptr ptr (&mPlayer, mCellStore); - return ptr; - } + MWWorld::Ptr getPlayer(); - void setBirthSign(const std::string &sign) { - mSign = sign; - } + void setBirthSign(const std::string &sign); - const std::string &getBirthSign() const { - return mSign; - } + const std::string &getBirthSign() const; void setDrawState (MWMechanics::DrawState_ state); - bool getAutoMove() const - { - return mAutoMove; - } + bool getAutoMove() const; MWMechanics::DrawState_ getDrawState(); /// \todo constness diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 339026ca60..439f761311 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -51,6 +51,7 @@ namespace class_.insertObject(ptr, physics); MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().mScale); + class_.adjustPosition(ptr); } catch (const std::exception& e) { @@ -99,7 +100,6 @@ namespace MWWorld } mRendering.removeCell(*iter); - //mPhysics->removeObject("Unnamed_43"); MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); MWBase::Environment::get().getMechanicsManager()->drop (*iter); @@ -115,14 +115,10 @@ namespace MWWorld if(result.second) { - /// \todo rescale depending on the state of a new GMST - insertCell (*cell, true); - - mRendering.cellAdded (cell); - float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; + // Load terrain physics first... if (cell->mCell->isExterior()) { ESM::Land* land = @@ -142,6 +138,12 @@ namespace MWWorld } } + // ... then references. This is important for adjustPosition to work correctly. + /// \todo rescale depending on the state of a new GMST + insertCell (*cell, true); + + mRendering.cellAdded (cell); + mRendering.configureAmbient(*cell); mRendering.requestMap(cell); mRendering.configureAmbient(*cell); @@ -165,6 +167,8 @@ namespace MWWorld float y = Ogre::Radian(pos.rot[1]).valueDegrees(); float z = Ogre::Radian(pos.rot[2]).valueDegrees(); world->rotateObject(player, x, y, z); + + MWWorld::Class::get(player).adjustPosition(player); } MWBase::MechanicsManager *mechMgr = @@ -355,6 +359,8 @@ namespace MWWorld float y = Ogre::Radian(position.rot[1]).valueDegrees(); float z = Ogre::Radian(position.rot[2]).valueDegrees(); world->rotateObject(world->getPlayer().getPlayer(), x, y, z); + + MWWorld::Class::get(world->getPlayer().getPlayer()).adjustPosition(world->getPlayer().getPlayer()); return; } @@ -435,7 +441,6 @@ namespace MWWorld insertCellRefList(mRendering, cell.mBooks, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mClothes, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mContainers, cell, *mPhysics, rescale); - insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mDoors, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mIngreds, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale); @@ -443,11 +448,13 @@ namespace MWWorld insertCellRefList(mRendering, cell.mLights, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mLockpicks, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mMiscItems, cell, *mPhysics, rescale); - insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mProbes, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mRepairs, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mStatics, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mWeapons, cell, *mPhysics, rescale); + // Load NPCs and creatures _after_ everything else (important for adjustPosition to work correctly) + insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale); } void Scene::addObjectToScene (const Ptr& ptr) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ae0e02c8c9..11ccd8f2fc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -706,9 +706,11 @@ namespace MWWorld void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z) { ESM::Position &pos = ptr.getRefData().getPosition(); + pos.pos[0] = x; pos.pos[1] = y; pos.pos[2] = z; + Ogre::Vector3 vec(x, y, z); CellStore *currCell = ptr.getCell(); @@ -717,7 +719,7 @@ namespace MWWorld if (*currCell != newCell) { - removeContainerScripts(ptr); + removeContainerScripts(ptr); if (isPlayer) { @@ -749,7 +751,7 @@ namespace MWWorld else { MWWorld::Ptr copy = - MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + MWWorld::Class::get(ptr).copyToCell(ptr, newCell, pos); mRendering->updateObjectCell(ptr, copy); @@ -779,12 +781,14 @@ namespace MWWorld bool World::moveObjectImp(const Ptr& ptr, float x, float y, float z) { CellStore *cell = ptr.getCell(); + if (cell->isExterior()) { int cellX, cellY; positionToIndex(x, y, cellX, cellY); cell = getExterior(cellX, cellY); } + moveObject(ptr, *cell, x, y, z); return cell != ptr.getCell(); @@ -822,6 +826,33 @@ namespace MWWorld } } + void World::adjustPosition(const Ptr &ptr) + { + Ogre::Vector3 pos (ptr.getRefData().getPosition().pos[0], ptr.getRefData().getPosition().pos[1], ptr.getRefData().getPosition().pos[2]); + + if(!ptr.getRefData().getBaseNode()) + { + // will be adjusted when Ptr's cell becomes active + return; + } + + float terrainHeight = mRendering->getTerrainHeightAt(pos); + + if (pos.z < terrainHeight) + pos.z = terrainHeight+400; // place slightly above. will snap down to ground with code below + + ptr.getRefData().getPosition().pos[2] = pos.z; + + if (!isFlying(ptr)) + { + Ogre::Vector3 traced = mPhysics->traceDown(ptr); + if (traced.z < pos.z) + pos.z = traced.z; + } + + moveObject(ptr, pos.x, pos.y, pos.z); + } + void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) { rotateObjectImp(ptr, Ogre::Vector3(Ogre::Degree(x).valueRadians(), @@ -1333,6 +1364,11 @@ namespace MWWorld return physactor && physactor->getOnGround(); } + bool World::vanityRotateCamera(float * rot) + { + return mRendering->vanityRotateCamera(rot); + } + void World::renderPlayer() { mRendering->renderPlayer(mPlayer->getPlayer()); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8cff50bd16..7b12babee9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -188,6 +188,9 @@ namespace MWWorld virtual Ptr searchPtrViaHandle (const std::string& handle); ///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found + virtual void adjustPosition (const Ptr& ptr); + ///< Adjust position after load to be on ground. Must be called after model load. + virtual void enable (const Ptr& ptr); virtual void disable (const Ptr& ptr); @@ -358,6 +361,8 @@ namespace MWWorld mRendering->changeVanityModeScale(factor); } + virtual bool vanityRotateCamera(float * rot); + virtual void renderPlayer(); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 88bf764445..e1e5fbe9bf 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -19,7 +19,7 @@ add_component_dir (nif ) add_component_dir (nifogre - ogrenifloader + ogrenifloader skeleton material mesh ) add_component_dir (nifbullet @@ -39,7 +39,7 @@ add_component_dir (esm loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat - loadweap records aipackage effectlist spelllist variant variantimp loadtes3 + loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref ) add_component_dir (misc @@ -54,7 +54,7 @@ add_component_dir (files add_component_dir (compiler context controlparser errorhandler exception exprparser extensions fileparser generator lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler - stringparser tokenloc + stringparser tokenloc nullerrorhandler ) add_component_dir (interpreter diff --git a/components/compiler/nullerrorhandler.cpp b/components/compiler/nullerrorhandler.cpp new file mode 100644 index 0000000000..3071701e8a --- /dev/null +++ b/components/compiler/nullerrorhandler.cpp @@ -0,0 +1,6 @@ + +#include "nullerrorhandler.hpp" + +void Compiler::NullErrorHandler::report (const std::string& message, const TokenLoc& loc, Type type) {} + +void Compiler::NullErrorHandler::report (const std::string& message, Type type) {} \ No newline at end of file diff --git a/components/compiler/nullerrorhandler.hpp b/components/compiler/nullerrorhandler.hpp new file mode 100644 index 0000000000..bb4db99a28 --- /dev/null +++ b/components/compiler/nullerrorhandler.hpp @@ -0,0 +1,21 @@ + +#ifndef COMPILER_NULLERRORHANDLER_H_INCLUDED +#define COMPILER_NULLERRORHANDLER_H_INCLUDED + +#include "errorhandler.hpp" + +namespace Compiler +{ + /// \brief Error handler implementation: Ignore all error messages + + class NullErrorHandler : public ErrorHandler + { + virtual void report (const std::string& message, const TokenLoc& loc, Type type); + ///< Report error to the user. + + virtual void report (const std::string& message, Type type); + ///< Report a file related error + }; +} + +#endif diff --git a/components/compiler/parser.cpp b/components/compiler/parser.cpp index 896458e482..8d11c4086d 100644 --- a/components/compiler/parser.cpp +++ b/components/compiler/parser.cpp @@ -148,6 +148,11 @@ namespace Compiler return false; } + bool Parser::parseComment (const std::string& comment, const TokenLoc& loc, Scanner& scanner) + { + return true; + } + // Handle an EOF token. // // - Default-implementation: Report an error. diff --git a/components/compiler/parser.hpp b/components/compiler/parser.hpp index 221e7c2c9f..4fec570e9f 100644 --- a/components/compiler/parser.hpp +++ b/components/compiler/parser.hpp @@ -82,6 +82,13 @@ namespace Compiler /// /// - Default-implementation: Report an error. + virtual bool parseComment (const std::string& comment, const TokenLoc& loc, + Scanner& scanner); + ///< Handle comment token. + /// \return fetch another token? + /// + /// - Default-implementation: ignored (and return true). + virtual void parseEOF (Scanner& scanner); ///< Handle EOF token. /// diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 420fd8f7f3..38a9265a1e 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -88,6 +88,10 @@ namespace Compiler } else if (c==';') { + std::string comment; + + comment += c; + while (get (c)) { if (c=='\n') @@ -95,11 +99,14 @@ namespace Compiler putback (c); break; } + else + comment += c; } + TokenLoc loc (mLoc); mLoc.mLiteral.clear(); - return true; + return parser.parseComment (comment, loc, *this); } else if (isWhitespace (c)) { diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp new file mode 100644 index 0000000000..fb03a90844 --- /dev/null +++ b/components/esm/cellref.hpp @@ -0,0 +1,90 @@ +#ifndef OPENMW_ESM_CELLREF_H +#define OPENMW_ESM_CELLREF_H + +#include + +#include "defs.hpp" + +namespace ESM +{ + class ESMWriter; + + /* Cell reference. This represents ONE object (of many) inside the + cell. The cell references are not loaded as part of the normal + loading process, but are rather loaded later on demand when we are + setting up a specific cell. + */ + + class CellRef + { + public: + + int mRefnum; // Reference number + std::string mRefID; // ID of object being referenced + + float mScale; // Scale applied to mesh + + // The NPC that owns this object (and will get angry if you steal + // it) + std::string mOwner; + + // I have no idea, looks like a link to a global variable? + std::string mGlob; + + // ID of creature trapped in this soul gem (?) + std::string mSoul; + + // ?? CNAM has a faction name, might be for objects/beds etc + // belonging to a faction. + std::string mFaction; + + // INDX might be PC faction rank required to use the item? Sometimes + // is -1, which I assume means "any rank". + int mFactIndex; + + // For weapon or armor, this is the remaining item health. + // For tools (lockpicks, probes, repair hammer) it is the remaining uses. + int mCharge; + + // Remaining enchantment charge + float mEnchantmentCharge; + + // This is 5 for Gold_005 references, 100 for Gold_100 and so on. + int mGoldValue; + + // For doors - true if this door teleports to somewhere else, false + // if it should open through animation. + bool mTeleport; + + // Teleport location for the door, if this is a teleporting door. + Position mDoorDest; + + // Destination cell for doors (optional) + std::string mDestCell; + + // Lock level for doors and containers + int mLockLevel; + std::string mKey, mTrap; // Key and trap ID names, if any + + // This corresponds to the "Reference Blocked" checkbox in the construction set, + // which prevents editing that reference. + // -1 is not blocked, otherwise it is blocked. + signed char mReferenceBlocked; + + // Track deleted references. 0 - not deleted, 1 - deleted, but respawns, 2 - deleted and does not respawn. + int mDeleted; + + // Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza + // Brindisi Dorom", where it has the value 100. Also only for + // activators. + int mFltv; + int mNam0; + + // Position and rotation of this object within the cell + Position mPos; + + void save(ESMWriter &esm); + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/loadbsgn.cpp b/components/esm/loadbsgn.cpp index b58071644c..cb500f6748 100644 --- a/components/esm/loadbsgn.cpp +++ b/components/esm/loadbsgn.cpp @@ -24,4 +24,12 @@ void BirthSign::save(ESMWriter &esm) mPowers.save(esm); } + void BirthSign::blank() + { + mName.clear(); + mDescription.clear(); + mTexture.clear(); + mPowers.mList.clear(); + } + } diff --git a/components/esm/loadbsgn.hpp b/components/esm/loadbsgn.hpp index b0bc28be48..434ddf68ea 100644 --- a/components/esm/loadbsgn.hpp +++ b/components/esm/loadbsgn.hpp @@ -20,6 +20,9 @@ struct BirthSign void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index afc953f54c..44412b5eb9 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -7,95 +7,19 @@ #include "esmcommon.hpp" #include "defs.hpp" - +#include "cellref.hpp" namespace MWWorld { class ESMStore; } - namespace ESM { class ESMReader; class ESMWriter; -/* Cell reference. This represents ONE object (of many) inside the - cell. The cell references are not loaded as part of the normal - loading process, but are rather loaded later on demand when we are - setting up a specific cell. - */ -class CellRef -{ -public: - int mRefnum; // Reference number - std::string mRefID; // ID of object being referenced - - float mScale; // Scale applied to mesh - - // The NPC that owns this object (and will get angry if you steal - // it) - std::string mOwner; - - // I have no idea, looks like a link to a global variable? - std::string mGlob; - - // ID of creature trapped in this soul gem (?) - std::string mSoul; - - // ?? CNAM has a faction name, might be for objects/beds etc - // belonging to a faction. - std::string mFaction; - - // INDX might be PC faction rank required to use the item? Sometimes - // is -1, which I assume means "any rank". - int mFactIndex; - - // For weapon or armor, this is the remaining item health. - // For tools (lockpicks, probes, repair hammer) it is the remaining uses. - int mCharge; - - // Remaining enchantment charge - float mEnchantmentCharge; - - // This is 5 for Gold_005 references, 100 for Gold_100 and so on. - int mGoldValue; - - // For doors - true if this door teleports to somewhere else, false - // if it should open through animation. - bool mTeleport; - - // Teleport location for the door, if this is a teleporting door. - Position mDoorDest; - - // Destination cell for doors (optional) - std::string mDestCell; - - // Lock level for doors and containers - int mLockLevel; - std::string mKey, mTrap; // Key and trap ID names, if any - - // This corresponds to the "Reference Blocked" checkbox in the construction set, - // which prevents editing that reference. - // -1 is not blocked, otherwise it is blocked. - signed char mReferenceBlocked; - - // Track deleted references. 0 - not deleted, 1 - deleted, but respawns, 2 - deleted and does not respawn. - int mDeleted; - - // Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza - // Brindisi Dorom", where it has the value 100. Also only for - // activators. - int mFltv; - int mNam0; - - // Position and rotation of this object within the cell - Position mPos; - - void save(ESMWriter &esm); -}; - /* Moved cell reference tracking object. This mainly stores the target cell of the reference, so we can easily know where it has been moved when another plugin tries to move it independently. diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index d9f367fd63..bdc4614625 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -1,5 +1,7 @@ #include "loadclas.hpp" +#include + #include "esmreader.hpp" #include "esmwriter.hpp" @@ -18,6 +20,23 @@ const char *Class::sGmstSpecializationIds[3] = { "sSpecializationStealth" }; + + int& Class::CLDTstruct::getSkill (int index, bool major) + { + if (index<0 || index>=5) + throw std::logic_error ("skill index out of range"); + + return mSkills[index][major ? 1 : 0]; + } + + int Class::CLDTstruct::getSkill (int index, bool major) const + { + if (index<0 || index>=5) + throw std::logic_error ("skill index out of range"); + + return mSkills[index][major ? 1 : 0]; + } + void Class::load(ESMReader &esm) { mName = esm.getHNString("FNAM"); diff --git a/components/esm/loadclas.hpp b/components/esm/loadclas.hpp index ac596af32c..4f85e6ee8b 100644 --- a/components/esm/loadclas.hpp +++ b/components/esm/loadclas.hpp @@ -58,6 +58,12 @@ struct Class // I have no idea how to autocalculate these items... int mCalc; + + int& getSkill (int index, bool major); + ///< Throws an exception for invalid values of \a index. + + int getSkill (int index, bool major) const; + ///< Throws an exception for invalid values of \a index. }; // 60 bytes std::string mId, mName, mDescription; diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index 6ea66977d5..e2712d462d 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -1,10 +1,27 @@ #include "loadfact.hpp" +#include + #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { + int& Faction::FADTstruct::getSkill (int index, bool ignored) + { + if (index<0 || index>=6) + throw std::logic_error ("skill index out of range"); + + return mSkills[index]; + } + + int Faction::FADTstruct::getSkill (int index, bool ignored) const + { + if (index<0 || index>=6) + throw std::logic_error ("skill index out of range"); + + return mSkills[index]; + } void Faction::load(ESMReader &esm) { @@ -33,7 +50,7 @@ void Faction::load(ESMReader &esm) void Faction::save(ESMWriter &esm) { esm.writeHNCString("FNAM", mName); - + for (int i = 0; i < 10; i++) { if (mRanks[i].empty()) @@ -43,7 +60,7 @@ void Faction::save(ESMWriter &esm) } esm.writeHNT("FADT", mData, 240); - + for (std::vector::iterator it = mReactions.begin(); it != mReactions.end(); ++it) { esm.writeHNString("ANAM", it->mFaction); @@ -51,4 +68,25 @@ void Faction::save(ESMWriter &esm) } } + void Faction::blank() + { + mName.clear(); + mData.mAttribute[0] = mData.mAttribute[1] = 0; + mData.mUnknown = -1; + mData.mIsHidden = 0; + + for (int i=0; i<10; ++i) + { + mData.mRankData[i].mAttribute1 = mData.mRankData[i].mAttribute2 = 0; + mData.mRankData[i].mSkill1 = mData.mRankData[i].mSkill2 = 0; + mData.mRankData[i].mFactReaction = 0; + + mRanks[i].clear(); + } + + for (int i=0; i<6; ++i) + mData.mSkills[i] = 0; + + mReactions.clear(); + } } diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index 49898b1cf2..891b996473 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -34,13 +34,19 @@ struct Faction struct FADTstruct { // Which attributes we like - int mAttribute1, mAttribute2; + int mAttribute[2]; RankData mRankData[10]; - int mSkillID[6]; // IDs of skills this faction require + int mSkills[6]; // IDs of skills this faction require int mUnknown; // Always -1? int mIsHidden; // 1 - hidden from player + + int& getSkill (int index, bool ignored = false); + ///< Throws an exception for invalid values of \a index. + + int getSkill (int index, bool ignored = false) const; + ///< Throws an exception for invalid values of \a index. }; // 240 bytes FADTstruct mData; @@ -58,6 +64,9 @@ struct Faction void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index b7db5db360..aa9656d724 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -22,17 +22,20 @@ struct LeveledListBase { enum Flags { - AllLevels = 0x01, // Calculate from all levels <= player - // level, not just the closest below - // player. - Each = 0x02 // Select a new item each time this + + Each = 0x01, // Select a new item each time this // list is instantiated, instead of // giving several identical items - }; // (used when a container has more + // (used when a container has more // than one instance of one leveled // list.) + AllLevels = 0x02 // Calculate from all levels <= player + // level, not just the closest below + // player. + }; + int mFlags; - unsigned char mChanceNone; // Chance that none are selected (0-255?) + unsigned char mChanceNone; // Chance that none are selected (0-100) std::string mId; // Record name used to read references. Must be set before load() is diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 46be29961d..b30077f23c 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -35,11 +35,11 @@ struct NPC Apparatus = 0x00100, RepairItem = 0x00200, Misc = 0x00400, + Potions = 0x02000, // Other services Spells = 0x00800, MagicItems = 0x01000, - Potions = 0x02000, Training = 0x04000, // What skills? Spellmaking = 0x08000, Enchanting = 0x10000, diff --git a/components/esm/loadrace.cpp b/components/esm/loadrace.cpp index 139ef081c0..955424e2b9 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -5,6 +5,15 @@ namespace ESM { + int Race::MaleFemale::getValue (bool male) const + { + return male ? mMale : mFemale; + } + + int Race::MaleFemaleF::getValue (bool male) const + { + return male ? mMale : mFemale; + } void Race::load(ESMReader &esm) { @@ -21,4 +30,25 @@ void Race::save(ESMWriter &esm) esm.writeHNOString("DESC", mDescription); } + void Race::blank() + { + mName.clear(); + mDescription.clear(); + + mPowers.mList.clear(); + + for (int i=0; i<7; ++i) + { + mData.mBonus[i].mSkill = -1; + mData.mBonus[i].mBonus = 0; + } + + for (int i=0; i<8; ++i) + mData.mAttributeValues[i].mMale = mData.mAttributeValues[i].mFemale = 1; + + mData.mHeight.mMale = mData.mHeight.mFemale = 1; + mData.mWeight.mMale = mData.mWeight.mFemale = 1; + + mData.mFlags = 0; + } } diff --git a/components/esm/loadrace.hpp b/components/esm/loadrace.hpp index 91a424c10b..6ecec8ebb9 100644 --- a/components/esm/loadrace.hpp +++ b/components/esm/loadrace.hpp @@ -26,11 +26,15 @@ struct Race struct MaleFemale { int mMale, mFemale; + + int getValue (bool male) const; }; struct MaleFemaleF { float mMale, mFemale; + + int getValue (bool male) const; }; enum Flags @@ -45,14 +49,7 @@ struct Race SkillBonus mBonus[7]; // Attribute values for male/female - MaleFemale mStrength, - mIntelligence, - mWillpower, - mAgility, - mSpeed, - mEndurance, - mPersonality, - mLuck; + MaleFemale mAttributeValues[8]; // The actual eye level height (in game units) is (probably) given // as 'height' times 128. This has not been tested yet. @@ -69,6 +66,9 @@ struct Race void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } diff --git a/components/esm/loadregn.cpp b/components/esm/loadregn.cpp index d39a294547..41c7f507ae 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -31,14 +31,14 @@ void Region::load(ESMReader &esm) void Region::save(ESMWriter &esm) { esm.writeHNCString("FNAM", mName); - + if (esm.getVersion() == VER_12) esm.writeHNT("WEAT", mData, sizeof(mData) - 2); else esm.writeHNT("WEAT", mData); - + esm.writeHNOCString("BNAM", mSleepList); - + esm.writeHNT("CNAM", mMapColor); for (std::vector::iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) { @@ -46,4 +46,17 @@ void Region::save(ESMWriter &esm) } } + void Region::blank() + { + mName.clear(); + + mData.mClear = mData.mCloudy = mData.mFoggy = mData.mOvercast = mData.mRain = + mData.mThunder = mData.mAsh, mData.mBlight = mData.mA = mData.mB = 0; + + mMapColor = 0; + + mName.clear(); + mSleepList.clear(); + mSoundList.clear(); + } } diff --git a/components/esm/loadregn.hpp b/components/esm/loadregn.hpp index 0496ef5af2..f2a3d9a108 100644 --- a/components/esm/loadregn.hpp +++ b/components/esm/loadregn.hpp @@ -48,6 +48,9 @@ struct Region void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index d9b6497d98..2c1b018d97 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -64,7 +64,7 @@ void Script::save(ESMWriter &esm) memcpy(data.mName.name, mId.c_str(), mId.size()); esm.writeHNT("SCHD", data, 52); - + if (!mVarNames.empty()) { esm.startSubRecord("SCVR"); @@ -76,10 +76,21 @@ void Script::save(ESMWriter &esm) } esm.startSubRecord("SCDT"); - esm.write(&mScriptData[0], mData.mScriptDataSize); + esm.write(reinterpret_cast(&mScriptData[0]), mData.mScriptDataSize); esm.endRecord("SCDT"); esm.writeHNOString("SCTX", mScriptText); } + void Script::blank() + { + mData.mNumShorts = mData.mNumLongs = mData.mNumFloats = 0; + mData.mScriptDataSize = 0; + mData.mStringTableSize = 0; + + mVarNames.clear(); + mScriptData.clear(); + mScriptText = "Begin " + mId + "\n\nEnd " + mId + "\n"; + } + } diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index 10a6d24b10..be7e839002 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -52,11 +52,14 @@ public: SCHDstruct mData; std::vector mVarNames; // Variable names - std::vector mScriptData; // Compiled bytecode + std::vector mScriptData; // Compiled bytecode std::string mScriptText; // Uncompiled script void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadskil.cpp b/components/esm/loadskil.cpp index b7259db94e..b57645f3b8 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -127,15 +127,18 @@ void Skill::save(ESMWriter &esm) { std::ostringstream stream; - stream << "#"; + if (index!=-1) + { + stream << "#"; - if (index<10) - stream << "0"; + if (index<10) + stream << "0"; - stream << index; + stream << index; - if (index>=0 && index=0 && indexskip(8); + growTime = nif->getFloat(); + fadeTime = nif->getFloat(); } }; @@ -96,12 +98,23 @@ public: class NiGravity : public Controlled { public: + float mForce; + /* 0 - Wind (fixed direction) + * 1 - Point (fixed origin) + */ + int mType; + Ogre::Vector3 mPosition; + Ogre::Vector3 mDirection; + void read(NIFStream *nif) { Controlled::read(nif); - // two floats, one int, six floats - nif->skip(9*4); + /*unknown*/nif->getFloat(); + mForce = nif->getFloat(); + mType = nif->getUInt(); + mPosition = nif->getVector3(); + mDirection = nif->getVector3(); } }; diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 8331b93b7d..aa6a9ef4ff 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -62,20 +62,106 @@ public: } }; -class NiBSPArrayController : public Controller +class NiParticleSystemController : public Controller { public: + struct Particle { + Ogre::Vector3 velocity; + float lifetime; + float lifespan; + float timestamp; + int vertex; + }; + + float velocity; + float velocityRandom; + + float verticalDir; // 0=up, pi/2=horizontal, pi=down + float verticalAngle; + float horizontalDir; + float horizontalAngle; + + float size; + float startTime; + float stopTime; + + float emitRate; + float lifetime; + float lifetimeRandom; + + int emitFlags; // Bit 0: Emit Rate toggle bit (0 = auto adjust, 1 = use Emit Rate value) + Ogre::Vector3 offsetRandom; + + NodePtr emitter; + + int numParticles; + int activeCount; + std::vector particles; + + ExtraPtr extra; + void read(NIFStream *nif) { Controller::read(nif); - // At the moment, just skip it all - nif->skip(111); - int s = nif->getUShort(); - nif->skip(15 + s*40); + velocity = nif->getFloat(); + velocityRandom = nif->getFloat(); + verticalDir = nif->getFloat(); + verticalAngle = nif->getFloat(); + horizontalDir = nif->getFloat(); + horizontalAngle = nif->getFloat(); + /*normal?*/ nif->getVector3(); + /*color?*/ nif->getVector4(); + size = nif->getFloat(); + startTime = nif->getFloat(); + stopTime = nif->getFloat(); + nif->getChar(); + emitRate = nif->getFloat(); + lifetime = nif->getFloat(); + lifetimeRandom = nif->getFloat(); + + emitFlags = nif->getUShort(); + offsetRandom = nif->getVector3(); + + emitter.read(nif); + + /* Unknown Short, 0? + * Unknown Float, 1.0? + * Unknown Int, 1? + * Unknown Int, 0? + * Unknown Short, 0? + */ + nif->skip(16); + + numParticles = nif->getUShort(); + activeCount = nif->getUShort(); + + particles.resize(numParticles); + for(size_t i = 0;i < particles.size();i++) + { + particles[i].velocity = nif->getVector3(); + nif->getVector3(); /* unknown */ + particles[i].lifetime = nif->getFloat(); + particles[i].lifespan = nif->getFloat(); + particles[i].timestamp = nif->getFloat(); + nif->getUShort(); /* unknown */ + particles[i].vertex = nif->getUShort(); + } + + nif->getUInt(); /* -1? */ + extra.read(nif); + nif->getUInt(); /* -1? */ + nif->getChar(); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + emitter.post(nif); + extra.post(nif); } }; -typedef NiBSPArrayController NiParticleSystemController; +typedef NiParticleSystemController NiBSPArrayController; class NiMaterialColorController : public Controller { diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 9bdba63961..f1f34184ba 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -165,23 +165,28 @@ public: class NiAutoNormalParticlesData : public ShapeData { public: + int numParticles; + + float particleRadius; + int activeCount; + std::vector sizes; + void read(NIFStream *nif) { ShapeData::read(nif); // Should always match the number of vertices - activeCount = nif->getUShort(); + numParticles = nif->getUShort(); - // Skip all the info, we don't support particles yet - nif->getFloat(); // Active radius ? - nif->getUShort(); // Number of valid entries in the following arrays ? + particleRadius = nif->getFloat(); + activeCount = nif->getUShort(); if(nif->getInt()) { // Particle sizes - nif->skip(activeCount * sizeof(float)); + nif->getFloats(sizes, vertices.size()); } } }; @@ -189,16 +194,16 @@ public: class NiRotatingParticlesData : public NiAutoNormalParticlesData { public: + std::vector rotations; + void read(NIFStream *nif) { NiAutoNormalParticlesData::read(nif); if(nif->getInt()) { - // Rotation quaternions. I THINK activeCount is correct here, - // but verts (vertex number) might also be correct, if there is - // any case where the two don't match. - nif->skip(activeCount * 4*sizeof(float)); + // Rotation quaternions. + nif->getQuaternions(rotations, vertices.size()); } } }; @@ -294,13 +299,17 @@ public: float time; char isSet; }; + std::vector mVis; void read(NIFStream *nif) { int count = nif->getInt(); - - /* Skip VisData */ - nif->skip(count*5); + mVis.resize(count); + for(size_t i = 0;i < mVis.size();i++) + { + mVis[i].time = nif->getFloat(); + mVis[i].isSet = nif->getChar(); + } } }; @@ -389,16 +398,13 @@ struct NiMorphData : public Record { int morphCount = nif->getInt(); int vertCount = nif->getInt(); - nif->getChar(); + /*relative targets?*/nif->getChar(); mMorphs.resize(morphCount); for(int i = 0;i < morphCount;i++) { mMorphs[i].mData.read(nif, true); - - mMorphs[i].mVertices.resize(vertCount); - for(int j = 0;j < vertCount;j++) - mMorphs[i].mVertices[j] = nif->getVector3(); + nif->getVector3s(mMorphs[i].mVertices, vertCount); } } }; diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index bf05e7576a..3b41e96a70 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -209,7 +209,7 @@ static const RecordFactoryEntry recordFactories [] = { { "NiNode", &construct , RC_NiNode }, { "AvoidNode", &construct , RC_NiNode }, { "NiBSParticleNode", &construct , RC_NiNode }, - { "NiBSAnimationNode", &construct , RC_NiNode }, + { "NiBSAnimationNode", &construct , RC_NiBSAnimationNode }, { "NiBillboardNode", &construct , RC_NiNode }, { "NiTriShape", &construct , RC_NiTriShape }, { "NiRotatingParticles", &construct , RC_NiRotatingParticles }, @@ -345,14 +345,18 @@ void NIFFile::parse() } } - /* After the data, the nif contains an int N and then a list of N - ints following it. This might be a list of the root nodes in the - tree, but for the moment we ignore it. - */ + size_t rootNum = nif.getUInt(); + roots.resize(rootNum); - // Once parsing is done, do post-processing. - for(size_t i=0; ipost(this); + for(size_t i = 0;i < rootNum;i++) + { + intptr_t idx = nif.getInt(); + roots[i] = ((idx >= 0) ? records.at(idx) : NULL); + } + + // Once parsing is done, do post-processing. + for(size_t i=0; ipost(this); } /// \todo move to the write cpp file @@ -380,6 +384,44 @@ void NiSkinInstance::post(NIFFile *nif) } } + +void Node::getProperties(const Nif::NiTexturingProperty *&texprop, + const Nif::NiMaterialProperty *&matprop, + const Nif::NiAlphaProperty *&alphaprop, + const Nif::NiVertexColorProperty *&vertprop, + const Nif::NiZBufferProperty *&zprop, + const Nif::NiSpecularProperty *&specprop, + const Nif::NiWireframeProperty *&wireprop) const +{ + if(parent) + parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + + for(size_t i = 0;i < props.length();i++) + { + // Entries may be empty + if(props[i].empty()) + continue; + + const Nif::Property *pr = props[i].getPtr(); + if(pr->recType == Nif::RC_NiTexturingProperty) + texprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiMaterialProperty) + matprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiAlphaProperty) + alphaprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiVertexColorProperty) + vertprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiZBufferProperty) + zprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiSpecularProperty) + specprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiWireframeProperty) + wireprop = static_cast(pr); + else + std::cerr<< "Unhandled property type: "<recName < records; + /// Root list + std::vector roots; + /// Parse the file void parse(); @@ -115,9 +118,18 @@ public: assert(res != NULL); return res; } - /// Number of records size_t numRecords() { return records.size(); } + + /// Get a given root + Record *getRoot(size_t index=0) + { + Record *res = roots.at(index); + assert(res != NULL); + return res; + } + /// Number of roots + size_t numRoots() { return roots.size(); } }; diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 02b931b7ed..a2595d17b8 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -168,6 +168,12 @@ public: for(size_t i = 0;i < vec.size();i++) vec[i] = getVector4(); } + void getQuaternions(std::vector &quat, size_t size) + { + quat.resize(size); + for(size_t i = 0;i < quat.size();i++) + quat[i] = getQuaternion(); + } }; } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index ab92d74f8d..9c345baab2 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -111,6 +111,14 @@ public: boneIndex = ind; } + void getProperties(const Nif::NiTexturingProperty *&texprop, + const Nif::NiMaterialProperty *&matprop, + const Nif::NiAlphaProperty *&alphaprop, + const Nif::NiVertexColorProperty *&vertprop, + const Nif::NiZBufferProperty *&zprop, + const Nif::NiSpecularProperty *&specprop, + const Nif::NiWireframeProperty *&wireprop) const; + Ogre::Matrix4 getLocalTransform() const; Ogre::Matrix4 getWorldTransform() const; }; diff --git a/components/nif/property.hpp b/components/nif/property.hpp index cd1e0a5d11..06c8260ce5 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -64,7 +64,7 @@ public: bool inUse; NiSourceTexturePtr texture; - int clamp, set, filter; + int clamp, uvSet, filter; short unknown2; void read(NIFStream *nif) @@ -75,7 +75,7 @@ public: texture.read(nif); clamp = nif->getInt(); filter = nif->getInt(); - set = nif->getInt(); + uvSet = nif->getInt(); // I have no idea, but I think these are actually two // PS2-specific shorts (ps2L and ps2K), followed by an unknown @@ -109,6 +109,17 @@ public: * 5 - Bump map texture * 6 - Decal texture */ + enum TextureType + { + BaseTexture = 0, + DarkTexture = 1, + DetailTexture = 2, + GlossTexture = 3, + GlowTexture = 4, + BumpTexture = 5, + DecalTexture = 6 + }; + Texture textures[7]; void read(NIFStream *nif) @@ -145,11 +156,11 @@ public: }; // These contain no other data than the 'flags' field in Property -typedef Property NiShadeProperty; -typedef Property NiDitherProperty; -typedef Property NiZBufferProperty; -typedef Property NiSpecularProperty; -typedef Property NiWireframeProperty; +class NiShadeProperty : public Property { }; +class NiDitherProperty : public Property { }; +class NiZBufferProperty : public Property { }; +class NiSpecularProperty : public Property { }; +class NiWireframeProperty : public Property { }; // The rest are all struct-based template @@ -313,10 +324,10 @@ struct S_StencilProperty } }; -typedef StructPropT NiAlphaProperty; -typedef StructPropT NiMaterialProperty; -typedef StructPropT NiVertexColorProperty; -typedef StructPropT NiStencilProperty; +class NiAlphaProperty : public StructPropT { }; +class NiMaterialProperty : public StructPropT { }; +class NiVertexColorProperty : public StructPropT { }; +class NiStencilProperty : public StructPropT { }; } // Namespace #endif diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 3a3cd9b84a..361af3f64c 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -59,6 +59,7 @@ enum RecordType RC_NiMaterialColorController, RC_NiBSPArrayController, RC_NiParticleSystemController, + RC_NiBSAnimationNode, RC_NiLight, RC_NiTextureEffect, RC_NiVertWeightsExtraData, diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 08625ee9cb..6bd43f6e35 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -91,21 +91,20 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) // likely a sign of incomplete code rather than faulty input. Nif::NIFFile::ptr pnif (Nif::NIFFile::create (resourceName.substr(0, resourceName.length()-7))); Nif::NIFFile & nif = *pnif.get (); - if (nif.numRecords() < 1) + if (nif.numRoots() < 1) { - warn("Found no records in NIF."); + warn("Found no root nodes in NIF."); return; } - // The first record is assumed to be the root node - Nif::Record *r = nif.getRecord(0); + Nif::Record *r = nif.getRoot(0); assert(r != NULL); Nif::Node *node = dynamic_cast(r); if (node == NULL) { - warn("First record in file was not a node, but a " + - r->recName + ". Skipping file."); + warn("First root in file was not a node, but a " + + r->recName + ". Skipping file."); return; } diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp new file mode 100644 index 0000000000..431b8219a9 --- /dev/null +++ b/components/nifogre/material.cpp @@ -0,0 +1,399 @@ +#include "material.hpp" + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + + +namespace NifOgre +{ + +// Conversion of blend / test mode from NIF +static const char *getBlendFactor(int mode) +{ + switch(mode) + { + case 0: return "one"; + case 1: return "zero"; + case 2: return "src_colour"; + case 3: return "one_minus_src_colour"; + case 4: return "dest_colour"; + case 5: return "one_minus_dest_colour"; + case 6: return "src_alpha"; + case 7: return "one_minus_src_alpha"; + case 8: return "dest_alpha"; + case 9: return "one_minus_dest_alpha"; + case 10: return "src_alpha_saturate"; + } + std::cerr<< "Unexpected blend mode: "<colors.size() != 0); + + // Texture + if(texprop) + { + for(int i = 0;i < 7;i++) + { + if(!texprop->textures[i].inUse) + continue; + if(texprop->textures[i].texture.empty()) + { + warn("Texture layer "+Ogre::StringConverter::toString(i)+" is in use but empty in "+name); + continue; + } + + const Nif::NiSourceTexture *st = texprop->textures[i].texture.getPtr(); + if(st->external) + texName[i] = findTextureName(st->filename); + else + warn("Found internal texture, ignoring."); + } + + Nif::ControllerPtr ctrls = texprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled texture controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + needTangents = !texName[Nif::NiTexturingProperty::BumpTexture].empty(); + + // Alpha modifiers + if(alphaprop) + { + alphaFlags = alphaprop->flags; + alphaTest = alphaprop->data.threshold; + + Nif::ControllerPtr ctrls = alphaprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled alpha controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + // Vertex color handling + if(vertprop) + { + vertMode = vertprop->data.vertmode; + // FIXME: Handle lightmode? + //lightMode = vertprop->data.lightmode; + + Nif::ControllerPtr ctrls = vertprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled vertex color controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + if(zprop) + { + depthFlags = zprop->flags; + // Depth function??? + + Nif::ControllerPtr ctrls = zprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled depth controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + if(specprop) + { + specFlags = specprop->flags; + + Nif::ControllerPtr ctrls = specprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled specular controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + if(wireprop) + { + wireFlags = wireprop->flags; + + Nif::ControllerPtr ctrls = wireprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled wireframe controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + // Material + if(matprop) + { + ambient = matprop->data.ambient; + diffuse = matprop->data.diffuse; + specular = matprop->data.specular; + emissive = matprop->data.emissive; + glossiness = matprop->data.glossiness; + alpha = matprop->data.alpha; + + Nif::ControllerPtr ctrls = matprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled material controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + { + // Generate a hash out of all properties that can affect the material. + size_t h = 0; + boost::hash_combine(h, ambient.x); + boost::hash_combine(h, ambient.y); + boost::hash_combine(h, ambient.z); + boost::hash_combine(h, diffuse.x); + boost::hash_combine(h, diffuse.y); + boost::hash_combine(h, diffuse.z); + boost::hash_combine(h, alpha); + boost::hash_combine(h, specular.x); + boost::hash_combine(h, specular.y); + boost::hash_combine(h, specular.z); + boost::hash_combine(h, glossiness); + boost::hash_combine(h, emissive.x); + boost::hash_combine(h, emissive.y); + boost::hash_combine(h, emissive.z); + for(int i = 0;i < 7;i++) + { + if(!texName[i].empty()) + boost::hash_combine(h, texName[i]); + } + boost::hash_combine(h, vertexColour); + boost::hash_combine(h, alphaFlags); + boost::hash_combine(h, alphaTest); + boost::hash_combine(h, vertMode); + boost::hash_combine(h, depthFlags); + boost::hash_combine(h, specFlags); + boost::hash_combine(h, wireFlags); + + std::map::iterator itr = sMaterialMap.find(h); + if (itr != sMaterialMap.end()) + { + // a suitable material exists already - use it + return itr->second; + } + // not found, create a new one + sMaterialMap.insert(std::make_pair(h, name)); + } + + // No existing material like this. Create a new one. + sh::MaterialInstance *instance = sh::Factory::getInstance().createMaterialInstance(name, "openmw_objects_base"); + if(vertMode == 0 || !vertexColour) + { + instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1))); + instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); + instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1))); + instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("0"))); + } + else if(vertMode == 1) + { + instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1))); + instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); + instance->setProperty("emissive", sh::makeProperty(new sh::StringValue("vertexcolour"))); + instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("1"))); + } + else if(vertMode == 2) + { + instance->setProperty("ambient", sh::makeProperty(new sh::StringValue("vertexcolour"))); + instance->setProperty("diffuse", sh::makeProperty(new sh::StringValue("vertexcolour"))); + instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1))); + instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("2"))); + } + else + std::cerr<< "Unhandled vertex mode: "<setProperty("specular", sh::makeProperty( + new sh::Vector4(specular.x, specular.y, specular.z, glossiness))); + } + + if(wireFlags) + { + instance->setProperty("polygon_mode", sh::makeProperty(new sh::StringValue("wireframe"))); + } + + instance->setProperty("diffuseMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BaseTexture])); + instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture])); + instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture])); + instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture])); + if (!texName[Nif::NiTexturingProperty::GlowTexture].empty()) + { + instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("emissiveMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::GlowTexture].uvSet))); + } + if (!texName[Nif::NiTexturingProperty::DetailTexture].empty()) + { + instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet))); + } + if (!texName[Nif::NiTexturingProperty::BumpTexture].empty()) + { + // force automips on normal maps for now + instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture] + " 4")); + } + + for(int i = 0;i < 7;i++) + { + if(i == Nif::NiTexturingProperty::BaseTexture || + i == Nif::NiTexturingProperty::DetailTexture || + i == Nif::NiTexturingProperty::BumpTexture || + i == Nif::NiTexturingProperty::GlowTexture) + continue; + if(!texName[i].empty()) + warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i)); + } + + if (vertexColour) + instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); + + // Add transparency if NiAlphaProperty was present + NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]); + if (result.first) + { + alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ + alphaTest = result.second; + depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on + } + + if((alphaFlags&1)) + { + std::string blend_mode; + blend_mode += getBlendFactor((alphaFlags>>1)&0xf); + blend_mode += " "; + blend_mode += getBlendFactor((alphaFlags>>5)&0xf); + instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue(blend_mode))); + } + + if((alphaFlags>>9)&1) + { + std::string reject; + reject += getTestMode((alphaFlags>>10)&0x7); + reject += " "; + reject += Ogre::StringConverter::toString(alphaTest); + instance->setProperty("alpha_rejection", sh::makeProperty(new sh::StringValue(reject))); + } + else + instance->getMaterial()->setShadowCasterMaterial("openmw_shadowcaster_noalpha"); + + // Ogre usually only sorts if depth write is disabled, so we want "force" instead of "on" + instance->setProperty("transparent_sorting", sh::makeProperty(new sh::StringValue( + ((alphaFlags&1) && !((alphaFlags>>13)&1)) ? "force" : "off"))); + + instance->setProperty("depth_check", sh::makeProperty(new sh::StringValue((depthFlags&1) ? "on" : "off"))); + instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); + // depth_func??? + + sh::Factory::getInstance()._ensureMaterial(name, "Default"); + return name; +} + +std::map NIFMaterialLoader::sMaterialMap; + +} diff --git a/components/nifogre/material.hpp b/components/nifogre/material.hpp new file mode 100644 index 0000000000..8843ac6c6c --- /dev/null +++ b/components/nifogre/material.hpp @@ -0,0 +1,57 @@ +#ifndef COMPONENTS_NIFOGRE_MATERIAL_HPP +#define COMPONENTS_NIFOGRE_MATERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace Nif +{ + class ShapeData; + class NiTexturingProperty; + class NiMaterialProperty; + class NiAlphaProperty; + class NiVertexColorProperty; + class NiZBufferProperty; + class NiSpecularProperty; + class NiWireframeProperty; +} + +namespace NifOgre +{ + +class NIFMaterialLoader { + static void warn(const std::string &msg) + { + std::cerr << "NIFMaterialLoader: Warn: " << msg << std::endl; + } + + static void fail(const std::string &msg) + { + std::cerr << "NIFMaterialLoader: Fail: "<< msg << std::endl; + abort(); + } + + static std::map sMaterialMap; + + static std::string findTextureName(const std::string &filename); + +public: + static Ogre::String getMaterial(const Nif::ShapeData *shapedata, + const Ogre::String &name, const Ogre::String &group, + const Nif::NiTexturingProperty *texprop, + const Nif::NiMaterialProperty *matprop, + const Nif::NiAlphaProperty *alphaprop, + const Nif::NiVertexColorProperty *vertprop, + const Nif::NiZBufferProperty *zprop, + const Nif::NiSpecularProperty *specprop, + const Nif::NiWireframeProperty *wireprop, + bool &needTangents); +}; + +} + +#endif diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp new file mode 100644 index 0000000000..c0a7af5c30 --- /dev/null +++ b/components/nifogre/mesh.cpp @@ -0,0 +1,366 @@ +#include "mesh.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "material.hpp" + +namespace NifOgre +{ + +// Helper class that computes the bounding box and of a mesh +class BoundsFinder +{ + struct MaxMinFinder + { + float max, min; + + MaxMinFinder() + { + min = std::numeric_limits::infinity(); + max = -min; + } + + void add(float f) + { + if (f > max) max = f; + if (f < min) min = f; + } + + // Return Max(max**2, min**2) + float getMaxSquared() + { + float m1 = max*max; + float m2 = min*min; + if (m1 >= m2) return m1; + return m2; + } + }; + + MaxMinFinder X, Y, Z; + +public: + // Add 'verts' vertices to the calculation. The 'data' pointer is + // expected to point to 3*verts floats representing x,y,z for each + // point. + void add(float *data, int verts) + { + for (int i=0;idata.getPtr(); + const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); + std::vector srcVerts = data->vertices; + std::vector srcNorms = data->normals; + Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; + bool vertShadowBuffer = false; + if(skin != NULL) + { + vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; + vertShadowBuffer = true; + + // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be + // explicitly attached later. + mesh->setSkeletonName(mName); + + // Get the skeleton resource, so vertices can be transformed into the bones' initial state. + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + skel = skelMgr->getByName(mName); + + // Convert vertices and normals to bone space from bind position. It would be + // better to transform the bones into bind position, but there doesn't seem to + // be a reliable way to do that. + std::vector newVerts(srcVerts.size(), Ogre::Vector3(0.0f)); + std::vector newNorms(srcNorms.size(), Ogre::Vector3(0.0f)); + + const Nif::NiSkinData *data = skin->data.getPtr(); + const Nif::NodeList &bones = skin->bones; + for(size_t b = 0;b < bones.length();b++) + { + Ogre::Bone *bone = skel->getBone(bones[b]->name); + Ogre::Matrix4 mat; + mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), + Ogre::Quaternion(data->bones[b].trafo.rotation)); + mat = bone->_getFullTransform() * mat; + + const std::vector &weights = data->bones[b].weights; + for(size_t i = 0;i < weights.size();i++) + { + size_t index = weights[i].vertex; + float weight = weights[i].weight; + + newVerts.at(index) += (mat*srcVerts[index]) * weight; + if(newNorms.size() > index) + { + Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f); + vec4 = mat*vec4 * weight; + newNorms[index] += Ogre::Vector3(&vec4[0]); + } + } + } + + srcVerts = newVerts; + srcNorms = newNorms; + } + else + { + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + if(skelMgr->getByName(mName).isNull()) + { + // No skinning and no skeleton, so just transform the vertices and + // normals into position. + Ogre::Matrix4 mat4 = shape->getWorldTransform(); + for(size_t i = 0;i < srcVerts.size();i++) + { + Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f); + vec4 = mat4*vec4; + srcVerts[i] = Ogre::Vector3(&vec4[0]); + } + for(size_t i = 0;i < srcNorms.size();i++) + { + Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f); + vec4 = mat4*vec4; + srcNorms[i] = Ogre::Vector3(&vec4[0]); + } + } + } + + // Set the bounding box first + BoundsFinder bounds; + bounds.add(&srcVerts[0][0], srcVerts.size()); + if(!bounds.isValid()) + { + float v[3] = { 0.0f, 0.0f, 0.0f }; + bounds.add(&v[0], 1); + } + + mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f, + bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f)); + mesh->_setBoundingSphereRadius(bounds.getRadius()); + + // This function is just one long stream of Ogre-barf, but it works + // great. + Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareVertexBufferSharedPtr vbuf; + Ogre::HardwareIndexBufferSharedPtr ibuf; + Ogre::VertexBufferBinding *bind; + Ogre::VertexDeclaration *decl; + int nextBuf = 0; + + Ogre::SubMesh *sub = mesh->createSubMesh(); + + // Add vertices + sub->useSharedVertices = false; + sub->vertexData = new Ogre::VertexData(); + sub->vertexData->vertexStart = 0; + sub->vertexData->vertexCount = srcVerts.size(); + + decl = sub->vertexData->vertexDeclaration; + bind = sub->vertexData->vertexBufferBinding; + if(srcVerts.size()) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + srcVerts.size(), vertUsage, vertShadowBuffer); + vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); + bind->setBinding(nextBuf++, vbuf); + } + + // Vertex normals + if(srcNorms.size()) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + srcNorms.size(), vertUsage, vertShadowBuffer); + vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); + bind->setBinding(nextBuf++, vbuf); + } + + // Vertex colors + const std::vector &colors = data->colors; + if(colors.size()) + { + Ogre::RenderSystem *rs = Ogre::Root::getSingleton().getRenderSystem(); + std::vector colorsRGB(colors.size()); + for(size_t i = 0;i < colorsRGB.size();i++) + { + Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]); + rs->convertColourValue(clr, &colorsRGB[i]); + } + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), + colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC); + vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true); + decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); + bind->setBinding(nextBuf++, vbuf); + } + + // Texture UV coordinates + size_t numUVs = data->uvlist.size(); + for(size_t i = 0;i < numUVs;i++) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), + srcVerts.size(), Ogre::HardwareBuffer::HBU_STATIC); + vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[i][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i); + bind->setBinding(nextBuf++, vbuf); + } + + // Triangle faces + const std::vector &srcIdx = data->triangles; + if(srcIdx.size()) + { + ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(), + Ogre::HardwareBuffer::HBU_STATIC); + ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true); + sub->indexData->indexBuffer = ibuf; + sub->indexData->indexCount = srcIdx.size(); + sub->indexData->indexStart = 0; + } + + // Assign bone weights for this TriShape + if(skin != NULL) + { + const Nif::NiSkinData *data = skin->data.getPtr(); + const Nif::NodeList &bones = skin->bones; + for(size_t i = 0;i < bones.length();i++) + { + Ogre::VertexBoneAssignment boneInf; + boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle(); + + const std::vector &weights = data->bones[i].weights; + for(size_t j = 0;j < weights.size();j++) + { + boneInf.vertexIndex = weights[j].vertex; + boneInf.weight = weights[j].weight; + sub->addBoneAssignment(boneInf); + } + } + } + + const Nif::NiTexturingProperty *texprop = NULL; + const Nif::NiMaterialProperty *matprop = NULL; + const Nif::NiAlphaProperty *alphaprop = NULL; + const Nif::NiVertexColorProperty *vertprop = NULL; + const Nif::NiZBufferProperty *zprop = NULL; + const Nif::NiSpecularProperty *specprop = NULL; + const Nif::NiWireframeProperty *wireprop = NULL; + bool needTangents = false; + + shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + std::string matname = NIFMaterialLoader::getMaterial(data, mesh->getName(), mGroup, + texprop, matprop, alphaprop, + vertprop, zprop, specprop, + wireprop, needTangents); + if(matname.length() > 0) + sub->setMaterialName(matname); + + // build tangents if the material needs them + if (needTangents) + { + unsigned short src,dest; + if (!mesh->suggestTangentVectorBuildParams(Ogre::VES_TANGENT, src,dest)) + mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest); + } +} + + +NIFMeshLoader::NIFMeshLoader(const std::string &name, const std::string &group, size_t idx) + : mName(name), mGroup(group), mShapeIndex(idx) +{ +} + +void NIFMeshLoader::loadResource(Ogre::Resource *resource) +{ + Ogre::Mesh *mesh = dynamic_cast(resource); + OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!"); + + Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); + if(mShapeIndex >= nif->numRecords()) + { + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + if(!skelMgr->getByName(mName).isNull()) + mesh->setSkeletonName(mName); + return; + } + + const Nif::Record *record = nif->getRecord(mShapeIndex); + createSubMesh(mesh, dynamic_cast(record)); +} + + +void NIFMeshLoader::createMesh(const std::string &name, const std::string &fullname, const std::string &group, size_t idx) +{ + NIFMeshLoader::LoaderMap::iterator loader; + loader = sLoaders.insert(std::make_pair(fullname, NIFMeshLoader(name, group, idx))).first; + + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + Ogre::MeshPtr mesh = meshMgr.createManual(fullname, group, &loader->second); + mesh->setAutoBuildEdgeLists(false); +} + +} diff --git a/components/nifogre/mesh.hpp b/components/nifogre/mesh.hpp new file mode 100644 index 0000000000..731e49c903 --- /dev/null +++ b/components/nifogre/mesh.hpp @@ -0,0 +1,55 @@ +#ifndef COMPONENTS_NIFOGRE_MESH_HPP +#define COMPONENTS_NIFOGRE_MESH_HPP + +#include +#include +#include +#include + +#include + +namespace Nif +{ + class NiTriShape; +} + +namespace NifOgre +{ + +/** Manual resource loader for NiTriShapes. This is the main class responsible + * for translating the internal NIF meshes into something Ogre can use. + */ +class NIFMeshLoader : Ogre::ManualResourceLoader +{ + static void warn(const std::string &msg) + { + std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; + } + + static void fail(const std::string &msg) + { + std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; + abort(); + } + + std::string mName; + std::string mGroup; + size_t mShapeIndex; + + // Convert NiTriShape to Ogre::SubMesh + void createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape); + + typedef std::map LoaderMap; + static LoaderMap sLoaders; + + NIFMeshLoader(const std::string &name, const std::string &group, size_t idx); + + virtual void loadResource(Ogre::Resource *resource); + +public: + static void createMesh(const std::string &name, const std::string &fullname, const std::string &group, size_t idx); +}; + +} + +#endif diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 5aa5ff80cb..48893bf4a8 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -25,29 +25,24 @@ #include -#include -#include -#include -#include #include -#include #include #include #include #include - -#include -#include -#include - -#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -typedef unsigned char ubyte; +#include "skeleton.hpp" +#include "material.hpp" +#include "mesh.hpp" namespace std { @@ -60,1088 +55,538 @@ ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) namespace NifOgre { -// Helper class that computes the bounding box and of a mesh -class BoundsFinder + +// FIXME: Should not be here. +class DefaultFunction : public Ogre::ControllerFunction { - struct MaxMinFinder +private: + float mFrequency; + float mPhase; + float mStartTime; + float mStopTime; + +public: + DefaultFunction(const Nif::Controller *ctrl, bool deltaInput) + : Ogre::ControllerFunction(deltaInput) + , mFrequency(ctrl->frequency) + , mPhase(ctrl->phase) + , mStartTime(ctrl->timeStart) + , mStopTime(ctrl->timeStop) { - float max, min; - - MaxMinFinder() + if(mDeltaInput) { - min = std::numeric_limits::infinity(); - max = -min; + mDeltaCount = mPhase; + while(mDeltaCount < mStartTime) + mDeltaCount += (mStopTime-mStartTime); + } + } + + virtual Ogre::Real calculate(Ogre::Real value) + { + if(mDeltaInput) + { + mDeltaCount += value*mFrequency; + mDeltaCount = std::fmod(mDeltaCount - mStartTime, + mStopTime - mStartTime) + mStartTime; + return mDeltaCount; } - void add(float f) + value = std::min(mStopTime, std::max(mStartTime, value+mPhase)); + return value; + } +}; + +class VisController +{ +public: + class Value : public NodeTargetValue + { + private: + std::vector mData; + + virtual bool calculate(Ogre::Real time) { - if (f > max) max = f; - if (f < min) min = f; + if(mData.size() == 0) + return true; + + for(size_t i = 1;i < mData.size();i++) + { + if(mData[i].time > time) + return mData[i-1].isSet; + } + return mData.back().isSet; } - // Return Max(max**2, min**2) - float getMaxSquared() + // FIXME: We are not getting all objects here. Skinned meshes get + // attached to the object's root node, and won't be connected via a + // TagPoint. + static void setVisible(Ogre::Node *node, int vis) { - float m1 = max*max; - float m2 = min*min; - if (m1 >= m2) return m1; - return m2; + Ogre::Node::ChildNodeIterator iter = node->getChildIterator(); + while(iter.hasMoreElements()) + { + node = iter.getNext(); + setVisible(node, vis); + + Ogre::TagPoint *tag = dynamic_cast(node); + if(tag != NULL) + { + Ogre::MovableObject *obj = tag->getChildObject(); + if(obj != NULL) + obj->setVisible(vis); + } + } + } + + public: + Value(Ogre::Node *target, const Nif::NiVisData *data) + : NodeTargetValue(target) + , mData(data->mVis) + { } + + virtual Ogre::Real getValue() const + { + // Should not be called + return 1.0f; + } + + virtual void setValue(Ogre::Real time) + { + bool vis = calculate(time); + setVisible(mNode, vis); } }; - MaxMinFinder X, Y, Z; - -public: - // Add 'verts' vertices to the calculation. The 'data' pointer is - // expected to point to 3*verts floats representing x,y,z for each - // point. - void add(float *data, int verts) - { - for (int i=0;i &ctrls, const std::vector &targets, float startTime, float stopTime) -{ - Ogre::Animation *anim = skel->createAnimation(name, stopTime-startTime); - - for(size_t i = 0;i < ctrls.size();i++) - { - const Nif::NiKeyframeController *kfc = ctrls[i]; - if(kfc->data.empty()) - continue; - const Nif::NiKeyframeData *kf = kfc->data.getPtr(); - - /* Get the keyframes and make sure they're sorted first to last */ - const Nif::QuaternionKeyList &quatkeys = kf->mRotations; - const Nif::Vector3KeyList &trankeys = kf->mTranslations; - const Nif::FloatKeyList &scalekeys = kf->mScales; - - Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); - Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); - Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); - - Ogre::Bone *bone = skel->getBone(targets[i]); - // NOTE: For some reason, Ogre doesn't like the node track ID being different from - // the bone ID - Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ? - anim->getNodeTrack(bone->getHandle()) : - anim->createNodeTrack(bone->getHandle(), bone); - - Ogre::Quaternion lastquat, curquat; - Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f); - Ogre::Vector3 lastscale(1.0f), curscale(1.0f); - if(quatiter != quatkeys.mKeys.end()) - lastquat = curquat = quatiter->mValue; - if(traniter != trankeys.mKeys.end()) - lasttrans = curtrans = traniter->mValue; - if(scaleiter != scalekeys.mKeys.end()) - lastscale = curscale = Ogre::Vector3(scaleiter->mValue); - float begTime = std::max(kfc->timeStart, startTime); - float endTime = std::min(kfc->timeStop, stopTime); - bool didlast = false; - - while(!didlast) - { - float curtime = std::numeric_limits::max(); - - //Get latest time - if(quatiter != quatkeys.mKeys.end()) - curtime = std::min(curtime, quatiter->mTime); - if(traniter != trankeys.mKeys.end()) - curtime = std::min(curtime, traniter->mTime); - if(scaleiter != scalekeys.mKeys.end()) - curtime = std::min(curtime, scaleiter->mTime); - - curtime = std::max(curtime, begTime); - if(curtime >= endTime) - { - didlast = true; - curtime = endTime; - } - - // Get the latest quaternions, translations, and scales for the - // current time - while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) - { - lastquat = curquat; - if(++quatiter != quatkeys.mKeys.end()) - curquat = quatiter->mValue; - } - while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) - { - lasttrans = curtrans; - if(++traniter != trankeys.mKeys.end()) - curtrans = traniter->mValue; - } - while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) - { - lastscale = curscale; - if(++scaleiter != scalekeys.mKeys.end()) - curscale = Ogre::Vector3(scaleiter->mValue); - } - - Ogre::TransformKeyFrame *kframe; - kframe = nodetrack->createNodeKeyFrame(curtime-startTime); - if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) - kframe->setRotation(curquat); - else - { - Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1; - float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); - kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); - } - if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin()) - kframe->setTranslate(curtrans); - else - { - Nif::Vector3KeyList::VecType::const_iterator last = traniter-1; - float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); - kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); - } - if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin()) - kframe->setScale(curscale); - else - { - Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; - float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); - kframe->setScale(lastscale + ((curscale-lastscale)*diff)); - } - } - } - anim->optimise(); -} - - -static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) -{ - TextKeyMap textkeys; - for(size_t i = 0;i < tk->list.size();i++) - { - const std::string &str = tk->list[i].text; - std::string::size_type pos = 0; - while(pos < str.length()) - { - if(::isspace(str[pos])) - { - pos++; - continue; - } - - std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); - std::string result = str.substr(pos, nextpos-pos); - textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); - - pos = nextpos; - } - } - return textkeys; -} - - -void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector &ctrls, Ogre::Bone *parent=NULL) -{ - Ogre::Bone *bone; - if(!skel->hasBone(node->name)) - bone = skel->createBone(node->name); - else - bone = skel->createBone(); - if(parent) parent->addChild(bone); - - bone->setOrientation(node->trafo.rotation); - bone->setPosition(node->trafo.pos); - bone->setScale(Ogre::Vector3(node->trafo.scale)); - bone->setBindingPose(); - - if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */ - node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ - node->recType == Nif::RC_NiTriShape /* Handled in the mesh loader */ - )) - warn("Unhandled "+node->recName+" "+node->name+" in "+skel->getName()); - - Nif::ControllerPtr ctrl = node->controller; - while(!ctrl.empty()) - { - if(ctrl->recType == Nif::RC_NiKeyframeController) - ctrls.push_back(static_cast(ctrl.getPtr())); - else - warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); - ctrl = ctrl->next; - } - - Nif::ExtraPtr e = node->extra; - while(!e.empty()) - { - if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot) - { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - textkeys = extractTextKeys(tk); - animroot = bone; - } - e = e->extra; - } - - const Nif::NiNode *ninode = dynamic_cast(node); - if(ninode) - { - const Nif::NodeList &children = ninode->children; - for(size_t i = 0;i < children.length();i++) - { - if(!children[i].empty()) - buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone); - } - } -} - - -typedef std::map LoaderMap; -static LoaderMap sLoaders; - public: -void loadResource(Ogre::Resource *resource) -{ - Ogre::Skeleton *skel = dynamic_cast(resource); - OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); - - Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName())); - const Nif::Node *node = static_cast(nif->getRecord(0)); - - std::vector ctrls; - Ogre::Bone *animroot = NULL; - TextKeyMap textkeys; - try { - buildBones(skel, node, animroot, textkeys, ctrls); - } - catch(std::exception &e) { - std::cerr<< "Exception while loading "<getName() < targets; - // TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file - if(ctrls.size() == 0) // No animations? Then we're done. - return; - - float maxtime = 0.0f; - for(size_t i = 0;i < ctrls.size();i++) + class Value : public NodeTargetValue { - const Nif::NiKeyframeController *ctrl = ctrls[i]; - maxtime = std::max(maxtime, ctrl->timeStop); - Nif::Named *target = dynamic_cast(ctrl->target.getPtr()); - if(target != NULL) - targets.push_back(target->name); - } + private: + Nif::QuaternionKeyList mRotations; + Nif::Vector3KeyList mTranslations; + Nif::FloatKeyList mScales; - if(targets.size() != ctrls.size()) - { - warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+ - Ogre::StringConverter::toString(ctrls.size())+" controllers)"); - return; - } + public: + Value(Ogre::Node *target, const Nif::NiKeyframeData *data) + : NodeTargetValue(target) + , mRotations(data->mRotations) + , mTranslations(data->mTranslations) + , mScales(data->mScales) + { } - if(!animroot) - { - warn(Ogre::StringConverter::toString(ctrls.size())+" animated node(s) in "+ - skel->getName()+", but no text keys."); - return; - } - - Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings(); - bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true)); - - std::string currentgroup; - TextKeyMap::const_iterator keyiter = textkeys.begin(); - for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++) - { - std::string::size_type sep = keyiter->second.find(':'); - if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) || - (sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) || - (sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0)) - continue; - currentgroup = keyiter->second.substr(0, sep); - - if(skel->hasAnimation(currentgroup)) - continue; - - TextKeyMap::const_iterator lastkeyiter = textkeys.end(); - while((--lastkeyiter)->first > keyiter->first) + virtual Ogre::Real getValue() const { - if(lastkeyiter->second.find(':') == currentgroup.length() && - lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0) - break; + // Should not be called + return 0.0f; } - buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first); - - TextKeyMap::const_iterator insiter = keyiter; - TextKeyMap groupkeys; - do { - sep = insiter->second.find(':'); - if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0) - groupkeys.insert(std::make_pair(insiter->first - keyiter->first, - insiter->second.substr(sep+2))); - else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) || - (sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0)) - groupkeys.insert(std::make_pair(insiter->first - keyiter->first, insiter->second)); - } while(insiter++ != lastkeyiter); - - bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys)); - } -} - - -static Ogre::SkeletonPtr createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) -{ - /* We need to be a little aggressive here, since some NIFs have a crap-ton - * of nodes and Ogre only supports 256 bones. We will skip a skeleton if: - * There are no bones used for skinning, there are no controllers on non- - * NiTriShape nodes, there are no nodes named "AttachLight", and the tree - * consists of NiNode, NiTriShape, and RootCollisionNode types only. - */ - if(!node->boneTrafo) - { - if(node->recType == Nif::RC_NiTriShape) - return Ogre::SkeletonPtr(); - if(node->controller.empty() && node->name != "AttachLight") + virtual void setValue(Ogre::Real time) { - if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) + if(mRotations.mKeys.size() > 0) { - const Nif::NiNode *ninode = static_cast(node); - const Nif::NodeList &children = ninode->children; - for(size_t i = 0;i < children.length();i++) + if(time <= mRotations.mKeys.front().mTime) + mNode->setOrientation(mRotations.mKeys.front().mValue); + else if(time >= mRotations.mKeys.back().mTime) + mNode->setOrientation(mRotations.mKeys.back().mValue); + else { - if(!children[i].empty()) + Nif::QuaternionKeyList::VecType::const_iterator iter(mRotations.mKeys.begin()+1); + for(;iter != mRotations.mKeys.end();iter++) { - Ogre::SkeletonPtr skel = createSkeleton(name, group, children[i].getPtr()); - if(!skel.isNull()) - return skel; + if(iter->mTime < time) + continue; + + Nif::QuaternionKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + mNode->setOrientation(Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue)); + break; } } - return Ogre::SkeletonPtr(); } - } - } - - Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - return skelMgr.create(name, group, true, &sLoaders[name]); -} - -}; -NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders; - - -// Conversion of blend / test mode from NIF -static const char *getBlendFactor(int mode) -{ - switch(mode) - { - case 0: return "one"; - case 1: return "zero"; - case 2: return "src_colour"; - case 3: return "one_minus_src_colour"; - case 4: return "dest_colour"; - case 5: return "one_minus_dest_colour"; - case 6: return "src_alpha"; - case 7: return "one_minus_src_alpha"; - case 8: return "dest_alpha"; - case 9: return "one_minus_dest_alpha"; - case 10: return "src_alpha_saturate"; - } - std::cerr<< "Unexpected blend mode: "< MaterialMap; - -static void warn(const std::string &msg) -{ - std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; -} - -static void fail(const std::string &msg) -{ - std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; - abort(); -} - - -public: -static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String &name, const Ogre::String &group, - const Nif::NiTexturingProperty *texprop, - const Nif::NiMaterialProperty *matprop, - const Nif::NiAlphaProperty *alphaprop, - const Nif::NiVertexColorProperty *vertprop, - const Nif::NiZBufferProperty *zprop, - const Nif::NiSpecularProperty *specprop) -{ - Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); - Ogre::MaterialPtr material = matMgr.getByName(name); - if(!material.isNull()) - return name; - - Ogre::Vector3 ambient(1.0f); - Ogre::Vector3 diffuse(1.0f); - Ogre::Vector3 specular(0.0f); - Ogre::Vector3 emissive(0.0f); - float glossiness = 0.0f; - float alpha = 1.0f; - int alphaFlags = 0; - int alphaTest = 0; - int vertMode = 2; - //int lightMode = 1; - int depthFlags = 3; - // Default should be 1, but Bloodmoon's models are broken - int specFlags = 0; - Ogre::String texName; - - bool vertexColour = (shape->data->colors.size() != 0); - - // Texture - if(texprop && texprop->textures[0].inUse) - { - const Nif::NiSourceTexture *st = texprop->textures[0].texture.getPtr(); - if(st->external) - { - /* Bethesda at some point converted all their BSA - * textures from tga to dds for increased load speed, but all - * texture file name references were kept as .tga. - */ - static const char path[] = "textures\\"; - - texName = st->filename; - Misc::StringUtils::toLower(texName); - - if(texName.compare(0, sizeof(path)-1, path) != 0) - texName = path + texName; - - Ogre::String::size_type pos = texName.rfind('.'); - if(pos != Ogre::String::npos && texName.compare(pos, texName.length() - pos, ".dds") != 0) + if(mTranslations.mKeys.size() > 0) { - // since we know all (GOTY edition or less) textures end - // in .dds, we change the extension - texName.replace(pos, texName.length(), ".dds"); - - // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods) - // verify, and revert if false (this call succeeds quickly, but fails slowly) - if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + if(time <= mTranslations.mKeys.front().mTime) + mNode->setPosition(mTranslations.mKeys.front().mValue); + else if(time >= mTranslations.mKeys.back().mTime) + mNode->setPosition(mTranslations.mKeys.back().mValue); + else { - texName = st->filename; - Misc::StringUtils::toLower(texName); - if(texName.compare(0, sizeof(path)-1, path) != 0) - texName = path + texName; + Nif::Vector3KeyList::VecType::const_iterator iter(mTranslations.mKeys.begin()+1); + for(;iter != mTranslations.mKeys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::Vector3KeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + mNode->setPosition(last->mValue + ((iter->mValue - last->mValue)*a)); + break; + } + } + } + if(mScales.mKeys.size() > 0) + { + if(time <= mScales.mKeys.front().mTime) + mNode->setScale(Ogre::Vector3(mScales.mKeys.front().mValue)); + else if(time >= mScales.mKeys.back().mTime) + mNode->setScale(Ogre::Vector3(mScales.mKeys.back().mValue)); + else + { + Nif::FloatKeyList::VecType::const_iterator iter(mScales.mKeys.begin()+1); + for(;iter != mScales.mKeys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + mNode->setScale(Ogre::Vector3(last->mValue + ((iter->mValue - last->mValue)*a))); + break; + } } } } - else warn("Found internal texture, ignoring."); - } - - // Alpha modifiers - if(alphaprop) - { - alphaFlags = alphaprop->flags; - alphaTest = alphaprop->data.threshold; - } - - // Vertex color handling - if(vertprop) - { - vertMode = vertprop->data.vertmode; - // FIXME: Handle lightmode? - //lightMode = vertprop->data.lightmode; - } - - if(zprop) - { - depthFlags = zprop->flags; - // Depth function??? - } - - if(specprop) - { - specFlags = specprop->flags; - } - - // Material - if(matprop) - { - ambient = matprop->data.ambient; - diffuse = matprop->data.diffuse; - specular = matprop->data.specular; - emissive = matprop->data.emissive; - glossiness = matprop->data.glossiness; - alpha = matprop->data.alpha; - } - - Ogre::String matname = name; - if(matprop || !texName.empty()) - { - // Generate a hash out of all properties that can affect the material. - size_t h = 0; - boost::hash_combine(h, ambient.x); - boost::hash_combine(h, ambient.y); - boost::hash_combine(h, ambient.z); - boost::hash_combine(h, diffuse.x); - boost::hash_combine(h, diffuse.y); - boost::hash_combine(h, diffuse.z); - boost::hash_combine(h, specular.x); - boost::hash_combine(h, specular.y); - boost::hash_combine(h, specular.z); - boost::hash_combine(h, emissive.x); - boost::hash_combine(h, emissive.y); - boost::hash_combine(h, emissive.z); - boost::hash_combine(h, texName); - boost::hash_combine(h, vertexColour); - boost::hash_combine(h, alphaFlags); - boost::hash_combine(h, alphaTest); - boost::hash_combine(h, vertMode); - boost::hash_combine(h, depthFlags); - boost::hash_combine(h, specFlags); - - std::map::iterator itr = MaterialMap.find(h); - if (itr != MaterialMap.end()) - { - // a suitable material exists already - use it - return itr->second; - } - // not found, create a new one - MaterialMap.insert(std::make_pair(h, matname)); - } - - // No existing material like this. Create a new one. - sh::MaterialInstance* instance = sh::Factory::getInstance ().createMaterialInstance (matname, "openmw_objects_base"); - if(vertMode == 0 || !vertexColour) - { - instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1))); - instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); - instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1))); - instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("0"))); - } - else if(vertMode == 1) - { - instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1))); - instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); - instance->setProperty("emissive", sh::makeProperty(new sh::StringValue("vertexcolour"))); - instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("1"))); - } - else if(vertMode == 2) - { - instance->setProperty("ambient", sh::makeProperty(new sh::StringValue("vertexcolour"))); - instance->setProperty("diffuse", sh::makeProperty(new sh::StringValue("vertexcolour"))); - instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1))); - instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("2"))); - } - else - std::cerr<< "Unhandled vertex mode: "<setProperty("specular", sh::makeProperty( - new sh::Vector4(specular.x, specular.y, specular.z, glossiness))); - } - - instance->setProperty("diffuseMap", sh::makeProperty(texName)); - - if (vertexColour) - instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); - - // Add transparency if NiAlphaProperty was present - NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); - if (result.first) - { - alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ - alphaTest = result.second; - } - - if((alphaFlags&1)) - { - std::string blend_mode; - blend_mode += getBlendFactor((alphaFlags>>1)&0xf); - blend_mode += " "; - blend_mode += getBlendFactor((alphaFlags>>5)&0xf); - instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue(blend_mode))); - } - - if((alphaFlags>>9)&1) - { - std::string reject; - reject += getTestMode((alphaFlags>>10)&0x7); - reject += " "; - reject += Ogre::StringConverter::toString(alphaTest); - instance->setProperty("alpha_rejection", sh::makeProperty(new sh::StringValue(reject))); - } - else - instance->getMaterial()->setShadowCasterMaterial("openmw_shadowcaster_noalpha"); - - // Ogre usually only sorts if depth write is disabled, so we want "force" instead of "on" - instance->setProperty("transparent_sorting", sh::makeProperty(new sh::StringValue( - ((alphaFlags&1) && !((alphaFlags>>13)&1)) ? "force" : "off"))); - - instance->setProperty("depth_check", sh::makeProperty(new sh::StringValue((depthFlags&1) ? "on" : "off"))); - instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); - // depth_func??? - - sh::Factory::getInstance()._ensureMaterial(matname, "Default"); - return matname; -} + }; + typedef DefaultFunction Function; }; -std::map NIFMaterialLoader::MaterialMap; - -/** Manual resource loader for NIF meshes. This is the main class - responsible for translating the internal NIF mesh structure into - something Ogre can use. - */ -class NIFMeshLoader : Ogre::ManualResourceLoader +class UVController { - std::string mName; - std::string mGroup; - size_t mShapeIndex; - - void warn(const std::string &msg) +public: + class Value : public Ogre::ControllerValue { - std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; + private: + Ogre::MaterialPtr mMaterial; + Nif::FloatKeyList mUTrans; + Nif::FloatKeyList mVTrans; + Nif::FloatKeyList mUScale; + Nif::FloatKeyList mVScale; + + static float lookupValue(const Nif::FloatKeyList &keys, float time, float def) + { + if(keys.mKeys.size() == 0) + return def; + + if(time <= keys.mKeys.front().mTime) + return keys.mKeys.front().mValue; + + Nif::FloatKeyList::VecType::const_iterator iter(keys.mKeys.begin()+1); + for(;iter != keys.mKeys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.mKeys.back().mValue; + } + + public: + Value(const Ogre::MaterialPtr &material, Nif::NiUVData *data) + : mMaterial(material) + , mUTrans(data->mKeyList[0]) + , mVTrans(data->mKeyList[1]) + , mUScale(data->mKeyList[2]) + , mVScale(data->mKeyList[3]) + { } + + virtual Ogre::Real getValue() const + { + // Should not be called + return 1.0f; + } + + virtual void setValue(Ogre::Real value) + { + float uTrans = lookupValue(mUTrans, value, 0.0f); + float vTrans = lookupValue(mVTrans, value, 0.0f); + float uScale = lookupValue(mUScale, value, 1.0f); + float vScale = lookupValue(mVScale, value, 1.0f); + + Ogre::Material::TechniqueIterator techs = mMaterial->getTechniqueIterator(); + while(techs.hasMoreElements()) + { + Ogre::Technique *tech = techs.getNext(); + Ogre::Technique::PassIterator passes = tech->getPassIterator(); + while(passes.hasMoreElements()) + { + Ogre::Pass *pass = passes.getNext(); + Ogre::TextureUnitState *tex = pass->getTextureUnitState(0); + tex->setTextureScroll(uTrans, vTrans); + tex->setTextureScale(uScale, vScale); + } + } + } + }; + + typedef DefaultFunction Function; +}; + + + +/** Manual resource loader for NIF objects (meshes, particle systems, etc). + * This is the main class responsible for translating the internal NIF + * structures into something Ogre can use. + */ +class NIFObjectLoader +{ + static void warn(const std::string &msg) + { + std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl; } - void fail(const std::string &msg) + static void fail(const std::string &msg) { - std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; + std::cerr << "NIFObjectLoader: Fail: "<< msg << std::endl; abort(); } - // Convert NiTriShape to Ogre::SubMesh - void handleNiTriShape(Ogre::Mesh *mesh, Nif::NiTriShape const *shape, - const Nif::NiTexturingProperty *texprop, - const Nif::NiMaterialProperty *matprop, - const Nif::NiAlphaProperty *alphaprop, - const Nif::NiVertexColorProperty *vertprop, - const Nif::NiZBufferProperty *zprop, - const Nif::NiSpecularProperty *specprop) + static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) { - Ogre::SkeletonPtr skel; - const Nif::NiTriShapeData *data = shape->data.getPtr(); - const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); - std::vector srcVerts = data->vertices; - std::vector srcNorms = data->normals; - if(skin != NULL) + Ogre::ParticleEmitter *emitter = partsys->addEmitter("Point"); + emitter->setDirection(Ogre::Vector3(0.0f, 0.0f, std::cos(partctrl->verticalDir))); + emitter->setAngle(Ogre::Radian(partctrl->verticalAngle)); + emitter->setParticleVelocity(partctrl->velocity-partctrl->velocityRandom, + partctrl->velocity+partctrl->velocityRandom); + emitter->setEmissionRate(partctrl->emitRate); + emitter->setTimeToLive(partctrl->lifetime-partctrl->lifetimeRandom, + partctrl->lifetime+partctrl->lifetimeRandom); + + Nif::ExtraPtr e = partctrl->extra; + while(!e.empty()) { - // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be - // explicitly attached later. - mesh->setSkeletonName(mName); - - // Get the skeleton resource, so vertices can be transformed into the bones' initial state. - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - skel = skelMgr->getByName(mName); - - // Convert vertices and normals to bone space from bind position. It would be - // better to transform the bones into bind position, but there doesn't seem to - // be a reliable way to do that. - std::vector newVerts(srcVerts.size(), Ogre::Vector3(0.0f)); - std::vector newNorms(srcNorms.size(), Ogre::Vector3(0.0f)); - - const Nif::NiSkinData *data = skin->data.getPtr(); - const Nif::NodeList &bones = skin->bones; - for(size_t b = 0;b < bones.length();b++) + if(e->recType == Nif::RC_NiParticleGrowFade) { - Ogre::Bone *bone = skel->getBone(bones[b]->name); - Ogre::Matrix4 mat; - mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), - Ogre::Quaternion(data->bones[b].trafo.rotation)); - mat = bone->_getFullTransform() * mat; + 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) + { + // TODO: Implement (Ogre::RotationAffector?) + } + else if(e->recType == Nif::RC_NiParticleColorModifier) + { + // TODO: Implement (Ogre::ColourInterpolatorAffector?) + } + else if(e->recType == Nif::RC_NiGravity) + { + // TODO: Implement + } + else + warn("Unhandled particle modifier "+e->recName); + e = e->extra; + } + } - const std::vector &weights = data->bones[b].weights; - for(size_t i = 0;i < weights.size();i++) + static Ogre::ParticleSystem *createParticleSystem(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, Ogre::Entity *entitybase, + const Nif::Node *partnode) + { + const Nif::NiAutoNormalParticlesData *particledata = NULL; + if(partnode->recType == Nif::RC_NiAutoNormalParticles) + particledata = static_cast(partnode)->data.getPtr(); + else if(partnode->recType == Nif::RC_NiRotatingParticles) + particledata = static_cast(partnode)->data.getPtr(); + + Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem(); + try { + std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); + if(partnode->name.length() > 0) + fullname += "@type="+partnode->name; + Misc::StringUtils::toLower(fullname); + + const Nif::NiTexturingProperty *texprop = NULL; + const Nif::NiMaterialProperty *matprop = NULL; + const Nif::NiAlphaProperty *alphaprop = NULL; + const Nif::NiVertexColorProperty *vertprop = NULL; + const Nif::NiZBufferProperty *zprop = NULL; + const Nif::NiSpecularProperty *specprop = NULL; + const Nif::NiWireframeProperty *wireprop = NULL; + bool needTangents = false; + + partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, + texprop, matprop, alphaprop, + vertprop, zprop, specprop, + wireprop, needTangents)); + + partsys->setDefaultDimensions(particledata->particleRadius*2.0f, + particledata->particleRadius*2.0f); + partsys->setCullIndividually(false); + partsys->setParticleQuota(particledata->numParticles); + + Nif::ControllerPtr ctrl = partnode->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiParticleSystemController) { - size_t index = weights[i].vertex; - float weight = weights[i].weight; + const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); - newVerts.at(index) += (mat*srcVerts[index]) * weight; - if(newNorms.size() > index) + createParticleEmitterAffectors(partsys, partctrl); + if(!partctrl->emitter.empty() && !partsys->isAttached()) { - Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f); - vec4 = mat*vec4 * weight; - newNorms[index] += Ogre::Vector3(&vec4[0]); + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); + Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid); + entitybase->attachObjectToBone(trgtbone->getName(), partsys); } } + ctrl = ctrl->next; } - srcVerts = newVerts; - srcNorms = newNorms; - } - else - { - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - if(skelMgr->getByName(mName).isNull()) + if(!partsys->isAttached()) { - // No skinning and no skeleton, so just transform the vertices and - // normals into position. - Ogre::Matrix4 mat4 = shape->getWorldTransform(); - for(size_t i = 0;i < srcVerts.size();i++) - { - Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f); - vec4 = mat4*vec4; - srcVerts[i] = Ogre::Vector3(&vec4[0]); - } - for(size_t i = 0;i < srcNorms.size();i++) - { - Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f); - vec4 = mat4*vec4; - srcNorms[i] = Ogre::Vector3(&vec4[0]); - } + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); + Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid); + entitybase->attachObjectToBone(trgtbone->getName(), partsys); } } - - // Set the bounding box first - BoundsFinder bounds; - bounds.add(&srcVerts[0][0], srcVerts.size()); - if(!bounds.isValid()) - { - float v[3] = { 0.0f, 0.0f, 0.0f }; - bounds.add(&v[0], 1); - } - - mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f, - bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f)); - mesh->_setBoundingSphereRadius(bounds.getRadius()); - - // This function is just one long stream of Ogre-barf, but it works - // great. - Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareVertexBufferSharedPtr vbuf; - Ogre::HardwareIndexBufferSharedPtr ibuf; - Ogre::VertexBufferBinding *bind; - Ogre::VertexDeclaration *decl; - int nextBuf = 0; - - Ogre::SubMesh *sub = mesh->createSubMesh(); - - // Add vertices - sub->useSharedVertices = false; - sub->vertexData = new Ogre::VertexData(); - sub->vertexData->vertexStart = 0; - sub->vertexData->vertexCount = srcVerts.size(); - - decl = sub->vertexData->vertexDeclaration; - bind = sub->vertexData->vertexBufferBinding; - if(srcVerts.size()) - { - vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - srcVerts.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, - true); - vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true); - - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); - bind->setBinding(nextBuf++, vbuf); - } - - // Vertex normals - if(srcNorms.size()) - { - vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - srcNorms.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, - true); - vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true); - - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); - bind->setBinding(nextBuf++, vbuf); - } - - // Vertex colors - const std::vector &colors = data->colors; - if(colors.size()) - { - Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); - std::vector colorsRGB(colors.size()); - for(size_t i = 0;i < colorsRGB.size();i++) - { - Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]); - rs->convertColourValue(clr, &colorsRGB[i]); - } - vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), - colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, - true); - vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true); - decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); - bind->setBinding(nextBuf++, vbuf); - } - - // Texture UV coordinates - size_t numUVs = data->uvlist.size(); - if(numUVs) - { - size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); - vbuf = hwBufMgr->createVertexBuffer(elemSize, srcVerts.size()*numUVs, - Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true); - for(size_t i = 0;i < numUVs;i++) - { - const std::vector &uvlist = data->uvlist[i]; - vbuf->writeData(i*srcVerts.size()*elemSize, elemSize*srcVerts.size(), &uvlist[0], true); - decl->addElement(nextBuf, i*srcVerts.size()*elemSize, Ogre::VET_FLOAT2, - Ogre::VES_TEXTURE_COORDINATES, i); - } - bind->setBinding(nextBuf++, vbuf); - } - - // Triangle faces - const std::vector &srcIdx = data->triangles; - if(srcIdx.size()) - { - ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(), - Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); - ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true); - sub->indexData->indexBuffer = ibuf; - sub->indexData->indexCount = srcIdx.size(); - sub->indexData->indexStart = 0; - } - - // Assign bone weights for this TriShape - if(skin != NULL) - { - const Nif::NiSkinData *data = skin->data.getPtr(); - const Nif::NodeList &bones = skin->bones; - for(size_t i = 0;i < bones.length();i++) - { - Ogre::VertexBoneAssignment boneInf; - boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle(); - - const std::vector &weights = data->bones[i].weights; - for(size_t j = 0;j < weights.size();j++) - { - boneInf.vertexIndex = weights[j].vertex; - boneInf.weight = weights[j].weight; - sub->addBoneAssignment(boneInf); - } - } - } - - std::string matname = NIFMaterialLoader::getMaterial(shape, mesh->getName(), mGroup, - texprop, matprop, alphaprop, - vertprop, zprop, specprop); - if(matname.length() > 0) - sub->setMaterialName(matname); - } - - bool findTriShape(Ogre::Mesh *mesh, const Nif::Node *node, - const Nif::NiTexturingProperty *texprop, - const Nif::NiMaterialProperty *matprop, - const Nif::NiAlphaProperty *alphaprop, - const Nif::NiVertexColorProperty *vertprop, - const Nif::NiZBufferProperty *zprop, - const Nif::NiSpecularProperty *specprop) - { - // Scan the property list for material information - const Nif::PropertyList &proplist = node->props; - for(size_t i = 0;i < proplist.length();i++) - { - // Entries may be empty - if(proplist[i].empty()) - continue; - - const Nif::Property *pr = proplist[i].getPtr(); - if(pr->recType == Nif::RC_NiTexturingProperty) - texprop = static_cast(pr); - else if(pr->recType == Nif::RC_NiMaterialProperty) - matprop = static_cast(pr); - else if(pr->recType == Nif::RC_NiAlphaProperty) - alphaprop = static_cast(pr); - else if(pr->recType == Nif::RC_NiVertexColorProperty) - vertprop = static_cast(pr); - else if(pr->recType == Nif::RC_NiZBufferProperty) - zprop = static_cast(pr); - else if(pr->recType == Nif::RC_NiSpecularProperty) - specprop = static_cast(pr); - else - warn("Unhandled property type: "+pr->recName); - } - - if(node->recType == Nif::RC_NiTriShape && mShapeIndex == node->recIndex) - { - handleNiTriShape(mesh, dynamic_cast(node), texprop, matprop, alphaprop, vertprop, zprop, specprop); - return true; - } - - const Nif::NiNode *ninode = dynamic_cast(node); - if(ninode) - { - const Nif::NodeList &children = ninode->children; - for(size_t i = 0;i < children.length();i++) - { - if(!children[i].empty()) - { - if(findTriShape(mesh, children[i].getPtr(), texprop, matprop, alphaprop, vertprop, zprop, specprop)) - return true; - } - } - } - return false; + catch(std::exception &e) { + std::cerr<< "Particles exception: "<destroyParticleSystem(partsys); + partsys = NULL; + }; + return partsys; } - typedef std::map LoaderMap; - static LoaderMap sLoaders; - -public: - NIFMeshLoader() - { } - NIFMeshLoader(const std::string &name, const std::string &group) - : mName(name), mGroup(group), mShapeIndex(~(size_t)0) - { } - - virtual void loadResource(Ogre::Resource *resource) + static void createObjects(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, const Nif::Node *node, + ObjectList &objectlist, int flags=0) { - Ogre::Mesh *mesh = dynamic_cast(resource); - OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!"); - - Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); - if(mShapeIndex >= nif->numRecords()) - { - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - if(!skelMgr->getByName(mName).isNull()) - mesh->setSkeletonName(mName); - return; - } - - const Nif::Node *node = dynamic_cast(nif->getRecord(0)); - findTriShape(mesh, node, NULL, NULL, NULL, NULL, NULL, NULL); - } - - void createMeshes(const Nif::Node *node, MeshInfoList &meshes, int flags=0) - { - // Do not create meshes for the collision shape (includes all children) + // Do not create objects for the collision shape (includes all children) if(node->recType == Nif::RC_RootCollisionNode) return; - flags |= node->flags; - - // Marker objects: just skip the entire node + // Marker objects: just skip the entire node branch /// \todo don't do this in the editor if (node->name.find("marker") != std::string::npos) return; + flags |= node->flags; + Nif::ExtraPtr e = node->extra; while(!e.empty()) { - Nif::NiStringExtraData *sd; - if((sd=dynamic_cast(e.getPtr())) != NULL) + if(e->recType == Nif::RC_NiStringExtraData) { + const Nif::NiStringExtraData *sd = static_cast(e.getPtr()); // String markers may contain important information // affecting the entire subtree of this obj if(sd->string == "MRK") { - // Marker objects. These are only visible in the + // Marker objects. These meshes are only visible in the // editor. - return; + flags |= 0x80000000; } } e = e->extra; } - if(node->recType == Nif::RC_NiTriShape && !(flags&0x01)) // Not hidden + if(node->recType == Nif::RC_NiCamera) { - const Nif::NiTriShape *shape = dynamic_cast(node); + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mCameras.push_back(trgtbone); + } - Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex); + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiVisController) + { + const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); + + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + Ogre::ControllerValueRealPtr srcval; /* Filled in later */ + Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, false)); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiKeyframeController) + { + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + if(!key->data.empty()) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + Ogre::ControllerValueRealPtr srcval; /* Filled in later */ + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false)); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + } + ctrl = ctrl->next; + } + + if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) + { + const Nif::NiTriShape *shape = static_cast(node); + + std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex); if(shape->name.length() > 0) fullname += "@shape="+shape->name; - Misc::StringUtils::toLower(fullname); - Ogre::MeshPtr mesh = meshMgr.getByName(fullname); - if(mesh.isNull()) - { - NIFMeshLoader *loader = &sLoaders[fullname]; - *loader = *this; - loader->mShapeIndex = shape->recIndex; - mesh = meshMgr.createManual(fullname, mGroup, loader); - mesh->setAutoBuildEdgeLists(false); + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + if(meshMgr.getByName(fullname).isNull()) + NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex); + + Ogre::Entity *entity = sceneMgr->createEntity(fullname); + entity->setVisible(!(flags&0x01)); + + objectlist.mEntities.push_back(entity); + if(objectlist.mSkelBase) + { + if(entity->hasSkeleton()) + entity->shareSkeletonInstanceWith(objectlist.mSkelBase); + else + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity); + } } - meshes.push_back(MeshInfo(mesh->getName(), shape->name)); + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiUVController) + { + const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); + + const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial(); + Ogre::ControllerValueRealPtr srcval(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, true)); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + ctrl = ctrl->next; + } + } + + if(node->recType == Nif::RC_NiAutoNormalParticles || + node->recType == Nif::RC_NiRotatingParticles) + { + Ogre::ParticleSystem *partsys = createParticleSystem(name, group, sceneMgr, objectlist.mSkelBase, node); + if(partsys != NULL) + { + partsys->setVisible(!(flags&0x01)); + objectlist.mParticles.push_back(partsys); + } } const Nif::NiNode *ninode = dynamic_cast(node); @@ -1151,146 +596,94 @@ public: for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - createMeshes(children[i].getPtr(), meshes, flags); + createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags); } } } - void createEmptyMesh(const Nif::Node *node, MeshInfoList &meshes) + static void createSkelBase(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, const Nif::Node *node, + ObjectList &objectlist) { /* 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. */ - std::string fullname = mName; - Misc::StringUtils::toLower(fullname); - Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - Ogre::MeshPtr mesh = meshMgr.getByName(fullname); - if(mesh.isNull()) - { - NIFMeshLoader *loader = &sLoaders[fullname]; - *loader = *this; + if(meshMgr.getByName(name).isNull()) + NIFMeshLoader::createMesh(name, name, group, ~(size_t)0); - mesh = meshMgr.createManual(fullname, mGroup, loader); - mesh->setAutoBuildEdgeLists(false); + objectlist.mSkelBase = sceneMgr->createEntity(name); + objectlist.mEntities.push_back(objectlist.mSkelBase); + } + +public: + static void load(Ogre::SceneManager *sceneMgr, ObjectList &objectlist, const std::string &name, const std::string &group) + { + Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + if(nif->numRoots() < 1) + { + nif->warn("Found no root nodes in "+name+"."); + return; } - meshes.push_back(MeshInfo(mesh->getName(), node->name)); + + const Nif::Record *r = nif->getRoot(0); + assert(r != NULL); + + const Nif::Node *node = dynamic_cast(r); + if(node == NULL) + { + nif->warn("First root in "+name+" was not a node, but a "+ + r->recName+"."); + return; + } + + if(Ogre::SkeletonManager::getSingleton().resourceExists(name) || + !NIFSkeletonLoader::createSkeleton(name, group, node).isNull()) + { + // Create a base skeleton entity if this NIF needs one + createSkelBase(name, group, sceneMgr, node, objectlist); + } + createObjects(name, group, sceneMgr, node, objectlist); } }; -NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -typedef std::map MeshInfoMap; -static MeshInfoMap sMeshInfoMap; - -MeshInfoList Loader::load(const std::string &name, const std::string &group) +ObjectList Loader::createObjects(Ogre::SceneNode *parentNode, std::string name, const std::string &group) { - MeshInfoMap::const_iterator meshiter = sMeshInfoMap.find(name); - if(meshiter != sMeshInfoMap.end()) - return meshiter->second; - - MeshInfoList &meshes = sMeshInfoMap[name]; - Nif::NIFFile::ptr pnif = Nif::NIFFile::create(name); - Nif::NIFFile &nif = *pnif.get(); - if(nif.numRecords() < 1) - { - nif.warn("Found no NIF records in "+name+"."); - return meshes; - } - - // The first record is assumed to be the root node - Nif::Record const *r = nif.getRecord(0); - assert(r != NULL); - - Nif::Node const *node = dynamic_cast(r); - if(node == NULL) - { - nif.warn("First record in "+name+" was not a node, but a "+ - r->recName+"."); - return meshes; - } - - bool hasSkel = Ogre::SkeletonManager::getSingleton().resourceExists(name); - if(!hasSkel) - hasSkel = !NIFSkeletonLoader::createSkeleton(name, group, node).isNull(); - - NIFMeshLoader meshldr(name, group); - if(hasSkel) - meshldr.createEmptyMesh(node, meshes); - meshldr.createMeshes(node, meshes, 0); - - return meshes; -} - -EntityList Loader::createEntities(Ogre::SceneNode *parentNode, std::string name, const std::string &group) -{ - EntityList entitylist; + ObjectList objectlist; Misc::StringUtils::toLower(name); - MeshInfoList meshes = load(name, group); - if(meshes.size() == 0) - return entitylist; + NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group); - Ogre::SceneManager *sceneMgr = parentNode->getCreator(); - for(size_t i = 0;i < meshes.size();i++) + for(size_t i = 0;i < objectlist.mEntities.size();i++) { - entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].mMeshName)); - Ogre::Entity *entity = entitylist.mEntities.back(); - if(!entitylist.mSkelBase && entity->hasSkeleton()) - entitylist.mSkelBase = entity; + Ogre::Entity *entity = objectlist.mEntities[i]; + if(!entity->isAttached()) + parentNode->attachObject(entity); } - if(entitylist.mSkelBase) - { - parentNode->attachObject(entitylist.mSkelBase); - for(size_t i = 0;i < entitylist.mEntities.size();i++) - { - Ogre::Entity *entity = entitylist.mEntities[i]; - if(entity != entitylist.mSkelBase && entity->hasSkeleton()) - { - entity->shareSkeletonInstanceWith(entitylist.mSkelBase); - parentNode->attachObject(entity); - } - else if(entity != entitylist.mSkelBase) - entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity); - } - } - else - { - for(size_t i = 0;i < entitylist.mEntities.size();i++) - parentNode->attachObject(entitylist.mEntities[i]); - } - - return entitylist; + return objectlist; } -EntityList Loader::createEntities(Ogre::Entity *parent, const std::string &bonename, - Ogre::SceneNode *parentNode, - std::string name, const std::string &group) +ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneNode *parentNode, + std::string name, const std::string &group) { - EntityList entitylist; + ObjectList objectlist; Misc::StringUtils::toLower(name); - MeshInfoList meshes = load(name, group); - if(meshes.size() == 0) - return entitylist; + NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group); bool isskinned = false; - Ogre::SceneManager *sceneMgr = parentNode->getCreator(); - std::string filter = "@shape=tri "+bonename; - Misc::StringUtils::toLower(filter); - for(size_t i = 0;i < meshes.size();i++) + for(size_t i = 0;i < objectlist.mEntities.size();i++) { - Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].mMeshName); - if(!entitylist.mSkelBase) + Ogre::Entity *ent = objectlist.mEntities[i]; + if(objectlist.mSkelBase != ent && ent->hasSkeleton()) { - if(ent->hasSkeleton()) - entitylist.mSkelBase = ent; - } - else if(!isskinned && ent->hasSkeleton()) isskinned = true; - entitylist.mEntities.push_back(ent); + break; + } } Ogre::Vector3 scale(1.0f); @@ -1299,107 +692,49 @@ EntityList Loader::createEntities(Ogre::Entity *parent, const std::string &bonen if(isskinned) { - for(size_t i = 0;i < entitylist.mEntities.size();i++) + std::string filter = "@shape=tri "+bonename; + Misc::StringUtils::toLower(filter); + for(size_t i = 0;i < objectlist.mEntities.size();i++) { - Ogre::Entity *entity = entitylist.mEntities[i]; + Ogre::Entity *entity = objectlist.mEntities[i]; if(entity->hasSkeleton()) { - if(entity != entitylist.mSkelBase) - entity->shareSkeletonInstanceWith(entitylist.mSkelBase); - if(entity->getMesh()->getName().find(filter) != std::string::npos) + if(entity == objectlist.mSkelBase || + entity->getMesh()->getName().find(filter) != std::string::npos) parentNode->attachObject(entity); } else { - if(entity->getMesh()->getName().find(filter) != std::string::npos) - entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity); + if(entity->getMesh()->getName().find(filter) == std::string::npos) + entity->detachFromParent(); } } } else { - for(size_t i = 0;i < entitylist.mEntities.size();i++) + for(size_t i = 0;i < objectlist.mEntities.size();i++) { - Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entitylist.mEntities[i]); - tag->setScale(scale); + Ogre::Entity *entity = objectlist.mEntities[i]; + if(!entity->isAttached()) + { + Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity); + tag->setScale(scale); + } } } - return entitylist; + return objectlist; } -Ogre::SkeletonPtr Loader::getSkeleton(std::string name, const std::string &group) +ObjectList Loader::createObjectBase(Ogre::SceneManager *sceneMgr, std::string name, const std::string &group) { - Ogre::SkeletonPtr skel; + ObjectList objectlist; Misc::StringUtils::toLower(name); - skel = Ogre::SkeletonManager::getSingleton().getByName(name); - if(!skel.isNull()) - return skel; + NIFObjectLoader::load(sceneMgr, objectlist, name, group); - Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); - if(nif->numRecords() < 1) - { - nif->warn("Found no NIF records in "+name+"."); - return skel; - } - - // The first record is assumed to be the root node - const Nif::Record *r = nif->getRecord(0); - assert(r != NULL); - - const Nif::Node *node = dynamic_cast(r); - if(node == NULL) - { - nif->warn("First record in "+name+" was not a node, but a "+ - r->recName+"."); - return skel; - } - - return NIFSkeletonLoader::createSkeleton(name, group, node); + return objectlist; } - -/* More code currently not in use, from the old D source. This was - used in the first attempt at loading NIF meshes, where each submesh - in the file was given a separate bone in a skeleton. Unfortunately - the OGRE skeletons can't hold more than 256 bones, and some NIFs go - way beyond that. The code might be of use if we implement animated - submeshes like this (the part of the NIF that is animated is - usually much less than the entire file, but the method might still - not be water tight.) - -// Insert a raw RGBA image into the texture system. -extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, void *data) -{ - TexturePtr texture = TextureManager::getSingleton().createManual( - name, // name - "General", // group - TEX_TYPE_2D, // type - width, height, // width & height - 0, // number of mipmaps - PF_BYTE_RGBA, // pixel format - TU_DEFAULT); // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for - // textures updated very often (e.g. each frame) - - // Get the pixel buffer - HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer(); - - // Lock the pixel buffer and get a pixel box - pixelBuffer->lock(HardwareBuffer::HBL_NORMAL); // for best performance use HBL_DISCARD! - const PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - - void *dest = pixelBox.data; - - // Copy the data - memcpy(dest, data, width*height*4); - - // Unlock the pixel buffer - pixelBuffer->unlock(); -} - - -*/ - -} // nsmaepace NifOgre +} // namespace NifOgre diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index b8b2e3c007..fa5182aeaa 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -25,11 +25,11 @@ #define OPENMW_COMPONENTS_NIFOGRE_OGRENIFLOADER_HPP #include -#include -#include +#include #include #include +#include // FIXME: This namespace really doesn't do anything Nif-specific. Any supportable @@ -39,43 +39,58 @@ namespace NifOgre typedef std::multimap TextKeyMap; static const char sTextKeyExtraDataID[] = "TextKeyExtraData"; -struct EntityList { - std::vector mEntities; +struct ObjectList { Ogre::Entity *mSkelBase; + std::vector mEntities; + std::vector mParticles; - EntityList() : mSkelBase(0) + // We could actually have Ogre::Camera objects, but that means more + // maintenance when switching cameras. The information in the NiCamera node + // is pretty much useless too anyway. So instead, this is just a list of + // bones in the mSkelBase which are NiCamera nodes. + std::vector mCameras; + + std::vector > mControllers; + + ObjectList() : mSkelBase(0) { } }; -/* This holds a list of mesh names, the names of their parent nodes, and the offset - * from their parent nodes. */ -struct MeshInfo { - std::string mMeshName; - std::string mTargetNode; - - MeshInfo(const std::string &name, const std::string &target) - : mMeshName(name), mTargetNode(target) - { } -}; -typedef std::vector MeshInfoList; - class Loader { - static MeshInfoList load(const std::string &name, const std::string &group); +public: + static ObjectList createObjects(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneNode *parentNode, + std::string name, + const std::string &group="General"); + + static ObjectList createObjects(Ogre::SceneNode *parentNode, + std::string name, + const std::string &group="General"); + + static ObjectList createObjectBase(Ogre::SceneManager *sceneMgr, + std::string name, + const std::string &group="General"); +}; + +// FIXME: Should be with other general Ogre extensions. +template +class NodeTargetValue : public Ogre::ControllerValue +{ +protected: + Ogre::Node *mNode; public: - static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, - Ogre::SceneNode *parentNode, - std::string name, - const std::string &group="General"); + NodeTargetValue(Ogre::Node *target) : mNode(target) + { } - static EntityList createEntities(Ogre::SceneNode *parentNode, - std::string name, - const std::string &group="General"); - - static Ogre::SkeletonPtr getSkeleton(std::string name, const std::string &group="General"); + void setNode(Ogre::Node *target) + { mNode = target; } + Ogre::Node *getNode() const + { return mNode; } }; +typedef Ogre::SharedPtr > NodeTargetValueRealPtr; } diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp new file mode 100644 index 0000000000..e97e91ef03 --- /dev/null +++ b/components/nifogre/skeleton.cpp @@ -0,0 +1,351 @@ +#include "skeleton.hpp" + +#include +#include +#include +#include + +#include +#include + +namespace NifOgre +{ + +void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime) +{ + Ogre::Animation *anim = skel->createAnimation(name, stopTime); + + for(size_t i = 0;i < ctrls.size();i++) + { + const Nif::NiKeyframeController *kfc = ctrls[i]; + if(kfc->data.empty()) + continue; + const Nif::NiKeyframeData *kf = kfc->data.getPtr(); + + /* Get the keyframes and make sure they're sorted first to last */ + const Nif::QuaternionKeyList &quatkeys = kf->mRotations; + const Nif::Vector3KeyList &trankeys = kf->mTranslations; + const Nif::FloatKeyList &scalekeys = kf->mScales; + + Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); + Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); + Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); + + Ogre::Bone *bone = skel->getBone(targets[i]); + // NOTE: For some reason, Ogre doesn't like the node track ID being different from + // the bone ID + Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ? + anim->getNodeTrack(bone->getHandle()) : + anim->createNodeTrack(bone->getHandle(), bone); + + Ogre::Quaternion lastquat, curquat; + Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f); + Ogre::Vector3 lastscale(1.0f), curscale(1.0f); + if(quatiter != quatkeys.mKeys.end()) + lastquat = curquat = quatiter->mValue; + if(traniter != trankeys.mKeys.end()) + lasttrans = curtrans = traniter->mValue; + if(scaleiter != scalekeys.mKeys.end()) + lastscale = curscale = Ogre::Vector3(scaleiter->mValue); + + bool didlast = false; + while(!didlast) + { + float curtime = std::numeric_limits::max(); + + //Get latest time + if(quatiter != quatkeys.mKeys.end()) + curtime = std::min(curtime, quatiter->mTime); + if(traniter != trankeys.mKeys.end()) + curtime = std::min(curtime, traniter->mTime); + if(scaleiter != scalekeys.mKeys.end()) + curtime = std::min(curtime, scaleiter->mTime); + + curtime = std::max(curtime, startTime); + if(curtime >= stopTime) + { + didlast = true; + curtime = stopTime; + } + + // Get the latest quaternions, translations, and scales for the + // current time + while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) + { + lastquat = curquat; + if(++quatiter != quatkeys.mKeys.end()) + curquat = quatiter->mValue; + } + while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) + { + lasttrans = curtrans; + if(++traniter != trankeys.mKeys.end()) + curtrans = traniter->mValue; + } + while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) + { + lastscale = curscale; + if(++scaleiter != scalekeys.mKeys.end()) + curscale = Ogre::Vector3(scaleiter->mValue); + } + + Ogre::TransformKeyFrame *kframe; + kframe = nodetrack->createNodeKeyFrame(curtime); + if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) + kframe->setRotation(curquat); + else + { + Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1; + float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); + kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); + } + if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin()) + kframe->setTranslate(curtrans); + else + { + Nif::Vector3KeyList::VecType::const_iterator last = traniter-1; + float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); + kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); + } + if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin()) + kframe->setScale(curscale); + else + { + Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; + float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); + kframe->setScale(lastscale + ((curscale-lastscale)*diff)); + } + } + } + anim->optimise(); +} + + +TextKeyMap NIFSkeletonLoader::extractTextKeys(const Nif::NiTextKeyExtraData *tk) +{ + TextKeyMap textkeys; + for(size_t i = 0;i < tk->list.size();i++) + { + const std::string &str = tk->list[i].text; + std::string::size_type pos = 0; + while(pos < str.length()) + { + if(::isspace(str[pos])) + { + pos++; + continue; + } + + std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); + std::string result = str.substr(pos, nextpos-pos); + textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); + + pos = nextpos; + } + } + return textkeys; +} + +void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector &ctrls, Ogre::Bone *parent) +{ + Ogre::Bone *bone; + if(!skel->hasBone(node->name)) + bone = skel->createBone(node->name); + else + bone = skel->createBone(); + if(parent) parent->addChild(bone); + mNifToOgreHandleMap[node->recIndex] = bone->getHandle(); + + bone->setOrientation(node->trafo.rotation); + bone->setPosition(node->trafo.pos); + bone->setScale(Ogre::Vector3(node->trafo.scale)); + bone->setBindingPose(); + + if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */ + node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ + node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ + node->recType == Nif::RC_NiCamera || + node->recType == Nif::RC_NiAutoNormalParticles || + node->recType == Nif::RC_NiRotatingParticles + )) + warn("Unhandled "+node->recName+" "+node->name+" in "+skel->getName()); + + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiKeyframeController) + ctrls.push_back(static_cast(ctrl.getPtr())); + else if(!(ctrl->recType == Nif::RC_NiParticleSystemController || + ctrl->recType == Nif::RC_NiVisController || + ctrl->recType == Nif::RC_NiUVController + )) + warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); + ctrl = ctrl->next; + } + + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + textkeys = extractTextKeys(tk); + animroot = bone; + } + e = e->extra; + } + + const Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + const Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone); + } + } +} + +void NIFSkeletonLoader::loadResource(Ogre::Resource *resource) +{ + Ogre::Skeleton *skel = dynamic_cast(resource); + OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); + + Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName())); + const Nif::Node *node = static_cast(nif->getRoot(0)); + + std::vector ctrls; + Ogre::Bone *animroot = NULL; + TextKeyMap textkeys; + try { + buildBones(skel, node, animroot, textkeys, ctrls); + } + catch(std::exception &e) { + std::cerr<< "Exception while loading "<getName() < targets; + // TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file + if(ctrls.size() == 0) // No animations? Then we're done. + return; + + float maxtime = 0.0f; + for(size_t i = 0;i < ctrls.size();i++) + { + const Nif::NiKeyframeController *ctrl = ctrls[i]; + maxtime = std::max(maxtime, ctrl->timeStop); + Nif::Named *target = dynamic_cast(ctrl->target.getPtr()); + if(target != NULL) + targets.push_back(target->name); + } + + if(targets.size() != ctrls.size()) + { + warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+ + Ogre::StringConverter::toString(ctrls.size())+" controllers)"); + return; + } + + Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings(); + bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true)); + + std::string currentgroup; + TextKeyMap::const_iterator keyiter = textkeys.begin(); + for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++) + { + std::string::size_type sep = keyiter->second.find(':'); + if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) || + (sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) || + (sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0)) + continue; + currentgroup = keyiter->second.substr(0, sep); + + if(skel->hasAnimation(currentgroup)) + continue; + + TextKeyMap::const_iterator lastkeyiter = textkeys.end(); + while((--lastkeyiter)->first > keyiter->first) + { + if(lastkeyiter->second.find(':') == currentgroup.length() && + lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0) + break; + } + + buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first); + + TextKeyMap::const_iterator insiter(keyiter); + TextKeyMap groupkeys; + do { + sep = insiter->second.find(':'); + if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0) + groupkeys.insert(std::make_pair(insiter->first, insiter->second.substr(sep+2))); + else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) || + (sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0)) + groupkeys.insert(std::make_pair(insiter->first, insiter->second)); + } while(insiter++ != lastkeyiter); + + bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys)); + } +} + + +Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) +{ + /* We need to be a little aggressive here, since some NIFs have a crap-ton + * of nodes and Ogre only supports 256 bones. We will skip a skeleton if: + * There are no bones used for skinning, there are no controllers on non- + * NiTriShape nodes, there are no nodes named "AttachLight", and the tree + * consists of NiNode, NiTriShape, and RootCollisionNode types only. + */ + if(!node->boneTrafo) + { + if(node->recType == Nif::RC_NiTriShape) + return Ogre::SkeletonPtr(); + if(node->controller.empty() && node->name != "AttachLight") + { + if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) + { + const Nif::NiNode *ninode = static_cast(node); + const Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + { + Ogre::SkeletonPtr skel = createSkeleton(name, group, children[i].getPtr()); + if(!skel.isNull()) + return skel; + } + } + return Ogre::SkeletonPtr(); + } + } + } + + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + return skelMgr.create(name, group, true, &sLoaders[name]); +} + +// Looks up an Ogre Bone handle ID from a NIF's record index. Should only be +// used when the bone name is insufficient as this is a relatively slow lookup +int NIFSkeletonLoader::lookupOgreBoneHandle(const std::string &nifname, int idx) +{ + LoaderMap::const_iterator loader = sLoaders.find(nifname); + if(loader != sLoaders.end()) + { + std::map::const_iterator entry = loader->second.mNifToOgreHandleMap.find(idx); + if(entry != loader->second.mNifToOgreHandleMap.end()) + return entry->second; + } + throw std::runtime_error("Invalid NIF record lookup ("+nifname+", index "+Ogre::StringConverter::toString(idx)+")"); +} + +NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders; + +} diff --git a/components/nifogre/skeleton.hpp b/components/nifogre/skeleton.hpp new file mode 100644 index 0000000000..c69c2a12fe --- /dev/null +++ b/components/nifogre/skeleton.hpp @@ -0,0 +1,63 @@ +#ifndef COMPONENTS_NIFOGRE_SKELETON_HPP +#define COMPONENTS_NIFOGRE_SKELETON_HPP + +#include +#include +#include + +#include + +#include "ogrenifloader.hpp" + +namespace Nif +{ + class NiTextKeyExtraData; + class Node; + class NiKeyframeController; +} + +namespace NifOgre +{ + +/** Manual resource loader for NIF skeletons. This is the main class + responsible for translating the internal NIF skeleton structure into + something Ogre can use (includes animations and node TextKeyData). + */ +class NIFSkeletonLoader : public Ogre::ManualResourceLoader +{ + static void warn(const std::string &msg) + { + std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl; + } + + static void fail(const std::string &msg) + { + std::cerr << "NIFSkeletonLoader: Fail: "<< msg << std::endl; + abort(); + } + + static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime); + + static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk); + void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector &ctrls, Ogre::Bone *parent=NULL); + + // Lookup to retrieve an Ogre bone handle for a given Nif record index + std::map mNifToOgreHandleMap; + + typedef std::map LoaderMap; + static LoaderMap sLoaders; + +public: + void loadResource(Ogre::Resource *resource); + + static Ogre::SkeletonPtr createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node); + + // Looks up an Ogre Bone handle ID from a NIF's record index. Should only + // be used when the bone name is insufficient as this is a relatively slow + // lookup + static int lookupOgreBoneHandle(const std::string &nifname, int idx); +}; + +} + +#endif diff --git a/credits.txt b/credits.txt index f06377500a..6456e30a10 100644 --- a/credits.txt +++ b/credits.txt @@ -16,6 +16,7 @@ Alexander Nadeau (wareya) Alexander Olofsson (Ace) Artem Kotsynyak (greye) athile +Britt Mathis (galdor557) BrotherBrick Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) diff --git a/extern/shiny/Docs/Macros.dox b/extern/shiny/Docs/Macros.dox index 0578c447f3..c04ebd3747 100644 --- a/extern/shiny/Docs/Macros.dox +++ b/extern/shiny/Docs/Macros.dox @@ -107,6 +107,23 @@ \section properties Property retrieval / binding + \subsection shPropertyHasValue shPropertyHasValue + + Usage: \@shPropertyHasValue(property) + + Gets replaced by 1 if the property's value is not empty, or 0 if it is empty. + Useful for checking whether an optional texture is present or not. + + Example: + \code + #if @shPropertyHasValue(specularMap) + // specular mapping code + #endif + #if @shPropertyHasValue(normalMap) + // normal mapping code + #endif + \endcode + \subsection shUniformProperty shUniformProperty Usage: \@shUniformProperty<4f|3f|2f|1f|int> (uniformName, property) @@ -130,15 +147,11 @@ Example: \code - #if @shPropertyBool(has_normal_map) + #if @shPropertyBool(has_vertex_colors) ... #endif \endcode - \subsection shPropertyNotBool shPropertyNotBool - - Same as shPropertyBool, but inverts the result (i.e. when shPropertyBool would return 0, this returns 1 and vice versa) - \subsection shPropertyString shPropertyString Retrieve a string property of the pass that this shader belongs to diff --git a/extern/shiny/Main/MaterialInstance.hpp b/extern/shiny/Main/MaterialInstance.hpp index 000f9d60c9..36ba37ddba 100644 --- a/extern/shiny/Main/MaterialInstance.hpp +++ b/extern/shiny/Main/MaterialInstance.hpp @@ -25,6 +25,7 @@ namespace sh public: virtual void requestedConfiguration (MaterialInstance* m, const std::string& configuration) = 0; ///< called before creating virtual void createdConfiguration (MaterialInstance* m, const std::string& configuration) = 0; ///< called after creating + virtual ~MaterialInstanceListener(){} }; /** diff --git a/extern/shiny/Main/ShaderInstance.cpp b/extern/shiny/Main/ShaderInstance.cpp index 1539128aba..b44c63c322 100644 --- a/extern/shiny/Main/ShaderInstance.cpp +++ b/extern/shiny/Main/ShaderInstance.cpp @@ -194,13 +194,6 @@ namespace sh bool val = retrieveValue(value, properties->getContext()).get(); replaceValue = val ? "1" : "0"; } - else if (cmd == "shPropertyNotBool") // same as above, but inverts the result - { - std::string propertyName = args[0]; - PropertyValuePtr value = properties->getProperty(propertyName); - bool val = retrieveValue(value, properties->getContext()).get(); - replaceValue = val ? "0" : "1"; - } else if (cmd == "shPropertyString") { std::string propertyName = args[0]; @@ -214,6 +207,14 @@ namespace sh std::string value = retrieveValue(properties->getProperty(propertyName), properties->getContext()).get(); replaceValue = (value == comparedAgainst) ? "1" : "0"; } + else if (isCmd(source, pos, "@shPropertyHasValue")) + { + assert(args.size() == 1); + std::string propertyName = args[0]; + PropertyValuePtr value = properties->getProperty(propertyName); + std::string val = retrieveValue(value, properties->getContext()).get(); + replaceValue = (val.empty() ? "0" : "1"); + } else throw std::runtime_error ("unknown command \"" + cmd + "\""); source.replace(pos, (end+1)-pos, replaceValue); diff --git a/extern/shiny/Main/ShaderSet.cpp b/extern/shiny/Main/ShaderSet.cpp index 413d7d1a26..628e0acee7 100644 --- a/extern/shiny/Main/ShaderSet.cpp +++ b/extern/shiny/Main/ShaderSet.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "Factory.hpp" @@ -26,6 +27,10 @@ namespace sh std::ifstream stream(sourceFile.c_str(), std::ifstream::in); std::stringstream buffer; + boost::filesystem::path p (sourceFile); + p = p.branch_path(); + mBasePath = p.string(); + buffer << stream.rdbuf(); stream.close(); mSource = buffer.str(); @@ -52,6 +57,12 @@ namespace sh size_t start = currentToken.find('(')+1; mGlobalSettings.push_back(currentToken.substr(start, currentToken.find(')')-start)); } + else if (boost::starts_with(currentToken, "@shPropertyHasValue")) + { + assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos)); + size_t start = currentToken.find('(')+1; + mPropertiesToExist.push_back(currentToken.substr(start, currentToken.find(')')-start)); + } else if (boost::starts_with(currentToken, "@shPropertyEqual")) { assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos) @@ -135,6 +146,11 @@ namespace sh { boost::hash_combine(seed, retrieveValue(currentGlobalSettings->getProperty(*it), NULL).get()); } + for (std::vector::iterator it = mPropertiesToExist.begin(); it != mPropertiesToExist.end(); ++it) + { + std::string v = retrieveValue(properties->getProperty(*it), properties->getContext()).get(); + boost::hash_combine(seed, static_cast(v != "")); + } boost::hash_combine(seed, static_cast(Factory::getInstance().getCurrentLanguage())); return seed; } diff --git a/extern/shiny/Main/ShaderSet.hpp b/extern/shiny/Main/ShaderSet.hpp index a423b6779f..b21278ac99 100644 --- a/extern/shiny/Main/ShaderSet.hpp +++ b/extern/shiny/Main/ShaderSet.hpp @@ -53,6 +53,10 @@ namespace sh std::vector mGlobalSettings; ///< names of the global settings that affect the shader source std::vector mProperties; ///< names of the per-material properties that affect the shader source + std::vector mPropertiesToExist; + ///< same as mProperties, however in this case, it is only relevant if the property is empty or not + /// (we don't care about the value) + ShaderInstanceMap mInstances; ///< maps permutation ID (generated from the properties) to \a ShaderInstance void parse(); ///< find out which properties and global settings affect the shader source diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index 9e65b516b7..9b2325744e 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -44,6 +44,9 @@ set(MATERIAL_FILES watersim_common.h watersim.mat watersim.shaderset + mygui.mat + mygui.shader + mygui.shaderset ) copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water "${OpenMW_BINARY_DIR}/resources/water/" "${WATER_FILES}") diff --git a/files/materials/mygui.mat b/files/materials/mygui.mat new file mode 100644 index 0000000000..78cba3f897 --- /dev/null +++ b/files/materials/mygui.mat @@ -0,0 +1,25 @@ +material MyGUI/NoTexture +{ + pass + { + vertex_program mygui_vertex + fragment_program mygui_fragment + shader_properties + { + has_texture false + } + } +} + +material MyGUI/OneTexture +{ + pass + { + vertex_program mygui_vertex + fragment_program mygui_fragment + shader_properties + { + has_texture true + } + } +} diff --git a/files/materials/mygui.shader b/files/materials/mygui.shader new file mode 100644 index 0000000000..014558d972 --- /dev/null +++ b/files/materials/mygui.shader @@ -0,0 +1,45 @@ +#include "core.h" + +#define TEXTURE @shPropertyBool(has_texture) + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM +#if TEXTURE + shVertexInput(float2, uv0) + shOutput(float2, UV) +#endif + shColourInput(float4) + shOutput(float4, colourPassthrough) + + SH_START_PROGRAM + { + shOutputPosition = float4(shInputPosition.xyz, 1.f); +#if TEXTURE + UV.xy = uv0; +#endif + colourPassthrough = colour; + } + +#else + + + SH_BEGIN_PROGRAM + +#if TEXTURE + shSampler2D(diffuseMap) + shInput(float2, UV) +#endif + + shInput(float4, colourPassthrough) + + SH_START_PROGRAM + { +#if TEXTURE + shOutputColour(0) = shSample(diffuseMap, UV.xy) * colourPassthrough; +#else + shOutputColour(0) = colourPassthrough; +#endif + } + +#endif diff --git a/files/materials/mygui.shaderset b/files/materials/mygui.shaderset new file mode 100644 index 0000000000..980cd4caf4 --- /dev/null +++ b/files/materials/mygui.shaderset @@ -0,0 +1,15 @@ +shader_set mygui_vertex +{ + source mygui.shader + type vertex + profiles_cg vs_2_0 vp40 arbvp1 + profiles_hlsl vs_3_0 vs_2_0 +} + +shader_set mygui_fragment +{ + source mygui.shader + type fragment + profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 + profiles_hlsl ps_3_0 ps_2_0 +} diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 5e18a666a7..b9277914bd 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -6,12 +6,19 @@ material openmw_objects_base emissive 0.0 0.0 0.0 vertmode 0 diffuseMap black.png + normalMap + emissiveMap + use_emissive_map false + use_detail_map false + emissiveMapUVSet 0 + detailMapUVSet 0 - is_transparent false // real transparency, alpha rejection doesn't count here scene_blend default depth_write default + depth_check default alpha_rejection default transparent_sorting default + polygon_mode default pass { @@ -21,7 +28,11 @@ material openmw_objects_base shader_properties { vertexcolor_mode $vertmode - is_transparent $is_transparent + normalMap $normalMap + emissiveMapUVSet $emissiveMapUVSet + detailMapUVSet $detailMapUVSet + emissiveMap $emissiveMap + detailMap $detailMap } diffuse $diffuse @@ -31,12 +42,36 @@ material openmw_objects_base scene_blend $scene_blend alpha_rejection $alpha_rejection depth_write $depth_write + depth_check $depth_check transparent_sorting $transparent_sorting + polygon_mode $polygon_mode texture_unit diffuseMap { direct_texture $diffuseMap create_in_ffp true + tex_coord_set $emissiveMapUVSet + } + + texture_unit normalMap + { + texture $normalMap + } + + texture_unit emissiveMap + { + create_in_ffp $use_emissive_map + colour_op add + direct_texture $emissiveMap + tex_coord_set $emissiveMapUVSet + } + + texture_unit detailMap + { + create_in_ffp $use_detail_map + colour_op_ex modulate_x2 src_current src_texture + direct_texture $detailMap + tex_coord_set $detailMapUVSet } texture_unit shadowMap0 diff --git a/files/materials/objects.shader b/files/materials/objects.shader index c8616e9d1e..acabde10e6 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -14,13 +14,20 @@ #define NEED_DEPTH #endif +#define NORMAL_MAP @shPropertyHasValue(normalMap) +#define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) +#define DETAIL_MAP @shPropertyHasValue(detailMap) + +// right now we support 2 UV sets max. implementing them is tedious, and we're probably not going to need more +#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet)) + +// if normal mapping is enabled, we force pixel lighting +#define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap)) #define UNDERWATER @shGlobalSettingBool(render_refraction) #define VERTEXCOLOR_MODE @shPropertyString(vertexcolor_mode) -#define VERTEX_LIGHTING 1 - #define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix) #ifdef SH_VERTEX_SHADER @@ -30,6 +37,8 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, textureMatrix0) @shAutoConstant(textureMatrix0, texture_matrix, 0) + #if (VIEWPROJ_FIX) || (SHADOWS) shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) #endif @@ -40,8 +49,22 @@ #endif shVertexInput(float2, uv0) - shOutput(float2, UV) +#if SECOND_UV_SET + shVertexInput(float2, uv1) +#endif + shOutput(float4, UV) + shNormalInput(float4) + +#if NORMAL_MAP + shTangentInput(float4) + shOutput(float3, tangentPassthrough) +#endif + +#if !VERTEX_LIGHTING + shOutput(float3, normalPassthrough) +#endif + #ifdef NEED_DEPTH shOutput(float, depthPassthrough) #endif @@ -52,6 +75,10 @@ shColourInput(float4) #endif +#if VERTEXCOLOR_MODE != 0 && !VERTEX_LIGHTING + shOutput(float4, colourPassthrough) +#endif + #if VERTEX_LIGHTING shUniform(float, lightCount) @shAutoConstant(lightCount, light_count) shUniform(float4, lightPosition[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightPosition, light_position_view_space_array, @shGlobalSettingString(num_lights)) @@ -93,7 +120,21 @@ SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + + UV.xy = shMatrixMult (textureMatrix0, float4(uv0,0,1)).xy; +#if SECOND_UV_SET + UV.zw = uv1; +#endif + +#if NORMAL_MAP + tangentPassthrough = tangent.xyz; +#endif +#if !VERTEX_LIGHTING + normalPassthrough = normal.xyz; +#endif +#if VERTEXCOLOR_MODE != 0 && !VERTEX_LIGHTING + colourPassthrough = colour; +#endif #ifdef NEED_DEPTH @@ -188,15 +229,39 @@ #endif SH_BEGIN_PROGRAM - shSampler2D(diffuseMap) - shInput(float2, UV) + shSampler2D(diffuseMap) + +#if NORMAL_MAP + shSampler2D(normalMap) +#endif + +#if EMISSIVE_MAP + shSampler2D(emissiveMap) +#endif + +#if DETAIL_MAP + shSampler2D(detailMap) +#endif + + shInput(float4, UV) + +#if NORMAL_MAP + shInput(float3, tangentPassthrough) +#endif +#if !VERTEX_LIGHTING + shInput(float3, normalPassthrough) +#endif #ifdef NEED_DEPTH shInput(float, depthPassthrough) #endif shInput(float3, objSpacePositionPassthrough) - + +#if VERTEXCOLOR_MODE != 0 && !VERTEX_LIGHTING + shInput(float4, colourPassthrough) +#endif + #if FOG shUniform(float3, fogColour) @shAutoConstant(fogColour, fog_colour) shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) @@ -222,23 +287,106 @@ #if (UNDERWATER) || (FOG) shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) - shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) + shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) #endif #if UNDERWATER - shUniform(float, waterLevel) @shSharedParameter(waterLevel) + shUniform(float, waterLevel) @shSharedParameter(waterLevel) shUniform(float, waterEnabled) @shSharedParameter(waterEnabled) #endif #if VERTEX_LIGHTING shInput(float4, lightResult) shInput(float3, directionalResult) +#else + shUniform(float, lightCount) @shAutoConstant(lightCount, light_count) + shUniform(float4, lightPosition[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightPosition, light_position_view_space_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightDiffuse[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightDiffuse, light_diffuse_colour_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightAttenuation[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightAttenuation, light_attenuation_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) + shUniform(float4x4, worldView) @shAutoConstant(worldView, worldview_matrix) + #if VERTEXCOLOR_MODE != 2 + shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) + #endif + #if VERTEXCOLOR_MODE != 2 + shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) + #endif + #if VERTEXCOLOR_MODE != 1 + shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + #endif #endif SH_START_PROGRAM { - shOutputColour(0) = shSample(diffuseMap, UV); - + shOutputColour(0) = shSample(diffuseMap, UV.xy); + +#if DETAIL_MAP +#if @shPropertyString(detailMapUVSet) + shOutputColour(0) *= shSample(detailMap, UV.zw)*2; +#else + shOutputColour(0) *= shSample(detailMap, UV.xy)*2; +#endif +#endif + +#if NORMAL_MAP + float3 normal = normalPassthrough; + float3 binormal = cross(tangentPassthrough.xyz, normal.xyz); + float3x3 tbn = float3x3(tangentPassthrough.xyz, binormal, normal.xyz); + + #if SH_GLSL + tbn = transpose(tbn); + #endif + + float3 TSnormal = shSample(normalMap, UV.xy).xyz * 2 - 1; + + normal = normalize (shMatrixMult( transpose(tbn), TSnormal )); +#endif + +#if !VERTEX_LIGHTING + float3 viewPos = shMatrixMult(worldView, float4(objSpacePositionPassthrough,1)).xyz; + float3 viewNormal = normalize(shMatrixMult(worldView, float4(normal.xyz, 0)).xyz); + + float3 lightDir; + float d; + float4 lightResult = float4(0,0,0,1); + @shForeach(@shGlobalSettingString(num_lights)) + lightDir = lightPosition[@shIterator].xyz - (viewPos * lightPosition[@shIterator].w); + d = length(lightDir); + lightDir = normalize(lightDir); + +#if VERTEXCOLOR_MODE == 2 + lightResult.xyz += colourPassthrough.xyz * lightDiffuse[@shIterator].xyz + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * max(dot(viewNormal.xyz, lightDir), 0); +#else + lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * max(dot(viewNormal.xyz, lightDir), 0); +#endif + +#if @shIterator == 0 + float3 directionalResult = lightResult.xyz; +#endif + + @shEndForeach + + +#if VERTEXCOLOR_MODE == 2 + lightResult.xyz += lightAmbient.xyz * colourPassthrough.xyz + materialEmissive.xyz; + lightResult.a *= colourPassthrough.a; +#endif +#if VERTEXCOLOR_MODE == 1 + lightResult.xyz += lightAmbient.xyz * materialAmbient.xyz + colourPassthrough.xyz; +#endif +#if VERTEXCOLOR_MODE == 0 + lightResult.xyz += lightAmbient.xyz * materialAmbient.xyz + materialEmissive.xyz; +#endif + +#if VERTEXCOLOR_MODE != 2 + lightResult.a *= materialDiffuse.a; +#endif +#endif + // shadows only for the first (directional) light #if SHADOWS float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); @@ -275,7 +423,7 @@ #if FOG float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); - + #if UNDERWATER shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, UNDERWATER_COLOUR, shSaturate(length(waterEyePos-worldPos) / VISIBILITY)); @@ -283,6 +431,14 @@ shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); #endif +#endif + +#if EMISSIVE_MAP + #if @shPropertyString(emissiveMapUVSet) + shOutputColour(0).xyz += shSample(emissiveMap, UV.zw).xyz; + #else + shOutputColour(0).xyz += shSample(emissiveMap, UV.xy).xyz; + #endif #endif // prevent negative colour output (for example with negative lights) diff --git a/files/materials/shadowcaster.shader b/files/materials/shadowcaster.shader index b312d414c5..b992366a7e 100644 --- a/files/materials/shadowcaster.shader +++ b/files/materials/shadowcaster.shader @@ -45,8 +45,7 @@ // use alpha channel of the first texture float alpha = shSample(texture1, UV).a; - // discard if alpha is less than 0.5 - if (alpha < 1.0) + if (alpha < 0.5) discard; #endif diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index c73b582f8d..de90a6cf61 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -1,6 +1,6 @@ #include "core.h" -#define IS_FIRST_PASS 1 +#define IS_FIRST_PASS (@shPropertyString(pass_index) == 0) #define FOG @shGlobalSettingBool(fog) @@ -23,6 +23,9 @@ #define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix) +#if !IS_FIRST_PASS +// This is not the first pass. +#endif #if NEED_DEPTH @shAllocatePassthrough(1, depth) @@ -222,7 +225,11 @@ float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); #endif - + +#if !IS_FIRST_PASS +float combinedAlpha = 0.f; +#endif + // Layer calculations @shForeach(@shPropertyString(num_blendmaps)) float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); @@ -232,12 +239,20 @@ @shForeach(@shPropertyString(num_layers)) -#if IS_FIRST_PASS == 1 && @shIterator == 0 - // first layer of first pass doesn't need a blend map +#if IS_FIRST_PASS + #if @shIterator == 0 + // first layer of first pass is the base layer and doesn't need a blend map albedo = shSample(diffuseMap0, UV * 10).rgb; -#else + #else albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); - + #endif +#else + #if @shIterator == 0 + albedo = shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator); + #else + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + #endif + combinedAlpha += blendValues@shPropertyString(blendmap_component_@shIterator); #endif @shEndForeach @@ -325,6 +340,12 @@ // prevent negative colour output (for example with negative lights) shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); + +#if IS_FIRST_PASS + shOutputColour(0).a = 1; +#else + shOutputColour(0).a = min(combinedAlpha, 1.f); +#endif } #endif diff --git a/files/mygui/openmw_enchanting_dialog.layout b/files/mygui/openmw_enchanting_dialog.layout index 41b8ffa938..f64d21deaa 100644 --- a/files/mygui/openmw_enchanting_dialog.layout +++ b/files/mygui/openmw_enchanting_dialog.layout @@ -97,11 +97,11 @@ - + - + diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 7b831d32c5..f71fa4320b 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -323,7 +323,8 @@ namespace Physic mHeightFieldMap [name] = hf; - dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_ActorInternal|CollisionType_ActorExternal); + dynamicsWorld->addRigidBody(body,CollisionType_World|CollisionType_Raycasting, + CollisionType_World|CollisionType_ActorInternal|CollisionType_ActorExternal|CollisionType_Raycasting); } void PhysicEngine::removeHeightField(int x, int y) diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index c9b5614008..f9117586fe 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -2,11 +2,18 @@ #include #include +#include #include +#include +#include + using namespace OEngine::GUI; +namespace MyGUI +{ + /* * As of MyGUI 3.2.0, MyGUI::OgreDataManager::isDataExist is unnecessarily complex * this override fixes the resulting performance issue. @@ -20,6 +27,532 @@ public: } }; + +/* + * As of MyGUI 3.2.0, rendering with shaders is not supported. + * We definitely need this though to run in GL3 core / DX11 at all. + * To make matters worse, subclassing OgreRenderManager doesn't seem to be possible here. :/ + */ +class ShaderBasedRenderManager : public RenderManager, + public IRenderTarget, + public Ogre::WindowEventListener, + public Ogre::RenderQueueListener, + public Ogre::RenderSystem::Listener +{ + // флаг для обновления всех и вся + bool mUpdate; + + IntSize mViewSize; + + Ogre::SceneManager* mSceneManager; + + VertexColourType mVertexFormat; + + // окно, на которое мы подписываемся для изменения размеров + Ogre::RenderWindow* mWindow; + + // вьюпорт, с которым работает система + unsigned short mActiveViewport; + + Ogre::RenderSystem* mRenderSystem; + Ogre::TextureUnitState::UVWAddressingMode mTextureAddressMode; + Ogre::LayerBlendModeEx mColorBlendMode, mAlphaBlendMode; + + RenderTargetInfo mInfo; + + typedef std::map MapTexture; + MapTexture mTextures; + + bool mIsInitialise; + bool mManualRender; + size_t mCountBatch; + + // ADDED + Ogre::GpuProgram* mVertexProgramNoTexture; + Ogre::GpuProgram* mVertexProgramOneTexture; + Ogre::GpuProgram* mFragmentProgramNoTexture; + Ogre::GpuProgram* mFragmentProgramOneTexture; + +public: + ShaderBasedRenderManager& getInstance() + { + return *getInstancePtr(); + } + ShaderBasedRenderManager* getInstancePtr() + { + return static_cast(RenderManager::getInstancePtr()); + } + + ShaderBasedRenderManager() : + mUpdate(false), + mSceneManager(nullptr), + mWindow(nullptr), + mActiveViewport(0), + mRenderSystem(nullptr), + mIsInitialise(false), + mManualRender(false), + mCountBatch(0) + { + } + + void initialise(Ogre::RenderWindow* _window, Ogre::SceneManager* _scene) + { + MYGUI_PLATFORM_ASSERT(!mIsInitialise, getClassTypeName() << " initialised twice"); + MYGUI_PLATFORM_LOG(Info, "* Initialise: " << getClassTypeName()); + + mColorBlendMode.blendType = Ogre::LBT_COLOUR; + mColorBlendMode.source1 = Ogre::LBS_TEXTURE; + mColorBlendMode.source2 = Ogre::LBS_DIFFUSE; + mColorBlendMode.operation = Ogre::LBX_MODULATE; + + mAlphaBlendMode.blendType = Ogre::LBT_ALPHA; + mAlphaBlendMode.source1 = Ogre::LBS_TEXTURE; + mAlphaBlendMode.source2 = Ogre::LBS_DIFFUSE; + mAlphaBlendMode.operation = Ogre::LBX_MODULATE; + + mTextureAddressMode.u = Ogre::TextureUnitState::TAM_CLAMP; + mTextureAddressMode.v = Ogre::TextureUnitState::TAM_CLAMP; + mTextureAddressMode.w = Ogre::TextureUnitState::TAM_CLAMP; + + mSceneManager = nullptr; + mWindow = nullptr; + mUpdate = false; + mRenderSystem = nullptr; + mActiveViewport = 0; + + Ogre::Root* root = Ogre::Root::getSingletonPtr(); + if (root != nullptr) + setRenderSystem(root->getRenderSystem()); + setRenderWindow(_window); + setSceneManager(_scene); + + // ADDED + sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); + mVertexProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getVertexProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); + mVertexProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getVertexProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); + mFragmentProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getFragmentProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); + mFragmentProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getFragmentProgram()->_getBindingDelegate(); + + + + MYGUI_PLATFORM_LOG(Info, getClassTypeName() << " successfully initialized"); + mIsInitialise = true; + } + + void shutdown() + { + MYGUI_PLATFORM_ASSERT(mIsInitialise, getClassTypeName() << " is not initialised"); + MYGUI_PLATFORM_LOG(Info, "* Shutdown: " << getClassTypeName()); + + destroyAllResources(); + + setSceneManager(nullptr); + setRenderWindow(nullptr); + setRenderSystem(nullptr); + + MYGUI_PLATFORM_LOG(Info, getClassTypeName() << " successfully shutdown"); + mIsInitialise = false; + } + + void setRenderSystem(Ogre::RenderSystem* _render) + { + // отписываемся + if (mRenderSystem != nullptr) + { + mRenderSystem->removeListener(this); + mRenderSystem = nullptr; + } + + mRenderSystem = _render; + + // подписываемся на рендер евент + if (mRenderSystem != nullptr) + { + mRenderSystem->addListener(this); + + // формат цвета в вершинах + Ogre::VertexElementType vertex_type = mRenderSystem->getColourVertexElementType(); + if (vertex_type == Ogre::VET_COLOUR_ARGB) + mVertexFormat = VertexColourType::ColourARGB; + else if (vertex_type == Ogre::VET_COLOUR_ABGR) + mVertexFormat = VertexColourType::ColourABGR; + + updateRenderInfo(); + } + } + + Ogre::RenderSystem* getRenderSystem() + { + return mRenderSystem; + } + + void setRenderWindow(Ogre::RenderWindow* _window) + { + // отписываемся + if (mWindow != nullptr) + { + Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this); + mWindow = nullptr; + } + + mWindow = _window; + + if (mWindow != nullptr) + { + Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this); + windowResized(mWindow); + } + } + + void setSceneManager(Ogre::SceneManager* _scene) + { + if (nullptr != mSceneManager) + { + mSceneManager->removeRenderQueueListener(this); + mSceneManager = nullptr; + } + + mSceneManager = _scene; + + if (nullptr != mSceneManager) + { + mSceneManager->addRenderQueueListener(this); + } + } + + void setActiveViewport(unsigned short _num) + { + mActiveViewport = _num; + + if (mWindow != nullptr) + { + Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this); + Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this); + + // рассылка обновлений + windowResized(mWindow); + } + } + + void renderQueueStarted(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& skipThisInvocation) + { + Gui* gui = Gui::getInstancePtr(); + if (gui == nullptr) + return; + + if (Ogre::RENDER_QUEUE_OVERLAY != queueGroupId) + return; + + Ogre::Viewport* viewport = mSceneManager->getCurrentViewport(); + if (nullptr == viewport + || !viewport->getOverlaysEnabled()) + return; + + if (mWindow->getNumViewports() <= mActiveViewport + || viewport != mWindow->getViewport(mActiveViewport)) + return; + + mCountBatch = 0; + + static Timer timer; + static unsigned long last_time = timer.getMilliseconds(); + unsigned long now_time = timer.getMilliseconds(); + unsigned long time = now_time - last_time; + + onFrameEvent((float)((double)(time) / (double)1000)); + + last_time = now_time; + + //begin(); + setManualRender(true); + onRenderToTarget(this, mUpdate); + //end(); + + // сбрасываем флаг + mUpdate = false; + } + + void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation) + { + } + + void eventOccurred(const Ogre::String& eventName, const Ogre::NameValuePairList* parameters) + { + if (eventName == "DeviceLost") + { + } + else if (eventName == "DeviceRestored") + { + // обновить всех + mUpdate = true; + } + } + + IVertexBuffer* createVertexBuffer() + { + return new OgreVertexBuffer(); + } + + void destroyVertexBuffer(IVertexBuffer* _buffer) + { + delete _buffer; + } + + // для оповещений об изменении окна рендера + void windowResized(Ogre::RenderWindow* _window) + { + if (_window->getNumViewports() > mActiveViewport) + { + Ogre::Viewport* port = _window->getViewport(mActiveViewport); +#if OGRE_VERSION >= MYGUI_DEFINE_VERSION(1, 7, 0) && OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0 + Ogre::OrientationMode orient = port->getOrientationMode(); + if (orient == Ogre::OR_DEGREE_90 || orient == Ogre::OR_DEGREE_270) + mViewSize.set(port->getActualHeight(), port->getActualWidth()); + else + mViewSize.set(port->getActualWidth(), port->getActualHeight()); +#else + mViewSize.set(port->getActualWidth(), port->getActualHeight()); +#endif + + // обновить всех + mUpdate = true; + + updateRenderInfo(); + + onResizeView(mViewSize); + } + } + + void updateRenderInfo() + { + if (mRenderSystem != nullptr) + { + mInfo.maximumDepth = mRenderSystem->getMaximumDepthInputValue(); + mInfo.hOffset = mRenderSystem->getHorizontalTexelOffset() / float(mViewSize.width); + mInfo.vOffset = mRenderSystem->getVerticalTexelOffset() / float(mViewSize.height); + mInfo.aspectCoef = float(mViewSize.height) / float(mViewSize.width); + mInfo.pixScaleX = 1.0f / float(mViewSize.width); + mInfo.pixScaleY = 1.0f / float(mViewSize.height); + } + } + + void doRender(IVertexBuffer* _buffer, ITexture* _texture, size_t _count) + { + if (getManualRender()) + { + begin(); + setManualRender(false); + } + + // ADDED + + if (_texture) + { + Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mVertexProgramOneTexture); + Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mFragmentProgramOneTexture); + } + else + { + Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mVertexProgramNoTexture); + Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mFragmentProgramNoTexture); + } + + if (_texture) + { + OgreTexture* texture = static_cast(_texture); + Ogre::TexturePtr texture_ptr = texture->getOgreTexture(); + if (!texture_ptr.isNull()) + { + mRenderSystem->_setTexture(0, true, texture_ptr); + mRenderSystem->_setTextureUnitFiltering(0, Ogre::FO_LINEAR, Ogre::FO_LINEAR, Ogre::FO_NONE); + } + } + + OgreVertexBuffer* buffer = static_cast(_buffer); + Ogre::RenderOperation* operation = buffer->getRenderOperation(); + operation->vertexData->vertexCount = _count; + + mRenderSystem->_render(*operation); + + ++ mCountBatch; + } + + void begin() + { + // set-up matrices + mRenderSystem->_setWorldMatrix(Ogre::Matrix4::IDENTITY); + mRenderSystem->_setViewMatrix(Ogre::Matrix4::IDENTITY); + +#if OGRE_VERSION >= MYGUI_DEFINE_VERSION(1, 7, 0) && OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0 + Ogre::OrientationMode orient = mWindow->getViewport(mActiveViewport)->getOrientationMode(); + mRenderSystem->_setProjectionMatrix(Ogre::Matrix4::IDENTITY * Ogre::Quaternion(Ogre::Degree(orient * 90.f), Ogre::Vector3::UNIT_Z)); +#else + mRenderSystem->_setProjectionMatrix(Ogre::Matrix4::IDENTITY); +#endif + + // initialise render settings + mRenderSystem->setLightingEnabled(false); + mRenderSystem->_setDepthBufferParams(false, false); + mRenderSystem->_setDepthBias(0, 0); + mRenderSystem->_setCullingMode(Ogre::CULL_NONE); + mRenderSystem->_setFog(Ogre::FOG_NONE); + mRenderSystem->_setColourBufferWriteEnabled(true, true, true, true); + mRenderSystem->unbindGpuProgram(Ogre::GPT_FRAGMENT_PROGRAM); + mRenderSystem->unbindGpuProgram(Ogre::GPT_VERTEX_PROGRAM); + mRenderSystem->setShadingType(Ogre::SO_GOURAUD); + + // initialise texture settings + mRenderSystem->_setTextureCoordCalculation(0, Ogre::TEXCALC_NONE); + mRenderSystem->_setTextureCoordSet(0, 0); + mRenderSystem->_setTextureUnitFiltering(0, Ogre::FO_LINEAR, Ogre::FO_LINEAR, Ogre::FO_NONE); + mRenderSystem->_setTextureAddressingMode(0, mTextureAddressMode); + mRenderSystem->_setTextureMatrix(0, Ogre::Matrix4::IDENTITY); +#if OGRE_VERSION < MYGUI_DEFINE_VERSION(1, 6, 0) + mRenderSystem->_setAlphaRejectSettings(Ogre::CMPF_ALWAYS_PASS, 0); +#else + mRenderSystem->_setAlphaRejectSettings(Ogre::CMPF_ALWAYS_PASS, 0, false); +#endif + mRenderSystem->_setTextureBlendMode(0, mColorBlendMode); + mRenderSystem->_setTextureBlendMode(0, mAlphaBlendMode); + mRenderSystem->_disableTextureUnitsFrom(1); + + // enable alpha blending + mRenderSystem->_setSceneBlending(Ogre::SBF_SOURCE_ALPHA, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA); + + // always use wireframe + // TODO: add option to enable wireframe mode in platform + mRenderSystem->_setPolygonMode(Ogre::PM_SOLID); + } + + void end() + { + } + + ITexture* createTexture(const std::string& _name) + { + MapTexture::const_iterator item = mTextures.find(_name); + MYGUI_PLATFORM_ASSERT(item == mTextures.end(), "Texture '" << _name << "' already exist"); + + OgreTexture* texture = new OgreTexture(_name, OgreDataManager::getInstance().getGroup()); + mTextures[_name] = texture; + return texture; + } + + void destroyTexture(ITexture* _texture) + { + if (_texture == nullptr) return; + + MapTexture::iterator item = mTextures.find(_texture->getName()); + MYGUI_PLATFORM_ASSERT(item != mTextures.end(), "Texture '" << _texture->getName() << "' not found"); + + mTextures.erase(item); + delete _texture; + } + + ITexture* getTexture(const std::string& _name) + { + MapTexture::const_iterator item = mTextures.find(_name); + if (item == mTextures.end()) + { + Ogre::TexturePtr texture = (Ogre::TexturePtr)Ogre::TextureManager::getSingleton().getByName(_name); + if (!texture.isNull()) + { + ITexture* result = createTexture(_name); + static_cast(result)->setOgreTexture(texture); + return result; + } + return nullptr; + } + return item->second; + } + + bool isFormatSupported(PixelFormat _format, TextureUsage _usage) + { + return Ogre::TextureManager::getSingleton().isFormatSupported( + Ogre::TEX_TYPE_2D, + OgreTexture::convertFormat(_format), + OgreTexture::convertUsage(_usage)); + } + + void destroyAllResources() + { + for (MapTexture::const_iterator item = mTextures.begin(); item != mTextures.end(); ++item) + { + delete item->second; + } + mTextures.clear(); + } + +#if MYGUI_DEBUG_MODE == 1 + bool checkTexture(ITexture* _texture) + { + for (MapTexture::const_iterator item = mTextures.begin(); item != mTextures.end(); ++item) + { + if (item->second == _texture) + return true; + } + return false; + } +#endif + + const IntSize& getViewSize() const + { + return mViewSize; + } + + VertexColourType getVertexFormat() + { + return mVertexFormat; + } + + const RenderTargetInfo& getInfo() + { + return mInfo; + } + + size_t getActiveViewport() + { + return mActiveViewport; + } + + Ogre::RenderWindow* getRenderWindow() + { + return mWindow; + } + + bool getManualRender() + { + return mManualRender; + } + + void setManualRender(bool _value) + { + mManualRender = _value; + } + + size_t getBatchCount() const + { + return mCountBatch; + } +}; + +} + + void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir) { assert(wnd); @@ -41,8 +574,8 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool // Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later. mLogManager = new LogManager(); - mRenderManager = new OgreRenderManager(); - mDataManager = new FixedOgreDataManager(); + mRenderManager = new MyGUI::ShaderBasedRenderManager(); + mDataManager = new MyGUI::FixedOgreDataManager(); LogManager::getInstance().setSTDOutputEnabled(logging); diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index 16673ef980..eec867ff8b 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -8,7 +8,7 @@ namespace MyGUI class Gui; class LogManager; class OgreDataManager; - class OgreRenderManager; + class ShaderBasedRenderManager; } namespace Ogre @@ -25,7 +25,7 @@ namespace GUI MyGUI::Gui *mGui; MyGUI::LogManager* mLogManager; MyGUI::OgreDataManager* mDataManager; - MyGUI::OgreRenderManager* mRenderManager; + MyGUI::ShaderBasedRenderManager* mRenderManager; Ogre::SceneManager* mSceneMgr; diff --git a/libs/openengine/ogre/particles.cpp b/libs/openengine/ogre/particles.cpp new file mode 100644 index 0000000000..3453b7f3d7 --- /dev/null +++ b/libs/openengine/ogre/particles.cpp @@ -0,0 +1,146 @@ +#include "particles.hpp" + +#include +#include +#include +#include + +class GrowFadeAffector : public Ogre::ParticleAffector +{ +public: + /** Command object for grow_time (see Ogre::ParamCommand).*/ + class CmdGrowTime : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GrowFadeAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getGrowTime()); + } + void doSet(void *target, const Ogre::String &val) + { + GrowFadeAffector *self = static_cast(target); + self->setGrowTime(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for fade_time (see Ogre::ParamCommand).*/ + class CmdFadeTime : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GrowFadeAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getFadeTime()); + } + void doSet(void *target, const Ogre::String &val) + { + GrowFadeAffector *self = static_cast(target); + self->setFadeTime(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Default constructor. */ + GrowFadeAffector(Ogre::ParticleSystem *psys) : ParticleAffector(psys) + { + mGrowTime = 0.0f; + mFadeTime = 0.0f; + + mType = "GrowFade"; + + // Init parameters + if(createParamDictionary("GrowFadeAffector")) + { + Ogre::ParamDictionary *dict = getParamDictionary(); + + Ogre::String grow_title("grow_time"); + Ogre::String fade_title("fade_time"); + Ogre::String grow_descr("Time from begin to reach full size."); + Ogre::String fade_descr("Time from end to shrink."); + + dict->addParameter(Ogre::ParameterDef(grow_title, grow_descr, Ogre::PT_REAL), &msGrowCmd); + dict->addParameter(Ogre::ParameterDef(fade_title, fade_descr, Ogre::PT_REAL), &msFadeCmd); + } + } + + /** See Ogre::ParticleAffector. */ + void _initParticle(Ogre::Particle *particle) + { + const Ogre::Real life_time = particle->totalTimeToLive; + Ogre::Real particle_time = particle->timeToLive; + + Ogre::Real width = mParent->getDefaultWidth(); + Ogre::Real height = mParent->getDefaultHeight(); + if(life_time-particle_time < mGrowTime) + { + Ogre::Real scale = (life_time-particle_time) / mGrowTime; + width *= scale; + height *= scale; + } + if(particle_time < mFadeTime) + { + Ogre::Real scale = particle_time / mFadeTime; + width *= scale; + height *= scale; + } + particle->setDimensions(width, height); + } + + /** See Ogre::ParticleAffector. */ + void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + const Ogre::Real life_time = p->totalTimeToLive; + Ogre::Real particle_time = p->timeToLive; + + Ogre::Real width = mParent->getDefaultWidth(); + Ogre::Real height = mParent->getDefaultHeight(); + if(life_time-particle_time < mGrowTime) + { + Ogre::Real scale = (life_time-particle_time) / mGrowTime; + width *= scale; + height *= scale; + } + if(particle_time < mFadeTime) + { + Ogre::Real scale = particle_time / mFadeTime; + width *= scale; + height *= scale; + } + p->setDimensions(width, height); + } + } + + void setGrowTime(Ogre::Real time) + { + mGrowTime = time; + } + Ogre::Real getGrowTime() const + { return mGrowTime; } + + void setFadeTime(Ogre::Real time) + { + mFadeTime = time; + } + Ogre::Real getFadeTime() const + { return mFadeTime; } + + static CmdGrowTime msGrowCmd; + static CmdFadeTime msFadeCmd; + +protected: + Ogre::Real mGrowTime; + Ogre::Real mFadeTime; +}; +GrowFadeAffector::CmdGrowTime GrowFadeAffector::msGrowCmd; +GrowFadeAffector::CmdFadeTime GrowFadeAffector::msFadeCmd; + +Ogre::ParticleAffector *GrowFadeAffectorFactory::createAffector(Ogre::ParticleSystem *psys) +{ + Ogre::ParticleAffector *p = new GrowFadeAffector(psys); + mAffectors.push_back(p); + return p; +} diff --git a/libs/openengine/ogre/particles.hpp b/libs/openengine/ogre/particles.hpp new file mode 100644 index 0000000000..d466bb0308 --- /dev/null +++ b/libs/openengine/ogre/particles.hpp @@ -0,0 +1,17 @@ +#ifndef OENGINE_OGRE_PARTICLES_H +#define OENGINE_OGRE_PARTICLES_H + +#include + +/** Factory class for GrowFadeAffector. */ +class GrowFadeAffectorFactory : public Ogre::ParticleAffectorFactory +{ + /** See Ogre::ParticleAffectorFactory */ + Ogre::String getName() const + { return "GrowFade"; } + + /** 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 309ba8a060..c9e91968f5 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -1,5 +1,6 @@ #include "renderer.hpp" #include "fader.hpp" +#include "particles.hpp" #include "OgreRoot.h" #include "OgreRenderWindow.h" @@ -8,6 +9,8 @@ #include "OgreTextureManager.h" #include "OgreTexture.h" #include "OgreHardwarePixelBuffer.h" +#include +#include "OgreParticleAffectorFactory.h" #include @@ -24,6 +27,7 @@ using namespace Ogre; using namespace OEngine::Render; + #if defined(__APPLE__) && !defined(__LP64__) CustomRoot::CustomRoot(const Ogre::String& pluginFileName, @@ -106,6 +110,11 @@ void OgreRenderer::loadPlugins() void OgreRenderer::unloadPlugins() { + std::vector::iterator ai; + for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();ai++) + OGRE_DELETE (*ai); + mAffectorFactories.clear(); + #ifdef ENABLE_PLUGIN_GL delete mGLPlugin; mGLPlugin = NULL; @@ -195,6 +204,13 @@ void OgreRenderer::configure(const std::string &logPath, Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); + Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot); + + Ogre::ParticleAffectorFactory *affector; + affector = OGRE_NEW GrowFadeAffectorFactory(); + 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 251dc9c54d..ea46f5ae62 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 ParticleAffectorFactory; } namespace OEngine @@ -94,6 +95,7 @@ namespace OEngine Ogre::D3D9Plugin* mD3D9Plugin; #endif Fader* mFader; + std::vector mAffectorFactories; bool logging; public: