diff --git a/.gitignore b/.gitignore index 776e2b6591..b757c53f10 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ data CMakeLists.txt.user *.swp *.swo +*.kate-swp +.cproject +.project +.settings/ 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..b989297b39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 22) +set (OPENMW_VERSION_MINOR 23) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") @@ -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 @@ -180,15 +181,8 @@ if (UNIX AND NOT APPLE) find_package (Threads) endif() -# find boost without components so we can use Boost_VERSION -find_package(Boost REQUIRED) -set(BOOST_COMPONENTS system filesystem program_options thread date_time) - -if (Boost_VERSION LESS 104900) - set(SHINY_USE_WAVE_SYSTEM_INSTALL "TRUE") - set(BOOST_COMPONENTS ${BOOST_COMPONENTS} wave) -endif() +set(BOOST_COMPONENTS system filesystem program_options thread date_time wave) IF(BOOST_STATIC) set(Boost_USE_STATIC_LIBS ON) @@ -654,12 +648,12 @@ endif (APPLE) if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) ## Non Debian based Linux building # paths - set(BINDIR "${CMAKE_INSTALL_PREFIX}/usr/bin" CACHE PATH "Where to install binaries") + set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") set(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") - set(DATADIR "${DATAROOTDIR}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") + set(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") set(DOCDIR "${DATAROOTDIR}/doc/openmw" CACHE PATH "Sets the doc directory to a non-default location.") set(MANDIR "${DATAROOTDIR}/man" CACHE PATH "Where to install manpages") - set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc/openmw" CACHE PATH "Set config dir") + set(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir") set(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") # Install binaries @@ -683,6 +677,10 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) # Install icon and .desktop INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications") + IF(BUILD_OPENCS) + INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "${ICONDIR}") + INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "${DATAROOTDIR}/applications") + ENDIF(BUILD_OPENCS) # Install global configuration files INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" ) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 3c9476d7a2..8e0900ba08 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -23,8 +23,7 @@ struct ESMData std::string author; std::string description; int version; - int type; - ESM::ESMReader::MasterList masters; + std::vector masters; std::deque mRecords; std::map > mCellRefs; @@ -52,6 +51,7 @@ struct Arguments unsigned int raw_given; unsigned int quiet_given; unsigned int loadcells_given; + bool plain_given; std::string mode; std::string encoding; @@ -78,6 +78,9 @@ bool parseOptions (int argc, char** argv, Arguments &info) ("type,t", bpo::value< std::vector >(), "Show only records of this type (four character record code). May " "be specified multiple times. Only affects dump mode.") + ("plain,p", "Print contents of dialogs, books and scripts. " + "(skipped by default)" + "Only affects dump mode.") ("quiet,q", "Supress all record information. Useful for speed tests.") ("loadcells,C", "Browse through contents of all cells.") @@ -162,6 +165,7 @@ bool parseOptions (int argc, char** argv, Arguments &info) info.raw_given = variables.count ("raw"); info.quiet_given = variables.count ("quiet"); info.loadcells_given = variables.count ("loadcells"); + info.plain_given = (variables.count("plain") > 0); // Font encoding settings info.encoding = variables["encoding"].as(); @@ -228,7 +232,10 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Refnum: " << ref.mRefnum << std::endl; std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; - std::cout << " INTV: " << ref.mIntv << " NAM9: " << ref.mIntv << std::endl; + std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; + std::cout << " Uses/health: '" << ref.mCharge << "'\n"; + std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; + std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; } } @@ -284,16 +291,13 @@ int load(Arguments& info) info.data.author = esm.getAuthor(); info.data.description = esm.getDesc(); info.data.masters = esm.getMasters(); - info.data.version = esm.getVer(); - info.data.type = esm.getType(); if (!quiet) { std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl - << "File format version: " << esm.getFVer() << std::endl - << "Special flag: " << esm.getSpecial() << std::endl; - ESM::ESMReader::MasterList m = esm.getMasters(); + << "File format version: " << esm.getFVer() << std::endl; + std::vector m = esm.getMasters(); if (!m.empty()) { std::cout << "Masters:" << std::endl; @@ -344,6 +348,7 @@ int load(Arguments& info) } record->setId(id); record->setFlags((int) flags); + record->setPrintPlain(info.plain_given); record->load(esm); if (!quiet && interested) record->print(); @@ -430,9 +435,9 @@ int clone(Arguments& info) esm.setAuthor(info.data.author); esm.setDescription(info.data.description); esm.setVersion(info.data.version); - esm.setType(info.data.type); + esm.setRecordCount (recordCount); - for (ESM::ESMReader::MasterList::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) + for (std::vector::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) esm.addMaster(it->name, it->size); std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary); diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index f08c31003c..3fb1166e8e 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -627,10 +627,10 @@ std::string bodyPartFlags(int flags) std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::BodyPart::BPF_Female) properties += "Female "; - if (flags & ESM::BodyPart::BPF_Playable) properties += "Playable "; + if (flags & ESM::BodyPart::BPF_NotPlayable) properties += "NotPlayable "; int unused = (0xFFFFFFFF ^ (ESM::BodyPart::BPF_Female| - ESM::BodyPart::BPF_Playable)); + ESM::BodyPart::BPF_NotPlayable)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 38fddd6b92..8e2de6494e 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -275,7 +275,7 @@ RecordBase::create(ESM::NAME type) } case ESM::REC_LOCK: { - record = new EsmTool::Record; + record = new EsmTool::Record; break; } case ESM::REC_LTEX: @@ -439,7 +439,7 @@ void Record::print() template<> void Record::print() { - std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Race: " << mData.mRace << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Type: " << meshTypeLabel(mData.mData.mType) << " (" << (int)mData.mData.mType << ")" << std::endl; @@ -464,12 +464,17 @@ void Record::print() std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl; std::cout << " SkillID: " << mData.mData.mSkillID << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; - std::cout << " Text: [skipped]" << std::endl; - // Skip until multi-line fields is controllable by a command line option. - // Mildly problematic because there are no parameter to print() currently. - // std::cout << "-------------------------------------------" << std::endl; - // std::cout << mData.mText << std::endl; - // std::cout << "-------------------------------------------" << std::endl; + if (mPrintPlain) + { + std::cout << " Text:" << std::endl; + std::cout << "START--------------------------------------" << std::endl; + std::cout << mData.mText << std::endl; + std::cout << "END----------------------------------------" << std::endl; + } + else + { + std::cout << " Text: [skipped]" << std::endl; + } } template<> @@ -679,14 +684,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] != "") { @@ -753,15 +758,6 @@ void Record::print() if (mData.mSound != "") std::cout << " Sound File: " << mData.mSound << std::endl; - if (mData.mResultScript != "") - { - std::cout << " Result Script: [skipped]" << std::endl; - // Skip until multi-line fields is controllable by a command line option. - // Mildly problematic because there are no parameter to print() currently. - // std::cout << "-------------------------------------------" << std::endl; - // std::cout << mData.mResultScript << std::endl; - // std::cout << "-------------------------------------------" << std::endl; - } std::cout << " Quest Status: " << questStatusLabel(mData.mQuestStatus) << " (" << mData.mQuestStatus << ")" << std::endl; @@ -771,6 +767,21 @@ void Record::print() std::vector::iterator sit; for (sit = mData.mSelects.begin(); sit != mData.mSelects.end(); sit++) std::cout << " Select Rule: " << ruleString(*sit) << std::endl; + + if (mData.mResultScript != "") + { + if (mPrintPlain) + { + std::cout << " Result Script:" << std::endl; + std::cout << "START--------------------------------------" << std::endl; + std::cout << mData.mResultScript << std::endl; + std::cout << "END----------------------------------------" << std::endl; + } + else + { + std::cout << " Result Script: [skipped]" << std::endl; + } + } } template<> @@ -864,14 +875,13 @@ void Record::print() } template<> -void Record::print() +void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; - std::cout << " Type: " << mData.mType << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; @@ -886,8 +896,6 @@ void Record::print() std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; - // BUG? No Type Label? - std::cout << " Type: " << mData.mType << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; @@ -902,7 +910,6 @@ void Record::print() std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; - std::cout << " Type: " << mData.mType << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; @@ -1103,53 +1110,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. @@ -1199,21 +1182,28 @@ void Record::print() std::cout << " Script Data Size: " << mData.mData.mScriptDataSize << std::endl; std::cout << " Table Size: " << mData.mData.mStringTableSize << std::endl; - std::cout << " Script: [skipped]" << std::endl; - // Skip until multi-line fields is controllable by a command line option. - // Mildly problematic because there are no parameter to print() currently. - // std::cout << "-------------------------------------------" << std::endl; - // std::cout << s->scriptText << std::endl; - // std::cout << "-------------------------------------------" << std::endl; + std::vector::iterator vit; for (vit = mData.mVarNames.begin(); vit != mData.mVarNames.end(); vit++) 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; + + if (mPrintPlain) + { + std::cout << " Script:" << std::endl; + std::cout << "START--------------------------------------" << std::endl; + std::cout << mData.mScriptText << std::endl; + std::cout << "END----------------------------------------" << std::endl; + } + else + { + std::cout << " Script: [skipped]" << std::endl; + } } template<> diff --git a/apps/esmtool/record.hpp b/apps/esmtool/record.hpp index c3daa9d0c3..78cf5d436e 100644 --- a/apps/esmtool/record.hpp +++ b/apps/esmtool/record.hpp @@ -21,9 +21,10 @@ namespace EsmTool std::string mId; int mFlags; ESM::NAME mType; + bool mPrintPlain; public: - RecordBase () {} + RecordBase () { mPrintPlain = false; } virtual ~RecordBase() {} const std::string &getId() const { @@ -46,6 +47,14 @@ namespace EsmTool return mType; } + bool getPrintPlain() const { + return mPrintPlain; + } + + void setPrintPlain(bool plain) { + mPrintPlain = plain; + } + virtual void load(ESM::ESMReader &esm) = 0; virtual void save(ESM::ESMWriter &esm) = 0; virtual void print() = 0; @@ -104,7 +113,7 @@ namespace EsmTool template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); - template<> void Record::print(); + template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index eb93d71e78..0c93474da9 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -102,3 +102,9 @@ if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(omwlauncher gcov) endif() + +# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream +if (UNIX AND NOT APPLE) +target_link_libraries(omwlauncher dl Xt) +endif() + diff --git a/apps/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/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 56c08582fe..9a9b8df41b 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -96,15 +96,15 @@ bool GameSettings::readFile(QTextStream &stream) QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { - QString line = stream.readLine().simplified(); + QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).simplified(); - QString value = keyRe.cap(2).simplified(); + QString key = keyRe.cap(1); + QString value = keyRe.cap(2); // Don't remove existing data entries if (key != QLatin1String("data")) diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index c4561a9102..21029b3ad9 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -53,7 +53,7 @@ public: QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { - QString line = stream.readLine().simplified(); + QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; @@ -66,8 +66,8 @@ public: if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).simplified(); - QString value = keyRe.cap(2).simplified(); + QString key = keyRe.cap(1); + QString value = keyRe.cap(2); if (!sectionPrefix.isEmpty()) key.prepend(sectionPrefix); diff --git a/apps/mwiniimporter/CMakeLists.txt b/apps/mwiniimporter/CMakeLists.txt index deab88ce28..702f665138 100644 --- a/apps/mwiniimporter/CMakeLists.txt +++ b/apps/mwiniimporter/CMakeLists.txt @@ -22,3 +22,8 @@ if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(mwiniimport gcov) endif() + +if(DPKG_PROGRAM) + INSTALL(TARGETS mwiniimport RUNTIME DESTINATION games COMPONENT mwiniimport) +endif() + diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index dacd3bb4ed..9787719af3 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -1,4 +1,3 @@ - set (OPENCS_SRC main.cpp) opencs_units (. editor) @@ -24,7 +23,8 @@ opencs_units (model/world opencs_units_noqt (model/world - universalid data record idcollection commands columnbase + universalid data record idcollection commands columnbase scriptcontext cell refidcollection + refidadapter refiddata refidadapterimp ) opencs_hdrs_noqt (model/world @@ -37,7 +37,8 @@ opencs_units (model/tools ) opencs_units_noqt (model/tools - stage verifier mandatoryid + stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck + birthsigncheck spellcheck ) @@ -56,11 +57,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 ) @@ -72,27 +73,34 @@ opencs_units_noqt (view/tools subviews ) -opencs_units (settings - customblock - proxyblock - settingcontainer - groupbox - settingsitem +opencs_units (view/settings abstractblock - settingwidget - itemblock - groupblock - toggleblock - usersettingsdialog - abstractpage + proxyblock abstractwidget - blankpage + usersettingsdialog editorpage ) -opencs_units_noqt (settings +opencs_units_noqt (view/settings + abstractpage + blankpage + groupblock + customblock + groupbox + itemblock + settingwidget + toggleblock support + ) + +opencs_units (model/settings usersettings + settingcontainer + ) + +opencs_units_noqt (model/settings + support + settingsitem ) set (OPENCS_US diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 5966d089e3..8dc5366a98 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -113,5 +113,7 @@ int CS::Editor::run() { mStartup.show(); + QApplication::setQuitOnLastWindowClosed (true); + return QApplication::exec(); } diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index c242a17ea8..1c4bcb1eeb 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -2,9 +2,9 @@ #define CS_EDITOR_H #include - +#ifndef Q_MOC_RUN #include - +#endif #include "model/doc/documentmanager.hpp" #include "view/doc/viewmanager.hpp" @@ -23,7 +23,6 @@ namespace CS FileDialog mFileDialog; Files::ConfigurationManager mCfgMgr; - void setupDataFiles(); // not implemented diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 3fd0881fbe..7a66487fb4 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -151,6 +151,10 @@ void CSMDoc::Document::addOptionalGlobals() ESM::Global global; global.mId = sGlobals[i]; global.mValue.setType (ESM::VT_Long); + + if (i==0) + global.mValue.setInteger (1); // dayspassed starts counting at 1 + addOptionalGlobal (global); } } @@ -190,15 +194,25 @@ void CSMDoc::Document::createBase() record.mId = sGlobals[i]; - record.mValue.setType (i==2 ? ESM::VT_Float : ESM::VT_Int); + record.mValue.setType (i==2 ? ESM::VT_Float : ESM::VT_Long); - if (i==0) + if (i==0 || i==1) record.mValue.setInteger (1); getData().getGlobals().add (record); } /// \todo add GMSTs + + for (int i=0; i<27; ++i) + { + ESM::Skill record; + record.mIndex = i; + record.mId = ESM::Skill::indexToId (record.mIndex); + record.blank(); + + getData().getSkills().add (record); + } } CSMDoc::Document::Document (const std::vector& files, bool new_) @@ -215,7 +229,7 @@ CSMDoc::Document::Document (const std::vector& files, b if (new_ && files.size()==1) createBase(); - else if (files.size()>1) + else { std::vector::const_iterator end = files.end(); @@ -238,6 +252,9 @@ CSMDoc::Document::Document (const std::vector& files, b connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); } +CSMDoc::Document::~Document() +{} + QUndoStack& CSMDoc::Document::getUndoStack() { return mUndoStack; @@ -291,11 +308,13 @@ void CSMDoc::Document::abortOperation (int type) } } + void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); } + void CSMDoc::Document::operationDone (int type) { emit stateChanged (getState(), this); @@ -309,9 +328,12 @@ void CSMDoc::Document::saving() if (mSaveCount>15) { + //clear the stack before resetting the save state + //to avoid emitting incorrect states + mUndoStack.setClean(); + mSaveCount = 0; mSaveTimer.stop(); - mUndoStack.setClean(); emit stateChanged (getState(), this); } } diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index aaab50c86f..94d5fe85cc 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -63,6 +63,7 @@ namespace CSMDoc public: Document (const std::vector& files, bool new_); + ~Document(); QUndoStack& getUndoStack(); diff --git a/apps/opencs/settings/settingcontainer.cpp b/apps/opencs/model/settings/settingcontainer.cpp similarity index 75% rename from apps/opencs/settings/settingcontainer.cpp rename to apps/opencs/model/settings/settingcontainer.cpp index 700c70fa4c..a75a84ec55 100644 --- a/apps/opencs/settings/settingcontainer.cpp +++ b/apps/opencs/model/settings/settingcontainer.cpp @@ -2,17 +2,17 @@ #include -CsSettings::SettingContainer::SettingContainer(QObject *parent) : +CSMSettings::SettingContainer::SettingContainer(QObject *parent) : QObject(parent), mValue (0), mValues (0) { } -CsSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) : +CSMSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) : QObject(parent), mValue (new QString (value)), mValues (0) { } -void CsSettings::SettingContainer::insert (const QString &value) +void CSMSettings::SettingContainer::insert (const QString &value) { if (mValue) { @@ -31,7 +31,7 @@ void CsSettings::SettingContainer::insert (const QString &value) } -void CsSettings::SettingContainer::update (const QString &value, int index) +void CSMSettings::SettingContainer::update (const QString &value, int index) { if (isEmpty()) mValue = new QString(value); @@ -43,7 +43,7 @@ void CsSettings::SettingContainer::update (const QString &value, int index) mValues->replace(index, value); } -QString CsSettings::SettingContainer::getValue (int index) const +QString CSMSettings::SettingContainer::getValue (int index) const { QString retVal(""); @@ -66,7 +66,7 @@ QString CsSettings::SettingContainer::getValue (int index) const return retVal; } -int CsSettings::SettingContainer::count () const +int CSMSettings::SettingContainer::count () const { int retVal = 0; diff --git a/apps/opencs/settings/settingcontainer.hpp b/apps/opencs/model/settings/settingcontainer.hpp similarity index 97% rename from apps/opencs/settings/settingcontainer.hpp rename to apps/opencs/model/settings/settingcontainer.hpp index 929c6a25da..eb264248df 100644 --- a/apps/opencs/settings/settingcontainer.hpp +++ b/apps/opencs/model/settings/settingcontainer.hpp @@ -5,7 +5,7 @@ class QStringList; -namespace CsSettings +namespace CSMSettings { class SettingContainer : public QObject { diff --git a/apps/opencs/settings/settingsitem.cpp b/apps/opencs/model/settings/settingsitem.cpp similarity index 82% rename from apps/opencs/settings/settingsitem.cpp rename to apps/opencs/model/settings/settingsitem.cpp index b2a8b509b9..fef9d3e2fe 100644 --- a/apps/opencs/settings/settingsitem.cpp +++ b/apps/opencs/model/settings/settingsitem.cpp @@ -1,6 +1,6 @@ #include "settingsitem.hpp" -bool CsSettings::SettingsItem::updateItem (const QStringList *values) +bool CSMSettings::SettingsItem::updateItem (const QStringList *values) { QStringList::ConstIterator it = values->begin(); @@ -30,7 +30,7 @@ bool CsSettings::SettingsItem::updateItem (const QStringList *values) return isValid; } -bool CsSettings::SettingsItem::updateItem (const QString &value) +bool CSMSettings::SettingsItem::updateItem (const QString &value) { //takes a value or a SettingsContainer and updates itself accordingly //after validating the data against it's own definition @@ -52,7 +52,7 @@ bool CsSettings::SettingsItem::updateItem (const QString &value) return success; } -bool CsSettings::SettingsItem::updateItem(int valueListIndex) +bool CSMSettings::SettingsItem::updateItem(int valueListIndex) { bool success = false; @@ -64,7 +64,7 @@ bool CsSettings::SettingsItem::updateItem(int valueListIndex) return success; } -bool CsSettings::SettingsItem::validate (const QString &value) +bool CSMSettings::SettingsItem::validate (const QString &value) { bool isValid = true; @@ -90,13 +90,13 @@ bool CsSettings::SettingsItem::validate (const QString &value) return isValid; } -void CsSettings::SettingsItem::setDefaultValue (const QString &value) +void CSMSettings::SettingsItem::setDefaultValue (const QString &value) { mDefaultValue = value; update (value); } -QString CsSettings::SettingsItem::getDefaultValue() const +QString CSMSettings::SettingsItem::getDefaultValue() const { return mDefaultValue; } diff --git a/apps/opencs/settings/settingsitem.hpp b/apps/opencs/model/settings/settingsitem.hpp similarity index 98% rename from apps/opencs/settings/settingsitem.hpp rename to apps/opencs/model/settings/settingsitem.hpp index 8168edee50..8be05a4c70 100644 --- a/apps/opencs/settings/settingsitem.hpp +++ b/apps/opencs/model/settings/settingsitem.hpp @@ -5,7 +5,7 @@ #include "support.hpp" #include "settingcontainer.hpp" -namespace CsSettings +namespace CSMSettings { class SettingsItem : public SettingContainer { diff --git a/apps/opencs/settings/support.cpp b/apps/opencs/model/settings/support.cpp similarity index 100% rename from apps/opencs/settings/support.cpp rename to apps/opencs/model/settings/support.cpp diff --git a/apps/opencs/model/settings/support.hpp b/apps/opencs/model/settings/support.hpp new file mode 100644 index 0000000000..1155d2a298 --- /dev/null +++ b/apps/opencs/model/settings/support.hpp @@ -0,0 +1,39 @@ +#ifndef MODEL_SUPPORT_HPP +#define MODEL_SUPPORT_HPP + +#include +#include + +class QLayout; +class QWidget; +class QListWidgetItem; + +namespace CSMSettings +{ + class SettingContainer; + + typedef QList SettingList; + typedef QMap SettingMap; + typedef QMap SectionMap; + + struct QStringPair + { + QStringPair(): left (""), right ("") + {} + + QStringPair (const QString &leftValue, const QString &rightValue) + : left (leftValue), right(rightValue) + {} + + QStringPair (const QStringPair &pair) + : left (pair.left), right (pair.right) + {} + + QString left; + QString right; + + bool isEmpty() const + { return (left.isEmpty() && right.isEmpty()); } + }; +} +#endif // MODEL_SUPPORT_HPP diff --git a/apps/opencs/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp similarity index 88% rename from apps/opencs/settings/usersettings.cpp rename to apps/opencs/model/settings/usersettings.cpp index 2ddef938b4..aabda86b33 100644 --- a/apps/opencs/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -30,16 +30,16 @@ namespace boost #endif /* (BOOST_VERSION <= 104600) */ -CsSettings::UserSettings::UserSettings(Files::ConfigurationManager &cfg) - : mCfgMgr(cfg) +CSMSettings::UserSettings::UserSettings() +{ + mUserSettingsInstance = this; +} + +CSMSettings::UserSettings::~UserSettings() { } -CsSettings::UserSettings::~UserSettings() -{ -} - -QFile *CsSettings::UserSettings::openFile (const QString &filename) +QFile *CSMSettings::UserSettings::openFile (const QString &filename) { QFile *file = new QFile(filename); @@ -63,7 +63,7 @@ QFile *CsSettings::UserSettings::openFile (const QString &filename) return file; } -bool CsSettings::UserSettings::writeFile(QFile *file, QMap &settings) +bool CSMSettings::UserSettings::writeFile(QFile *file, QMap &settings) { if (!file) return false; @@ -88,7 +88,7 @@ bool CsSettings::UserSettings::writeFile(QFile *file, QMap PathContainer; class QFile; -namespace CsSettings { +namespace CSMSettings { - class UserSettings + struct UserSettings: public QObject { + + Q_OBJECT + public: - UserSettings(Files::ConfigurationManager &cfg); - ~UserSettings(); + + static UserSettings &instance() + { + static UserSettings instance; + + return instance; + } QFile *openFile (const QString &); bool writeFile(QFile *file, QMap §ions); void getSettings (QTextStream &stream, SectionMap &settings); private: - Files::ConfigurationManager &mCfgMgr; + + UserSettings *mUserSettingsInstance; + UserSettings(); + ~UserSettings(); + + UserSettings (UserSettings const &); //not implemented + void operator= (UserSettings const &); //not implemented + + signals: + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); }; } 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/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp new file mode 100644 index 0000000000..897aeab473 --- /dev/null +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -0,0 +1,37 @@ + +#include "skillcheck.hpp" + +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection& skills) +: mSkills (skills) +{} + +int CSMTools::SkillCheckStage::setup() +{ + return mSkills.getSize(); +} + +void CSMTools::SkillCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Skill& skill = mSkills.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId); + + for (int i=0; i<4; ++i) + if (skill.mData.mUseValue[i]<0) + { + std::ostringstream stream; + + stream << id.toString() << "|Use value #" << i << " of " << skill.mId << " is negative"; + + messages.push_back (stream.str()); + } + + if (skill.mDescription.empty()) + messages.push_back (id.toString() + "|" + skill.mId + " has an empty description"); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/skillcheck.hpp b/apps/opencs/model/tools/skillcheck.hpp new file mode 100644 index 0000000000..30a3f01cad --- /dev/null +++ b/apps/opencs/model/tools/skillcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_SKILLCHECK_H +#define CSM_TOOLS_SKILLCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that skill records are internally consistent + class SkillCheckStage : public Stage + { + const CSMWorld::IdCollection& mSkills; + + public: + + SkillCheckStage (const CSMWorld::IdCollection& skills); + + 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 8dd1c0fe6f..803861203c 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -12,6 +12,14 @@ #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) { @@ -51,6 +59,22 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier() mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(), 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/cell.cpp b/apps/opencs/model/world/cell.cpp new file mode 100644 index 0000000000..759468fa8f --- /dev/null +++ b/apps/opencs/model/world/cell.cpp @@ -0,0 +1,20 @@ + +#include "cell.hpp" + +#include + +void CSMWorld::Cell::load (ESM::ESMReader &esm) +{ + mName = mId; + + ESM::Cell::load (esm, true); /// \todo set this to false, once the bug in ESM::Cell::load is fixed + + if (!(mData.mFlags & Interior)) + { + std::ostringstream stream; + + stream << "#" << mData.mX << " " << mData.mY; + + mId = stream.str(); + } +} \ No newline at end of file diff --git a/apps/opencs/model/world/cell.hpp b/apps/opencs/model/world/cell.hpp new file mode 100644 index 0000000000..6a9676a559 --- /dev/null +++ b/apps/opencs/model/world/cell.hpp @@ -0,0 +1,17 @@ +#ifndef CSM_WOLRD_CELL_H +#define CSM_WOLRD_CELL_H + +#include + +namespace CSMWorld +{ + /// \brief Wrapper for Cell record + struct Cell : public ESM::Cell + { + std::string mId; + + void load (ESM::ESMReader &esm); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index c44abda2b2..8cc435f3a9 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -31,7 +31,17 @@ namespace CSMWorld Display_Float, Display_Var, Display_GmstVarType, - Display_GlobalVarType + Display_GlobalVarType, + Display_Specialisation, + Display_Attribute, + Display_Boolean, + Display_SpellType, + Display_Script, + Display_ApparatusType, + Display_ArmorType, + Display_ClothingType, + Display_CreatureType, + Display_WeaponType }; std::string mTitle; diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 018825831b..f1d8d4ae62 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -1,6 +1,12 @@ #ifndef CSM_WOLRD_COLUMNS_H #define CSM_WOLRD_COLUMNS_H +#include + +#include + +#include + #include "columnbase.hpp" namespace CSMWorld @@ -35,7 +41,7 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return record.get().mId.c_str(); + return QString::fromUtf8 (record.get().mId.c_str()); } virtual bool isEditable() const @@ -127,17 +133,17 @@ namespace CSMWorld { case ESM::VT_String: - return record.get().mValue.getString().c_str(); break; + return QString::fromUtf8 (record.get().mValue.getString().c_str()); case ESM::VT_Int: case ESM::VT_Short: case ESM::VT_Long: - return record.get().mValue.getInteger(); break; + return record.get().mValue.getInteger(); case ESM::VT_Float: - return record.get().mValue.getFloat(); break; + return record.get().mValue.getFloat(); default: return QVariant(); } @@ -177,6 +183,596 @@ namespace CSMWorld return true; } }; + + template + struct DescriptionColumn : public Column + { + DescriptionColumn() : Column ("Description", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mDescription.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mDescription = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SpecialisationColumn : public Column + { + SpecialisationColumn() : Column ("Specialisation", ColumnBase::Display_Specialisation) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mSpecialization; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mSpecialization = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct UseValueColumn : public Column + { + int mIndex; + + UseValueColumn (int index) + : Column ("Use value #" + boost::lexical_cast (index), + ColumnBase::Display_Float), mIndex (index) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mUseValue[mIndex]; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mUseValue[mIndex] = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct AttributeColumn : public Column + { + AttributeColumn() : Column ("Attribute", ColumnBase::Display_Attribute) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mAttribute; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mAttribute = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct NameColumn : public Column + { + NameColumn() : Column ("Name", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mName.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mName = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct AttributesColumn : public Column + { + int mIndex; + + AttributesColumn (int index) + : Column ("Attribute #" + boost::lexical_cast (index), + ColumnBase::Display_Attribute), mIndex (index) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mAttribute[mIndex]; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mAttribute[mIndex] = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SkillsColumn : public Column + { + int mIndex; + bool mMajor; + + 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.getSkill (mIndex, mMajor); + + return QString::fromUtf8 (ESM::Skill::indexToId (skill).c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + std::istringstream stream (data.toString().toUtf8().constData()); + + int index = -1; + char c; + + stream >> c >> index; + + if (index!=-1) + { + ESXRecordT record2 = record.get(); + + record2.mData.getSkill (mIndex, mMajor) = index; + + record.setModified (record2); + } + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PlayableColumn : public Column + { + PlayableColumn() : Column ("Playable", ColumnBase::Display_Boolean) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mIsPlayable!=0; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mIsPlayable = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + 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; + } + }; + + template + struct RegionColumn : public Column + { + RegionColumn() : Column ("Region", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mRegion.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mRegion = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index e22ecf9921..eaded5b702 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,7 +1,7 @@ #include "commands.hpp" -#include +#include #include "idtableproxymodel.hpp" #include "idtable.hpp" @@ -71,7 +71,7 @@ void CSMWorld::RevertCommand::redo() void CSMWorld::RevertCommand::undo() { - mModel.setRecord (*mOld); + mModel.setRecord (mId, *mOld); } CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent) @@ -104,5 +104,5 @@ void CSMWorld::DeleteCommand::redo() void CSMWorld::DeleteCommand::undo() { - mModel.setRecord (*mOld); + mModel.setRecord (mId, *mOld); } \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index bbd8667b34..1701e42899 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include @@ -12,7 +12,7 @@ #include "idtable.hpp" #include "columns.hpp" -void CSMWorld::Data::addModel (QAbstractTableModel *model, UniversalId::Type type1, +void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1, UniversalId::Type type2) { mModels.push_back (model); @@ -33,16 +33,120 @@ 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) + mSkills.addColumn (new UseValueColumn (i)); + mSkills.addColumn (new DescriptionColumn); + + 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, true)); + for (int i=0; i<5; ++i) + 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)); + + mCells.addColumn (new StringIdColumn); + mCells.addColumn (new RecordStateColumn); + mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); + mCells.addColumn (new NameColumn); + mCells.addColumn (new FlagColumn ("Sleep forbidden", ESM::Cell::NoSleep)); + mCells.addColumn (new FlagColumn ("Interior Water", ESM::Cell::HasWater)); + mCells.addColumn (new FlagColumn ("Interior Sky", ESM::Cell::QuasiEx)); + mCells.addColumn (new RegionColumn); + addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); + addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill); + 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); + addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); + addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, + UniversalId::Type_Referenceable); } CSMWorld::Data::~Data() { - for (std::vector::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) + for (std::vector::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) delete *iter; } @@ -66,9 +170,119 @@ CSMWorld::IdCollection& CSMWorld::Data::getGmsts() return mGmsts; } -QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) +const CSMWorld::IdCollection& CSMWorld::Data::getSkills() const { - std::map::iterator iter = mModelIndex.find (id.getType()); + return mSkills; +} + +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; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getCells() const +{ + return mCells; +} + +CSMWorld::IdCollection& CSMWorld::Data::getCells() +{ + return mCells; +} + +const CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() const +{ + return mReferenceables; +} + +CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() +{ + return mReferenceables; +} + +QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) +{ + std::map::iterator iter = mModelIndex.find (id.getType()); if (iter==mModelIndex.end()) throw std::logic_error ("No table model available for " + id.toString()); @@ -102,7 +316,40 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) { case ESM::REC_GLOB: mGlobals.load (reader, base); break; 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; + case ESM::REC_CELL: mCells.load (reader, base); break; + case ESM::REC_ACTI: mReferenceables.load (reader, base, UniversalId::Type_Activator); break; + case ESM::REC_ALCH: mReferenceables.load (reader, base, UniversalId::Type_Potion); break; + case ESM::REC_APPA: mReferenceables.load (reader, base, UniversalId::Type_Apparatus); break; + case ESM::REC_ARMO: mReferenceables.load (reader, base, UniversalId::Type_Armor); break; + case ESM::REC_BOOK: mReferenceables.load (reader, base, UniversalId::Type_Book); break; + case ESM::REC_CLOT: mReferenceables.load (reader, base, UniversalId::Type_Clothing); break; + case ESM::REC_CONT: mReferenceables.load (reader, base, UniversalId::Type_Container); break; + case ESM::REC_CREA: mReferenceables.load (reader, base, UniversalId::Type_Creature); break; + case ESM::REC_DOOR: mReferenceables.load (reader, base, UniversalId::Type_Door); break; + case ESM::REC_INGR: mReferenceables.load (reader, base, UniversalId::Type_Ingredient); break; + case ESM::REC_LEVC: + mReferenceables.load (reader, base, UniversalId::Type_CreatureLevelledList); break; + case ESM::REC_LEVI: + mReferenceables.load (reader, base, UniversalId::Type_ItemLevelledList); break; + case ESM::REC_LIGH: mReferenceables.load (reader, base, UniversalId::Type_Light); break; + case ESM::REC_LOCK: mReferenceables.load (reader, base, UniversalId::Type_Lockpick); break; + case ESM::REC_MISC: + mReferenceables.load (reader, base, UniversalId::Type_Miscellaneous); break; + case ESM::REC_NPC_: mReferenceables.load (reader, base, UniversalId::Type_Npc); break; + case ESM::REC_PROB: mReferenceables.load (reader, base, UniversalId::Type_Probe); break; + case ESM::REC_REPA: mReferenceables.load (reader, base, UniversalId::Type_Repair); break; + case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break; + case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break; default: diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 3745346511..ad6e4ba692 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -8,11 +8,22 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "idcollection.hpp" #include "universalid.hpp" +#include "cell.hpp" +#include "refidcollection.hpp" -class QAbstractTableModel; +class QAbstractItemModel; namespace CSMWorld { @@ -20,14 +31,25 @@ namespace CSMWorld { IdCollection mGlobals; IdCollection mGmsts; - std::vector mModels; - std::map mModelIndex; + IdCollection mSkills; + IdCollection mClasses; + IdCollection mFactions; + IdCollection mRaces; + IdCollection mSounds; + IdCollection mScripts; + IdCollection mRegions; + IdCollection mBirthsigns; + IdCollection mSpells; + IdCollection mCells; + RefIdCollection mReferenceables; + std::vector mModels; + std::map mModelIndex; // not implemented Data (const Data&); Data& operator= (const Data&); - void addModel (QAbstractTableModel *model, UniversalId::Type type1, + void addModel (QAbstractItemModel *model, UniversalId::Type type1, UniversalId::Type type2 = UniversalId::Type_None); public: @@ -44,7 +66,51 @@ namespace CSMWorld IdCollection& getGmsts(); - QAbstractTableModel *getTableModel (const UniversalId& id); + const IdCollection& getSkills() const; + + 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(); + + const IdCollection& getCells() const; + + IdCollection& getCells(); + + const RefIdCollection& getReferenceables() const; + + RefIdCollection& getReferenceables(); + + QAbstractItemModel *getTableModel (const UniversalId& id); ///< If no table model is available for \a id, an exception is thrown. /// /// \note The returned table may either be the model for the ID itself or the model that diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 9b69dfb889..193e4f8e2e 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -16,6 +16,7 @@ #include #include "columnbase.hpp" +#include "universalid.hpp" namespace CSMWorld { @@ -45,15 +46,19 @@ namespace CSMWorld virtual void setData (int index, int column, const QVariant& data) = 0; - virtual void merge() = 0; +// Not in use. Temporarily removed so that the implementation of RefIdCollection can continue without +// these functions for now. +// virtual void merge() = 0; ///< Merge modified into base. - virtual void purge() = 0; +// virtual void purge() = 0; ///< Remove records that are flagged as erased. virtual void removeRows (int index, int count) = 0; - virtual void appendBlankRecord (const std::string& id) = 0; + virtual void appendBlankRecord (const std::string& id, + UniversalId::Type type = UniversalId::Type_None) = 0; + ///< \param type Will be ignored, unless the collection supports multiple record types virtual int searchId (const std::string& id) const = 0; ////< Search record with \a id. @@ -63,22 +68,47 @@ namespace CSMWorld ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. + ///< \param type Will be ignored, unless the collection supports multiple record types - virtual void appendRecord (const RecordBase& record) = 0; + virtual void appendRecord (const RecordBase& record, + UniversalId::Type type = UniversalId::Type_None) = 0; ///< If the record type does not match, an exception is thrown. - virtual std::string getId (const RecordBase& record) const = 0; - ///< Return ID for \a record. - /// - /// \attention Throws an exception, if the type of \a record does not match. - virtual const RecordBase& getRecord (const std::string& id) const = 0; - virtual void load (ESM::ESMReader& reader, bool base) = 0; + virtual const RecordBase& getRecord (int index) const = 0; + + virtual void load (ESM::ESMReader& reader, bool base, + UniversalId::Type type = UniversalId::Type_None) = 0; + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0; + ///< \param type Will be ignored, unless the collection supports multiple record types }; - ///< \brief Collection of ID-based records + ///< \brief Access to ID field in records template + struct IdAccessor + { + std::string& getId (ESXRecordT& record); + + const std::string getId (const ESXRecordT& record) const; + }; + + template + std::string& IdAccessor::getId (ESXRecordT& record) + { + return record.mId; + } + + template + const std::string IdAccessor::getId (const ESXRecordT& record) const + { + return record.mId; + } + + ///< \brief Collection of ID-based records + template > class IdCollection : public IdCollectionBase { std::vector > mRecords; @@ -120,7 +150,9 @@ namespace CSMWorld virtual void removeRows (int index, int count) ; - virtual void appendBlankRecord (const std::string& id); + virtual void appendBlankRecord (const std::string& id, + UniversalId::Type type = UniversalId::Type_None); + ///< \param type Will be ignored, unless the collection supports multiple record types virtual int searchId (const std::string& id) const; ////< Search record with \a id. @@ -131,36 +163,40 @@ namespace CSMWorld /// /// \attention \a record must not change the ID. - virtual void appendRecord (const RecordBase& record); + virtual void appendRecord (const RecordBase& record, + UniversalId::Type type = UniversalId::Type_None); ///< If the record type does not match, an exception is thrown. + ///< \param type Will be ignored, unless the collection supports multiple record types - virtual std::string getId (const RecordBase& record) const; - ///< Return ID for \a record. - /// - /// \attention Throw san exception, if the type of \a record does not match. + virtual const Record& getRecord (const std::string& id) const; - virtual const RecordBase& getRecord (const std::string& id) const; + virtual const Record& getRecord (int index) const; - virtual void load (ESM::ESMReader& reader, bool base); + virtual void load (ESM::ESMReader& reader, bool base, + UniversalId::Type type = UniversalId::Type_None); + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const; + ///< \param type Will be ignored, unless the collection supports multiple record types void addColumn (Column *column); }; - template - IdCollection::IdCollection() + template + IdCollection::IdCollection() {} - template - IdCollection::~IdCollection() + template + IdCollection::~IdCollection() { for (typename std::vector *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter) delete *iter; } - template - void IdCollection::add (const ESXRecordT& record) + template + void IdCollection::add (const ESXRecordT& record) { - std::string id = Misc::StringUtils::lowerCase(record.mId); + std::string id = Misc::StringUtils::lowerCase (IdAccessorT().getId (record)); std::map::iterator iter = mIndex.find (id); @@ -179,20 +215,20 @@ namespace CSMWorld } } - template - int IdCollection::getSize() const + template + int IdCollection::getSize() const { return mRecords.size(); } - template - std::string IdCollection::getId (int index) const + template + std::string IdCollection::getId (int index) const { - return mRecords.at (index).get().mId; + return IdAccessorT().getId (mRecords.at (index).get()); } - template - int IdCollection::getIndex (const std::string& id) const + template + int IdCollection::getIndex (const std::string& id) const { int index = searchId (id); @@ -202,38 +238,38 @@ namespace CSMWorld return index; } - template - int IdCollection::getColumns() const + template + int IdCollection::getColumns() const { return mColumns.size(); } - template - QVariant IdCollection::getData (int index, int column) const + template + QVariant IdCollection::getData (int index, int column) const { return mColumns.at (column)->get (mRecords.at (index)); } - template - void IdCollection::setData (int index, int column, const QVariant& data) + template + void IdCollection::setData (int index, int column, const QVariant& data) { return mColumns.at (column)->set (mRecords.at (index), data); } - template - const ColumnBase& IdCollection::getColumn (int column) const + template + const ColumnBase& IdCollection::getColumn (int column) const { return *mColumns.at (column); } - template - void IdCollection::addColumn (Column *column) + template + void IdCollection::addColumn (Column *column) { mColumns.push_back (column); } - template - void IdCollection::merge() + template + void IdCollection::merge() { for (typename std::vector >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) iter->merge(); @@ -241,16 +277,22 @@ namespace CSMWorld purge(); } - template - void IdCollection::purge() + template + void IdCollection::purge() { - mRecords.erase (std::remove_if (mRecords.begin(), mRecords.end(), - std::mem_fun_ref (&Record::isErased) // I want lambda :( - ), mRecords.end()); + int i = 0; + + while (i (mRecords.size())) + { + if (mRecords[i].isErased()) + removeRows (i, 1); + else + ++i; + } } - template - void IdCollection::removeRows (int index, int count) + template + void IdCollection::removeRows (int index, int count) { mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count); @@ -274,17 +316,18 @@ namespace CSMWorld } } - template - void IdCollection::appendBlankRecord (const std::string& id) + template + void IdCollection::appendBlankRecord (const std::string& id, + UniversalId::Type type) { ESXRecordT record; - record.mId = id; + IdAccessorT().getId (record) = id; record.blank(); add (record); } - template - int IdCollection::searchId (const std::string& id) const + template + int IdCollection::searchId (const std::string& id) const { std::string id2 = Misc::StringUtils::lowerCase(id); @@ -296,35 +339,32 @@ namespace CSMWorld return iter->second; } - template - void IdCollection::replace (int index, const RecordBase& record) + template + void IdCollection::replace (int index, const RecordBase& record) { mRecords.at (index) = dynamic_cast&> (record); } - template - void IdCollection::appendRecord (const RecordBase& record) + template + void IdCollection::appendRecord (const RecordBase& record, + UniversalId::Type type) { mRecords.push_back (dynamic_cast&> (record)); - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (getId (record)), mRecords.size()-1)); + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( + dynamic_cast&> (record).get())), + mRecords.size()-1)); } - template - std::string IdCollection::getId (const RecordBase& record) const - { - const Record& record2 = dynamic_cast&> (record); - return (record2.isModified() ? record2.mModified : record2.mBase).mId; - } - - template - void IdCollection::load (ESM::ESMReader& reader, bool base) + template + void IdCollection::load (ESM::ESMReader& reader, bool base, + UniversalId::Type type) { std::string id = reader.getHNOString ("NAME"); - int index = searchId (id); - if (reader.isNextSub ("DELE")) { + int index = searchId (id); + reader.skipRecord(); if (index==-1) @@ -347,9 +387,11 @@ namespace CSMWorld else { ESXRecordT record; - record.mId = id; + IdAccessorT().getId (record) = id; record.load (reader); + int index = searchId (IdAccessorT().getId (record)); + if (index==-1) { // new record @@ -372,12 +414,25 @@ namespace CSMWorld } } - template - const RecordBase& IdCollection::getRecord (const std::string& id) const + template + int IdCollection::getAppendIndex (UniversalId::Type type) const + { + return static_cast (mRecords.size()); + } + + template + const Record& IdCollection::getRecord (const std::string& id) const { int index = getIndex (id); return mRecords.at (index); } + + template + const Record& IdCollection::getRecord (int index) const + { + return mRecords.at (index); + } + } #endif diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index afed6b6eda..386ca87025 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -96,9 +96,28 @@ bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& paren return true; } +QModelIndex CSMWorld::IdTable::index (int row, int column, const QModelIndex& parent) const +{ + if (parent.isValid()) + return QModelIndex(); + + if (row<0 || row>=mIdCollection->getSize()) + return QModelIndex(); + + if (column<0 || column>=mIdCollection->getColumns()) + return QModelIndex(); + + return createIndex (row, column); +} + +QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const +{ + return QModelIndex(); +} + void CSMWorld::IdTable::addRecord (const std::string& id) { - int index = mIdCollection->getSize(); + int index = mIdCollection->getAppendIndex(); beginInsertRows (QModelIndex(), index, index); @@ -112,13 +131,13 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) return index (mIdCollection->getIndex (id), column); } -void CSMWorld::IdTable::setRecord (const RecordBase& record) +void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record) { - int index = mIdCollection->searchId (mIdCollection->getId (record)); + int index = mIdCollection->searchId (id); if (index==-1) { - int index = mIdCollection->getSize(); + int index = mIdCollection->getAppendIndex(); beginInsertRows (QModelIndex(), index, index); diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index deaebaa38a..de0dde56e2 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -1,14 +1,14 @@ #ifndef CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H -#include +#include namespace CSMWorld { class IdCollectionBase; class RecordBase; - class IdTable : public QAbstractTableModel + class IdTable : public QAbstractItemModel { Q_OBJECT @@ -39,11 +39,16 @@ namespace CSMWorld virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); + virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) + const; + + virtual QModelIndex parent (const QModelIndex& index) const; + void addRecord (const std::string& id); QModelIndex getModelIndex (const std::string& id, int column) const; - void setRecord (const RecordBase& record); + void setRecord (const std::string& id, const RecordBase& record); ///< Add record or overwrite existing recrod. const RecordBase& getRecord (const std::string& id) const; diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp index 53bb7ea2ce..861fc47a3b 100644 --- a/apps/opencs/model/world/record.hpp +++ b/apps/opencs/model/world/record.hpp @@ -22,6 +22,9 @@ namespace CSMWorld virtual RecordBase *clone() const = 0; + virtual void assign (const RecordBase& record) = 0; + ///< Will throw an exception if the types don't match. + bool isDeleted() const; bool isErased() const; @@ -37,9 +40,14 @@ namespace CSMWorld virtual RecordBase *clone() const; + virtual void assign (const RecordBase& record); + const ESXRecordT& get() const; ///< Throws an exception, if the record is deleted. + ESXRecordT& get(); + ///< Throws an exception, if the record is deleted. + const ESXRecordT& getBase() const; ///< Throws an exception, if the record is deleted. Returns modified, if there is no base. @@ -56,13 +64,28 @@ namespace CSMWorld return new Record (*this); } + template + void Record::assign (const RecordBase& record) + { + *this = dynamic_cast& > (record); + } + template const ESXRecordT& Record::get() const { if (mState==State_Erased) throw std::logic_error ("attempt to access a deleted record"); - return mState==State_BaseOnly ? mBase : mModified; + return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified; + } + + template + ESXRecordT& Record::get() + { + if (mState==State_Erased) + throw std::logic_error ("attempt to access a deleted record"); + + return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified; } template @@ -83,7 +106,7 @@ namespace CSMWorld mModified = modified; if (mState!=State_ModifiedOnly) - mState = mBase==mModified ? State_BaseOnly : State_Modified; + mState = State_Modified; } template diff --git a/apps/opencs/model/world/refidadapter.cpp b/apps/opencs/model/world/refidadapter.cpp new file mode 100644 index 0000000000..94ae38c3c2 --- /dev/null +++ b/apps/opencs/model/world/refidadapter.cpp @@ -0,0 +1,6 @@ + +#include "refidadapter.hpp" + +CSMWorld::RefIdAdapter::RefIdAdapter() {} + +CSMWorld::RefIdAdapter::~RefIdAdapter() {} \ No newline at end of file diff --git a/apps/opencs/model/world/refidadapter.hpp b/apps/opencs/model/world/refidadapter.hpp new file mode 100644 index 0000000000..df0ceae706 --- /dev/null +++ b/apps/opencs/model/world/refidadapter.hpp @@ -0,0 +1,37 @@ +#ifndef CSM_WOLRD_REFIDADAPTER_H +#define CSM_WOLRD_REFIDADAPTER_H + +#include + +class QVariant; + +namespace CSMWorld +{ + class RefIdColumn; + class RefIdData; + class RecordBase; + + class RefIdAdapter + { + // not implemented + RefIdAdapter (const RefIdAdapter&); + RefIdAdapter& operator= (const RefIdAdapter&); + + public: + + RefIdAdapter(); + + virtual ~RefIdAdapter(); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int idnex) + const = 0; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const = 0; + ///< If the data type does not match an exception is thrown. + + virtual std::string getId (const RecordBase& record) const = 0; + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp new file mode 100644 index 0000000000..f00e9fc77b --- /dev/null +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -0,0 +1,575 @@ + +#include "refidadapterimp.hpp" + +CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const InventoryColumns& columns, + const RefIdColumn *autoCalc) +: InventoryRefIdAdapter (UniversalId::Type_Potion, columns), + mAutoCalc (autoCalc) +{} + +QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); + + if (column==mAutoCalc) + return record.get().mData.mAutoCalc!=0; + + return InventoryRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); + + if (column==mAutoCalc) + record.get().mData.mAutoCalc = value.toInt(); + else + InventoryRefIdAdapter::setData (column, data, index, value); +} + + +CSMWorld::ApparatusRefIdAdapter::ApparatusRefIdAdapter (const InventoryColumns& columns, + const RefIdColumn *type, const RefIdColumn *quality) +: InventoryRefIdAdapter (UniversalId::Type_Apparatus, columns), + mType (type), mQuality (quality) +{} + +QVariant CSMWorld::ApparatusRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); + + if (column==mType) + return record.get().mData.mType; + + if (column==mQuality) + return record.get().mData.mQuality; + + return InventoryRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); + + if (column==mType) + record.get().mData.mType = value.toInt(); + else if (column==mQuality) + record.get().mData.mQuality = value.toFloat(); + else + InventoryRefIdAdapter::setData (column, data, index, value); +} + + +CSMWorld::ArmorRefIdAdapter::ArmorRefIdAdapter (const EnchantableColumns& columns, + const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor) +: EnchantableRefIdAdapter (UniversalId::Type_Armor, columns), + mType (type), mHealth (health), mArmor (armor) +{} + +QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); + + if (column==mType) + return record.get().mData.mType; + + if (column==mHealth) + return record.get().mData.mHealth; + + if (column==mArmor) + return record.get().mData.mArmor; + + return EnchantableRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); + + if (column==mType) + record.get().mData.mType = value.toInt(); + else if (column==mHealth) + record.get().mData.mHealth = value.toInt(); + else if (column==mArmor) + record.get().mData.mArmor = value.toInt(); + else + EnchantableRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns, + const RefIdColumn *scroll, const RefIdColumn *skill) +: EnchantableRefIdAdapter (UniversalId::Type_Book, columns), + mScroll (scroll), mSkill (skill) +{} + +QVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); + + if (column==mScroll) + return record.get().mData.mIsScroll!=0; + + if (column==mSkill) + return record.get().mData.mSkillID; + + return EnchantableRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); + + if (column==mScroll) + record.get().mData.mIsScroll = value.toInt(); + else if (column==mSkill) + record.get().mData.mSkillID = value.toInt(); + else + EnchantableRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns, + const RefIdColumn *type) +: EnchantableRefIdAdapter (UniversalId::Type_Clothing, columns), mType (type) +{} + +QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); + + if (column==mType) + return record.get().mData.mType; + + return EnchantableRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); + + if (column==mType) + record.get().mData.mType = value.toInt(); + else + EnchantableRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns, + const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn) +: NameRefIdAdapter (UniversalId::Type_Container, columns), mWeight (weight), + mOrganic (organic), mRespawn (respawn) +{} + +QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); + + if (column==mWeight) + return record.get().mWeight; + + if (column==mOrganic) + return (record.get().mFlags & ESM::Container::Organic)!=0; + + if (column==mRespawn) + return (record.get().mFlags & ESM::Container::Respawn)!=0; + + return NameRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); + + if (column==mWeight) + record.get().mWeight = value.toFloat(); + else if (column==mOrganic) + { + if (value.toInt()) + record.get().mFlags |= ESM::Container::Organic; + else + record.get().mFlags &= ~ESM::Container::Organic; + } + else if (column==mRespawn) + { + if (value.toInt()) + record.get().mFlags |= ESM::Container::Respawn; + else + record.get().mFlags &= ~ESM::Container::Respawn; + } + else + NameRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) +: ActorColumns (actorColumns) +{} + +CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) +: ActorRefIdAdapter (UniversalId::Type_Creature, columns), mColumns (columns) +{} + +QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); + + if (column==mColumns.mType) + return record.get().mData.mType; + + if (column==mColumns.mSoul) + return record.get().mData.mSoul; + + if (column==mColumns.mScale) + return record.get().mScale; + + if (column==mColumns.mOriginal) + return QString::fromUtf8 (record.get().mOriginal.c_str()); + + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + return (record.get().mFlags & iter->second)!=0; + + return ActorRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); + + if (column==mColumns.mType) + record.get().mData.mType = value.toInt(); + else if (column==mColumns.mSoul) + record.get().mData.mSoul = value.toInt(); + else if (column==mColumns.mScale) + record.get().mScale = value.toFloat(); + else if (column==mColumns.mOriginal) + record.get().mOriginal = value.toString().toUtf8().constData(); + else + { + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + { + if (value.toInt()!=0) + record.get().mFlags |= iter->second; + else + record.get().mFlags &= ~iter->second; + } + else + ActorRefIdAdapter::setData (column, data, index, value); + } +} + +CSMWorld::DoorRefIdAdapter::DoorRefIdAdapter (const NameColumns& columns, + const RefIdColumn *openSound, const RefIdColumn *closeSound) +: NameRefIdAdapter (UniversalId::Type_Door, columns), mOpenSound (openSound), + mCloseSound (closeSound) +{} + +QVariant CSMWorld::DoorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); + + if (column==mOpenSound) + return QString::fromUtf8 (record.get().mOpenSound.c_str()); + + if (column==mCloseSound) + return QString::fromUtf8 (record.get().mCloseSound.c_str()); + + return NameRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); + + if (column==mOpenSound) + record.get().mOpenSound = value.toString().toUtf8().constData(); + else if (column==mCloseSound) + record.get().mCloseSound = value.toString().toUtf8().constData(); + else + NameRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns) +: InventoryColumns (columns) {} + +CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns) +: InventoryRefIdAdapter (UniversalId::Type_Light, columns), mColumns (columns) +{} + +QVariant CSMWorld::LightRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); + + if (column==mColumns.mTime) + return record.get().mData.mTime; + + if (column==mColumns.mRadius) + return record.get().mData.mRadius; + + if (column==mColumns.mColor) + return record.get().mData.mColor; + + if (column==mColumns.mSound) + return QString::fromUtf8 (record.get().mSound.c_str()); + + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + return (record.get().mData.mFlags & iter->second)!=0; + + return InventoryRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); + + if (column==mColumns.mTime) + record.get().mData.mTime = value.toInt(); + else if (column==mColumns.mRadius) + record.get().mData.mRadius = value.toInt(); + else if (column==mColumns.mColor) + record.get().mData.mColor = value.toInt(); + else if (column==mColumns.mSound) + record.get().mSound = value.toString().toUtf8().constData(); + else + { + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + { + if (value.toInt()!=0) + record.get().mData.mFlags |= iter->second; + else + record.get().mData.mFlags &= ~iter->second; + } + else + InventoryRefIdAdapter::setData (column, data, index, value); + } +} + +CSMWorld::MiscRefIdAdapter::MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key) +: InventoryRefIdAdapter (UniversalId::Type_Miscellaneous, columns), mKey (key) +{} + +QVariant CSMWorld::MiscRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); + + if (column==mKey) + return record.get().mData.mIsKey!=0; + + return InventoryRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); + + if (column==mKey) + record.get().mData.mIsKey = value.toInt(); + else + InventoryRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns) {} + +CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) +: ActorRefIdAdapter (UniversalId::Type_Npc, columns), mColumns (columns) +{} + +QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) + const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + if (column==mColumns.mRace) + return QString::fromUtf8 (record.get().mRace.c_str()); + + if (column==mColumns.mClass) + return QString::fromUtf8 (record.get().mClass.c_str()); + + if (column==mColumns.mFaction) + return QString::fromUtf8 (record.get().mFaction.c_str()); + + if (column==mColumns.mHair) + return QString::fromUtf8 (record.get().mHair.c_str()); + + if (column==mColumns.mHead) + return QString::fromUtf8 (record.get().mHead.c_str()); + + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + return (record.get().mFlags & iter->second)!=0; + + return ActorRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + if (column==mColumns.mRace) + record.get().mRace = value.toString().toUtf8().constData(); + else if (column==mColumns.mClass) + record.get().mClass = value.toString().toUtf8().constData(); + else if (column==mColumns.mFaction) + record.get().mFaction = value.toString().toUtf8().constData(); + else if (column==mColumns.mHair) + record.get().mHair = value.toString().toUtf8().constData(); + else if (column==mColumns.mHead) + record.get().mHead = value.toString().toUtf8().constData(); + else + { + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + { + if (value.toInt()!=0) + record.get().mFlags |= iter->second; + else + record.get().mFlags &= ~iter->second; + } + else + ActorRefIdAdapter::setData (column, data, index, value); + } +} + +CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns) +: EnchantableColumns (columns) {} + +CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns) +: EnchantableRefIdAdapter (UniversalId::Type_Weapon, columns), mColumns (columns) +{} + +QVariant CSMWorld::WeaponRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); + + if (column==mColumns.mType) + return record.get().mData.mType; + + if (column==mColumns.mHealth) + return record.get().mData.mHealth; + + if (column==mColumns.mSpeed) + return record.get().mData.mSpeed; + + if (column==mColumns.mReach) + return record.get().mData.mReach; + + for (int i=0; i<2; ++i) + { + if (column==mColumns.mChop[i]) + return record.get().mData.mChop[i]; + + if (column==mColumns.mSlash[i]) + return record.get().mData.mSlash[i]; + + if (column==mColumns.mThrust[i]) + return record.get().mData.mThrust[i]; + } + + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + return (record.get().mData.mFlags & iter->second)!=0; + + return EnchantableRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); + + if (column==mColumns.mType) + record.get().mData.mType = value.toInt(); + else if (column==mColumns.mHealth) + record.get().mData.mHealth = value.toInt(); + else if (column==mColumns.mSpeed) + record.get().mData.mSpeed = value.toFloat(); + else if (column==mColumns.mReach) + record.get().mData.mReach = value.toFloat(); + else if (column==mColumns.mChop[0]) + record.get().mData.mChop[0] = value.toInt(); + else if (column==mColumns.mChop[1]) + record.get().mData.mChop[1] = value.toInt(); + else if (column==mColumns.mSlash[0]) + record.get().mData.mSlash[0] = value.toInt(); + else if (column==mColumns.mSlash[1]) + record.get().mData.mSlash[1] = value.toInt(); + else if (column==mColumns.mThrust[0]) + record.get().mData.mThrust[0] = value.toInt(); + else if (column==mColumns.mThrust[1]) + record.get().mData.mThrust[1] = value.toInt(); + else + { + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + { + if (value.toInt()!=0) + record.get().mData.mFlags |= iter->second; + else + record.get().mData.mFlags &= ~iter->second; + } + else + EnchantableRefIdAdapter::setData (column, data, index, value); + } +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp new file mode 100644 index 0000000000..d5d84a8f7e --- /dev/null +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -0,0 +1,766 @@ +#ifndef CSM_WOLRD_REFIDADAPTERIMP_H +#define CSM_WOLRD_REFIDADAPTERIMP_H + +#include + +#include + +#include +#include + +#include "record.hpp" +#include "refiddata.hpp" +#include "universalid.hpp" +#include "refidadapter.hpp" + +namespace CSMWorld +{ + struct BaseColumns + { + const RefIdColumn *mId; + const RefIdColumn *mModified; + const RefIdColumn *mType; + }; + + /// \brief Base adapter for all refereceable record types + template + class BaseRefIdAdapter : public RefIdAdapter + { + UniversalId::Type mType; + BaseColumns mBase; + + public: + + BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base); + + virtual std::string getId (const RecordBase& record) const; + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + + UniversalId::Type getType() const; + }; + + template + BaseRefIdAdapter::BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base) + : mType (type), mBase (base) + {} + + template + std::string BaseRefIdAdapter::getId (const RecordBase& record) const + { + return dynamic_cast&> (record).get().mId; + } + + template + QVariant BaseRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, mType))); + + if (column==mBase.mId) + return QString::fromUtf8 (record.get().mId.c_str()); + + if (column==mBase.mModified) + { + if (record.mState==Record::State_Erased) + return static_cast (Record::State_Deleted); + + return static_cast (record.mState); + } + + if (column==mBase.mType) + return static_cast (mType); + + return QVariant(); + } + + template + void BaseRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, mType))); + + if (column==mBase.mModified) + record.mState = static_cast (value.toInt()); + } + + template + UniversalId::Type BaseRefIdAdapter::getType() const + { + return mType; + } + + + struct ModelColumns : public BaseColumns + { + const RefIdColumn *mModel; + + ModelColumns (const BaseColumns& base) : BaseColumns (base) {} + }; + + /// \brief Adapter for IDs with models (all but levelled lists) + template + class ModelRefIdAdapter : public BaseRefIdAdapter + { + ModelColumns mModel; + + public: + + ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + ModelRefIdAdapter::ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns) + : BaseRefIdAdapter (type, columns), mModel (columns) + {} + + template + QVariant ModelRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mModel.mModel) + return QString::fromUtf8 (record.get().mModel.c_str()); + + return BaseRefIdAdapter::getData (column, data, index); + } + + template + void ModelRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mModel.mModel) + record.get().mModel = value.toString().toUtf8().constData(); + else + BaseRefIdAdapter::setData (column, data, index, value); + } + + struct NameColumns : public ModelColumns + { + const RefIdColumn *mName; + const RefIdColumn *mScript; + + NameColumns (const ModelColumns& base) : ModelColumns (base) {} + }; + + /// \brief Adapter for IDs with names (all but levelled lists and statics) + template + class NameRefIdAdapter : public ModelRefIdAdapter + { + NameColumns mName; + + public: + + NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + NameRefIdAdapter::NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns) + : ModelRefIdAdapter (type, columns), mName (columns) + {} + + template + QVariant NameRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mName.mName) + return QString::fromUtf8 (record.get().mName.c_str()); + + if (column==mName.mScript) + return QString::fromUtf8 (record.get().mScript.c_str()); + + return ModelRefIdAdapter::getData (column, data, index); + } + + template + void NameRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mName.mName) + record.get().mName = value.toString().toUtf8().constData(); + else if (column==mName.mScript) + record.get().mScript = value.toString().toUtf8().constData(); + else + ModelRefIdAdapter::setData (column, data, index, value); + } + + struct InventoryColumns : public NameColumns + { + const RefIdColumn *mIcon; + const RefIdColumn *mWeight; + const RefIdColumn *mValue; + + InventoryColumns (const NameColumns& base) : NameColumns (base) {} + }; + + /// \brief Adapter for IDs that can go into an inventory + template + class InventoryRefIdAdapter : public NameRefIdAdapter + { + InventoryColumns mInventory; + + public: + + InventoryRefIdAdapter (UniversalId::Type type, const InventoryColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + InventoryRefIdAdapter::InventoryRefIdAdapter (UniversalId::Type type, + const InventoryColumns& columns) + : NameRefIdAdapter (type, columns), mInventory (columns) + {} + + template + QVariant InventoryRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mInventory.mIcon) + return QString::fromUtf8 (record.get().mIcon.c_str()); + + if (column==mInventory.mWeight) + return record.get().mData.mWeight; + + if (column==mInventory.mValue) + return record.get().mData.mValue; + + return NameRefIdAdapter::getData (column, data, index); + } + + template + void InventoryRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mInventory.mIcon) + record.get().mIcon = value.toString().toUtf8().constData(); + else if (column==mInventory.mWeight) + record.get().mData.mWeight = value.toFloat(); + else if (column==mInventory.mValue) + record.get().mData.mValue = value.toInt(); + else + NameRefIdAdapter::setData (column, data, index, value); + } + + class PotionRefIdAdapter : public InventoryRefIdAdapter + { + const RefIdColumn *mAutoCalc; + + public: + + PotionRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *autoCalc); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct EnchantableColumns : public InventoryColumns + { + const RefIdColumn *mEnchantment; + const RefIdColumn *mEnchantmentPoints; + + EnchantableColumns (const InventoryColumns& base) : InventoryColumns (base) {} + }; + + /// \brief Adapter for enchantable IDs + template + class EnchantableRefIdAdapter : public InventoryRefIdAdapter + { + EnchantableColumns mEnchantable; + + public: + + EnchantableRefIdAdapter (UniversalId::Type type, const EnchantableColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + EnchantableRefIdAdapter::EnchantableRefIdAdapter (UniversalId::Type type, + const EnchantableColumns& columns) + : InventoryRefIdAdapter (type, columns), mEnchantable (columns) + {} + + template + QVariant EnchantableRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mEnchantable.mEnchantment) + return QString::fromUtf8 (record.get().mEnchant.c_str()); + + if (column==mEnchantable.mEnchantmentPoints) + return static_cast (record.get().mData.mEnchant); + + return InventoryRefIdAdapter::getData (column, data, index); + } + + template + void EnchantableRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, + int index, const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mEnchantable.mEnchantment) + record.get().mEnchant = value.toString().toUtf8().constData(); + else if (column==mEnchantable.mEnchantmentPoints) + record.get().mData.mEnchant = value.toInt(); + else + InventoryRefIdAdapter::setData (column, data, index, value); + } + + struct ToolColumns : public InventoryColumns + { + const RefIdColumn *mQuality; + const RefIdColumn *mUses; + + ToolColumns (const InventoryColumns& base) : InventoryColumns (base) {} + }; + + /// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes) + template + class ToolRefIdAdapter : public InventoryRefIdAdapter + { + ToolColumns mTools; + + public: + + ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + ToolRefIdAdapter::ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns) + : InventoryRefIdAdapter (type, columns), mTools (columns) + {} + + template + QVariant ToolRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mTools.mQuality) + return record.get().mData.mQuality; + + if (column==mTools.mUses) + return record.get().mData.mUses; + + return InventoryRefIdAdapter::getData (column, data, index); + } + + template + void ToolRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, + int index, const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mTools.mQuality) + record.get().mData.mQuality = value.toFloat(); + else if (column==mTools.mUses) + record.get().mData.mUses = value.toInt(); + else + InventoryRefIdAdapter::setData (column, data, index, value); + } + + struct ActorColumns : public NameColumns + { + const RefIdColumn *mHasAi; + const RefIdColumn *mHello; + const RefIdColumn *mFlee; + const RefIdColumn *mFight; + const RefIdColumn *mAlarm; + std::map mServices; + + ActorColumns (const NameColumns& base) : NameColumns (base) {} + }; + + /// \brief Adapter for actor IDs (handles common AI functionality) + template + class ActorRefIdAdapter : public NameRefIdAdapter + { + ActorColumns mActors; + + public: + + ActorRefIdAdapter (UniversalId::Type type, const ActorColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + ActorRefIdAdapter::ActorRefIdAdapter (UniversalId::Type type, + const ActorColumns& columns) + : NameRefIdAdapter (type, columns), mActors (columns) + {} + + template + QVariant ActorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mActors.mHasAi) + return record.get().mHasAI!=0; + + if (column==mActors.mHello) + return record.get().mAiData.mHello; + + if (column==mActors.mFlee) + return record.get().mAiData.mFlee; + + if (column==mActors.mFight) + return record.get().mAiData.mFight; + + if (column==mActors.mAlarm) + return record.get().mAiData.mAlarm; + + std::map::const_iterator iter = + mActors.mServices.find (column); + + if (iter!=mActors.mServices.end()) + return (record.get().mAiData.mServices & iter->second)!=0; + + return NameRefIdAdapter::getData (column, data, index); + } + + template + void ActorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mActors.mHasAi) + record.get().mHasAI = value.toInt(); + else if (column==mActors.mHello) + record.get().mAiData.mHello = value.toInt(); + else if (column==mActors.mFlee) + record.get().mAiData.mFlee = value.toInt(); + else if (column==mActors.mFight) + record.get().mAiData.mFight = value.toInt(); + else if (column==mActors.mAlarm) + record.get().mAiData.mAlarm = value.toInt(); + else + { + typename std::map::const_iterator iter = + mActors.mServices.find (column); + if (iter!=mActors.mServices.end()) + { + if (value.toInt()!=0) + record.get().mAiData.mServices |= iter->second; + else + record.get().mAiData.mServices &= ~iter->second; + } + else + NameRefIdAdapter::setData (column, data, index, value); + } + } + + class ApparatusRefIdAdapter : public InventoryRefIdAdapter + { + const RefIdColumn *mType; + const RefIdColumn *mQuality; + + public: + + ApparatusRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *type, + const RefIdColumn *quality); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class ArmorRefIdAdapter : public EnchantableRefIdAdapter + { + const RefIdColumn *mType; + const RefIdColumn *mHealth; + const RefIdColumn *mArmor; + + public: + + ArmorRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, + const RefIdColumn *health, const RefIdColumn *armor); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class BookRefIdAdapter : public EnchantableRefIdAdapter + { + const RefIdColumn *mScroll; + const RefIdColumn *mSkill; + + public: + + BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *scroll, + const RefIdColumn *skill); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class ClothingRefIdAdapter : public EnchantableRefIdAdapter + { + const RefIdColumn *mType; + + public: + + ClothingRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class ContainerRefIdAdapter : public NameRefIdAdapter + { + const RefIdColumn *mWeight; + const RefIdColumn *mOrganic; + const RefIdColumn *mRespawn; + + public: + + ContainerRefIdAdapter (const NameColumns& columns, const RefIdColumn *weight, + const RefIdColumn *organic, const RefIdColumn *respawn); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct CreatureColumns : public ActorColumns + { + std::map mFlags; + const RefIdColumn *mType; + const RefIdColumn *mSoul; + const RefIdColumn *mScale; + const RefIdColumn *mOriginal; + + CreatureColumns (const ActorColumns& actorColumns); + }; + + class CreatureRefIdAdapter : public ActorRefIdAdapter + { + CreatureColumns mColumns; + + public: + + CreatureRefIdAdapter (const CreatureColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class DoorRefIdAdapter : public NameRefIdAdapter + { + const RefIdColumn *mOpenSound; + const RefIdColumn *mCloseSound; + + public: + + DoorRefIdAdapter (const NameColumns& columns, const RefIdColumn *openSound, + const RefIdColumn *closeSound); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct LightColumns : public InventoryColumns + { + const RefIdColumn *mTime; + const RefIdColumn *mRadius; + const RefIdColumn *mColor; + const RefIdColumn *mSound; + std::map mFlags; + + LightColumns (const InventoryColumns& columns); + }; + + class LightRefIdAdapter : public InventoryRefIdAdapter + { + LightColumns mColumns; + + public: + + LightRefIdAdapter (const LightColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class MiscRefIdAdapter : public InventoryRefIdAdapter + { + const RefIdColumn *mKey; + + public: + + MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct NpcColumns : public ActorColumns + { + std::map mFlags; + const RefIdColumn *mRace; + const RefIdColumn *mClass; + const RefIdColumn *mFaction; + const RefIdColumn *mHair; + const RefIdColumn *mHead; + + NpcColumns (const ActorColumns& actorColumns); + }; + + class NpcRefIdAdapter : public ActorRefIdAdapter + { + NpcColumns mColumns; + + public: + + NpcRefIdAdapter (const NpcColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct WeaponColumns : public EnchantableColumns + { + const RefIdColumn *mType; + const RefIdColumn *mHealth; + const RefIdColumn *mSpeed; + const RefIdColumn *mReach; + const RefIdColumn *mChop[2]; + const RefIdColumn *mSlash[2]; + const RefIdColumn *mThrust[2]; + std::map mFlags; + + WeaponColumns (const EnchantableColumns& columns); + }; + + class WeaponRefIdAdapter : public EnchantableRefIdAdapter + { + WeaponColumns mColumns; + + public: + + WeaponRefIdAdapter (const WeaponColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; +} + +#endif diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp new file mode 100644 index 0000000000..e11fe0fc83 --- /dev/null +++ b/apps/opencs/model/world/refidcollection.cpp @@ -0,0 +1,540 @@ + +#include "refidcollection.hpp" + +#include + +#include "refidadapter.hpp" +#include "refidadapterimp.hpp" + +CSMWorld::RefIdColumn::RefIdColumn (const std::string& title, Display displayType, int flag, + bool editable, bool userEditable) +: ColumnBase (title, displayType, flag), mEditable (editable), mUserEditable (userEditable) +{} + +bool CSMWorld::RefIdColumn::isEditable() const +{ + return mEditable; +} + +bool CSMWorld::RefIdColumn::isUserEditable() const +{ + return mUserEditable; +} + + +const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdaptor (UniversalId::Type type) const +{ + std::map::const_iterator iter = mAdapters.find (type); + + if (iter==mAdapters.end()) + throw std::logic_error ("unsupported type in RefIdCollection"); + + return *iter->second; +} + +CSMWorld::RefIdCollection::RefIdCollection() +{ + BaseColumns baseColumns; + + mColumns.push_back (RefIdColumn ("ID", ColumnBase::Display_String, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mId = &mColumns.back(); + mColumns.push_back (RefIdColumn ("*", ColumnBase::Display_Integer, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mModified = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Type", ColumnBase::Display_Integer, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mType = &mColumns.back(); + + ModelColumns modelColumns (baseColumns); + + mColumns.push_back (RefIdColumn ("Model", ColumnBase::Display_String)); + modelColumns.mModel = &mColumns.back(); + + NameColumns nameColumns (modelColumns); + + mColumns.push_back (RefIdColumn ("Name", ColumnBase::Display_String)); + nameColumns.mName = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Script", ColumnBase::Display_String)); + nameColumns.mScript = &mColumns.back(); + + InventoryColumns inventoryColumns (nameColumns); + + mColumns.push_back (RefIdColumn ("Icon", ColumnBase::Display_String)); + inventoryColumns.mIcon = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Weight", ColumnBase::Display_Float)); + inventoryColumns.mWeight = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Value", ColumnBase::Display_Integer)); + inventoryColumns.mValue = &mColumns.back(); + + EnchantableColumns enchantableColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn ("Enchantment", ColumnBase::Display_String)); + enchantableColumns.mEnchantment = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Enchantment Points", ColumnBase::Display_Integer)); + enchantableColumns.mEnchantmentPoints = &mColumns.back(); + + ToolColumns toolsColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn ("Quality", ColumnBase::Display_Float)); + toolsColumns.mQuality = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Uses", ColumnBase::Display_Integer)); + toolsColumns.mUses = &mColumns.back(); + + ActorColumns actorsColumns (nameColumns); + + mColumns.push_back (RefIdColumn ("AI", ColumnBase::Display_Boolean)); + actorsColumns.mHasAi = &mColumns.back(); + mColumns.push_back (RefIdColumn ("AI Hello", ColumnBase::Display_Integer)); + actorsColumns.mHello = &mColumns.back(); + mColumns.push_back (RefIdColumn ("AI Flee", ColumnBase::Display_Integer)); + actorsColumns.mFlee = &mColumns.back(); + mColumns.push_back (RefIdColumn ("AI Fight", ColumnBase::Display_Integer)); + actorsColumns.mFight = &mColumns.back(); + mColumns.push_back (RefIdColumn ("AI Alarm", ColumnBase::Display_Integer)); + actorsColumns.mAlarm = &mColumns.back(); + + static const struct + { + const char *mName; + unsigned int mFlag; + } sServiceTable[] = + { + { "Buys Weapons", ESM::NPC::Weapon}, + { "Buys Armor", ESM::NPC::Armor}, + { "Buys Clothing", ESM::NPC::Clothing}, + { "Buys Books", ESM::NPC::Books}, + { "Buys Ingredients", ESM::NPC::Ingredients}, + { "Buys Lockpicks", ESM::NPC::Picks}, + { "Buys Probes", ESM::NPC::Probes}, + { "Buys Lights", ESM::NPC::Lights}, + { "Buys Apparati", ESM::NPC::Apparatus}, + { "Buys Repair Items", ESM::NPC::RepairItem}, + { "Buys Misc Items", ESM::NPC::Misc}, + { "Buys Potions", ESM::NPC::Potions}, + { "Buys Magic Items", ESM::NPC::MagicItems}, + { "Sells Spells", ESM::NPC::Spells}, + { "Trainer", ESM::NPC::Training}, + { "Spellmaking", ESM::NPC::Spellmaking}, + { "Enchanting Service", ESM::NPC::Enchanting}, + { "Repair Serivce", ESM::NPC::Repair}, + { 0, 0 } + }; + + for (int i=0; sServiceTable[i].mName; ++i) + { + mColumns.push_back (RefIdColumn (sServiceTable[i].mName, ColumnBase::Display_Boolean)); + actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag)); + } + + mColumns.push_back (RefIdColumn ("Auto Calc", ColumnBase::Display_Boolean)); + const RefIdColumn *autoCalc = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Apparatus Type", ColumnBase::Display_ApparatusType)); + const RefIdColumn *apparatusType = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Armor Type", ColumnBase::Display_ArmorType)); + const RefIdColumn *armorType = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Health", ColumnBase::Display_Integer)); + const RefIdColumn *health = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Armor Value", ColumnBase::Display_Integer)); + const RefIdColumn *armor = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Scroll", ColumnBase::Display_Boolean)); + const RefIdColumn *scroll = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Attribute", ColumnBase::Display_Attribute)); + const RefIdColumn *attribute = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Clothing Type", ColumnBase::Display_ClothingType)); + const RefIdColumn *clothingType = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Weight Capacity", ColumnBase::Display_Float)); + const RefIdColumn *weightCapacity = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Organic Container", ColumnBase::Display_Boolean)); + const RefIdColumn *organic = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Respawn", ColumnBase::Display_Boolean)); + const RefIdColumn *respawn = &mColumns.back(); + + CreatureColumns creatureColumns (actorsColumns); + + mColumns.push_back (RefIdColumn ("Creature Type", ColumnBase::Display_CreatureType)); + creatureColumns.mType = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Soul Points", ColumnBase::Display_Integer)); + creatureColumns.mSoul = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Scale", ColumnBase::Display_Float)); + creatureColumns.mScale = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Original Creature", ColumnBase::Display_String)); + creatureColumns.mOriginal = &mColumns.back(); + + static const struct + { + const char *mName; + unsigned int mFlag; + } sCreatureFlagTable[] = + { + { "Biped", ESM::Creature::Biped }, + { "Has Weapon", ESM::Creature::Weapon }, + { "No Movement", ESM::Creature::None }, + { "Swims", ESM::Creature::Swims }, + { "Flies", ESM::Creature::Flies }, + { "Walks", ESM::Creature::Walks }, + { "Essential", ESM::Creature::Essential }, + { "Skeleton Blood", ESM::Creature::Skeleton }, + { "Metal Blood", ESM::Creature::Metal }, + { 0, 0 } + }; + + // for re-use in NPC records + const RefIdColumn *essential = 0; + const RefIdColumn *skeletonBlood = 0; + const RefIdColumn *metalBlood = 0; + + for (int i=0; sCreatureFlagTable[i].mName; ++i) + { + mColumns.push_back (RefIdColumn (sCreatureFlagTable[i].mName, ColumnBase::Display_Boolean)); + creatureColumns.mFlags.insert (std::make_pair (&mColumns.back(), sCreatureFlagTable[i].mFlag)); + + switch (sCreatureFlagTable[i].mFlag) + { + case ESM::Creature::Essential: essential = &mColumns.back(); break; + case ESM::Creature::Skeleton: skeletonBlood = &mColumns.back(); break; + case ESM::Creature::Metal: metalBlood = &mColumns.back(); break; + } + } + + creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn)); + + mColumns.push_back (RefIdColumn ("Open Sound", ColumnBase::Display_String)); + const RefIdColumn *openSound = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Close Sound", ColumnBase::Display_String)); + const RefIdColumn *closeSound = &mColumns.back(); + + LightColumns lightColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn ("Duration", ColumnBase::Display_Integer)); + lightColumns.mTime = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Radius", ColumnBase::Display_Integer)); + lightColumns.mRadius = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Colour", ColumnBase::Display_Integer)); + lightColumns.mColor = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Sound", ColumnBase::Display_String)); + lightColumns.mSound = &mColumns.back(); + + static const struct + { + const char *mName; + unsigned int mFlag; + } sLightFlagTable[] = + { + { "Dynamic", ESM::Light::Dynamic }, + { "Portable", ESM::Light::Carry }, + { "Negative Light", ESM::Light::Negative }, + { "Flickering", ESM::Light::Flicker }, + { "Slow Flickering", ESM::Light::Flicker }, + { "Pulsing", ESM::Light::Pulse }, + { "Slow Pulsing", ESM::Light::PulseSlow }, + { "Fire", ESM::Light::Fire }, + { "Off by default", ESM::Light::OffDefault }, + { 0, 0 } + }; + + for (int i=0; sLightFlagTable[i].mName; ++i) + { + mColumns.push_back (RefIdColumn (sLightFlagTable[i].mName, ColumnBase::Display_Boolean)); + lightColumns.mFlags.insert (std::make_pair (&mColumns.back(), sLightFlagTable[i].mFlag)); + } + + mColumns.push_back (RefIdColumn ("Key", ColumnBase::Display_Boolean)); + const RefIdColumn *key = &mColumns.back(); + + NpcColumns npcColumns (actorsColumns); + + mColumns.push_back (RefIdColumn ("Race", ColumnBase::Display_String)); + npcColumns.mRace = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Class", ColumnBase::Display_String)); + npcColumns.mClass = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Faction", ColumnBase::Display_String)); + npcColumns.mFaction = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Hair", ColumnBase::Display_String)); + npcColumns.mHair = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Head", ColumnBase::Display_String)); + npcColumns.mHead = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Female", ColumnBase::Display_Boolean)); + npcColumns.mFlags.insert (std::make_pair (&mColumns.back(), ESM::NPC::Female)); + + npcColumns.mFlags.insert (std::make_pair (essential, ESM::NPC::Essential)); + + npcColumns.mFlags.insert (std::make_pair (respawn, ESM::NPC::Respawn)); + + npcColumns.mFlags.insert (std::make_pair (autoCalc, ESM::NPC::Autocalc)); + + npcColumns.mFlags.insert (std::make_pair (skeletonBlood, ESM::NPC::Skeleton)); + + npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal)); + + WeaponColumns weaponColumns (enchantableColumns); + + mColumns.push_back (RefIdColumn ("Weapon Type", ColumnBase::Display_WeaponType)); + weaponColumns.mType = &mColumns.back(); + + weaponColumns.mHealth = health; + + mColumns.push_back (RefIdColumn ("Weapon Speed", ColumnBase::Display_Float)); + weaponColumns.mSpeed = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Weapon Reach", ColumnBase::Display_Float)); + weaponColumns.mReach = &mColumns.back(); + + for (int i=0; i<2; ++i) + { + std::string suffix = i==0 ? "Min " : "Max "; + + mColumns.push_back (RefIdColumn ("Chop" + suffix, ColumnBase::Display_Integer)); + weaponColumns.mChop[i] = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Slash" + suffix, ColumnBase::Display_Integer)); + weaponColumns.mSlash[i] = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Thrust" + suffix, ColumnBase::Display_Integer)); + weaponColumns.mThrust[i] = &mColumns.back(); + } + + static const struct + { + const char *mName; + unsigned int mFlag; + } sWeaponFlagTable[] = + { + { "Magical", ESM::Weapon::Magical }, + { "Silver", ESM::Weapon::Silver }, + { 0, 0 } + }; + + for (int i=0; sWeaponFlagTable[i].mName; ++i) + { + mColumns.push_back (RefIdColumn (sWeaponFlagTable[i].mName, ColumnBase::Display_Boolean)); + weaponColumns.mFlags.insert (std::make_pair (&mColumns.back(), sWeaponFlagTable[i].mFlag)); + } + + mAdapters.insert (std::make_pair (UniversalId::Type_Activator, + new NameRefIdAdapter (UniversalId::Type_Activator, nameColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Potion, + new PotionRefIdAdapter (inventoryColumns, autoCalc))); + mAdapters.insert (std::make_pair (UniversalId::Type_Apparatus, + new ApparatusRefIdAdapter (inventoryColumns, apparatusType, toolsColumns.mQuality))); + mAdapters.insert (std::make_pair (UniversalId::Type_Armor, + new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor))); + mAdapters.insert (std::make_pair (UniversalId::Type_Book, + new BookRefIdAdapter (enchantableColumns, scroll, attribute))); + mAdapters.insert (std::make_pair (UniversalId::Type_Clothing, + new ClothingRefIdAdapter (enchantableColumns, clothingType))); + mAdapters.insert (std::make_pair (UniversalId::Type_Container, + new ContainerRefIdAdapter (nameColumns, weightCapacity, organic, respawn))); + mAdapters.insert (std::make_pair (UniversalId::Type_Creature, + new CreatureRefIdAdapter (creatureColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Door, + new DoorRefIdAdapter (nameColumns, openSound, closeSound))); + mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient, + new InventoryRefIdAdapter (UniversalId::Type_Ingredient, inventoryColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, + new BaseRefIdAdapter ( + UniversalId::Type_CreatureLevelledList, baseColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_ItemLevelledList, + new BaseRefIdAdapter (UniversalId::Type_ItemLevelledList, baseColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Light, + new LightRefIdAdapter (lightColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Lockpick, + new ToolRefIdAdapter (UniversalId::Type_Lockpick, toolsColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Miscellaneous, + new MiscRefIdAdapter (inventoryColumns, key))); + mAdapters.insert (std::make_pair (UniversalId::Type_Npc, + new NpcRefIdAdapter (npcColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Probe, + new ToolRefIdAdapter (UniversalId::Type_Probe, toolsColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Repair, + new ToolRefIdAdapter (UniversalId::Type_Repair, toolsColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Static, + new ModelRefIdAdapter (UniversalId::Type_Static, modelColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Weapon, + new WeaponRefIdAdapter (weaponColumns))); +} + +CSMWorld::RefIdCollection::~RefIdCollection() +{ + for (std::map::iterator iter (mAdapters.begin()); + iter!=mAdapters.end(); ++iter) + delete iter->second; +} + +int CSMWorld::RefIdCollection::getSize() const +{ + return mData.getSize(); +} + +std::string CSMWorld::RefIdCollection::getId (int index) const +{ + return getData (index, 0).toString().toUtf8().constData(); +} + +int CSMWorld::RefIdCollection::getIndex (const std::string& id) const +{ + int index = searchId (id); + + if (index==-1) + throw std::runtime_error ("invalid ID: " + id); + + return index; +} + +int CSMWorld::RefIdCollection::getColumns() const +{ + return mColumns.size(); +} + +const CSMWorld::ColumnBase& CSMWorld::RefIdCollection::getColumn (int column) const +{ + return mColumns.at (column); +} + +QVariant CSMWorld::RefIdCollection::getData (int index, int column) const +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + const RefIdAdapter& adaptor = findAdaptor (localIndex.second); + + return adaptor.getData (&mColumns.at (column), mData, localIndex.first); +} + +void CSMWorld::RefIdCollection::setData (int index, int column, const QVariant& data) +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + const RefIdAdapter& adaptor = findAdaptor (localIndex.second); + + adaptor.setData (&mColumns.at (column), mData, localIndex.first, data); +} + +void CSMWorld::RefIdCollection::removeRows (int index, int count) +{ + mData.erase (index, count); +} + +void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) +{ + mData.appendRecord (type, id); +} + +int CSMWorld::RefIdCollection::searchId (const std::string& id) const +{ + RefIdData::LocalIndex localIndex = mData.searchId (id); + + if (localIndex.first==-1) + return -1; + + return mData.localToGlobalIndex (localIndex); +} + +void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) +{ + mData.getRecord (mData.globalToLocalIndex (index)).assign (record); +} + +void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, + UniversalId::Type type) +{ + std::string id = findAdaptor (type).getId (record); + + int index = mData.getAppendIndex (type); + + mData.appendRecord (type, id); + + mData.getRecord (mData.globalToLocalIndex (index)).assign (record); +} + +const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const +{ + return mData.getRecord (mData.searchId (id)); +} + +const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (int index) const +{ + return mData.getRecord (mData.globalToLocalIndex (index)); +} + +void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type) +{ + std::string id = reader.getHNOString ("NAME"); + + int index = searchId (id); + + if (reader.isNextSub ("DELE")) + { + reader.skipRecord(); + + if (index==-1) + { + // deleting a record that does not exist + + // ignore it for now + + /// \todo report the problem to the user + } + else if (base) + { + mData.erase (index, 1); + } + else + { + mData.getRecord (mData.globalToLocalIndex (index)).mState = RecordBase::State_Deleted; + } + } + else + { + if (index==-1) + { + // new record + int index = mData.getAppendIndex (type); + mData.appendRecord (type, id); + + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + mData.load (localIndex, reader, base); + + mData.getRecord (localIndex).mState = + base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + } + else + { + // old record + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + if (!base) + if (mData.getRecord (localIndex).mState==RecordBase::State_Erased) + throw std::logic_error ("attempt to access a deleted record"); + + mData.load (localIndex, reader, base); + + if (!base) + mData.getRecord (localIndex).mState = RecordBase::State_Modified; + } + } +} + +int CSMWorld::RefIdCollection::getAppendIndex (UniversalId::Type type) const +{ + return mData.getAppendIndex (type); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp new file mode 100644 index 0000000000..dc86c128cb --- /dev/null +++ b/apps/opencs/model/world/refidcollection.hpp @@ -0,0 +1,95 @@ +#ifndef CSM_WOLRD_REFIDCOLLECTION_H +#define CSM_WOLRD_REFIDCOLLECTION_H + +#include +#include +#include + +#include "columnbase.hpp" +#include "idcollection.hpp" +#include "refiddata.hpp" + +namespace CSMWorld +{ + class RefIdAdapter; + + class RefIdColumn : public ColumnBase + { + bool mEditable; + bool mUserEditable; + + public: + + RefIdColumn (const std::string& title, Display displayType, + int flag = Flag_Table | Flag_Dialogue, bool editable = true, + bool userEditable = true); + + virtual bool isEditable() const; + + virtual bool isUserEditable() const; + }; + + class RefIdCollection : public IdCollectionBase + { + private: + + RefIdData mData; + std::deque mColumns; + std::map mAdapters; + + private: + + const RefIdAdapter& findAdaptor (UniversalId::Type) const; + ///< Throws an exception if no adaptor for \a Type can be found. + + public: + + RefIdCollection(); + + virtual ~RefIdCollection(); + + virtual int getSize() const; + + virtual std::string getId (int index) const; + + virtual int getIndex (const std::string& id) const; + + virtual int getColumns() const; + + virtual const ColumnBase& getColumn (int column) const; + + virtual QVariant getData (int index, int column) const; + + virtual void setData (int index, int column, const QVariant& data); + + virtual void removeRows (int index, int count); + + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual int searchId (const std::string& id) const; + ////< Search record with \a id. + /// \return index of record (if found) or -1 (not found) + + virtual void replace (int index, const RecordBase& record); + ///< If the record type does not match, an exception is thrown. + /// + /// \attention \a record must not change the ID. + + virtual void appendRecord (const RecordBase& record, UniversalId::Type type); + ///< If the record type does not match, an exception is thrown. + /// + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual const RecordBase& getRecord (const std::string& id) const; + + virtual const RecordBase& getRecord (int index) const; + + virtual void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); + + virtual int getAppendIndex (UniversalId::Type type) const; + ///< \param type Will be ignored, unless the collection supports multiple record types + }; +} + +#endif diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp new file mode 100644 index 0000000000..c95db045f5 --- /dev/null +++ b/apps/opencs/model/world/refiddata.cpp @@ -0,0 +1,198 @@ + +#include "refiddata.hpp" + +#include + +#include + +CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {} + +CSMWorld::RefIdData::RefIdData() +{ + mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Potion, &mPotions)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Apparatus, &mApparati)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Armor, &mArmors)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Book, &mBooks)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Clothing, &mClothing)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Container, &mContainers)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Creature, &mCreatures)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Door, &mDoors)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Ingredient, &mIngredients)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, + &mCreatureLevelledLists)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_ItemLevelledList, &mItemLevelledLists)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Light, &mLights)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Lockpick, &mLockpicks)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Miscellaneous, &mMiscellaneous)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Npc, &mNpcs)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Probe, &mProbes)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Repair, &mRepairs)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Static, &mStatics)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Weapon, &mWeapons)); +} + +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const +{ + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) + { + if (indexsecond->getSize()) + return LocalIndex (index, iter->first); + + index -= iter->second->getSize(); + } + + throw std::runtime_error ("RefIdData index out of range"); +} + +int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index) + const +{ + std::map::const_iterator end = + mRecordContainers.find (index.second); + + if (end==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + int globalIndex = index.first; + + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=end; ++iter) + globalIndex += iter->second->getSize(); + + return globalIndex; +} + +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId ( + const std::string& id) const +{ + std::string id2 = Misc::StringUtils::lowerCase (id); + + std::map >::const_iterator iter = mIndex.find (id2); + + if (iter==mIndex.end()) + return std::make_pair (-1, CSMWorld::UniversalId::Type_None); + + return iter->second; +} + +void CSMWorld::RefIdData::erase (int index, int count) +{ + LocalIndex localIndex = globalToLocalIndex (index); + + std::map::const_iterator iter = + mRecordContainers.find (localIndex.second); + + while (count>0 && iter!=mRecordContainers.end()) + { + int size = iter->second->getSize(); + + if (localIndex.first+count>size) + { + erase (localIndex, size-localIndex.first); + count -= size-localIndex.first; + + ++iter; + + if (iter==mRecordContainers.end()) + throw std::runtime_error ("invalid count value for erase operation"); + + localIndex.first = 0; + localIndex.second = iter->first; + } + else + { + erase (localIndex, count); + count = 0; + } + } +} + +const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const +{ + std::map::const_iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + return iter->second->getRecord (index.first); +} + +CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + return iter->second->getRecord (index.first); +} + +void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id) +{ + std::map::iterator iter = + mRecordContainers.find (type); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->appendRecord (id); + + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), + LocalIndex (iter->second->getSize()-1, type))); +} + +int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const +{ + int index = 0; + + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) + { + index += iter->second->getSize(); + + if (type==iter->first) + break; + } + + return index; +} + +void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->load (index.first, reader, base); +} + +void CSMWorld::RefIdData::erase (const LocalIndex& index, int count) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + for (int i=index.first; i::iterator result = + mIndex.find (Misc::StringUtils::lowerCase (iter->second->getId (i))); + + if (result!=mIndex.end()) + mIndex.erase (result); + } + + iter->second->erase (index.first, count); +} + +int CSMWorld::RefIdData::getSize() const +{ + return mIndex.size(); +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp new file mode 100644 index 0000000000..475566fb54 --- /dev/null +++ b/apps/opencs/model/world/refiddata.hpp @@ -0,0 +1,188 @@ +#ifndef CSM_WOLRD_REFIDDATA_H +#define CSM_WOLRD_REFIDDATA_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "record.hpp" +#include "universalid.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace CSMWorld +{ + struct RefIdDataContainerBase + { + virtual ~RefIdDataContainerBase(); + + virtual int getSize() const = 0; + + virtual const RecordBase& getRecord (int index) const = 0; + + virtual RecordBase& getRecord (int index)= 0; + + virtual void appendRecord (const std::string& id) = 0; + + virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; + + virtual void erase (int index, int count) = 0; + + virtual std::string getId (int index) const = 0; + }; + + template + struct RefIdDataContainer : public RefIdDataContainerBase + { + std::vector > mContainer; + + virtual int getSize() const; + + virtual const RecordBase& getRecord (int index) const; + + virtual RecordBase& getRecord (int index); + + virtual void appendRecord (const std::string& id); + + virtual void load (int index, ESM::ESMReader& reader, bool base); + + virtual void erase (int index, int count); + + virtual std::string getId (int index) const; + }; + + template + int RefIdDataContainer::getSize() const + { + return static_cast (mContainer.size()); + } + + template + const RecordBase& RefIdDataContainer::getRecord (int index) const + { + return mContainer.at (index); + } + + template + RecordBase& RefIdDataContainer::getRecord (int index) + { + return mContainer.at (index); + } + + template + void RefIdDataContainer::appendRecord (const std::string& id) + { + Record record; + record.mModified.mId = id; + record.mModified.blank(); + record.mState = RecordBase::State_ModifiedOnly; + + mContainer.push_back (record); + } + + template + void RefIdDataContainer::load (int index, ESM::ESMReader& reader, bool base) + { + (base ? mContainer.at (index).mBase : mContainer.at (index).mModified).load (reader); + } + + template + void RefIdDataContainer::erase (int index, int count) + { + if (index<0 || index+count>=getSize()) + throw std::runtime_error ("invalid RefIdDataContainer index"); + + mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count); + } + + template + std::string RefIdDataContainer::getId (int index) const + { + return mContainer.at (index).get().mId; + } + + class RefIdData + { + public: + + typedef std::pair LocalIndex; + + private: + + RefIdDataContainer mActivators; + RefIdDataContainer mPotions; + RefIdDataContainer mApparati; + RefIdDataContainer mArmors; + RefIdDataContainer mBooks; + RefIdDataContainer mClothing; + RefIdDataContainer mContainers; + RefIdDataContainer mCreatures; + RefIdDataContainer mDoors; + RefIdDataContainer mIngredients; + RefIdDataContainer mCreatureLevelledLists; + RefIdDataContainer mItemLevelledLists; + RefIdDataContainer mLights; + RefIdDataContainer mLockpicks; + RefIdDataContainer mMiscellaneous; + RefIdDataContainer mNpcs; + RefIdDataContainer mProbes; + RefIdDataContainer mRepairs; + RefIdDataContainer mStatics; + RefIdDataContainer mWeapons; + + std::map mIndex; + + std::map mRecordContainers; + + void erase (const LocalIndex& index, int count); + ///< Must not spill over into another type. + + public: + + RefIdData(); + + LocalIndex globalToLocalIndex (int index) const; + + int localToGlobalIndex (const LocalIndex& index) const; + + LocalIndex searchId (const std::string& id) const; + + void erase (int index, int count); + + const RecordBase& getRecord (const LocalIndex& index) const; + + RecordBase& getRecord (const LocalIndex& index); + + void appendRecord (UniversalId::Type type, const std::string& id); + + int getAppendIndex (UniversalId::Type type) const; + + void load (const LocalIndex& index, ESM::ESMReader& reader, bool base); + + int getSize() const; + }; +} + +#endif 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 c006852bc6..bd1632e3e7 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -19,6 +19,18 @@ namespace { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" }, { 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_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, + "Referenceables" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -27,6 +39,40 @@ namespace { { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" }, { 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_Record, CSMWorld::UniversalId::Type_Cell, "Cell" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Activator, "Activator" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Potion, "Potion" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Apparatus, "Apparatus" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Armor, "Armor" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Book, "Book" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Clothing, "Clothing" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Container, "Container" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Creature, "Creature" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Door, "Door" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Ingredient, "Ingredient" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_CreatureLevelledList, + "Creature Levelled List" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_ItemLevelledList, + "Item Levelled List" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Light, "Light" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Lockpick, "Lockpick" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Miscellaneous, + "Miscellaneous" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Npc, "NPC" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Probe, "Probe" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Repair, "Repair" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Static, "Static" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Weapon, "Weapon" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -43,48 +89,45 @@ CSMWorld::UniversalId::UniversalId (const std::string& universalId) { std::string::size_type index = universalId.find (':'); - if (index==std::string::npos) + if (index!=std::string::npos) { 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 9ff7d17b18..2c4b14eaf5 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -37,7 +37,49 @@ namespace CSMWorld Type_Global, Type_VerificationResults, Type_Gmsts, - Type_Gmst + Type_Gmst, + Type_Skills, + Type_Skill, + Type_Classes, + 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, + Type_Cells, + Type_Cell, + Type_Referenceables, + Type_Referenceable, + Type_Activator, + Type_Potion, + Type_Apparatus, + Type_Armor, + Type_Book, + Type_Clothing, + Type_Container, + Type_Creature, + Type_Door, + Type_Ingredient, + Type_CreatureLevelledList, + Type_ItemLevelledList, + Type_Light, + Type_Lockpick, + Type_Miscellaneous, + Type_Npc, + Type_Probe, + Type_Repair, + Type_Static, + Type_Weapon }; diff --git a/apps/opencs/settings/usersettingsdialog.ui b/apps/opencs/settings/usersettingsdialog.ui deleted file mode 100644 index 4cd9dd1137..0000000000 --- a/apps/opencs/settings/usersettingsdialog.ui +++ /dev/null @@ -1,68 +0,0 @@ - - - UserSettingsDialog - - - - 0 - 0 - 638 - 478 - - - - Dialog - - - - - 10 - 440 - 621 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - buttonBox - accepted() - UserSettingsDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - UserSettingsDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp index ce001afaa9..7ee4b87260 100644 --- a/apps/opencs/view/doc/operations.cpp +++ b/apps/opencs/view/doc/operations.cpp @@ -56,7 +56,7 @@ void CSVDoc::Operations::quitOperation (int type) mLayout->removeItem ((*iter)->getLayout()); - delete *iter; + (*iter)->deleteLater(); mOperations.erase (iter); if (oldCount > 1) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 85821833be..2857f4a54a 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -7,17 +7,16 @@ #include #include #include +#include #include "../../model/doc/document.hpp" #include "../world/subviews.hpp" #include "../tools/subviews.hpp" -#include "../../settings/usersettingsdialog.hpp" +#include "../settings/usersettingsdialog.hpp" #include "viewmanager.hpp" #include "operations.hpp" #include "subview.hpp" -#include - void CSVDoc::View::closeEvent (QCloseEvent *event) { if (!mViewManager.closeRequest (this)) @@ -39,6 +38,20 @@ void CSVDoc::View::setupFileMenu() mSave = new QAction (tr ("&Save"), this); 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); + + QAction *exit = new QAction (tr ("&Exit"), this); + connect (exit, SIGNAL (triggered()), this, SLOT (exit())); + connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); + + file->addAction(exit); } void CSVDoc::View::setupEditMenu() @@ -52,6 +65,10 @@ void CSVDoc::View::setupEditMenu() mRedo= mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); mRedo->setShortcuts (QKeySequence::Redo); edit->addAction (mRedo); + + QAction *userSettings = new QAction (tr ("&Preferences"), this); + connect (userSettings, SIGNAL (triggered()), this, SLOT (showUserSettings())); + edit->addAction (userSettings); } void CSVDoc::View::setupViewMenu() @@ -75,18 +92,49 @@ void CSVDoc::View::setupWorldMenu() connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); world->addAction (gmsts); - mVerify = new QAction (tr ("&Verify"), this); - connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); - world->addAction (mVerify); -} + QAction *skills = new QAction (tr ("Skills"), this); + connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); + world->addAction (skills); -void CSVDoc::View::setupSettingsMenu() -{ - QMenu *settings = menuBar()->addMenu( (tr ("&Settings"))); + QAction *classes = new QAction (tr ("Classes"), this); + connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); + world->addAction (classes); - QAction *userSettings = new QAction (tr ("User Settings"), this); - connect (userSettings, SIGNAL (triggered()), this, SLOT (showUserSettings())); - settings->addAction (userSettings); + 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); + + QAction *cells = new QAction (tr ("Cells"), this); + connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); + world->addAction (cells); + + QAction *referenceables = new QAction (tr ("Referenceables"), this); + connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); + world->addAction (referenceables); } @@ -96,7 +144,6 @@ void CSVDoc::View::setupUi() setupEditMenu(); setupViewMenu(); setupWorldMenu(); - setupSettingsMenu(); } void CSVDoc::View::updateTitle() @@ -128,15 +175,15 @@ void CSVDoc::View::updateActions() mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying)); } -CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent) - : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews), QMainWindow (viewParent) +CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) + : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), + mViewTotal (totalViews) { - setDockOptions (QMainWindow::AllowNestedDocks); - resize (300, 300); /// \todo get default size from settings and set reasonable minimal size - mSubViewWindow = new QMainWindow(); - setCentralWidget (mSubViewWindow); + mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); + + setCentralWidget (&mSubViewWindow); mOperations = new Operations; addDockWidget (Qt::BottomDockWidgetArea, mOperations); @@ -211,7 +258,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); - mSubViewWindow->addDockWidget (Qt::TopDockWidgetArea, view); + mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, SLOT (addSubView (const CSMWorld::UniversalId&))); @@ -244,22 +291,82 @@ void CSVDoc::View::addGmstsSubView() addSubView (CSMWorld::UniversalId::Type_Gmsts); } +void CSVDoc::View::addSkillsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Skills); +} + +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::addCellsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Cells); +} + +void CSVDoc::View::addReferenceablesSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Referenceables); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); updateActions(); } -QDockWidget *CSVDoc::View::getOperations() const +CSVDoc::Operations *CSVDoc::View::getOperations() const { return mOperations; } +void CSVDoc::View::exit() +{ + emit exitApplicationRequest (this); +} + void CSVDoc::View::showUserSettings() { - CsSettings::UserSettingsDialog *settingsDialog = new CsSettings::UserSettingsDialog(this); + CSVSettings::UserSettingsDialog *settingsDialog = new CSVSettings::UserSettingsDialog(this); - connect (settingsDialog, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)), + connect (&(CSMSettings::UserSettings::instance()), SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)), this, SLOT (slotUpdateEditorSetting (const QString &, const QString &)) ); settingsDialog->show(); @@ -271,11 +378,7 @@ void CSVDoc::View::slotUpdateEditorSetting(const QString &settingName, const QSt if (lastValue != settingValue) { - if (settingName == "Undo Stack Size"); - - if (settingName == "Top-Level Window Count"); - - if (settingName == "Reuse Subwindows"); + //evaluate settingName against tokens to determine which function to call to update Editor application. lastValue = settingValue; } diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index a46b72d42d..0552a86cbb 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -41,7 +41,8 @@ namespace CSVDoc std::vector mEditingActions; Operations *mOperations; SubViewFactoryManager mSubViewFactory; - QMainWindow* mSubViewWindow; + QMainWindow mSubViewWindow; + // not implemented View (const View&); @@ -59,17 +60,18 @@ namespace CSVDoc void setupWorldMenu(); - void setupSettingsMenu(); - void setupUi(); void updateTitle(); void updateActions(); + void exitApplication(); + public: - View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent); + View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); + ///< The ownership of \a document is not transferred to *this. virtual ~View(); @@ -84,7 +86,7 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); - QDockWidget *getOperations() const; + Operations *getOperations() const; signals: @@ -92,10 +94,14 @@ namespace CSVDoc void loadDocumentRequest(); + void exitApplicationRequest (CSVDoc::View *view); + public slots: void addSubView (const CSMWorld::UniversalId& id); + void abortOperation (int type); + void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue); private slots: @@ -104,13 +110,35 @@ namespace CSVDoc void save(); + void exit(); + void verify(); void addGlobalsSubView(); void addGmstsSubView(); - void abortOperation (int type); + void addSkillsSubView(); + + void addClassesSubView(); + + void addFactionsSubView(); + + void addRacesSubView(); + + void addSoundsSubView(); + + void addScriptsSubView(); + + void addRegionsSubView(); + + void addBirthsignsSubView(); + + void addSpellsSubView(); + + void addCellsSubView(); + + void addReferenceablesSubView(); void showUserSettings(); }; diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 81fb2f4e38..d044098fe1 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -15,6 +15,11 @@ #include "view.hpp" +#include +#include +#include +#include + void CSVDoc::ViewManager::updateIndices() { std::map > documents; @@ -34,8 +39,53 @@ void CSVDoc::ViewManager::updateIndices() } CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) -: mDocumentManager (documentManager) + : mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false) { + static const char *sSpecialisations[] = + { + "Combat", "Magic", "Stealth", 0 + }; + + static const char *sAttributes[] = + { + "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", + "Luck", 0 + }; + + static const char *sSpellTypes[] = + { + "Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0 + }; + + static const char *sApparatusTypes[] = + { + "Mortar & Pestle", "Albemic", "Calcinator", "Retort", 0 + }; + + static const char *sArmorTypes[] = + { + "Helmet", "Cuirass", "Left Pauldron", "Right Pauldron", "Greaves", "Boots", "Left Gauntlet", + "Right Gauntlet", "Shield", "Left Bracer", "Right Bracer", 0 + }; + + static const char *sClothingTypes[] = + { + "Pants", "Shoes", "Shirt", "Belt", "Robe", "Right Glove", "Left Glove", "Skirt", "Ring", + "Amulet", 0 + }; + + static const char *sCreatureTypes[] = + { + "Creature", "Deadra", "Undead", "Humanoid", 0 + }; + + static const char *sWeaponTypes[] = + { + "Short Blade 1H", "Long Blade 1H", "Long Blade 2H", "Blunt 1H", "Blunt 2H Close", + "Blunt 2H Wide", "Spear 2H", "Axe 1H", "Axe 2H", "Bow", "Crossbow", "Thrown", "Arrow", + "Bolt", 0 + }; + mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType, @@ -44,6 +94,29 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType, new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float)); + mDelegateFactories->add (CSMWorld::ColumnBase::Display_Specialisation, + new CSVWorld::EnumDelegateFactory (sSpecialisations)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_Attribute, + new CSVWorld::EnumDelegateFactory (sAttributes, true)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_SpellType, + new CSVWorld::EnumDelegateFactory (sSpellTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_ApparatusType, + new CSVWorld::EnumDelegateFactory (sApparatusTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_ArmorType, + new CSVWorld::EnumDelegateFactory (sArmorTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_ClothingType, + new CSVWorld::EnumDelegateFactory (sClothingTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_CreatureType, + new CSVWorld::EnumDelegateFactory (sCreatureTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_WeaponType, + new CSVWorld::EnumDelegateFactory (sWeaponTypes)); } CSVDoc::ViewManager::~ViewManager() @@ -66,18 +139,11 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) this, SLOT (progress (int, int, int, int, CSMDoc::Document *))); } - QMainWindow *mainWindow = new QMainWindow; + View *view = new View (*this, document, countViews (document)+1); - View *view = new View (*this, document, countViews (document)+1, mainWindow); mViews.push_back (view); - if (mViews.size()==1) - { - QRect scr = QApplication::desktop()->screenGeometry(); - QRect rect = view->geometry(); - view->move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); - } view->show(); connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest())); @@ -103,23 +169,143 @@ bool CSVDoc::ViewManager::closeRequest (View *view) { std::vector::iterator iter = std::find (mViews.begin(), mViews.end(), view); + bool continueWithClose = true; + if (iter!=mViews.end()) { bool last = countViews (view->getDocument())<=1; - /// \todo check if save is in progress -> warn user about possible data loss - /// \todo check if document has not been saved -> return false and start close dialogue - - mViews.erase (iter); - view->deleteLater(); - if (last) - mDocumentManager.removeDocument (view->getDocument()); + continueWithClose = notifySaveOnClose (view); else + { + (*iter)->deleteLater(); + mViews.erase (iter); + updateIndices(); + } } - return true; + return continueWithClose; +} + +bool CSVDoc::ViewManager::notifySaveOnClose (CSVDoc::View *view) +{ + bool result = true; + CSMDoc::Document *document = view->getDocument(); + + //notify user of saving in progress + if ( (document->getState() & CSMDoc::State_Saving) ) + result = showSaveInProgressMessageBox (view); + + //notify user of unsaved changes and process response + else if ( document->getState() & CSMDoc::State_Modified) + result = showModifiedDocumentMessageBox (view); + + return result; +} + +bool CSVDoc::ViewManager::showModifiedDocumentMessageBox (CSVDoc::View *view) +{ + QMessageBox messageBox; + CSMDoc::Document *document = view->getDocument(); + + messageBox.setText ("The document has been modified."); + messageBox.setInformativeText ("Do you want to save your changes?"); + messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + messageBox.setDefaultButton (QMessageBox::Save); + + bool retVal = true; + + connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); + + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + + mUserWarned = true; + int response = messageBox.exec(); + mUserWarned = false; + + switch (response) + { + case QMessageBox::Save: + + document->save(); + mExitOnSaveStateChange = true; + retVal = false; + break; + + case QMessageBox::Discard: + + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + break; + + case QMessageBox::Cancel: + + //disconnect to prevent unintended view closures + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + retVal = false; + break; + + default: + break; + + } + + return retVal; +} + +bool CSVDoc::ViewManager::showSaveInProgressMessageBox (CSVDoc::View *view) +{ + QMessageBox messageBox; + CSMDoc::Document *document = view->getDocument(); + + messageBox.setText ("The document is currently being saved."); + messageBox.setInformativeText("Do you want to close now and abort saving, or wait until saving has completed?"); + + QPushButton* waitButton = messageBox.addButton (tr("Wait"), QMessageBox::YesRole); + QPushButton* closeButton = messageBox.addButton (tr("Close Now"), QMessageBox::RejectRole); + QPushButton* cancelButton = messageBox.addButton (tr("Cancel"), QMessageBox::NoRole); + + messageBox.setDefaultButton (waitButton); + + bool retVal = true; + + //Connections shut down message box if operation ends before user makes a decision. + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); + + //set / clear the user warned flag to indicate whether or not the message box is currently active. + mUserWarned = true; + messageBox.exec(); + mUserWarned = false; + + //if closed by the warning handler, defaults to the RejectRole button (closeButton) + if (messageBox.clickedButton() == waitButton) + { + //save the View iterator for shutdown after the save operation ends + mExitOnSaveStateChange = true; + retVal = false; + } + + else if (messageBox.clickedButton() == closeButton) + { + //disconnect to avoid segmentation fault + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + + view->abortOperation(CSMDoc::State_Saving); + mExitOnSaveStateChange = true; + } + + else if (messageBox.clickedButton() == cancelButton) + { + //abort shutdown, allow save to complete + //disconnection to prevent unintended view closures + mExitOnSaveStateChange = false; + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + retVal = false; + } + + return retVal; } void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document) @@ -135,3 +321,25 @@ void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, if ((*iter)->getDocument()==document) (*iter)->updateProgress (current, max, type, threads); } + +void CSVDoc::ViewManager::onExitWarningHandler (int state, CSMDoc::Document *document) +{ + if ( !(state & CSMDoc::State_Saving) ) + { + //if the user is being warned (message box is active), shut down the message box, + //as there is no save operation currently running + if ( mUserWarned ) + emit closeMessageBox(); + + //otherwise, the user has closed the message box before the save operation ended. + //exit the application + else if (mExitOnSaveStateChange) + QApplication::instance()->exit(); + } +} + +void CSVDoc::ViewManager::exitApplication (CSVDoc::View *view) +{ + if (notifySaveOnClose (view)) + QApplication::instance()->exit(); +} diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 72e7a3e1a1..90f23eaa11 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -27,12 +27,17 @@ namespace CSVDoc CSMDoc::DocumentManager& mDocumentManager; std::vector mViews; CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories; + bool mExitOnSaveStateChange; + bool mUserWarned; // not implemented ViewManager (const ViewManager&); ViewManager& operator= (const ViewManager&); void updateIndices(); + bool notifySaveOnClose (View *view = 0); + bool showModifiedDocumentMessageBox (View *view); + bool showSaveInProgressMessageBox (View *view); public: @@ -54,13 +59,21 @@ namespace CSVDoc void loadDocumentRequest(); + void closeMessageBox(); + + public slots: + + void exitApplication (CSVDoc::View *view); + private slots: void documentStateChanged (int state, CSMDoc::Document *document); void progress (int current, int max, int type, int threads, CSMDoc::Document *document); + + void onExitWarningHandler(int state, CSMDoc::Document* document); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/settings/abstractblock.cpp b/apps/opencs/view/settings/abstractblock.cpp similarity index 60% rename from apps/opencs/settings/abstractblock.cpp rename to apps/opencs/view/settings/abstractblock.cpp index 8fab5819c9..72d3603484 100644 --- a/apps/opencs/settings/abstractblock.cpp +++ b/apps/opencs/view/settings/abstractblock.cpp @@ -1,18 +1,19 @@ #include "abstractblock.hpp" -CsSettings::AbstractBlock::AbstractBlock(QWidget* parent) +CSVSettings::AbstractBlock::AbstractBlock(QWidget* parent) : QObject (parent), mBox ( new GroupBox (parent) ), mWidgetParent (parent) {} -CsSettings::AbstractBlock::AbstractBlock(bool isVisible, QWidget* parent) +CSVSettings::AbstractBlock::AbstractBlock(bool isVisible, QWidget* parent) : QObject (parent), mBox ( new GroupBox (isVisible, parent)), mWidgetParent (parent) {} -QLayout *CsSettings::AbstractBlock::createLayout (OcsWidgetOrientation direction, bool isZeroMargin, QWidget* parent) +QLayout *CSVSettings::AbstractBlock::createLayout (Orientation direction, + bool isZeroMargin, QWidget* parent) { QLayout *layout = 0; - if (direction == OCS_VERTICAL) + if (direction == Orient_Vertical) layout = new QVBoxLayout (parent); else layout = new QHBoxLayout (parent); @@ -23,40 +24,40 @@ QLayout *CsSettings::AbstractBlock::createLayout (OcsWidgetOrientation direction return layout; } -QGroupBox *CsSettings::AbstractBlock::getGroupBox() +QGroupBox *CSVSettings::AbstractBlock::getGroupBox() { return mBox; } -CsSettings::AbstractWidget *CsSettings::AbstractBlock::buildWidget (const QString& widgetName, WidgetDef &def, - QLayout *layout, bool isConnected) const +CSVSettings::AbstractWidget *CSVSettings::AbstractBlock::buildWidget (const QString& widgetName, WidgetDef &def, + QLayout *layout, bool isConnected) const { AbstractWidget *widg = 0; switch (def.type) { - case OCS_RADIO_WIDGET: + case Widget_RadioButton: widg = createSettingWidget (def, layout); break; - case OCS_SPIN_WIDGET: + case Widget_SpinBox: widg = createSettingWidget (def, layout); break; - case OCS_CHECK_WIDGET: + case Widget_CheckBox: widg = createSettingWidget (def, layout); break; - case OCS_TEXT_WIDGET: + case Widget_LineEdit: widg = createSettingWidget (def, layout); break; - case OCS_LIST_WIDGET: + case Widget_ListBox: widg = createSettingWidget (def, layout); break; - case OCS_COMBO_WIDGET: + case Widget_ComboBox: widg = createSettingWidget (def, layout); break; @@ -76,32 +77,32 @@ CsSettings::AbstractWidget *CsSettings::AbstractBlock::buildWidget (const QStrin return widg; } -void CsSettings::AbstractBlock::setVisible (bool isVisible) +void CSVSettings::AbstractBlock::setVisible (bool isVisible) { mBox->setBorderVisibility (isVisible); } -bool CsSettings::AbstractBlock::isVisible () const +bool CSVSettings::AbstractBlock::isVisible () const { return mBox->borderVisibile(); } -QWidget *CsSettings::AbstractBlock::getParent() const +QWidget *CSVSettings::AbstractBlock::getParent() const { return mWidgetParent; } -void CsSettings::AbstractBlock::slotUpdate (const QString &value) +void CSVSettings::AbstractBlock::slotUpdate (const QString &value) { slotUpdateSetting (objectName(), value); } -void CsSettings::AbstractBlock::slotSetEnabled(bool value) +void CSVSettings::AbstractBlock::slotSetEnabled(bool value) { mBox->setEnabled(value); } -void CsSettings::AbstractBlock::slotUpdateSetting (const QString &settingName, const QString &settingValue) +void CSVSettings::AbstractBlock::slotUpdateSetting (const QString &settingName, const QString &settingValue) { bool doEmit = true; updateBySignal (settingName, settingValue, doEmit); diff --git a/apps/opencs/settings/abstractblock.hpp b/apps/opencs/view/settings/abstractblock.hpp similarity index 82% rename from apps/opencs/settings/abstractblock.hpp rename to apps/opencs/view/settings/abstractblock.hpp index aa57d4fcbd..42e00b6d78 100644 --- a/apps/opencs/settings/abstractblock.hpp +++ b/apps/opencs/view/settings/abstractblock.hpp @@ -5,10 +5,10 @@ #include #include "settingwidget.hpp" -#include "settingsitem.hpp" +#include "../../model/settings/settingsitem.hpp" #include "groupbox.hpp" -namespace CsSettings +namespace CSVSettings { class AbstractBlock : public QObject @@ -17,7 +17,7 @@ namespace CsSettings protected: - typedef QMap SettingsItemMap; + typedef QMap SettingsItemMap; GroupBox *mBox; QWidget *mWidgetParent; @@ -30,14 +30,14 @@ namespace CsSettings void setVisible (bool isVisible); bool isVisible() const; - virtual SettingList *getSettings() = 0; - virtual bool updateSettings (const SettingMap &settings) = 0; + virtual CSMSettings::SettingList *getSettings() = 0; + virtual bool updateSettings (const CSMSettings::SettingMap &settings) = 0; virtual bool updateBySignal (const QString &name, const QString &value, bool &doEmit) { return false; } protected: - QLayout *createLayout (OcsWidgetOrientation direction, bool isZeroMargin, QWidget* parent = 0); + QLayout *createLayout (Orientation direction, bool isZeroMargin, QWidget* parent = 0); AbstractWidget *buildWidget (const QString &widgetName, WidgetDef &wDef, QLayout *layout = 0, bool isConnected = true) const; diff --git a/apps/opencs/settings/abstractpage.cpp b/apps/opencs/view/settings/abstractpage.cpp similarity index 53% rename from apps/opencs/settings/abstractpage.cpp rename to apps/opencs/view/settings/abstractpage.cpp index 1fd60fbf0f..512b5abbd1 100644 --- a/apps/opencs/settings/abstractpage.cpp +++ b/apps/opencs/view/settings/abstractpage.cpp @@ -10,28 +10,28 @@ #include #include -CsSettings::AbstractPage::AbstractPage(QWidget *parent): +CSVSettings::AbstractPage::AbstractPage(QWidget *parent): QWidget(parent) { } -CsSettings::AbstractPage::AbstractPage(const QString &pageName, QWidget *parent): +CSVSettings::AbstractPage::AbstractPage(const QString &pageName, QWidget *parent): QWidget(parent) { QWidget::setObjectName (pageName); } -CsSettings::AbstractPage::~AbstractPage() +CSVSettings::AbstractPage::~AbstractPage() { } -CsSettings::SettingList *CsSettings::AbstractPage::getSettings() +CSMSettings::SettingList *CSVSettings::AbstractPage::getSettings() { - SettingList *settings = new SettingList(); + CSMSettings::SettingList *settings = new CSMSettings::SettingList(); foreach (AbstractBlock *block, mAbstractBlocks) { - SettingList *groupSettings = block->getSettings(); + CSMSettings::SettingList *groupSettings = block->getSettings(); settings->append (*groupSettings); } diff --git a/apps/opencs/settings/abstractpage.hpp b/apps/opencs/view/settings/abstractpage.hpp similarity index 85% rename from apps/opencs/settings/abstractpage.hpp rename to apps/opencs/view/settings/abstractpage.hpp index b5ee86f574..15ac82a62d 100644 --- a/apps/opencs/settings/abstractpage.hpp +++ b/apps/opencs/view/settings/abstractpage.hpp @@ -10,15 +10,13 @@ class SettingMap; class SettingList; -namespace CsSettings { +namespace CSVSettings { typedef QList AbstractBlockList; class AbstractPage: public QWidget { - Q_OBJECT - protected: AbstractBlockList mAbstractBlocks; @@ -32,9 +30,9 @@ namespace CsSettings { virtual void setupUi()=0; - virtual void initializeWidgets (const SettingMap &settings) = 0; + virtual void initializeWidgets (const CSMSettings::SettingMap &settings) = 0; - SettingList *getSettings(); + CSMSettings::SettingList *getSettings(); void setObjectName(); diff --git a/apps/opencs/settings/abstractwidget.cpp b/apps/opencs/view/settings/abstractwidget.cpp similarity index 57% rename from apps/opencs/settings/abstractwidget.cpp rename to apps/opencs/view/settings/abstractwidget.cpp index a23c3ee1bc..94044e267b 100644 --- a/apps/opencs/settings/abstractwidget.cpp +++ b/apps/opencs/view/settings/abstractwidget.cpp @@ -3,7 +3,7 @@ #include #include -void CsSettings::AbstractWidget::build(QWidget *widget, WidgetDef &def, bool noLabel) +void CSVSettings::AbstractWidget::build(QWidget *widget, WidgetDef &def, bool noLabel) { if (!mLayout) createLayout(def.orientation, true); @@ -12,7 +12,7 @@ void CsSettings::AbstractWidget::build(QWidget *widget, WidgetDef &def, bool noL } -void CsSettings::AbstractWidget::buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel) +void CSVSettings::AbstractWidget::buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel) { if (def.widgetWidth > -1) widget->setFixedWidth (def.widgetWidth); @@ -31,10 +31,10 @@ void CsSettings::AbstractWidget::buildLabelAndWidget (QWidget *widget, WidgetDef mLayout->setAlignment (widget, getAlignment (def.widgetAlignment)); } -void CsSettings::AbstractWidget::createLayout - (OcsWidgetOrientation direction, bool isZeroMargin) +void CSVSettings::AbstractWidget::createLayout + (Orientation direction, bool isZeroMargin) { - if (direction == OCS_VERTICAL) + if (direction == Orient_Vertical) mLayout = new QVBoxLayout (); else mLayout = new QHBoxLayout (); @@ -43,36 +43,36 @@ void CsSettings::AbstractWidget::createLayout mLayout->setContentsMargins(0, 0, 0, 0); } -QFlags CsSettings::AbstractWidget::getAlignment (CsSettings::OcsAlignment flag) +QFlags CSVSettings::AbstractWidget::getAlignment (CSVSettings::Alignment flag) { return QFlags(static_cast(flag)); } -QLayout *CsSettings::AbstractWidget::getLayout() +QLayout *CSVSettings::AbstractWidget::getLayout() { return mLayout; } -void CsSettings::AbstractWidget::slotUpdateWidget (const QString &value) +void CSVSettings::AbstractWidget::slotUpdateWidget (const QString &value) { updateWidget (value); } -void CsSettings::AbstractWidget::slotUpdateItem(const QString &value) +void CSVSettings::AbstractWidget::slotUpdateItem(const QString &value) { emit signalUpdateItem (value); } -void CsSettings::AbstractWidget::slotUpdateItem(bool value) +void CSVSettings::AbstractWidget::slotUpdateItem(bool value) { if (value) emit signalUpdateItem (widget()->objectName()); } -void CsSettings::AbstractWidget::slotUpdateItem(int value) +void CSVSettings::AbstractWidget::slotUpdateItem(int value) { emit signalUpdateItem (QString::number(value)); } -void CsSettings::AbstractWidget::slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous) +void CSVSettings::AbstractWidget::slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous) {} diff --git a/apps/opencs/settings/abstractwidget.hpp b/apps/opencs/view/settings/abstractwidget.hpp similarity index 88% rename from apps/opencs/settings/abstractwidget.hpp rename to apps/opencs/view/settings/abstractwidget.hpp index 1adf8e84fe..ef00993fcf 100644 --- a/apps/opencs/settings/abstractwidget.hpp +++ b/apps/opencs/view/settings/abstractwidget.hpp @@ -6,7 +6,7 @@ class QLayout; -namespace CsSettings +namespace CSVSettings { class AbstractWidget : public QObject { @@ -35,12 +35,12 @@ namespace CsSettings virtual void updateWidget (const QString &value) = 0; //converts user-defined enum to Qt equivalents - QFlags getAlignment (CsSettings::OcsAlignment flag); + QFlags getAlignment (Alignment flag); private: //widget initialization utilities - void createLayout (CsSettings::OcsWidgetOrientation direction, bool isZeroMargin); + void createLayout (Orientation direction, bool isZeroMargin); void buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel); diff --git a/apps/opencs/settings/blankpage.cpp b/apps/opencs/view/settings/blankpage.cpp similarity index 74% rename from apps/opencs/settings/blankpage.cpp rename to apps/opencs/view/settings/blankpage.cpp index 1e2ab9c0f6..57d5c7caa7 100644 --- a/apps/opencs/settings/blankpage.cpp +++ b/apps/opencs/view/settings/blankpage.cpp @@ -13,23 +13,23 @@ #include #endif -#include "usersettings.hpp" +#include "../../model/settings/usersettings.hpp" #include "groupblock.hpp" #include "toggleblock.hpp" -CsSettings::BlankPage::BlankPage(QWidget *parent): +CSVSettings::BlankPage::BlankPage(QWidget *parent): AbstractPage("Blank", parent) { initPage(); } -CsSettings::BlankPage::BlankPage(const QString &title, QWidget *parent): +CSVSettings::BlankPage::BlankPage(const QString &title, QWidget *parent): AbstractPage(title, parent) { initPage(); } -void CsSettings::BlankPage::initPage() +void CSVSettings::BlankPage::initPage() { // Hacks to get the stylesheet look properly #ifdef Q_OS_MAC @@ -40,7 +40,7 @@ void CsSettings::BlankPage::initPage() setupUi(); } -void CsSettings::BlankPage::setupUi() +void CSVSettings::BlankPage::setupUi() { QGroupBox *pageBox = new QGroupBox(this); QLayout* pageLayout = new QVBoxLayout(); @@ -49,7 +49,7 @@ void CsSettings::BlankPage::setupUi() pageLayout->addWidget(pageBox); } -void CsSettings::BlankPage::initializeWidgets (const SettingMap &settings) +void CSVSettings::BlankPage::initializeWidgets (const CSMSettings::SettingMap &settings) { //iterate each item in each blocks in this section //validate the corresponding setting against the defined valuelist if any. diff --git a/apps/opencs/settings/blankpage.hpp b/apps/opencs/view/settings/blankpage.hpp similarity index 79% rename from apps/opencs/settings/blankpage.hpp rename to apps/opencs/view/settings/blankpage.hpp index 9dd6c5e56d..648d373f31 100644 --- a/apps/opencs/settings/blankpage.hpp +++ b/apps/opencs/view/settings/blankpage.hpp @@ -5,14 +5,13 @@ class QGroupBox; -namespace CsSettings { +namespace CSVSettings { class UserSettings; class AbstractBlock; class BlankPage : public AbstractPage { - Q_OBJECT public: @@ -20,7 +19,7 @@ namespace CsSettings { BlankPage (const QString &title, QWidget *parent); void setupUi(); - void initializeWidgets (const SettingMap &settings); + void initializeWidgets (const CSMSettings::SettingMap &settings); private: void initPage(); diff --git a/apps/opencs/settings/customblock.cpp b/apps/opencs/view/settings/customblock.cpp similarity index 77% rename from apps/opencs/settings/customblock.cpp rename to apps/opencs/view/settings/customblock.cpp index f70db47a0a..f441895693 100644 --- a/apps/opencs/settings/customblock.cpp +++ b/apps/opencs/view/settings/customblock.cpp @@ -3,11 +3,11 @@ #include "itemblock.hpp" #include "proxyblock.hpp" -CsSettings::CustomBlock::CustomBlock (QWidget *parent) : AbstractBlock (parent) +CSVSettings::CustomBlock::CustomBlock (QWidget *parent) : AbstractBlock (parent) { } -int CsSettings::CustomBlock::build(GroupBlockDefList &defList, GroupBlockDefList::iterator *it) +int CSVSettings::CustomBlock::build(GroupBlockDefList &defList, GroupBlockDefList::iterator *it) { int retVal = 0; @@ -37,7 +37,7 @@ int CsSettings::CustomBlock::build(GroupBlockDefList &defList, GroupBlockDefList return retVal; } -CsSettings::GroupBox *CsSettings::CustomBlock::buildGroupBox (CsSettings::OcsWidgetOrientation orientation) +CSVSettings::GroupBox *CSVSettings::CustomBlock::buildGroupBox (Orientation orientation) { GroupBox *box = new GroupBox (false, mBox); QLayout *layout = createLayout (orientation, true, box); @@ -45,7 +45,7 @@ CsSettings::GroupBox *CsSettings::CustomBlock::buildGroupBox (CsSettings::OcsWid return box; } -int CsSettings::CustomBlock::buildGroupBlock(GroupBlockDef &def) +int CSVSettings::CustomBlock::buildGroupBlock(GroupBlockDef &def) { GroupBlock *block = new GroupBlock (getParent()); @@ -57,7 +57,7 @@ int CsSettings::CustomBlock::buildGroupBlock(GroupBlockDef &def) return block->build(def); } -int CsSettings::CustomBlock::buildProxyBlock(GroupBlockDef& def, ProxyBlock *block) +int CSVSettings::CustomBlock::buildProxyBlock(GroupBlockDef& def, ProxyBlock *block) { if (def.properties.size() != 1) return -1; @@ -91,13 +91,13 @@ int CsSettings::CustomBlock::buildProxyBlock(GroupBlockDef& def, ProxyBlock *blo return 0; } -CsSettings::SettingList *CsSettings::CustomBlock::getSettings() +CSMSettings::SettingList *CSVSettings::CustomBlock::getSettings() { - SettingList *settings = new SettingList(); + CSMSettings::SettingList *settings = new CSMSettings::SettingList(); foreach (GroupBlock *block, mGroupList) { - SettingList *groupSettings = block->getSettings(); + CSMSettings::SettingList *groupSettings = block->getSettings(); if (groupSettings) settings->append(*groupSettings); @@ -106,7 +106,7 @@ CsSettings::SettingList *CsSettings::CustomBlock::getSettings() return settings; } -bool CsSettings::CustomBlock::updateSettings (const SettingMap &settings) +bool CSVSettings::CustomBlock::updateSettings (const CSMSettings::SettingMap &settings) { bool success = true; diff --git a/apps/opencs/settings/customblock.hpp b/apps/opencs/view/settings/customblock.hpp similarity index 73% rename from apps/opencs/settings/customblock.hpp rename to apps/opencs/view/settings/customblock.hpp index ba83464da9..b2f2a02617 100644 --- a/apps/opencs/settings/customblock.hpp +++ b/apps/opencs/view/settings/customblock.hpp @@ -3,7 +3,7 @@ #include "abstractblock.hpp" -namespace CsSettings +namespace CSVSettings { class ProxyBlock; @@ -19,13 +19,13 @@ namespace CsSettings explicit CustomBlock (QWidget *parent = 0); - bool updateSettings (const SettingMap &settings); - SettingList *getSettings(); + bool updateSettings (const CSMSettings::SettingMap &settings); + CSMSettings::SettingList *getSettings(); int build (GroupBlockDefList &defList, GroupBlockDefList::Iterator *it = 0); protected: - GroupBox *buildGroupBox (OcsWidgetOrientation orientation); + GroupBox *buildGroupBox (Orientation orientation); private: diff --git a/apps/opencs/settings/editorpage.cpp b/apps/opencs/view/settings/editorpage.cpp similarity index 86% rename from apps/opencs/settings/editorpage.cpp rename to apps/opencs/view/settings/editorpage.cpp index eb6b58ef92..ad5a15f6fd 100644 --- a/apps/opencs/settings/editorpage.cpp +++ b/apps/opencs/view/settings/editorpage.cpp @@ -13,11 +13,11 @@ #include #endif -#include "usersettings.hpp" +#include "../../model/settings/usersettings.hpp" #include "groupblock.hpp" #include "toggleblock.hpp" -CsSettings::EditorPage::EditorPage(QWidget *parent): +CSVSettings::EditorPage::EditorPage(QWidget *parent): AbstractPage("Editor", parent) { // Hacks to get the stylesheet look properly @@ -29,7 +29,7 @@ CsSettings::EditorPage::EditorPage(QWidget *parent): setupUi(); } -void CsSettings::EditorPage::setupUi() +void CSVSettings::EditorPage::setupUi() { GroupBlockDef undoStack (QString("Undo Stack Size")); GroupBlockDef topLevelWindowCount (QString("Maximum Top-Level Window Count")); @@ -48,7 +48,7 @@ void CsSettings::EditorPage::setupUi() undoStackItem->minMax.left = "0"; undoStackItem->minMax.right = "64"; - WidgetDef stackWidget (OCS_SPIN_WIDGET); + WidgetDef stackWidget (Widget_SpinBox); stackWidget.minMax = &(undoStackItem->minMax); stackWidget.widgetWidth = 50; @@ -63,7 +63,7 @@ void CsSettings::EditorPage::setupUi() topLevelItem->minMax.left = "1"; topLevelItem->minMax.right = "256"; - WidgetDef topLvlWinWidget (OCS_SPIN_WIDGET); + WidgetDef topLvlWinWidget (Widget_SpinBox); topLvlWinWidget.minMax = &(topLevelItem->minMax); topLvlWinWidget.widgetWidth = 50; @@ -76,9 +76,9 @@ void CsSettings::EditorPage::setupUi() SettingsItemDef *reuseSubItem = new SettingsItemDef (reuseSubwindow.title, "Reuse Subwindows"); *(reuseSubItem->valueList) << "None" << "Top-Level" << "Document-Level"; - WidgetDef reuseSubWidget (OCS_RADIO_WIDGET); + WidgetDef reuseSubWidget (Widget_RadioButton); reuseSubWidget.valueList = (reuseSubItem->valueList); - reuseSubWidget.widgetAlignment = OCS_LEFT; + reuseSubWidget.widgetAlignment = Align_Left; reuseSubwindow.properties << reuseSubItem; reuseSubItem->widget = reuseSubWidget; @@ -89,23 +89,23 @@ void CsSettings::EditorPage::setupUi() //custom width SettingsItemDef *widthItem = new SettingsItemDef ("Window Width", "640"); - widthItem->widget = WidgetDef (OCS_TEXT_WIDGET); + widthItem->widget = WidgetDef (Widget_LineEdit); widthItem->widget.widgetWidth = 45; //custom height SettingsItemDef *heightItem = new SettingsItemDef ("Window Height", "480"); - heightItem->widget = WidgetDef (OCS_TEXT_WIDGET); + heightItem->widget = WidgetDef (Widget_LineEdit); heightItem->widget.widgetWidth = 45; heightItem->widget.caption = "x"; customWindowSize.properties << widthItem << heightItem; - customWindowSize.widgetOrientation = OCS_HORIZONTAL; + customWindowSize.widgetOrientation = Orient_Horizontal; customWindowSize.isVisible = false; //pre-defined SettingsItemDef *widthByHeightItem = new SettingsItemDef ("Window Size", "640x480"); - WidgetDef widthByHeightWidget = WidgetDef (OCS_COMBO_WIDGET); + WidgetDef widthByHeightWidget = WidgetDef (Widget_ComboBox); widthByHeightWidget.widgetWidth = 90; *(widthByHeightItem->valueList) << "640x480" << "800x600" << "1024x768"; @@ -125,12 +125,12 @@ void CsSettings::EditorPage::setupUi() // window size toggle windowSizeToggle.captions << "Pre-Defined" << "Custom"; - windowSizeToggle.widgetOrientation = OCS_VERTICAL; + windowSizeToggle.widgetOrientation = Orient_Vertical; windowSizeToggle.isVisible = false; //define a widget for each group in the toggle for (int i = 0; i < 2; i++) - windowSizeToggle.widgets << new WidgetDef (OCS_RADIO_WIDGET); + windowSizeToggle.widgets << new WidgetDef (Widget_RadioButton); windowSizeToggle.widgets.at(0)->isDefault = false; @@ -153,7 +153,7 @@ void CsSettings::EditorPage::setupUi() } } -void CsSettings::EditorPage::initializeWidgets (const SettingMap &settings) +void CSVSettings::EditorPage::initializeWidgets (const CSMSettings::SettingMap &settings) { //iterate each item in each blocks in this section //validate the corresponding setting against the defined valuelist if any. diff --git a/apps/opencs/settings/editorpage.hpp b/apps/opencs/view/settings/editorpage.hpp similarity index 81% rename from apps/opencs/settings/editorpage.hpp rename to apps/opencs/view/settings/editorpage.hpp index 87ae7da83c..0c1e80e899 100644 --- a/apps/opencs/settings/editorpage.hpp +++ b/apps/opencs/view/settings/editorpage.hpp @@ -5,7 +5,7 @@ class QGroupBox; -namespace CsSettings { +namespace CSVSettings { class UserSettings; class AbstractBlock; @@ -19,7 +19,7 @@ namespace CsSettings { EditorPage(QWidget *parent = 0); void setupUi(); - void initializeWidgets (const SettingMap &settings); + void initializeWidgets (const CSMSettings::SettingMap &settings); signals: void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); diff --git a/apps/opencs/settings/groupblock.cpp b/apps/opencs/view/settings/groupblock.cpp similarity index 71% rename from apps/opencs/settings/groupblock.cpp rename to apps/opencs/view/settings/groupblock.cpp index b5917ec387..b18f629ba1 100644 --- a/apps/opencs/settings/groupblock.cpp +++ b/apps/opencs/view/settings/groupblock.cpp @@ -1,15 +1,15 @@ #include "groupblock.hpp" #include "itemblock.hpp" -CsSettings::GroupBlock::GroupBlock (QWidget* parent) +CSVSettings::GroupBlock::GroupBlock (QWidget* parent) : AbstractBlock (parent) {} -CsSettings::GroupBlock::GroupBlock (bool isVisible, QWidget *parent) +CSVSettings::GroupBlock::GroupBlock (bool isVisible, QWidget *parent) : AbstractBlock (isVisible, parent) {} -int CsSettings::GroupBlock::build (GroupBlockDef &def) +int CSVSettings::GroupBlock::build (GroupBlockDef &def) { if (def.properties.size() == 0) @@ -44,14 +44,14 @@ int CsSettings::GroupBlock::build (GroupBlockDef &def) return retVal; } -CsSettings::SettingList *CsSettings::GroupBlock::getSettings() +CSMSettings::SettingList *CSVSettings::GroupBlock::getSettings() { - SettingList *settings = 0; + CSMSettings::SettingList *settings = 0; foreach (ItemBlock *block, mItemBlockList) { if (!settings) - settings = new SettingList(); + settings = new CSMSettings::SettingList(); settings->append(*(block->getSettings ())); } @@ -59,7 +59,7 @@ CsSettings::SettingList *CsSettings::GroupBlock::getSettings() return settings; } -CsSettings::ItemBlock *CsSettings::GroupBlock::getItemBlock (const QString &name, ItemBlockList *blockList) +CSVSettings::ItemBlock *CSVSettings::GroupBlock::getItemBlock (const QString &name, ItemBlockList *blockList) { ItemBlock *retBlock = 0; @@ -78,7 +78,7 @@ CsSettings::ItemBlock *CsSettings::GroupBlock::getItemBlock (const QString &name return retBlock; } -CsSettings::ItemBlock *CsSettings::GroupBlock::getItemBlock (int index) +CSVSettings::ItemBlock *CSVSettings::GroupBlock::getItemBlock (int index) { ItemBlock *retBlock = 0; @@ -88,14 +88,14 @@ CsSettings::ItemBlock *CsSettings::GroupBlock::getItemBlock (int index) return retBlock; } -bool CsSettings::GroupBlock::updateSettings (const SettingMap &settings) +bool CSVSettings::GroupBlock::updateSettings (const CSMSettings::SettingMap &settings) { bool success = true; //update all non-proxy settings foreach (ItemBlock *block, mItemBlockList) { - SettingContainer *setting = settings[block->objectName()]; + CSMSettings::SettingContainer *setting = settings[block->objectName()]; if (setting) { diff --git a/apps/opencs/settings/groupblock.hpp b/apps/opencs/view/settings/groupblock.hpp similarity index 80% rename from apps/opencs/settings/groupblock.hpp rename to apps/opencs/view/settings/groupblock.hpp index f3b0c4bae5..a8fd4e3b44 100644 --- a/apps/opencs/settings/groupblock.hpp +++ b/apps/opencs/view/settings/groupblock.hpp @@ -4,7 +4,7 @@ #include #include "abstractblock.hpp" -namespace CsSettings +namespace CSVSettings { class ItemBlock; @@ -18,9 +18,9 @@ namespace CsSettings int build (GroupBlockDef &def); - bool updateSettings (const SettingMap &settings); + bool updateSettings (const CSMSettings::SettingMap &settings); - SettingList *getSettings(); + CSMSettings::SettingList *getSettings(); ItemBlock *getItemBlock (const QString &name, ItemBlockList *blockList = 0); ItemBlock *getItemBlock (int index); diff --git a/apps/opencs/settings/groupbox.cpp b/apps/opencs/view/settings/groupbox.cpp similarity index 65% rename from apps/opencs/settings/groupbox.cpp rename to apps/opencs/view/settings/groupbox.cpp index c996a22aa1..da2cc25711 100644 --- a/apps/opencs/settings/groupbox.cpp +++ b/apps/opencs/view/settings/groupbox.cpp @@ -1,21 +1,21 @@ #include "groupbox.hpp" -const QString CsSettings::GroupBox::INVISIBLE_BOX_STYLE = +const QString CSVSettings::GroupBox::INVISIBLE_BOX_STYLE = QString::fromUtf8("QGroupBox { border: 0px; padding 0px; margin: 0px;}"); -CsSettings::GroupBox::GroupBox(QWidget *parent) : +CSVSettings::GroupBox::GroupBox(QWidget *parent) : QGroupBox (parent) { initBox(); } -CsSettings::GroupBox::GroupBox (bool isVisible, QWidget *parent) : +CSVSettings::GroupBox::GroupBox (bool isVisible, QWidget *parent) : QGroupBox (parent) { initBox(isVisible); } -void CsSettings::GroupBox::initBox(bool isVisible) +void CSVSettings::GroupBox::initBox(bool isVisible) { setFlat (true); VISIBLE_BOX_STYLE = styleSheet(); @@ -24,12 +24,12 @@ void CsSettings::GroupBox::initBox(bool isVisible) setStyleSheet (INVISIBLE_BOX_STYLE); } -bool CsSettings::GroupBox::borderVisibile() const +bool CSVSettings::GroupBox::borderVisibile() const { return (styleSheet() != INVISIBLE_BOX_STYLE); } -void CsSettings::GroupBox::setTitle (const QString &title) +void CSVSettings::GroupBox::setTitle (const QString &title) { if (borderVisibile() ) { @@ -38,7 +38,7 @@ void CsSettings::GroupBox::setTitle (const QString &title) } } -void CsSettings::GroupBox::setBorderVisibility (bool value) +void CSVSettings::GroupBox::setBorderVisibility (bool value) { if (value) setStyleSheet(VISIBLE_BOX_STYLE); @@ -46,7 +46,7 @@ void CsSettings::GroupBox::setBorderVisibility (bool value) setStyleSheet(INVISIBLE_BOX_STYLE); } -void CsSettings::GroupBox::setMinimumWidth() +void CSVSettings::GroupBox::setMinimumWidth() { //set minimum width to accommodate title, if needed //1.5 multiplier to account for bold title. diff --git a/apps/opencs/settings/groupbox.hpp b/apps/opencs/view/settings/groupbox.hpp similarity index 94% rename from apps/opencs/settings/groupbox.hpp rename to apps/opencs/view/settings/groupbox.hpp index d229dbc8ef..43cb2e0c33 100644 --- a/apps/opencs/settings/groupbox.hpp +++ b/apps/opencs/view/settings/groupbox.hpp @@ -3,12 +3,10 @@ #include -namespace CsSettings +namespace CSVSettings { class GroupBox : public QGroupBox { - Q_OBJECT - static const QString INVISIBLE_BOX_STYLE; QString VISIBLE_BOX_STYLE; //not a const... diff --git a/apps/opencs/settings/itemblock.cpp b/apps/opencs/view/settings/itemblock.cpp similarity index 68% rename from apps/opencs/settings/itemblock.cpp rename to apps/opencs/view/settings/itemblock.cpp index 261319d8db..9cb0ae1a1a 100644 --- a/apps/opencs/settings/itemblock.cpp +++ b/apps/opencs/view/settings/itemblock.cpp @@ -2,12 +2,12 @@ #include -CsSettings::ItemBlock::ItemBlock (QWidget* parent) +CSVSettings::ItemBlock::ItemBlock (QWidget* parent) : mSetting (0), AbstractBlock (false, parent) { } -int CsSettings::ItemBlock::build(SettingsItemDef &iDef) +int CSVSettings::ItemBlock::build(SettingsItemDef &iDef) { buildItemBlock (iDef); buildItemBlockWidgets (iDef); @@ -15,7 +15,7 @@ int CsSettings::ItemBlock::build(SettingsItemDef &iDef) return 0; } -void CsSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) +void CSVSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) { WidgetDef wDef = iDef.widget; QLayout *blockLayout = 0; @@ -24,8 +24,8 @@ void CsSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) switch (wDef.type) { - case OCS_CHECK_WIDGET: - case OCS_RADIO_WIDGET: + case Widget_CheckBox: + case Widget_RadioButton: foreach (QString item, *(iDef.valueList)) { @@ -37,8 +37,8 @@ void CsSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) break; - case OCS_COMBO_WIDGET: - case OCS_LIST_WIDGET: + case Widget_ComboBox: + case Widget_ListBox: //assign the item's value list to the widget's value list. //pass through to default to finish widget construction. @@ -56,13 +56,13 @@ void CsSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) } } -void CsSettings::ItemBlock::buildItemBlock (SettingsItemDef &iDef) +void CSVSettings::ItemBlock::buildItemBlock (SettingsItemDef &iDef) { QString defaultValue = iDef.defaultValue; setObjectName(iDef.name); - mSetting = new SettingsItem (objectName(), + mSetting = new CSMSettings::SettingsItem (objectName(), iDef.hasMultipleValues, iDef.defaultValue, parent()); @@ -74,7 +74,7 @@ void CsSettings::ItemBlock::buildItemBlock (SettingsItemDef &iDef) } -bool CsSettings::ItemBlock::update (const QString &value) +bool CSVSettings::ItemBlock::update (const QString &value) { bool success = updateItem (value); @@ -85,13 +85,13 @@ bool CsSettings::ItemBlock::update (const QString &value) } -bool CsSettings::ItemBlock::updateItem (const QString &value) +bool CSVSettings::ItemBlock::updateItem (const QString &value) { return mSetting->updateItem(value); } -bool CsSettings::ItemBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) +bool CSVSettings::ItemBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) { bool success = (mSetting->getValue() != value); @@ -101,15 +101,15 @@ bool CsSettings::ItemBlock::updateBySignal(const QString &name, const QString &v return success; } -CsSettings::SettingList *CsSettings::ItemBlock::getSettings () +CSMSettings::SettingList *CSVSettings::ItemBlock::getSettings () { - SettingList *list = new SettingList(); + CSMSettings::SettingList *list = new CSMSettings::SettingList(); list->push_back(mSetting); return list; } -QString CsSettings::ItemBlock::getValue() const +QString CSVSettings::ItemBlock::getValue() const { return mSetting->getValue(); } diff --git a/apps/opencs/settings/itemblock.hpp b/apps/opencs/view/settings/itemblock.hpp similarity index 76% rename from apps/opencs/settings/itemblock.hpp rename to apps/opencs/view/settings/itemblock.hpp index 8aab657453..c7714ac64a 100644 --- a/apps/opencs/settings/itemblock.hpp +++ b/apps/opencs/view/settings/itemblock.hpp @@ -3,21 +3,21 @@ #include "abstractblock.hpp" -namespace CsSettings +namespace CSVSettings { class ItemBlock : public AbstractBlock { - SettingsItem *mSetting; + CSMSettings::SettingsItem *mSetting; WidgetList mWidgetList; public: ItemBlock (QWidget* parent = 0); - bool updateSettings (const SettingMap &settings) { return false; } + bool updateSettings (const CSMSettings::SettingMap &settings) { return false; } - SettingList *getSettings (); + CSMSettings::SettingList *getSettings (); QString getValue () const; int getSettingCount(); diff --git a/apps/opencs/settings/proxyblock.cpp b/apps/opencs/view/settings/proxyblock.cpp similarity index 82% rename from apps/opencs/settings/proxyblock.cpp rename to apps/opencs/view/settings/proxyblock.cpp index 46e404a120..71a3070841 100644 --- a/apps/opencs/settings/proxyblock.cpp +++ b/apps/opencs/view/settings/proxyblock.cpp @@ -1,11 +1,11 @@ #include "proxyblock.hpp" #include "itemblock.hpp" -CsSettings::ProxyBlock::ProxyBlock (QWidget *parent) +CSVSettings::ProxyBlock::ProxyBlock (QWidget *parent) : GroupBlock (parent) { } -int CsSettings::ProxyBlock::build (GroupBlockDef &proxyDef) +int CSVSettings::ProxyBlock::build (GroupBlockDef &proxyDef) { //get the list of pre-defined values for the proxy mValueList = proxyDef.properties.at(0)->valueList; @@ -19,7 +19,7 @@ int CsSettings::ProxyBlock::build (GroupBlockDef &proxyDef) return success; } -void CsSettings::ProxyBlock::addSetting (ItemBlock *settingBlock, QStringList *proxyList) +void CSVSettings::ProxyBlock::addSetting (ItemBlock *settingBlock, QStringList *proxyList) { //connect the item block of the proxied seting to the generic update slot connect (settingBlock, SIGNAL (signalUpdateSetting(const QString &, const QString &)), @@ -29,23 +29,23 @@ void CsSettings::ProxyBlock::addSetting (ItemBlock *settingBlock, QStringList *p mProxyList << proxyList; } -bool CsSettings::ProxyBlock::updateSettings (const SettingMap &settings) +bool CSVSettings::ProxyBlock::updateSettings (const CSMSettings::SettingMap &settings) { return updateByProxiedSettings(&settings); } -bool CsSettings::ProxyBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) +bool CSVSettings::ProxyBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) { doEmit = false; return updateProxiedSettings(); } -void CsSettings::ProxyBlock::slotUpdateProxySetting (const QString &name, const QString &value) +void CSVSettings::ProxyBlock::slotUpdateProxySetting (const QString &name, const QString &value) { updateByProxiedSettings(); } -bool CsSettings::ProxyBlock::updateProxiedSettings() +bool CSVSettings::ProxyBlock::updateProxiedSettings() { foreach (ItemBlock *block, mProxiedItemBlockList) { @@ -74,7 +74,7 @@ bool CsSettings::ProxyBlock::updateProxiedSettings() return true; } -bool CsSettings::ProxyBlock::updateByProxiedSettings(const SettingMap *settings) +bool CSVSettings::ProxyBlock::updateByProxiedSettings(const CSMSettings::SettingMap *settings) { bool success = false; int commonIndex = -1; @@ -143,7 +143,7 @@ bool CsSettings::ProxyBlock::updateByProxiedSettings(const SettingMap *settings) return success; } -CsSettings::ItemBlock *CsSettings::ProxyBlock::getProxiedItemBlock (const QString &name) +CSVSettings::ItemBlock *CSVSettings::ProxyBlock::getProxiedItemBlock (const QString &name) { return getItemBlock (name, &mProxiedItemBlockList); } diff --git a/apps/opencs/settings/proxyblock.hpp b/apps/opencs/view/settings/proxyblock.hpp similarity index 80% rename from apps/opencs/settings/proxyblock.hpp rename to apps/opencs/view/settings/proxyblock.hpp index d35d7da693..f757842eaf 100644 --- a/apps/opencs/settings/proxyblock.hpp +++ b/apps/opencs/view/settings/proxyblock.hpp @@ -3,7 +3,7 @@ #include "groupblock.hpp" -namespace CsSettings +namespace CSVSettings { class ProxyBlock : public GroupBlock { @@ -23,14 +23,14 @@ namespace CsSettings void addSetting (ItemBlock* settingBlock, QStringList *proxyList); int build (GroupBlockDef &def); - SettingList *getSettings() { return 0; } - bool updateSettings (const SettingMap &settings); + CSMSettings::SettingList *getSettings() { return 0; } + bool updateSettings (const CSMSettings::SettingMap &settings); bool updateBySignal (const QString &name, const QString &value, bool &doEmit); private: ItemBlock *getProxiedItemBlock (const QString &name); - bool updateByProxiedSettings(const SettingMap *settings = 0); + bool updateByProxiedSettings(const CSMSettings::SettingMap *settings = 0); bool updateProxiedSettings(); private slots: diff --git a/apps/opencs/settings/settingwidget.cpp b/apps/opencs/view/settings/settingwidget.cpp similarity index 100% rename from apps/opencs/settings/settingwidget.cpp rename to apps/opencs/view/settings/settingwidget.cpp diff --git a/apps/opencs/settings/settingwidget.hpp b/apps/opencs/view/settings/settingwidget.hpp similarity index 92% rename from apps/opencs/settings/settingwidget.hpp rename to apps/opencs/view/settings/settingwidget.hpp index 4e4bad1321..b29523a3a6 100644 --- a/apps/opencs/settings/settingwidget.hpp +++ b/apps/opencs/view/settings/settingwidget.hpp @@ -14,7 +14,7 @@ #include "abstractwidget.hpp" -namespace CsSettings +namespace CSVSettings { //VALID FOR RADIOBUTTON / CHECKBOX (or other toggle widget with it's own label) template @@ -25,7 +25,7 @@ namespace CsSettings public: - explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget* parent = 0) + explicit SettingWidget (WidgetDef &def, QLayout *layout, QWidget* parent = 0) : AbstractWidget (layout, parent), mWidget (new T1 (parent)) { mWidget->setText(def.caption); @@ -55,7 +55,7 @@ namespace CsSettings public: - SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0) + SettingWidget (WidgetDef &def, QLayout *layout, QWidget *parent = 0) : AbstractWidget (layout, parent), mWidget (new QSpinBox (parent)) { def.caption += tr(" (%1 to %2)").arg(def.minMax->left).arg(def.minMax->right); @@ -91,7 +91,7 @@ namespace CsSettings }; template <> - class SettingWidget : public CsSettings::AbstractWidget + class SettingWidget : public CSVSettings::AbstractWidget { QComboBox *mWidget; @@ -143,7 +143,7 @@ namespace CsSettings }; template <> - class SettingWidget : public CsSettings::AbstractWidget + class SettingWidget : public CSVSettings::AbstractWidget { QLineEdit *mWidget; @@ -176,7 +176,7 @@ namespace CsSettings }; template <> - class SettingWidget : public CsSettings::AbstractWidget + class SettingWidget : public CSVSettings::AbstractWidget { QListWidget *mWidget; diff --git a/apps/opencs/view/settings/support.cpp b/apps/opencs/view/settings/support.cpp new file mode 100644 index 0000000000..d79edfdb33 --- /dev/null +++ b/apps/opencs/view/settings/support.cpp @@ -0,0 +1 @@ +#include "support.hpp" diff --git a/apps/opencs/settings/support.hpp b/apps/opencs/view/settings/support.hpp similarity index 61% rename from apps/opencs/settings/support.hpp rename to apps/opencs/view/settings/support.hpp index 6953cbb614..7185d27062 100644 --- a/apps/opencs/settings/support.hpp +++ b/apps/opencs/view/settings/support.hpp @@ -1,17 +1,13 @@ -#ifndef SUPPORT_HPP -#define SUPPORT_HPP +#ifndef VIEW_SUPPORT_HPP +#define VIEW_SUPPORT_HPP -#include +#include #include -class QLayout; -class QWidget; -class QListWidgetItem; +#include "../../model/settings/support.hpp" -namespace CsSettings +namespace CSVSettings { - - class SettingContainer; struct WidgetDef; class ItemBlock; class GroupBlock; @@ -20,86 +16,63 @@ namespace CsSettings typedef QList GroupBlockDefList; typedef QList GroupBlockList; typedef QList ItemBlockList; - typedef QList SettingList; - typedef QMap SettingMap; - typedef QMap SectionMap; typedef QList ProxyList; typedef QList WidgetList; typedef QMap ItemBlockMap; - enum OcsWidgetOrientation + enum Orientation { - OCS_HORIZONTAL, - OCS_VERTICAL + Orient_Horizontal, + Orient_Vertical }; - enum OcsWidgetType + enum WidgetType { - OCS_CHECK_WIDGET, - OCS_COMBO_WIDGET, - OCS_TEXT_WIDGET, - OCS_LIST_WIDGET, - OCS_RADIO_WIDGET, - OCS_SPIN_WIDGET, - OCS_UNDEFINED_WIDGET + Widget_CheckBox, + Widget_ComboBox, + Widget_LineEdit, + Widget_ListBox, + Widget_RadioButton, + Widget_SpinBox, + Widget_Undefined }; - enum OcsAlignment + enum Alignment { - OCS_LEFT = Qt::AlignLeft, - OCS_CENTER = Qt::AlignHCenter, - OCS_RIGHT = Qt::AlignRight - }; - - struct QStringPair - { - QStringPair(): left (""), right ("") - {} - - QStringPair (const QString &leftValue, const QString &rightValue) - : left (leftValue), right(rightValue) - {} - - QStringPair (const QStringPair &pair) - : left (pair.left), right (pair.right) - {} - - QString left; - QString right; - - bool isEmpty() - { return (left.isEmpty() && right.isEmpty()); } + Align_Left = Qt::AlignLeft, + Align_Center = Qt::AlignHCenter, + Align_Right = Qt::AlignRight }; //template for defining the widget of a property. struct WidgetDef { - OcsWidgetType type; //type of widget providing input + WidgetType type; //type of widget providing input int labelWidth; //width of caption label int widgetWidth; //width of input widget - OcsWidgetOrientation orientation; //label / widget orientation (horizontal / vertical) + Orientation orientation; //label / widget orientation (horizontal / vertical) QString inputMask; //input mask (line edit) QString caption; //label caption. Leave empty for multiple items. See BlockDef::captionList QString value; //widget value. Leave empty for multiple items. See BlockDef::valueList - QStringPair *minMax; //Min/Max QString value pair. If empty, assigned to property item value pair. + CSMSettings::QStringPair *minMax; //Min/Max QString value pair. If empty, assigned to property item value pair. QStringList *valueList; //value list for list widgets. If left empty, is assigned to property item value list during block build(). bool isDefault; //isDefault - determined at runtime. - OcsAlignment valueAlignment; //left / center / right-justify text in widget - OcsAlignment widgetAlignment; //left / center / right-justify widget in group box + Alignment valueAlignment; //left / center / right-justify text in widget + Alignment widgetAlignment; //left / center / right-justify widget in group box WidgetDef() : labelWidth (-1), widgetWidth (-1), - orientation (OCS_HORIZONTAL), - isDefault (true), valueAlignment (OCS_CENTER), - widgetAlignment (OCS_RIGHT), + orientation (Orient_Horizontal), + isDefault (true), valueAlignment (Align_Center), + widgetAlignment (Align_Right), inputMask (""), value (""), caption (""), valueList (0) {} - WidgetDef (OcsWidgetType widgType) - : type (widgType), orientation (OCS_HORIZONTAL), - caption (""), value (""), valueAlignment (OCS_CENTER), - widgetAlignment (OCS_RIGHT), + WidgetDef (WidgetType widgType) + : type (widgType), orientation (Orient_Horizontal), + caption (""), value (""), valueAlignment (Align_Center), + widgetAlignment (Align_Right), labelWidth (-1), widgetWidth (-1), valueList (0), isDefault (true) {} @@ -116,15 +89,15 @@ namespace CsSettings //Used to populate option widget captions or list widget item lists (see WidgetDef::caption / value) QString defaultValue; bool hasMultipleValues; - QStringPair minMax; //minimum / maximum value pair + CSMSettings::QStringPair minMax; //minimum / maximum value pair WidgetDef widget; //definition of the input widget for this setting - OcsWidgetOrientation orientation; //general orientation of the widget / label for this property + Orientation orientation; //general orientation of the widget / label for this property ProxyList *proxyList; //list of property and corresponding default values for proxy widget - SettingsItemDef() : name (""), defaultValue (""), orientation (OCS_VERTICAL), hasMultipleValues (false) + SettingsItemDef() : name (""), defaultValue (""), orientation (Orient_Vertical), hasMultipleValues (false) {} - SettingsItemDef (QString propName, QString propDefault, OcsWidgetOrientation propOrient = OCS_VERTICAL) + SettingsItemDef (QString propName, QString propDefault, Orientation propOrient = Orient_Vertical) : name (propName), defaultValue (propDefault), orientation (propOrient), hasMultipleValues(false), valueList (new QStringList), proxyList ( new ProxyList) {} @@ -139,16 +112,16 @@ namespace CsSettings QStringList captions; //list of captions for widgets at the block level (not associated with any particular property) WidgetList widgets; //list of widgets at the block level (not associated with any particular property) QList properties; //list of the property(ies) which are subordinate to the property block. - OcsWidgetOrientation widgetOrientation; //general orientation of widgets in group block + Orientation widgetOrientation; //general orientation of widgets in group block bool isVisible; //determines whether or not box border/title are visible bool isProxy; //indicates whether or not this block defines a proxy block QString defaultValue; //generic default value attribute - GroupBlockDef (): title(""), widgetOrientation (OCS_VERTICAL), isVisible (true), isProxy (false), defaultValue ("") + GroupBlockDef (): title(""), widgetOrientation (Orient_Vertical), isVisible (true), isProxy (false), defaultValue ("") {} GroupBlockDef (QString blockTitle) - : title (blockTitle), widgetOrientation (OCS_VERTICAL), isProxy (false), isVisible (true), defaultValue ("") + : title (blockTitle), widgetOrientation (Orient_Vertical), isProxy (false), isVisible (true), defaultValue ("") {} }; @@ -157,14 +130,15 @@ namespace CsSettings QString title; QString defaultValue; //default value for widgets unique to the custom block GroupBlockDefList blockDefList; //list of settings groups that comprise the settings within the custom block - OcsWidgetOrientation blockOrientation; + Orientation blockOrientation; - CustomBlockDef (): title (""), defaultValue (""), blockOrientation (OCS_HORIZONTAL) + CustomBlockDef (): title (""), defaultValue (""), blockOrientation (Orient_Horizontal) {} CustomBlockDef (const QString &blockTitle) - : title (blockTitle), defaultValue (""), blockOrientation (OCS_HORIZONTAL) + : title (blockTitle), defaultValue (""), blockOrientation (Orient_Horizontal) {} }; } -#endif // SUPPORT_HPP + +#endif // VIEW_SUPPORT_HPP diff --git a/apps/opencs/settings/toggleblock.cpp b/apps/opencs/view/settings/toggleblock.cpp similarity index 89% rename from apps/opencs/settings/toggleblock.cpp rename to apps/opencs/view/settings/toggleblock.cpp index f30ffceb05..12a42ca9c5 100644 --- a/apps/opencs/settings/toggleblock.cpp +++ b/apps/opencs/view/settings/toggleblock.cpp @@ -3,11 +3,11 @@ #include "groupbox.hpp" #include "itemblock.hpp" -CsSettings::ToggleBlock::ToggleBlock(QWidget *parent) : +CSVSettings::ToggleBlock::ToggleBlock(QWidget *parent) : CustomBlock(parent) {} -int CsSettings::ToggleBlock::build(CustomBlockDef &def) +int CSVSettings::ToggleBlock::build(CustomBlockDef &def) { if (def.blockDefList.size()==0) return -1; @@ -49,7 +49,7 @@ int CsSettings::ToggleBlock::build(CustomBlockDef &def) return 0; } -CsSettings::GroupBox *CsSettings::ToggleBlock::buildToggleWidgets (GroupBlockDef &def, QString &defaultToggle) +CSVSettings::GroupBox *CSVSettings::ToggleBlock::buildToggleWidgets (GroupBlockDef &def, QString &defaultToggle) { GroupBox *box = new GroupBox (false, getParent()); @@ -61,7 +61,7 @@ CsSettings::GroupBox *CsSettings::ToggleBlock::buildToggleWidgets (GroupBlockDef WidgetDef *wDef = def.widgets.at(i); wDef->caption = caption; - wDef->widgetAlignment = OCS_LEFT; + wDef->widgetAlignment = Align_Left; AbstractWidget *widg = buildWidget (caption, *wDef, layout, false); diff --git a/apps/opencs/settings/toggleblock.hpp b/apps/opencs/view/settings/toggleblock.hpp similarity index 95% rename from apps/opencs/settings/toggleblock.hpp rename to apps/opencs/view/settings/toggleblock.hpp index 990dc08266..8afe701b85 100644 --- a/apps/opencs/settings/toggleblock.hpp +++ b/apps/opencs/view/settings/toggleblock.hpp @@ -5,7 +5,7 @@ #include "customblock.hpp" -namespace CsSettings +namespace CSVSettings { class GroupBlock; class GroupBox; diff --git a/apps/opencs/settings/usersettingsdialog.cpp b/apps/opencs/view/settings/usersettingsdialog.cpp similarity index 71% rename from apps/opencs/settings/usersettingsdialog.cpp rename to apps/opencs/view/settings/usersettingsdialog.cpp index 9777573aea..012fc04087 100644 --- a/apps/opencs/settings/usersettingsdialog.cpp +++ b/apps/opencs/view/settings/usersettingsdialog.cpp @@ -12,13 +12,13 @@ #include "blankpage.hpp" #include "editorpage.hpp" -#include "support.hpp" +#include "../../model/settings/support.hpp" #include "settingwidget.hpp" #include -CsSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : - QMainWindow (parent), mUserSettings (mCfgMgr), mStackedWidget (0) +CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : + QMainWindow (parent), mStackedWidget (0) { setWindowTitle(QString::fromUtf8 ("User Settings")); buildPages(); @@ -31,22 +31,22 @@ CsSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); } -CsSettings::UserSettingsDialog::~UserSettingsDialog() +CSVSettings::UserSettingsDialog::~UserSettingsDialog() { } -void CsSettings::UserSettingsDialog::closeEvent (QCloseEvent *event) +void CSVSettings::UserSettingsDialog::closeEvent (QCloseEvent *event) { writeSettings(); } -void CsSettings::UserSettingsDialog::setWidgetStates (SectionMap settingsMap) +void CSVSettings::UserSettingsDialog::setWidgetStates (CSMSettings::SectionMap settingsMap) { //iterate the tabWidget's pages (sections) for (int i = 0; i < mStackedWidget->count(); i++) { //get the settings defined for the entire section - CsSettings::SettingMap *settings = settingsMap [mStackedWidget->widget(i)->objectName()]; + CSMSettings::SettingMap *settings = settingsMap [mStackedWidget->widget(i)->objectName()]; //if found, initialize the page's widgets if (settings) @@ -57,7 +57,7 @@ void CsSettings::UserSettingsDialog::setWidgetStates (SectionMap settingsMap) } } -void CsSettings::UserSettingsDialog::buildPages() +void CSVSettings::UserSettingsDialog::buildPages() { //craete central widget with it's layout and immediate children QWidget *centralWidget = new QWidget (this); @@ -81,21 +81,21 @@ void CsSettings::UserSettingsDialog::buildPages() createPage("Page3"); } -void CsSettings::UserSettingsDialog::createSamplePage() +void CSVSettings::UserSettingsDialog::createSamplePage() { //add pages to stackedwidget and items to listwidget - CsSettings::AbstractPage *page - = new CsSettings::EditorPage(this); + CSVSettings::AbstractPage *page + = new CSVSettings::EditorPage(this); mStackedWidget->addWidget (page); new QListWidgetItem (page->objectName(), mListWidget); connect ( page, SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), - this, SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); + &(CSMSettings::UserSettings::instance()), SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); } -void CsSettings::UserSettingsDialog::positionWindow () +void CSVSettings::UserSettingsDialog::positionWindow () { QRect scr = QApplication::desktop()->screenGeometry(); @@ -103,14 +103,14 @@ void CsSettings::UserSettingsDialog::positionWindow () } -CsSettings::SectionMap CsSettings::UserSettingsDialog::loadSettings () +CSMSettings::SectionMap CSVSettings::UserSettingsDialog::loadSettings () { QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); mPaths.append(QString("opencs.cfg")); mPaths.append(userPath + QString("opencs.cfg")); - SectionMap settingsMap; + CSMSettings::SectionMap settingsMap; foreach (const QString &path, mPaths) { @@ -135,7 +135,7 @@ CsSettings::SectionMap CsSettings::UserSettingsDialog::loadSettings () QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); - mUserSettings.getSettings(stream, settingsMap); + CSMSettings::UserSettings::instance().getSettings(stream, settingsMap); } file.close(); @@ -144,9 +144,9 @@ CsSettings::SectionMap CsSettings::UserSettingsDialog::loadSettings () return settingsMap; } -void CsSettings::UserSettingsDialog::writeSettings() +void CSVSettings::UserSettingsDialog::writeSettings() { - QMap settings; + QMap settings; for (int i = 0; i < mStackedWidget->count(); ++i) { @@ -154,16 +154,16 @@ void CsSettings::UserSettingsDialog::writeSettings() settings [page->objectName()] = page->getSettings(); } - mUserSettings.writeFile(mUserSettings.openFile(mPaths.back()), settings); + CSMSettings::UserSettings::instance().writeFile(CSMSettings::UserSettings::instance().openFile(mPaths.back()), settings); } -CsSettings::AbstractPage *CsSettings::UserSettingsDialog::getAbstractPage (int index) +CSVSettings::AbstractPage *CSVSettings::UserSettingsDialog::getAbstractPage (int index) { return dynamic_cast(mStackedWidget->widget(index)); } -void CsSettings::UserSettingsDialog::slotChangePage(QListWidgetItem *current, QListWidgetItem *previous) +void CSVSettings::UserSettingsDialog::slotChangePage(QListWidgetItem *current, QListWidgetItem *previous) { if (!current) current = previous; diff --git a/apps/opencs/settings/usersettingsdialog.hpp b/apps/opencs/view/settings/usersettingsdialog.hpp similarity index 83% rename from apps/opencs/settings/usersettingsdialog.hpp rename to apps/opencs/view/settings/usersettingsdialog.hpp index c69906ebc7..31d40ca017 100644 --- a/apps/opencs/settings/usersettingsdialog.hpp +++ b/apps/opencs/view/settings/usersettingsdialog.hpp @@ -5,18 +5,22 @@ #include #include +#ifndef Q_MOC_RUN #include -#include "usersettings.hpp" -#include "support.hpp" +#endif + +#include "../../model/settings/usersettings.hpp" +#include "../../model/settings/support.hpp" class QHBoxLayout; class AbstractWidget; class QStackedWidget; class QListWidget; -namespace CsSettings { +namespace CSVSettings { class AbstractPage; + class UserSettingsDialog : public QMainWindow { Q_OBJECT @@ -24,7 +28,6 @@ namespace CsSettings { QStringList mPaths; QListWidget *mListWidget; QStackedWidget *mStackedWidget; - UserSettings mUserSettings; Files::ConfigurationManager mCfgMgr; public: @@ -35,10 +38,10 @@ namespace CsSettings { void closeEvent (QCloseEvent *event); AbstractPage *getAbstractPage (int index); - void setWidgetStates (SectionMap settingsMap); + void setWidgetStates (CSMSettings::SectionMap settingsMap); void buildPages(); void positionWindow (); - SectionMap loadSettings(); + CSMSettings::SectionMap loadSettings(); void writeSettings(); void createSamplePage(); @@ -61,9 +64,6 @@ namespace CsSettings { resize (mStackedWidget->sizeHint()); } - signals: - void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); - public slots: void slotChangePage (QListWidgetItem*, QListWidgetItem*); }; diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index e16de99eff..cedb20de92 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -24,7 +24,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM widget->setLayout (layout); - QAbstractTableModel *model = document.getData().getTableModel (id); + QAbstractItemModel *model = document.getData().getTableModel (id); int columns = model->columnCount(); diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 7a8b45373d..dd194abe9a 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -1,6 +1,7 @@ #include "enumdelegate.hpp" +#include #include #include @@ -43,6 +44,9 @@ CSVWorld::EnumDelegate::EnumDelegate (const std::vector QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { + if (!index.data().isValid()) + return 0; + QComboBox *comboBox = new QComboBox (parent); for (std::vector >::const_iterator iter (mValues.begin()); @@ -72,23 +76,39 @@ void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - QStyleOptionViewItemV4 option2 (option); + if (index.data().isValid()) + { + QStyleOptionViewItemV4 option2 (option); - int value = index.data().toInt(); + int value = index.data().toInt(); - for (std::vector >::const_iterator iter (mValues.begin()); - iter!=mValues.end(); ++iter) - if (iter->first==value) - { - option2.text = iter->second; + for (std::vector >::const_iterator iter (mValues.begin()); + iter!=mValues.end(); ++iter) + if (iter->first==value) + { + option2.text = iter->second; - QApplication::style()->drawControl (QStyle::CE_ItemViewItem, &option2, painter); + QApplication::style()->drawControl (QStyle::CE_ItemViewItem, &option2, painter); - break; - } + break; + } + } } +CSVWorld::EnumDelegateFactory::EnumDelegateFactory() {} + +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]); +} + CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (QUndoStack& undoStack, QObject *parent) const { diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index f11252371e..58f19ff782 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -45,6 +45,12 @@ namespace CSVWorld public: + EnumDelegateFactory(); + + 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 351007ded5..3d05f88608 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -5,15 +5,38 @@ #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_Global, - new CSVDoc::SubViewFactoryWithCreateFlag (true)); + manager.add (CSMWorld::UniversalId::Type_Skills, + new CSVDoc::SubViewFactoryWithCreateFlag (false)); + + 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_Cells, + CSMWorld::UniversalId::Type_Referenceables, + + 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)); } \ No newline at end of file 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/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 5ada1d84f3..5bbedbf3d0 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -116,6 +116,16 @@ void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemMode ///< \todo provide some kind of feedback to the user, indicating that editing is currently not possible. } +QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if (!index.data().isValid()) + return 0; + + return QStyledItemDelegate::createEditor (parent, option, index); +} + + void CSVWorld::CommandDelegate::setEditLock (bool locked) { mEditLock = locked; diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 5334abf9c8..95dfec6c58 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -99,6 +99,9 @@ namespace CSVWorld virtual void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; + virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + void setEditLock (bool locked); bool isEditLocked() const; diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8df2136e5e..660e451156 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -14,7 +14,7 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky player animation npcanimation creatureanimation activatoranimation + renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction ) @@ -24,13 +24,16 @@ 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 + 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 enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons + merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks + keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview + tradeitemmodel companionitemmodel pickpocketitemmodel ) add_openmw_dir (mwdialogue @@ -53,7 +56,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 + esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor ) add_openmw_dir (mwclass @@ -62,9 +65,9 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanagerimp stat character creaturestats magiceffects movement actors activators + mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow - aiescort aiactivate + aiescort aiactivate repair enchanting pathfinding security ) add_openmw_dir (mwbase @@ -121,6 +124,12 @@ if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) endif() +# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream +if (UNIX AND NOT APPLE) +target_link_libraries(openmw dl Xt) +endif() + + if(APPLE) find_library(CARBON_FRAMEWORK Carbon) find_library(COCOA_FRAMEWORK Cocoa) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index ce84b8dfe1..abc9517269 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, 8, '0'); + 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(mArchives.size()-i, 8, '0'); + + 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 @@ -351,8 +366,9 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create the world mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, - mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap, + mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, mActivationDistanceOverride)); + MWBase::Environment::get().getWorld()->setupPlayer(); //Load translation data mTranslationDataStorage.setEncoder(mEncoder); @@ -363,8 +379,10 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( - mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage)); + if (mNewGame) + mEnvironment.getWindowManager()->setNewGame(true); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); @@ -393,25 +411,34 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) MWBase::Environment::get().getWorld()->getPlayer(), *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser, keybinderUserExists)); - // load cell - ESM::Position pos; - pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - pos.pos[2] = 0; - mEnvironment.getWorld()->renderPlayer(); - if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (mCellName)) + if (!mNewGame) { - MWBase::Environment::get().getWorld()->indexToPosition (exterior->mData.mX, exterior->mData.mY, - pos.pos[0], pos.pos[1], true); - MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + // load cell + ESM::Position pos; + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; + pos.pos[2] = 0; + + if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (mCellName)) + { + MWBase::Environment::get().getWorld()->indexToPosition (exterior->mData.mX, exterior->mData.mY, + pos.pos[0], pos.pos[1], true); + MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + } + else + { + pos.pos[0] = pos.pos[1] = 0; + MWBase::Environment::get().getWorld()->changeToInteriorCell (mCellName, pos); + } } else - { - pos.pos[0] = pos.pos[1] = 0; - MWBase::Environment::get().getWorld()->changeToInteriorCell (mCellName, pos); - } + mEnvironment.getWorld()->startNewGame(); + Ogre::FrameEvent event; + event.timeSinceLastEvent = 0; + event.timeSinceLastFrame = 0; + frameRenderingQueued(event); mOgre->getRoot()->addFrameListener (this); // scripts diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index a205e78db7..12996cc309 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -23,8 +23,12 @@ namespace MWBase DialogueManager() {} + virtual void clear() = 0; + virtual ~DialogueManager() {} + virtual bool isInChoice() const = 0; + virtual void startDialogue (const MWWorld::Ptr& actor) = 0; virtual void addTopic (const std::string& topic) = 0; @@ -39,7 +43,9 @@ namespace MWBase //calbacks for the GUI virtual void keywordSelected (const std::string& keyword) = 0; virtual void goodbyeSelected() = 0; - virtual void questionAnswered (const std::string& answer) = 0; + virtual void questionAnswered (int answer) = 0; + + virtual bool checkServiceRefused () = 0; virtual void persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index b3dfea45b7..51e51edda9 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -33,6 +33,8 @@ namespace MWBase Journal() {} + virtual void clear() = 0; + virtual ~Journal() {} virtual void addEntry (const std::string& id, int index) = 0; diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index b8733259ff..38794269b9 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -82,7 +82,7 @@ namespace MWBase virtual int getDerivedDisposition(const MWWorld::Ptr& ptr) = 0; ///< Calculate the diposition of an NPC toward the player. - + virtual int countDeaths (const std::string& id) const = 0; ///< Return the number of deaths for actors with the given ID. @@ -99,6 +99,9 @@ namespace MWBase float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; ///< Perform a persuasion action on NPC + virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; + ///< Forces an object to refresh its animation state. + virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; ///< Run animation for a MW-reference. Calls to this function for references that are currently not /// in the scene should be ignored. diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index ae146e0646..32df2bfa3b 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -35,6 +35,8 @@ namespace MWBase virtual ~ScriptManager() {} + virtual void resetGlobalScripts() = 0; + virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; ///< Run the script with the given name (compile first, if not compiled yet) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 93cc8e44a2..ecea548a58 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -81,16 +81,22 @@ namespace MWBase */ virtual void update() = 0; + virtual void setNewGame(bool newgame) = 0; + virtual void pushGuiMode (MWGui::GuiMode mode) = 0; virtual void popGuiMode() = 0; virtual void removeGuiMode (MWGui::GuiMode mode) = 0; ///< can be anywhere in the stack + virtual void updatePlayer() = 0; + virtual MWGui::GuiMode getMode() const = 0; virtual bool isGuiMode() const = 0; + virtual bool isConsoleMode() const = 0; + virtual void toggleVisible (MWGui::GuiWindow wnd) = 0; /// Disallow all inventory mode windows @@ -180,8 +186,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; @@ -198,7 +204,10 @@ namespace MWBase virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector()) = 0; + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false) = 0; + virtual void staticMessageBox(const std::string& message) = 0; + virtual void removeStaticMessageBox() = 0; + virtual void enterPressed () = 0; virtual int readPressedButton() = 0; ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) @@ -229,13 +238,22 @@ namespace MWBase virtual void enableRest() = 0; virtual bool getRestEnabled() = 0; + virtual bool getJournalAllowed() = 0; virtual bool getPlayerSleeping() = 0; virtual void wakeUpPlayer() = 0; + virtual void showCompanionWindow(MWWorld::Ptr actor) = 0; virtual void startSpellMaking(MWWorld::Ptr actor) = 0; virtual void startEnchanting(MWWorld::Ptr actor) = 0; + virtual void startSelfEnchanting(MWWorld::Ptr soulgem) = 0; virtual void startTraining(MWWorld::Ptr actor) = 0; + virtual void startRepair(MWWorld::Ptr actor) = 0; + virtual void startRepairItem(MWWorld::Ptr item) = 0; + + virtual void showSoulgemDialog (MWWorld::Ptr item) = 0; + + virtual void frameStarted(float dt) = 0; virtual void changePointer (const std::string& name) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6cd5b90b40..de1a347925 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -44,8 +44,14 @@ namespace MWRender class Animation; } +namespace MWMechanics +{ + class Movement; +} + namespace MWWorld { + class Fallback; class CellStore; class Player; class LocalScripts; @@ -53,7 +59,7 @@ namespace MWWorld class ESMStore; class RefData; - typedef std::vector > PtrMovementList; + typedef std::vector > PtrMovementList; } namespace MWBase @@ -88,6 +94,8 @@ namespace MWBase virtual ~World() {} + virtual void startNewGame() = 0; + virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. @@ -103,11 +111,7 @@ namespace MWBase virtual void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches) = 0; - virtual void setFallbackValues (const std::map& fallbackMap) = 0; - - virtual std::string getFallback (const std::string& key) const = 0; - - virtual std::string getFallback (const std::string& key, const std::string& def) const = 0; + virtual const MWWorld::Fallback *getFallback () const = 0; virtual MWWorld::Player& getPlayer() = 0; @@ -142,7 +146,7 @@ namespace MWBase virtual char getGlobalVariableType (const std::string& name) const = 0; ///< Return ' ', if there is no global variable with this name. - + virtual std::vector getGlobals () const = 0; virtual std::string getCurrentCellName() const = 0; @@ -214,6 +218,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; @@ -225,6 +232,8 @@ namespace MWBase virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; + virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; + virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0; ///< place an object in a "safe" location (ie not in the void, etc). @@ -238,6 +247,9 @@ namespace MWBase virtual void doPhysics (const MWWorld::PtrMovementList &actors, float duration) = 0; ///< Run physics simulation and modify \a world accordingly. + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; + ///< cast a Ray and return true if there is an object in the ray path. + virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. @@ -247,27 +259,44 @@ namespace MWBase ///< Toggle a render mode. ///< \return Resulting mode - virtual const ESM::Potion *createRecord (const ESM::Potion& record) - = 0; - ///< Create a new recrod (of type potion) in the ESM store. + virtual const ESM::Potion *createRecord (const ESM::Potion& record) = 0; + ///< Create a new record (of type potion) in the ESM store. /// \return pointer to created record - virtual const ESM::Spell *createRecord (const ESM::Spell& record) - = 0; - ///< Create a new recrod (of type spell) in the ESM store. + virtual const ESM::Spell *createRecord (const ESM::Spell& record) = 0; + ///< Create a new record (of type spell) in the ESM store. /// \return pointer to created record - virtual const ESM::Class *createRecord (const ESM::Class& record) - = 0; - ///< Create a new recrod (of type class) in the ESM store. + virtual const ESM::Class *createRecord (const ESM::Class& record) = 0; + ///< Create a new record (of type class) in the ESM store. /// \return pointer to created record virtual const ESM::Cell *createRecord (const ESM::Cell& record) = 0; - ///< Create a new recrod (of type cell) in the ESM store. + ///< Create a new record (of type cell) in the ESM store. /// \return pointer to created record virtual const ESM::NPC *createRecord(const ESM::NPC &record) = 0; - ///< Create a new recrod (of type npc) in the ESM store. + ///< Create a new record (of type npc) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Armor *createRecord (const ESM::Armor& record) = 0; + ///< Create a new record (of type armor) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Weapon *createRecord (const ESM::Weapon& record) = 0; + ///< Create a new record (of type weapon) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Clothing *createRecord (const ESM::Clothing& record) = 0; + ///< Create a new record (of type clothing) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Enchantment *createRecord (const ESM::Enchantment& record) = 0; + ///< Create a new record (of type enchantment) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Book *createRecord (const ESM::Book& record) = 0; + ///< Create a new record (of type book) in the ESM store. /// \return pointer to created record virtual void update (float duration, bool paused) = 0; @@ -293,13 +322,27 @@ namespace MWBase virtual void togglePOV() = 0; virtual void togglePreviewMode(bool enable) = 0; - virtual bool toggleVanityMode(bool enable, bool force) = 0; + virtual bool toggleVanityMode(bool enable) = 0; 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 setupPlayer() = 0; virtual void renderPlayer() = 0; - + + virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0; + ///< if activated, should this door be opened or closed? + virtual void activateDoor(const MWWorld::Ptr& door) = 0; + ///< activate (open or close) an non-teleport door + + virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object + virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object + virtual float getWindSpeed() = 0; + + virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; + ///< get all containers in active cells owned by this Npc + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; virtual int canRest() = 0; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 2c561eb858..b56ed118fa 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -35,7 +35,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const @@ -159,4 +159,16 @@ 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; + } + + float Apparatus::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 7045f62d6f..17b8b9254f 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -13,6 +13,8 @@ namespace MWClass public: + virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering @@ -54,6 +56,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 654cb87fd6..f8e4dc40af 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" @@ -38,7 +40,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Armor::getModel(const MWWorld::Ptr &ptr) const @@ -247,8 +249,9 @@ namespace MWClass text += "\n#{sArmorRating}: " + MWGui::ToolTips::toString(ref->mBase->mData.mArmor); - /// \todo store the current armor health somewhere - text += "\n#{sCondition}: " + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); + int remainingHealth = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mHealth; + text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight) + " (" + typeText + ")"; text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); @@ -259,6 +262,8 @@ namespace MWClass } info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; info.text = text; @@ -273,6 +278,86 @@ namespace MWClass return ref->mBase->mEnchant; } + void Armor::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ESM::Armor newItem = *ref->mBase; + newItem.mId=""; + newItem.mName=newName; + newItem.mData.mEnchant=enchCharge; + newItem.mEnchant=enchId; + const ESM::Armor *record = MWBase::Environment::get().getWorld()->createRecord (newItem); + ref->mBase = record; + } + + std::pair 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) + { + return std::make_pair(0, "#{sNotifyMessage13}"); + } + } + } + + 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) + { + return std::make_pair(0, "#{sNotifyMessage14}"); + } + } + } + } + + if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) + { + MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if(weapon == invStore.end()) + return std::make_pair(1,""); + + if(weapon->getTypeName() == typeid(ESM::Weapon).name() && + (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 std::make_pair(3,""); + } + return std::make_pair(1,""); + } + } + return std::make_pair(1,""); + } + boost::shared_ptr Armor::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); @@ -290,4 +375,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell); } + + float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mEnchant/10.f; + } + + bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Armor; + } + + float Armor::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 51c0ea21c9..d8d09d5bb0 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -12,6 +12,8 @@ namespace MWClass public: + virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering @@ -65,11 +67,21 @@ 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 void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \n + /// Second item in the pair specifies the error message + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float 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 6c3b7b86c3..40ba217338 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -33,7 +33,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Book::getModel(const MWWorld::Ptr &ptr) const @@ -147,6 +147,21 @@ namespace MWClass return ref->mBase->mEnchant; } + void Book::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ESM::Book newItem = *ref->mBase; + newItem.mId=""; + newItem.mName=newName; + newItem.mData.mIsScroll = 1; + newItem.mData.mEnchant=enchCharge; + newItem.mEnchant=enchId; + const ESM::Book *record = MWBase::Environment::get().getWorld()->createRecord (newItem); + ref->mBase = record; + } + boost::shared_ptr Book::use (const MWWorld::Ptr& ptr) const { return boost::shared_ptr(new MWWorld::ActionRead(ptr)); @@ -160,4 +175,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell); } + + float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mEnchant/10.f; + } + + bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Books; + } + + float Book::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index acb1aac06e..7fb8a95077 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -51,10 +51,18 @@ 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 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 virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + + virtual float getWeight (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 892ac091ce..df05a7910b 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" @@ -36,7 +37,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Clothing::getModel(const MWWorld::Ptr &ptr) const @@ -207,6 +208,8 @@ namespace MWClass } info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; info.text = text; @@ -221,6 +224,59 @@ namespace MWClass return ref->mBase->mEnchant; } + void Clothing::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ESM::Clothing newItem = *ref->mBase; + newItem.mId=""; + newItem.mName=newName; + newItem.mData.mEnchant=enchCharge; + newItem.mEnchant=enchId; + const ESM::Clothing *record = MWBase::Environment::get().getWorld()->createRecord (newItem); + ref->mBase = record; + } + + std::pair Clothing::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + // slots that this item can be equipped in + std::pair, bool> slots = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + + 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) + return std::make_pair(0, "#{sNotifyMessage13}"); + } + } + + 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) + return std::make_pair(0, "#{sNotifyMessage15}"); + } + } + } + } + return std::make_pair (1, ""); + } + boost::shared_ptr Clothing::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); @@ -238,4 +294,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell); } + + float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mEnchant/10.f; + } + + bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Clothing; + } + + float Clothing::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index f7801848f6..e2e1188a1a 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -59,11 +59,23 @@ 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 void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index a2d75131eb..4ee95b96e1 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -115,7 +115,7 @@ namespace MWClass if (needKey && hasKey) { - MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}", std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}"); ptr.getCellRef().mLockLevel = 0; // using a key disarms the trap ptr.getCellRef().mTrap = ""; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 19f327dcbb..a4eda71267 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(); @@ -191,6 +197,12 @@ namespace MWClass return info; } + float Creature::getArmorRating (const MWWorld::Ptr& ptr) const + { + /// \todo add Shield magic effect magnitude here, controlled by a GMST (Vanilla vs MCP behaviour) + return 0.f; + } + float Creature::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); @@ -203,9 +215,9 @@ namespace MWClass const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather + weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Feather)).mMagnitude; - weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden + weight += stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Burden)).mMagnitude; if (weight<0) weight = 0; @@ -213,6 +225,22 @@ namespace MWClass return weight; } + + int Creature::getServices(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + if (ref->mBase->mHasAI) + return ref->mBase->mAiData.mServices; + else + return 0; + } + + bool Creature::isPersistent(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + return ref->mBase->mPersistent; + } + MWWorld::Ptr Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index a96c18a8c5..b5657e265a 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. @@ -54,9 +56,16 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. + virtual float getArmorRating (const MWWorld::Ptr& ptr) const; + ///< @return combined armor rating of this actor + virtual bool isEssential (const MWWorld::Ptr& ptr) const; ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) + virtual int getServices (const MWWorld::Ptr& actor) const; + + virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index fb63299397..91d38b8eff 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -12,6 +12,7 @@ #include "../mwworld/nullaction.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/actionteleport.hpp" +#include "../mwworld/actiondoor.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" @@ -71,7 +72,7 @@ namespace MWClass ptr.get(); const std::string &openSound = ref->mBase->mOpenSound; - //const std::string &closeSound = ref->mBase->closeSound; + const std::string &closeSound = ref->mBase->mCloseSound; const std::string lockedSound = "LockedDoor"; const std::string trapActivationSound = "Disarm Trap Fail"; @@ -98,7 +99,7 @@ namespace MWClass if (needKey && hasKey) { - MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}", std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}"); ptr.getCellRef().mLockLevel = 0; // using a key disarms the trap ptr.getCellRef().mTrap = ""; @@ -139,12 +140,11 @@ namespace MWClass else { // animated door - // TODO return action for rotating the door - - // This is a little pointless, but helps with testing - boost::shared_ptr action(new MWWorld::NullAction); - - action->setSound(openSound); + boost::shared_ptr action(new MWWorld::ActionDoor(ptr)); + if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr)) + action->setSound(openSound); + else + action->setSound(closeSound); return action; } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index bbba45df58..2cc607f5fb 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -46,7 +46,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const @@ -197,4 +197,17 @@ 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; + } + + + float Ingredient::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 0afd202fb4..690dd601a7 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -56,6 +56,10 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getWeight (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 200f6e2d4a..ccc9d1de6d 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -50,7 +50,7 @@ namespace MWClass const std::string &model = ref->mBase->mModel; if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,ref->mBase->mData.mFlags & ESM::Light::Carry); if (!ref->mBase->mSound.empty()) MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, MWBase::SoundManager::Play_Loop); @@ -203,4 +203,16 @@ 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; + } + + float Light::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 640e1705bd..7f747ef483 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -57,6 +57,10 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getWeight (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 7e909437cf..084490742d 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -1,7 +1,7 @@ #include "lockpick.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -36,13 +36,13 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); assert(ref->mBase != NULL); const std::string &model = ref->mBase->mModel; @@ -54,8 +54,8 @@ namespace MWClass std::string Lockpick::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mName; } @@ -75,8 +75,8 @@ namespace MWClass std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mScript; } @@ -92,8 +92,8 @@ namespace MWClass int Lockpick::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mData.mValue; } @@ -102,7 +102,7 @@ namespace MWClass { boost::shared_ptr instance (new Lockpick); - registerClass (typeid (ESM::Tool).name(), instance); + registerClass (typeid (ESM::Lockpick).name(), instance); } std::string Lockpick::getUpSoundId (const MWWorld::Ptr& ptr) const @@ -117,24 +117,24 @@ namespace MWClass std::string Lockpick::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mIcon; } bool Lockpick::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); @@ -142,9 +142,9 @@ namespace MWClass std::string text; - /// \todo store remaining uses somewhere + int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses; - text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses); + text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); @@ -171,9 +171,29 @@ namespace MWClass MWWorld::Ptr Lockpick::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return MWWorld::Ptr(&cell.mLockpicks.insert(*ref), &cell); } + + bool Lockpick::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Picks; + } + + int Lockpick::getItemMaxHealth (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mUses; + } + + float Lockpick::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 0961b55b24..a7cf3791fc 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -57,6 +57,13 @@ 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; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + ///< Return item max health or throw an exception, if class does not have item health }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index d43a44359f..6b384be992 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -15,6 +15,7 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/actionsoulgem.hpp" #include "../mwgui/tooltips.hpp" @@ -23,6 +24,18 @@ #include +namespace +{ +bool isGold (const MWWorld::Ptr& ptr) +{ + return Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_001") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_005") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_010") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100"); +} +} + namespace MWClass { void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -39,7 +52,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const @@ -89,7 +102,15 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mValue; + int value = (ptr.getCellRef().mGoldValue == 1) ? ref->mBase->mData.mValue : ptr.getCellRef().mGoldValue; + + if (ptr.getCellRef().mSoul != "") + { + const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mRef.mSoul); + value *= creature->mData.mSoul; + } + + return value; } void Miscellaneous::registerSelf() @@ -101,25 +122,15 @@ namespace MWClass std::string Miscellaneous::getUpSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - if (ref->mBase->mName == MWBase::Environment::get().getWorld()->getStore().get().find("sGold")->getString()) - { + if (isGold(ptr)) return std::string("Item Gold Up"); - } return std::string("Item Misc Up"); } std::string Miscellaneous::getDownSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - if (ref->mBase->mName == MWBase::Environment::get().getWorld()->getStore().get().find("sGold")->getString()) - { + if (isGold(ptr)) return std::string("Item Gold Down"); - } return std::string("Item Misc Down"); } @@ -150,12 +161,15 @@ namespace MWClass int count = ptr.getRefData().getCount(); - bool isGold = (ref->mBase->mName == store.get().find("sGold")->getString()); - if (isGold && count == 1) - count = ref->mBase->mData.mValue; + bool gold = isGold(ptr); + + if (gold && ptr.getCellRef().mGoldValue != 1) + count = ptr.getCellRef().mGoldValue; + else if (gold) + count *= ref->mBase->mData.mValue; std::string countString; - if (!isGold) + if (!gold) countString = MWGui::ToolTips::getCountString(count); else // gold displays its count also if it's 1. countString = " (" + boost::lexical_cast(count) + ")"; @@ -171,10 +185,10 @@ namespace MWClass std::string text; - if (!isGold) + if (!gold) { text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); } if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { @@ -195,7 +209,7 @@ namespace MWClass const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - if (MWWorld::Class::get(ptr).getName(ptr) == store.get().find("sGold")->getString()) { + if (isGold(ptr)) { int goldAmount = ptr.getRefData().getCount(); std::string base = "Gold_001"; @@ -214,7 +228,8 @@ namespace MWClass MWWorld::LiveCellRef *ref = newRef.getPtr().get(); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); - newPtr.getRefData ().setCount(goldAmount); + newPtr.getRefData ().setCount(1); + newPtr.getCellRef().mGoldValue = goldAmount; } else { MWWorld::LiveCellRef *ref = ptr.get(); @@ -222,4 +237,28 @@ namespace MWClass } return newPtr; } + + boost::shared_ptr Miscellaneous::use (const MWWorld::Ptr& ptr) const + { + if (ptr.getCellRef().mSoul == "") + return boost::shared_ptr(new MWWorld::NullAction()); + else + 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); + } + + float Miscellaneous::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } + } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index a5a79a8f6d..16a8e8c055 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -49,6 +49,14 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) + const; + ///< Generate action for using via inventory menu + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + 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 d4e5e5cd67..4574792ae4 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -49,6 +49,67 @@ namespace { return new CustomData (*this); } + + void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) + { + // race bonus + const ESM::Race *race = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mRace); + + bool male = (npc->mFlags & ESM::NPC::Female) == 0; + + int level = creatureStats.getLevel(); + + for (int i=0; imData.mAttributeValues[i]; + creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); + } + + // class bonus + const ESM::Class *class_ = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); + + for (int i=0; i<2; ++i) + { + int attribute = class_->mData.mAttribute[i]; + if (attribute>=0 && attribute<8) + { + creatureStats.getAttribute(attribute).setBase ( + creatureStats.getAttribute(attribute).getBase() + 10); + } + } + + // skill bonus + for (int attribute=0; attributegetStore().get().find(j); + + if (skill->mData.mAttribute != attribute) + continue; + + // is this a minor or major skill? + float add=0.2; + for (int k=0; k<5; ++k) + { + if (class_->mData.mSkills[k][0] == j) + add=0.5; + } + for (int k=0; k<5; ++k) + { + if (class_->mData.mSkills[k][1] == j) + add=1.0; + } + modifierSum += add; + } + creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase() + + static_cast((level-1) * modifierSum+0.5), 100) ); + } + } } namespace MWClass @@ -76,8 +137,7 @@ namespace MWClass fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase"); fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); - // Added in Tribunal/Bloodmoon, may not exist - fWereWolfRunMult = gmst.search("fWereWolfRunMult"); + fWereWolfRunMult = gmst.find("fWereWolfRunMult"); inited = true; } @@ -126,15 +186,14 @@ namespace MWClass } else { - for (int i=0; i<8; ++i) - data->mCreatureStats.getAttribute (i).set (10); - for (int i=0; i<3; ++i) data->mCreatureStats.setDynamic (i, 10); data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition); data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); + + autoCalculateAttributes(ref->mBase, data->mCreatureStats); } data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); @@ -160,6 +219,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)); @@ -171,6 +235,12 @@ namespace MWClass MWBase::Environment::get().getMechanicsManager()->add(ptr); } + bool Npc::isPersistent(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + return ref->mBase->mPersistent; + } + std::string Npc::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -368,7 +438,7 @@ namespace MWClass moveSpeed = runSpeed; else moveSpeed = walkSpeed; - if(getMovementSettings(ptr).mLeftRight != 0 && getMovementSettings(ptr).mForwardBackward == 0) + if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; return moveSpeed; @@ -414,14 +484,24 @@ namespace MWClass Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const { - Ogre::Vector3 vector; - vector.x = getMovementSettings(ptr).mLeftRight; - vector.y = getMovementSettings(ptr).mForwardBackward; - vector.z = getMovementSettings(ptr).mUpDown; - - return vector; + MWMechanics::Movement &movement = getMovementSettings(ptr); + Ogre::Vector3 vec(movement.mPosition); + movement.mPosition[0] = 0.0f; + movement.mPosition[1] = 0.0f; + movement.mPosition[2] = 0.0f; + return vec; } - + + Ogre::Vector3 Npc::getRotationVector (const MWWorld::Ptr& ptr) const + { + MWMechanics::Movement &movement = getMovementSettings(ptr); + Ogre::Vector3 vec(movement.mRotation); + movement.mRotation[0] = 0.0f; + movement.mRotation[1] = 0.0f; + movement.mRotation[2] = 0.0f; + return vec; + } + bool Npc::isEssential (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -471,9 +551,9 @@ namespace MWClass const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather + weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Feather)).mMagnitude; - weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden + weight += stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Burden)).mMagnitude; if (weight<0) weight = 0; @@ -505,12 +585,84 @@ namespace MWClass stats.useSkill (skill, *class_, usageType); } + float Npc::getArmorRating (const MWWorld::Ptr& ptr) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + int ratings[MWWorld::InventoryStore::Slots]; + + int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt(); + float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); + float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat(); + int unarmoredSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(ESM::Skill::Unarmored).getModified(); + + for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i) + { + MWWorld::ContainerStoreIterator it = invStore.getSlot(i); + if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name()) + { + // unarmored + ratings[i] = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); + } + else + { + MWWorld::LiveCellRef *ref = + it->get(); + + int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it); + int armorSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(armorSkillType).getModified(); + + if (ref->mBase->mData.mWeight == 0) + ratings[i] = ref->mBase->mData.mArmor; + else + ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill; + } + } + + float shield = MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; + + return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3 + + (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet] + + ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots] + + ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron] + ) * 0.1 + + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet) + * 0.05 + + shield; + } + + void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const { y = 0; x = 0; } + void Npc::adjustScale(const MWWorld::Ptr &ptr, float &scale) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + const ESM::Race* race = + MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); + + if (ref->mBase->isMale()) + scale *= race->mData.mHeight.mMale; + else + scale *= race->mData.mHeight.mFemale; + } + + int Npc::getServices(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + if (ref->mBase->mHasAI) + return ref->mBase->mAiData.mServices; + else + return 0; + } + MWWorld::Ptr Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index f41edb0df6..83bfbdcbfe 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. @@ -81,7 +83,7 @@ namespace MWClass virtual bool getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce = false) const; - ////< Check if a stance is active or not. + ///< Check if a stance is active or not. virtual float getSpeed (const MWWorld::Ptr& ptr) const; ///< Return movement speed. @@ -96,6 +98,9 @@ namespace MWClass ///< Return desired movement vector (determined based on movement settings, /// stance and stats). + virtual Ogre::Vector3 getRotationVector (const MWWorld::Ptr& ptr) const; + ///< Return desired rotations, as euler angles. + virtual float getCapacity (const MWWorld::Ptr& ptr) const; ///< Return total weight that fits into the object. Throws an exception, if the object can't /// hold other objects. @@ -104,12 +109,17 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. + virtual float getArmorRating (const MWWorld::Ptr& ptr) const; + ///< @return combined armor rating of this actor + virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id, const MWWorld::Ptr& actor) const; ///< Apply \a id on \a ptr. /// \param actor Actor that is resposible for the ID being applied to \a ptr. /// \return Any effect? + virtual void adjustScale (const MWWorld::Ptr &ptr, float &scale) const; + virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const; ///< Inform actor \a ptr that a skill use has succeeded. @@ -117,7 +127,11 @@ namespace MWClass virtual bool isEssential (const MWWorld::Ptr& ptr) const; ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) + + virtual int getServices (const MWWorld::Ptr& actor) const; + virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 0ac78a2e40..c2d2920d0c 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -38,7 +38,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Potion::getModel(const MWWorld::Ptr &ptr) const @@ -194,4 +194,16 @@ 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; + } + + float Potion::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index d595f7e694..0f0578ca02 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -52,6 +52,10 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getWeight (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 a28be17e7e..6b7e432300 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -1,7 +1,7 @@ #include "probe.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -36,7 +36,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Probe::getModel(const MWWorld::Ptr &ptr) const @@ -141,9 +141,9 @@ namespace MWClass std::string text; - /// \todo store remaining uses somewhere + int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses; - text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses); + text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); @@ -175,4 +175,24 @@ 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; + } + + int Probe::getItemMaxHealth (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mUses; + } + + float Probe::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index d9f90baf6d..832169f8b7 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -57,6 +57,13 @@ 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; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + ///< Return item max health or throw an exception, if class does not have item health }; } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 39a7f65e07..e2993029b4 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -1,7 +1,7 @@ #include "repair.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -12,6 +12,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/actionrepair.hpp" #include "../mwgui/tooltips.hpp" @@ -34,7 +35,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Repair::getModel(const MWWorld::Ptr &ptr) const @@ -120,6 +121,19 @@ namespace MWClass return (ref->mBase->mName != ""); } + bool Repair::hasItemHealth (const MWWorld::Ptr& ptr) const + { + return true; + } + + int Repair::getItemMaxHealth (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mUses; + } + MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -131,9 +145,9 @@ namespace MWClass std::string text; - /// \todo store remaining uses somewhere + int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses; - text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses); + text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); @@ -156,4 +170,21 @@ namespace MWClass return MWWorld::Ptr(&cell.mRepairs.insert(*ref), &cell); } + + boost::shared_ptr Repair::use (const MWWorld::Ptr& ptr) const + { + return boost::shared_ptr(new MWWorld::ActionRepair(ptr)); + } + + bool Repair::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::RepairItem; + } + + float Repair::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index c58e38f96b..28ca5ad4c0 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -49,6 +49,22 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) + const; + ///< Generate action for using via inventory menu (default implementation: return a + /// null action). + + virtual bool hasItemHealth (const MWWorld::Ptr& ptr) const; + ///< \return Item health data available? (default implementation: false) + + 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 float getWeight (const MWWorld::Ptr& ptr) const; + + 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 d8c11558cc..cb394d089f 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -36,7 +36,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Weapon::getModel(const MWWorld::Ptr &ptr) const @@ -75,7 +75,10 @@ namespace MWClass bool Weapon::hasItemHealth (const MWWorld::Ptr& ptr) const { - return true; + MWWorld::LiveCellRef *ref = + ptr.get(); + + return (ref->mBase->mData.mType < 11); // thrown weapons and arrows/bolts don't have health, only quantity } int Weapon::getItemMaxHealth (const MWWorld::Ptr& ptr) const @@ -334,15 +337,21 @@ namespace MWClass } } - /// \todo store the current weapon health somewhere if (ref->mBase->mData.mType < 11) // thrown weapons and arrows/bolts don't have health, only quantity - text += "\n#{sCondition}: " + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); + { + int remainingHealth = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mHealth; + text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); + } text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); 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"); @@ -361,6 +370,46 @@ namespace MWClass return ref->mBase->mEnchant; } + void Weapon::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ESM::Weapon newItem = *ref->mBase; + newItem.mId=""; + newItem.mName=newName; + newItem.mData.mEnchant=enchCharge; + newItem.mEnchant=enchId; + const ESM::Weapon *record = MWBase::Environment::get().getWorld()->createRecord (newItem); + ref->mBase = record; + } + + std::pair 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 std::make_pair (2, ""); + } + } + return std::make_pair(1, ""); + } + return std::make_pair (0, ""); + } + boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); @@ -378,4 +427,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell); } + + float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mEnchant/10.f; + } + + bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Weapon; + } + + float Weapon::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 06cf88c5f2..3902ef6128 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -65,11 +65,23 @@ 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 void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< 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; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 5258bb8a05..7093ff91e0 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -50,12 +50,11 @@ namespace MWDialogue , mTemporaryDispositionChange(0.f) , mPermanentDispositionChange(0.f), mScriptVerbose (scriptVerbose) , mTranslationDataStorage(translationDataStorage) + , mTalkedTo(false) { mChoice = -1; mIsInChoice = false; mCompilerContext.setExtensions (&extensions); - mDialogueMap.clear(); - mActorKnownTopics.clear(); const MWWorld::Store &dialogs = MWBase::Environment::get().getWorld()->getStore().get(); @@ -67,6 +66,14 @@ namespace MWDialogue } } + void DialogueManager::clear() + { + mKnownTopics.clear(); + mTalkedTo = false; + mTemporaryDispositionChange = 0; + mPermanentDispositionChange = 0; + } + void DialogueManager::addTopic (const std::string& topic) { mKnownTopics[Misc::StringUtils::lowerCase(topic)] = true; @@ -117,6 +124,8 @@ namespace MWDialogue void DialogueManager::startDialogue (const MWWorld::Ptr& actor) { + mLastTopic = ""; + mChoice = -1; mIsInChoice = false; @@ -127,6 +136,9 @@ namespace MWDialogue mActorKnownTopics.clear(); + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + win->startDialogue(actor, MWWorld::Class::get (actor).getName (actor)); + //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI updateTopics(); @@ -145,8 +157,6 @@ namespace MWDialogue { //initialise the GUI MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->startDialogue(actor, MWWorld::Class::get (actor).getName (actor)); creatureStats.talkedToPlayer(); @@ -158,9 +168,9 @@ namespace MWDialogue parseText (info->mResponse); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); + win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript); - mLastTopic = it->mId; + mLastTopic = Misc::StringUtils::lowerCase(it->mId); mLastDialogue = *info; break; } @@ -260,6 +270,7 @@ namespace MWDialogue parseText (info->mResponse); + std::string title; if (dialogue.mType==ESM::Dialogue::Persuasion) { std::string modifiedTopic = "s" + topic; @@ -269,13 +280,13 @@ namespace MWDialogue const MWWorld::Store& gmsts = MWBase::Environment::get().getWorld()->getStore().get(); - win->addTitle (gmsts.find (modifiedTopic)->getString()); + title = gmsts.find (modifiedTopic)->getString(); } else - win->addTitle (topic); + title = topic; MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); + win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); MWBase::Environment::get().getJournal()->addTopic (topic, info->mId); executeScript (info->mResultScript); @@ -286,9 +297,7 @@ namespace MWDialogue else { // no response found, print a fallback text - win->addTitle (topic); - win->addText ("…"); - + win->addResponse ("…", topic); } } @@ -367,6 +376,9 @@ namespace MWDialogue if (services & ESM::NPC::Enchanting) windowServices |= MWGui::DialogueWindow::Service_Enchant; + if (services & ESM::NPC::Repair) + windowServices |= MWGui::DialogueWindow::Service_Repair; + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->setServices (windowServices); @@ -395,6 +407,11 @@ namespace MWDialogue updateTopics(); } + bool DialogueManager::isInChoice() const + { + return mIsInChoice; + } + void DialogueManager::goodbyeSelected() { // Do not close the dialogue window if the player has to answer a question @@ -413,51 +430,42 @@ namespace MWDialogue mTemporaryDispositionChange = 0; } - void DialogueManager::questionAnswered (const std::string& answer) + void DialogueManager::questionAnswered (int answer) { - if (mChoiceMap.find(answer) != mChoiceMap.end()) + mChoice = answer; + + if (mDialogueMap.find(mLastTopic) != mDialogueMap.end()) { - mChoice = mChoiceMap[answer]; + Filter filter (mActor, mChoice, mTalkedTo); - if (mDialogueMap.find(mLastTopic) != mDialogueMap.end()) + if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic + || mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting) { - if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic) + if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true)) { - Filter filter (mActor, mChoice, mTalkedTo); + std::string text = info->mResponse; + parseText (text); - if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true)) - { - mChoiceMap.clear(); - mChoice = -1; - mIsInChoice = false; - std::string text = info->mResponse; - parseText (text); + mChoice = -1; + mIsInChoice = false; + MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); - MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext)); - MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); - executeScript (info->mResultScript); - mLastTopic = mLastTopic; - mLastDialogue = *info; - } + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); + MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext)); + MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); + executeScript (info->mResultScript); + mLastDialogue = *info; } } - - updateTopics(); } - } - void DialogueManager::printError (const std::string& error) - { - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->addText(error); + updateTopics(); } void DialogueManager::askQuestion (const std::string& question, int choice) { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->askQuestion(question); - mChoiceMap[Misc::StringUtils::lowerCase(question)] = choice; + win->addChoice(question, choice); mIsInChoice = true; } @@ -518,12 +526,41 @@ namespace MWDialogue mTemporaryDispositionChange += delta; } + bool DialogueManager::checkServiceRefused() + { + Filter filter (mActor, mChoice, mTalkedTo); + + const MWWorld::Store &dialogues = + MWBase::Environment::get().getWorld()->getStore().get(); + + const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal"); + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + + std::vector infos = filter.list (dialogue, false, false, true); + if (!infos.empty()) + { + const ESM::DialInfo* info = infos[0]; + + parseText (info->mResponse); + + const MWWorld::Store& gmsts = + MWBase::Environment::get().getWorld()->getStore().get(); + + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); + + win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), + gmsts.find ("sServiceRefusal")->getString()); + + executeScript (info->mResultScript); + return true; + } + return false; + } + std::vector ParseHyperText(const std::string& text) { std::vector result; - MyGUI::UString utext(text); - size_t pos_begin, pos_end, iteration_pos = 0; for(;;) { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 0c0299619c..de1ac77c6f 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -30,7 +30,6 @@ namespace MWDialogue bool mTalkedTo; int mChoice; - std::map mChoiceMap; std::string mLastTopic; ESM::DialInfo mLastDialogue; bool mIsInChoice; @@ -46,14 +45,16 @@ namespace MWDialogue bool compile (const std::string& cmd,std::vector& code); void executeScript (const std::string& script); - void printError (const std::string& error); - void executeTopic (const std::string& topic, bool randomResponse=false); public: DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage); + virtual void clear(); + + virtual bool isInChoice() const; + virtual void startDialogue (const MWWorld::Ptr& actor); virtual void addTopic (const std::string& topic); @@ -65,10 +66,12 @@ namespace MWDialogue virtual MWWorld::Ptr getActor() const; ///< Return the actor the player is currently talking to. + virtual bool checkServiceRefused (); + //calbacks for the GUI virtual void keywordSelected (const std::string& keyword); virtual void goodbyeSelected(); - virtual void questionAnswered (const std::string& answer); + virtual void questionAnswered (int answer); virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 10740794ea..52c7bd4f34 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -73,6 +73,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const if (iter->second < info.mData.mRank) return false; } + else if (info.mData.mRank != -1) + { + // if there is a rank condition, but the NPC is not in a faction, always fail + return false; + } // Gender if (!isCreature) @@ -121,7 +126,7 @@ bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const return true; } -bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info) const +bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert) const { bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); @@ -129,14 +134,19 @@ bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info) const return true; int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); - - return actorDisposition >= info.mData.mDisposition; + // For service refusal, the disposition check is inverted. However, a value of 0 still means "always succeed". + return invert ? (info.mData.mDisposition == 0 || actorDisposition < info.mData.mDisposition) + : (actorDisposition >= info.mData.mDisposition); } bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const { - if (select.isNpcOnly() && mActor.getTypeName()!=typeid (ESM::NPC).name()) - return select.isInverted(); + if (select.isNpcOnly() && (mActor.getTypeName() != typeid (ESM::NPC).name())) + // If the actor is a creature, we do not test the conditions applicable + // only to NPCs. Such conditions can never be satisfied, apart + // inverted ones (NotClass, NotRace, NotFaction return true + // because creatures are not of any race, class or faction). + return select.getType() == SelectWrapper::Type_Inverted; switch (select.getType()) { @@ -144,6 +154,9 @@ bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const case SelectWrapper::Type_Integer: return select.selectCompare (getSelectStructInteger (select)); case SelectWrapper::Type_Numeric: return testSelectStructNumeric (select); case SelectWrapper::Type_Boolean: return select.selectCompare (getSelectStructBoolean (select)); + + // We must not do the comparison for inverted functions (eg. Function_NotClass) + case SelectWrapper::Type_Inverted: return getSelectStructBoolean (select); } return true; @@ -190,7 +203,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c if (imData.mNumLongs) return select.selectCompare (locals.mLongs[i]); - i -= script->mData.mNumShorts; + i -= script->mData.mNumLongs; return select.selectCompare (locals.mFloats.at (i)); } @@ -412,25 +425,49 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co return false; - case SelectWrapper::Function_Id: + case SelectWrapper::Function_NotId: - return select.getName()==Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor)); + return select.getName()!=Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor)); - case SelectWrapper::Function_Faction: + case SelectWrapper::Function_NotFaction: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mFaction)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mFaction)!=select.getName(); - case SelectWrapper::Function_Class: + case SelectWrapper::Function_NotClass: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mClass)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mClass)!=select.getName(); - case SelectWrapper::Function_Race: + case SelectWrapper::Function_NotRace: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)!=select.getName(); - case SelectWrapper::Function_Cell: + case SelectWrapper::Function_NotCell: - return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)!=select.getName(); + + case SelectWrapper::Function_NotLocal: + { + std::string scriptName = MWWorld::Class::get (mActor).getScript (mActor); + + if (scriptName.empty()) + // This actor has no attached script, so there is no local variable + return true; + + const ESM::Script *script = + MWBase::Environment::get().getWorld()->getStore().get().find (scriptName); + + std::string name = select.getName(); + + int i = 0; + for (; i < static_cast (script->mVarNames.size()); ++i) + if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) + break; + + if (i >= static_cast (script->mVarNames.size())) + return true; // script does not have a variable of this name + + return false; + } case SelectWrapper::Function_SameGender: @@ -458,7 +495,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_PcCorprus: return MWWorld::Class::get (player).getCreatureStats (player). - getMagicEffects().get (132).mMagnitude!=0; + getMagicEffects().get (ESM::MagicEffect::Corprus).mMagnitude!=0; case SelectWrapper::Function_PcExpelled: { @@ -537,8 +574,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, @@ -570,7 +607,7 @@ const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, } std::vector MWDialogue::Filter::list (const ESM::Dialogue& dialogue, - bool fallbackToInfoRefusal, bool searchAll) const + bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const { std::vector infos; @@ -582,7 +619,7 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue { if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) { - if (testDisposition (*iter)) { + if (testDisposition (*iter, invertDisposition)) { infos.push_back(&*iter); if (!searchAll) break; @@ -604,7 +641,7 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue for (std::vector::const_iterator iter = infoRefusalDialogue.mInfo.begin(); iter!=infoRefusalDialogue.mInfo.end(); ++iter) - if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter)) { + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter, invertDisposition)) { infos.push_back(&*iter); if (!searchAll) break; diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index 069bf6353d..5c6d092ad9 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -30,8 +30,8 @@ namespace MWDialogue bool testSelectStructs (const ESM::DialInfo& info) const; ///< Are all select structs matching? - bool testDisposition (const ESM::DialInfo& info) const; - ///< Is the actor disposition toward the player high enough? + bool testDisposition (const ESM::DialInfo& info, bool invert=false) const; + ///< Is the actor disposition toward the player high enough (or low enough, if \a invert is true)? bool testSelectStruct (const SelectWrapper& select) const; @@ -54,7 +54,7 @@ namespace MWDialogue Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); std::vector list (const ESM::Dialogue& dialogue, - bool fallbackToInfoRefusal, bool searchAll) const; + bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition=false) const; const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; ///< Get a matching response for the requested dialogue. diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 19a9f42b5d..9d009b48b2 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -3,7 +3,7 @@ #include -namespace MWWorld +namespace MWWorld { struct ESMStore; } @@ -27,7 +27,7 @@ namespace MWDialogue static std::string idFromIndex (const std::string& topic, int index); }; - /// \biref A quest entry with a timestamp. + /// \brief A quest entry with a timestamp. struct StampedJournalEntry : public JournalEntry { int mDay; diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 5e2bc6bc01..23cfb5fdd1 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -29,6 +29,13 @@ namespace MWDialogue Journal::Journal() {} + void Journal::clear() + { + mJournal.clear(); + mQuests.clear(); + mTopics.clear(); + } + void Journal::addEntry (const std::string& id, int index) { // bail out of we already have heard this... diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 7d71fd7d06..f4f8eb1c29 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -21,6 +21,8 @@ namespace MWDialogue Journal(); + virtual void clear(); + virtual void addEntry (const std::string& id, int index); ///< Add a journal entry. diff --git a/apps/openmw/mwdialogue/selectwrapper.cpp b/apps/openmw/mwdialogue/selectwrapper.cpp index 9d705f6bec..64bd1d2449 100644 --- a/apps/openmw/mwdialogue/selectwrapper.cpp +++ b/apps/openmw/mwdialogue/selectwrapper.cpp @@ -112,12 +112,12 @@ MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::getFunction() con case '4': return Function_Journal; case '5': return Function_Item; case '6': return Function_Dead; - case '7': return Function_Id; - case '8': return Function_Faction; - case '9': return Function_Class; - case 'A': return Function_Race; - case 'B': return Function_Cell; - case 'C': return Function_Local; + case '7': return Function_NotId; + case '8': return Function_NotFaction; + case '9': return Function_NotClass; + case 'A': return Function_NotRace; + case 'B': return Function_NotCell; + case 'C': return Function_NotLocal; } return Function_None; @@ -219,7 +219,6 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const static const Function booleanFunctions[] = { Function_False, - Function_Id, Function_Faction, Function_Class, Function_Race, Function_Cell, Function_SameGender, Function_SameRace, Function_SameFaction, Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus, Function_PcExpelled, @@ -231,6 +230,13 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const Function_None // end marker }; + static const Function invertedBooleanFunctions[] = + { + Function_NotId, Function_NotFaction, Function_NotClass, + Function_NotRace, Function_NotCell, Function_NotLocal, + Function_None // end marker + }; + Function function = getFunction(); for (int i=0; integerFunctions[i]!=Function_None; ++i) @@ -245,21 +251,18 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const if (booleanFunctions[i]==function) return Type_Boolean; + for (int i=0; invertedBooleanFunctions[i]!=Function_None; ++i) + if (invertedBooleanFunctions[i]==function) + return Type_Inverted; + return Type_None; } -bool MWDialogue::SelectWrapper::isInverted() const -{ - char type = mSelect.mSelectRule[1]; - - return type=='7' || type=='8' || type=='9' || type=='A' || type=='B' || type=='C'; -} - bool MWDialogue::SelectWrapper::isNpcOnly() const { static const Function functions[] = { - Function_Faction, SelectWrapper::Function_Class, SelectWrapper::Function_Race, + Function_NotFaction, Function_NotClass, Function_NotRace, Function_SameGender, Function_SameRace, Function_SameFaction, Function_PcSkill, Function_PcExpelled, @@ -283,17 +286,17 @@ bool MWDialogue::SelectWrapper::isNpcOnly() const bool MWDialogue::SelectWrapper::selectCompare (int value) const { - return selectCompareImp (mSelect, value)!=isInverted(); // logic XOR + return selectCompareImp (mSelect, value); } bool MWDialogue::SelectWrapper::selectCompare (float value) const { - return selectCompareImp (mSelect, value)!=isInverted(); // logic XOR + return selectCompareImp (mSelect, value); } bool MWDialogue::SelectWrapper::selectCompare (bool value) const { - return selectCompareImp (mSelect, static_cast (value))!=isInverted(); // logic XOR + return selectCompareImp (mSelect, static_cast (value)); } std::string MWDialogue::SelectWrapper::getName() const diff --git a/apps/openmw/mwdialogue/selectwrapper.hpp b/apps/openmw/mwdialogue/selectwrapper.hpp index b77ed7985a..f4bc898a4b 100644 --- a/apps/openmw/mwdialogue/selectwrapper.hpp +++ b/apps/openmw/mwdialogue/selectwrapper.hpp @@ -17,11 +17,12 @@ namespace MWDialogue Function_Journal, Function_Item, Function_Dead, - Function_Id, - Function_Faction, - Function_Class, - Function_Race, - Function_Cell, + Function_NotId, + Function_NotFaction, + Function_NotClass, + Function_NotRace, + Function_NotCell, + Function_NotLocal, Function_Local, Function_Global, Function_SameGender, Function_SameRace, Function_SameFaction, @@ -50,7 +51,8 @@ namespace MWDialogue Type_None, Type_Integer, Type_Numeric, - Type_Boolean + Type_Boolean, + Type_Inverted }; private: @@ -67,8 +69,6 @@ namespace MWDialogue Type getType() const; - bool isInverted() const; - bool isNpcOnly() const; ///< \attention Do not call any of the select functions for this select struct! diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index fce6126001..136cb7c981 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -1,6 +1,7 @@ #include "alchemywindow.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -8,8 +9,11 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" + +#include "inventoryitemmodel.hpp" +#include "sortfilteritemmodel.hpp" +#include "itemview.hpp" namespace { @@ -22,13 +26,26 @@ namespace path.append(".dds"); return path; } + + std::string getCountString(const int count) + { + if (count == 1) + return ""; + if (count > 9999) + return boost::lexical_cast(int(count/1000.f)) + "k"; + else + return boost::lexical_cast(count); + } + } namespace MWGui { - AlchemyWindow::AlchemyWindow(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_alchemy_window.layout", parWindowManager) - , ContainerBase(0), mApparatus (4), mIngredients (4) + AlchemyWindow::AlchemyWindow() + : WindowBase("openmw_alchemy_window.layout") + , mApparatus (4) + , mIngredients (4) + , mSortModel(NULL) { getWidget(mCreateButton, "CreateButton"); getWidget(mCancelButton, "CancelButton"); @@ -42,6 +59,10 @@ namespace MWGui getWidget(mApparatus[3], "Apparatus4"); getWidget(mEffectsBox, "CreatedEffects"); getWidget(mNameEdit, "NameEdit"); + getWidget(mItemView, "ItemView"); + + + mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem); mIngredients[0]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); mIngredients[1]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); @@ -51,12 +72,6 @@ namespace MWGui mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked); - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); - center(); } @@ -64,8 +79,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 +92,40 @@ namespace MWGui if (result == MWMechanics::Alchemy::Result_NoName) { - mWindowManager.messageBox("#{sNotifyMessage37}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage37}"); return; } // check if mortar & pestle is available (always needed) if (result == MWMechanics::Alchemy::Result_NoMortarAndPestle) { - mWindowManager.messageBox("#{sNotifyMessage45}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage45}"); return; } // make sure 2 or more ingredients were selected if (result == MWMechanics::Alchemy::Result_LessThanTwoIngredients) { - mWindowManager.messageBox("#{sNotifyMessage6a}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage6a}"); return; } if (result == MWMechanics::Alchemy::Result_NoEffects) { - mWindowManager.messageBox("#{sNotifyMessage8}", std::vector()); + 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}", std::vector()); + 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}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); } @@ -128,12 +143,14 @@ namespace MWGui void AlchemyWindow::open() { - openContainer (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); // this sets mPtr - setFilter (ContainerBase::Filter_Ingredients); + InventoryItemModel* model = new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mSortModel = new SortFilterItemModel(model); + mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients); + mItemView->setModel (mSortModel); mNameEdit->setCaption(""); - mAlchemy.setAlchemist (mPtr); + mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); int index = 0; @@ -157,8 +174,9 @@ namespace MWGui update(); } - void AlchemyWindow::onSelectedItemImpl(MWWorld::Ptr item) + void AlchemyWindow::onSelectedItem(int index) { + MWWorld::Ptr item = mSortModel->getItem(index).mBase; int res = mAlchemy.addIngredient(item); if (res != -1) @@ -170,19 +188,10 @@ namespace MWGui } } - std::vector AlchemyWindow::itemsToIgnore() - { - std::vector ignore; - // don't show ingredients that are currently selected in the "available ingredients" box. - for (int i=0; i<4; ++i) - if (mIngredients[i]->isUserString("ToolTipType")) - ignore.push_back(*mIngredients[i]->getUserData()); - - return ignore; - } - void AlchemyWindow::update() { + mSortModel->clearDragItems(); + MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy.beginIngredients (); for (int i=0; i<4; ++i) { @@ -195,6 +204,9 @@ namespace MWGui ++it; } + if (!item.isEmpty()) + mSortModel->addDragItem(item, item.getRefData().getCount()); + if (ingredient->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0)); @@ -216,7 +228,7 @@ namespace MWGui text->setCaption(getCountString(ingredient->getUserData()->getRefData().getCount())); } - drawItems(); + mItemView->update(); std::vector effects; ESM::EffectList list; @@ -232,7 +244,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..3a58ebf060 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -5,20 +5,25 @@ #include "../mwmechanics/alchemy.hpp" -#include "window_base.hpp" -#include "container.hpp" #include "widgets.hpp" +#include "windowbase.hpp" namespace MWGui { - class AlchemyWindow : public WindowBase, public ContainerBase + class ItemView; + class SortFilterItemModel; + + class AlchemyWindow : public WindowBase { public: - AlchemyWindow(MWBase::WindowManager& parWindowManager); + AlchemyWindow(); virtual void open(); - protected: + private: + ItemView* mItemView; + SortFilterItemModel* mSortModel; + MyGUI::Button* mCreateButton; MyGUI::Button* mCancelButton; @@ -30,16 +35,11 @@ namespace MWGui void onCreateButtonClicked(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender); - virtual void onSelectedItemImpl(MWWorld::Ptr item); - virtual std::vector itemsToIgnore(); + void onSelectedItem(int index); void removeIngredient(MyGUI::Widget* ingredient); - 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..9656067097 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -3,246 +3,245 @@ #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "widgets.hpp" -using namespace MWGui; -using namespace Widgets; - namespace { -bool sortBirthSigns(const std::pair& left, const std::pair& right) -{ - return left.second->mName.compare (right.second->mName) < 0; -} - -} - -BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_birth.layout", parWindowManager) -{ - // Centre dialog - center(); - - getWidget(mSpellArea, "SpellArea"); - - getWidget(mBirthImage, "BirthsignImage"); - - getWidget(mBirthList, "BirthsignList"); - mBirthList->setScrollVisible(true); - mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); - - updateBirths(); - updateSpells(); -} - -void BirthDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} - -void BirthDialog::open() -{ - WindowModal::open(); - updateBirths(); - updateSpells(); -} - - -void BirthDialog::setBirthId(const std::string &birthId) -{ - mCurrentBirthId = birthId; - mBirthList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mBirthList->getItemCount(); - for (size_t i = 0; i < count; ++i) + bool sortBirthSigns(const std::pair& left, const std::pair& right) { - if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) + return left.second->mName.compare (right.second->mName) < 0; + } + +} + +namespace MWGui +{ + + BirthDialog::BirthDialog() + : WindowModal("openmw_chargen_birth.layout") + { + // Centre dialog + center(); + + getWidget(mSpellArea, "SpellArea"); + + getWidget(mBirthImage, "BirthsignImage"); + + getWidget(mBirthList, "BirthsignList"); + mBirthList->setScrollVisible(true); + mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); + + updateBirths(); + updateSpells(); + } + + void BirthDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void BirthDialog::open() + { + WindowModal::open(); + updateBirths(); + updateSpells(); + } + + + void BirthDialog::setBirthId(const std::string &birthId) + { + mCurrentBirthId = birthId; + mBirthList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mBirthList->getItemCount(); + for (size_t i = 0; i < count; ++i) { - mBirthList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - break; - } - } - - updateSpells(); -} - -// widget controls - -void BirthDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} - -void BirthDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - const std::string *birthId = mBirthList->getItemDataAt(_index); - if (boost::iequals(mCurrentBirthId, *birthId)) - return; - - mCurrentBirthId = *birthId; - updateSpells(); -} - -// update widget content - -void BirthDialog::updateBirths() -{ - mBirthList->removeAllItems(); - - const MWWorld::Store &signs = - MWBase::Environment::get().getWorld()->getStore().get(); - - // sort by name - std::vector < std::pair > birthSigns; - - MWWorld::Store::iterator it = signs.begin(); - for (; it != signs.end(); ++it) - { - birthSigns.push_back(std::make_pair(it->mId, &(*it))); - } - std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); - - int index = 0; - for (std::vector >::const_iterator it2 = birthSigns.begin(); - it2 != birthSigns.end(); ++it2, ++index) - { - mBirthList->addItem(it2->second->mName, it2->first); - if (mCurrentBirthId.empty()) - { - mBirthList->setIndexSelected(index); - mCurrentBirthId = it2->first; - } - else if (boost::iequals(it2->first, mCurrentBirthId)) - { - mBirthList->setIndexSelected(index); - } - } -} - -void BirthDialog::updateSpells() -{ - for (std::vector::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSpellItems.clear(); - - if (mCurrentBirthId.empty()) - return; - - MWSpellPtr spellWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); - - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::BirthSign *birth = - store.get().find(mCurrentBirthId); - - std::string texturePath = std::string("textures\\") + birth->mTexture; - fixTexturePath(texturePath); - mBirthImage->setImageTexture(texturePath); - - std::vector abilities, powers, spells; - - std::vector::const_iterator it = birth->mPowers.mList.begin(); - std::vector::const_iterator end = birth->mPowers.mList.end(); - for (; it != end; ++it) - { - const std::string &spellId = *it; - const ESM::Spell *spell = store.get().search(spellId); - if (!spell) - continue; // Skip spells which cannot be found - ESM::Spell::SpellType type = static_cast(spell->mData.mType); - if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) - continue; // We only want spell, ability and powers. - - if (type == ESM::Spell::ST_Ability) - abilities.push_back(spellId); - else if (type == ESM::Spell::ST_Power) - powers.push_back(spellId); - else if (type == ESM::Spell::ST_Spell) - spells.push_back(spellId); - } - - int i = 0; - - struct { - const std::vector &spells; - const char *label; - } - categories[3] = { - {abilities, "sBirthsignmenu1"}, - {powers, "sPowers"}, - {spells, "sBirthsignmenu2"} - }; - - for (int category = 0; category < 3; ++category) - { - 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, "")); - mSpellItems.push_back(label); - coord.top += lineHeight; - - std::vector::const_iterator end = categories[category].spells.end(); - for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) + if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) { - 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); + mBirthList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } + } - mSpellItems.push_back(spellWidget); - coord.top += lineHeight; + updateSpells(); + } - MyGUI::IntCoord spellCoord = coord; - spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? - spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? MWEffectList::EF_Constant : 0); - coord.top = spellCoord.top; + // widget controls - ++i; + void BirthDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + + void BirthDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + const std::string *birthId = mBirthList->getItemDataAt(_index); + if (boost::iequals(mCurrentBirthId, *birthId)) + return; + + mCurrentBirthId = *birthId; + updateSpells(); + } + + // update widget content + + void BirthDialog::updateBirths() + { + mBirthList->removeAllItems(); + + const MWWorld::Store &signs = + MWBase::Environment::get().getWorld()->getStore().get(); + + // sort by name + std::vector < std::pair > birthSigns; + + MWWorld::Store::iterator it = signs.begin(); + for (; it != signs.end(); ++it) + { + birthSigns.push_back(std::make_pair(it->mId, &(*it))); + } + std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); + + int index = 0; + for (std::vector >::const_iterator it2 = birthSigns.begin(); + it2 != birthSigns.end(); ++it2, ++index) + { + mBirthList->addItem(it2->second->mName, it2->first); + if (mCurrentBirthId.empty()) + { + mBirthList->setIndexSelected(index); + mCurrentBirthId = it2->first; + } + else if (boost::iequals(it2->first, mCurrentBirthId)) + { + mBirthList->setIndexSelected(index); } } } + + void BirthDialog::updateSpells() + { + for (std::vector::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSpellItems.clear(); + + if (mCurrentBirthId.empty()) + return; + + Widgets::MWSpellPtr spellWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); + + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::BirthSign *birth = + store.get().find(mCurrentBirthId); + + std::string texturePath = std::string("textures\\") + birth->mTexture; + Widgets::fixTexturePath(texturePath); + mBirthImage->setImageTexture(texturePath); + + std::vector abilities, powers, spells; + + std::vector::const_iterator it = birth->mPowers.mList.begin(); + std::vector::const_iterator end = birth->mPowers.mList.end(); + for (; it != end; ++it) + { + const std::string &spellId = *it; + const ESM::Spell *spell = store.get().search(spellId); + if (!spell) + continue; // Skip spells which cannot be found + ESM::Spell::SpellType type = static_cast(spell->mData.mType); + if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) + continue; // We only want spell, ability and powers. + + if (type == ESM::Spell::ST_Ability) + abilities.push_back(spellId); + else if (type == ESM::Spell::ST_Power) + powers.push_back(spellId); + else if (type == ESM::Spell::ST_Spell) + spells.push_back(spellId); + } + + int i = 0; + + struct { + const std::vector &spells; + const char *label; + } + categories[3] = { + {abilities, "sBirthsignmenu1"}, + {powers, "sPowers"}, + {spells, "sBirthsignmenu2"} + }; + + for (int category = 0; category < 3; ++category) + { + if (!categories[category].spells.empty()) + { + MyGUI::TextBox* label = mSpellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); + label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, "")); + mSpellItems.push_back(label); + coord.top += lineHeight; + + std::vector::const_iterator end = categories[category].spells.end(); + for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) + { + const std::string &spellId = *it; + spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); + spellWidget->setSpellId(spellId); + + mSpellItems.push_back(spellWidget); + coord.top += lineHeight; + + MyGUI::IntCoord spellCoord = coord; + spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? + spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? Widgets::MWEffectList::EF_Constant : 0); + coord.top = spellCoord.top; + + ++i; + } + } + } + } + } 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/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp new file mode 100644 index 0000000000..ef688be1bd --- /dev/null +++ b/apps/openmw/mwgui/bookpage.cpp @@ -0,0 +1,1227 @@ +#include "bookpage.hpp" + +#include "MyGUI_FontManager.h" +#include "MyGUI_RenderItem.h" +#include "MyGUI_RenderManager.h" +#include "MyGUI_TextureUtility.h" +#include "MyGUI_FactoryManager.h" + +#include +#include +#include +#include + +#include + +namespace MWGui +{ +struct TypesetBookImpl; +struct PageDisplay; +struct BookPageImpl; + +static bool ucsSpace (int codePoint); +static bool ucsLineBreak (int codePoint); +static bool ucsBreakingSpace (int codePoint); + +struct BookTypesetter::Style { virtual ~Style () {} }; + +struct TypesetBookImpl : TypesetBook +{ + typedef std::vector Content; + typedef std::list Contents; + typedef Utf8Stream::Point Utf8Point; + typedef std::pair Range; + + struct StyleImpl : BookTypesetter::Style + { + MyGUI::IFont* mFont; + MyGUI::Colour mHotColour; + MyGUI::Colour mActiveColour; + MyGUI::Colour mNormalColour; + InteractiveId mInteractiveId; + + bool match (MyGUI::IFont* tstFont, MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, + MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) + { + return (mFont == tstFont) && + partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); + } + + bool match (char const * tstFont, MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, + MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) + { + return (mFont->getResourceName () == tstFont) && + partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); + } + + bool partal_match (MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, + MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) + { + return + (mHotColour == tstHotColour ) && + (mActiveColour == tstActiveColour ) && + (mNormalColour == tstNormalColour ) && + (mInteractiveId == tstInteractiveId ) ; + } + }; + + typedef std::list Styles; + + struct Run + { + StyleImpl* mStyle; + Range mRange; + int mLeft, mRight; + int mPrintableChars; + }; + + typedef std::vector Runs; + + struct Line + { + Runs mRuns; + MyGUI::IntRect mRect; + }; + + typedef std::vector Lines; + + struct Section + { + Lines mLines; + MyGUI::IntRect mRect; + }; + + typedef std::vector
Sections; + + typedef std::pair Page; + typedef std::vector Pages; + + Pages mPages; + Sections mSections; + Contents mContents; + Styles mStyles; + MyGUI::IntRect mRect; + + virtual ~TypesetBookImpl () {} + + Range addContent (BookTypesetter::Utf8Span text) + { + Contents::iterator i = mContents.insert (mContents.end (), Content (text.first, text.second)); + + if (i->size () == 0) + return Range (Utf8Point (NULL), Utf8Point (NULL)); + + Utf8Point begin = &i->front (); + Utf8Point end = &i->front () + i->size (); + + return Range (begin, end); + } + + size_t pageCount () const { return mPages.size (); } + + std::pair getSize () const + { + return std::make_pair (mRect.width (), mRect.height ()); + } + + template + void visitRuns (int top, int bottom, MyGUI::IFont* Font, Visitor const & visitor) const + { + for (Sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i) + { + if (top >= mRect.bottom || bottom <= i->mRect.top) + continue; + + for (Lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) + { + if (top >= j->mRect.bottom || bottom <= j->mRect.top) + continue; + + for (Runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k) + if (!Font || k->mStyle->mFont == Font) + visitor (*i, *j, *k); + } + } + } + + template + void visitRuns (int top, int bottom, Visitor const & visitor) const + { + visitRuns (top, bottom, NULL, visitor); + } + + StyleImpl * hitTest (int left, int top) const + { + for (Sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i) + { + if (top < i->mRect.top || top >= i->mRect.bottom) + continue; + + int left1 = left - i->mRect.left; + + for (Lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) + { + if (top < j->mRect.top || top >= j->mRect.bottom) + continue; + + int left2 = left1 - j->mRect.left; + + for (Runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k) + { + if (left2 < k->mLeft || left2 >= k->mRight) + continue; + + return k->mStyle; + } + } + } + + return nullptr; + } + + MyGUI::IFont* affectedFont (StyleImpl* style) + { + for (Styles::iterator i = mStyles.begin (); i != mStyles.end (); ++i) + if (&*i == style) + return i->mFont; + return NULL; + } + + struct Typesetter; +}; + +struct TypesetBookImpl::Typesetter : BookTypesetter +{ + typedef TypesetBookImpl Book; + typedef boost::shared_ptr BookPtr; + + int mPageWidth; + int mPageHeight; + + BookPtr mBook; + Section * mSection; + Line * mLine; + Run * mRun; + + std::vector mSectionAlignment; + + Book::Content const * mCurrentContent; + Alignment mCurrentAlignment; + + Typesetter (size_t width, size_t height) : + mPageWidth (width), mPageHeight(height), + mSection (NULL), mLine (NULL), mRun (NULL), + mCurrentAlignment (AlignLeft), + mCurrentContent (NULL) + { + mBook = boost::make_shared (); + } + + virtual ~Typesetter () + { + } + + Style * createStyle (char const * fontName, Colour fontColour) + { + for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) + if (i->match (fontName, fontColour, fontColour, fontColour, 0)) + return &*i; + + StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); + + style.mFont = MyGUI::FontManager::getInstance().getByName(fontName); + style.mHotColour = fontColour; + style.mActiveColour = fontColour; + style.mNormalColour = fontColour; + style.mInteractiveId = 0; + + return &style; + } + + Style* createHotStyle (Style* baseStyle, Colour normalColour, Colour hoverColour, Colour activeColour, InteractiveId id, bool unique) + { + StyleImpl* BaseStyle = dynamic_cast (baseStyle); + + if (!unique) + for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) + if (i->match (BaseStyle->mFont, hoverColour, activeColour, normalColour, id)) + return &*i; + + StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); + + style.mFont = BaseStyle->mFont; + style.mHotColour = hoverColour; + style.mActiveColour = activeColour; + style.mNormalColour = normalColour; + style.mInteractiveId = id; + + return &style; + } + + void write (Style * style, Utf8Span text) + { + Range range = mBook->addContent (text); + + writeImpl (dynamic_cast (style), range.first, range.second); + } + + intptr_t addContent (Utf8Span text, bool select) + { + Contents::iterator i = mBook->mContents.insert (mBook->mContents.end (), Content (text.first, text.second)); + + if (select) + mCurrentContent = &(*i); + + return reinterpret_cast (&(*i)); + } + + void selectContent (intptr_t contentHandle) + { + mCurrentContent = reinterpret_cast (contentHandle); + } + + void write (Style * style, size_t begin, size_t end) + { + assert (mCurrentContent != NULL); + assert (end <= mCurrentContent->size ()); + assert (begin <= mCurrentContent->size ()); + + Utf8Point begin_ = &mCurrentContent->front () + begin; + Utf8Point end_ = &mCurrentContent->front () + end ; + + writeImpl (dynamic_cast (style), begin_, end_); + } + + void lineBreak (float margin) + { + assert (margin == 0); //TODO: figure out proper behavior here... + + mRun = NULL; + mLine = NULL; + } + + void sectionBreak (float margin) + { + if (mBook->mSections.size () > 0) + { + mRun = NULL; + mLine = NULL; + mSection = NULL; + + if (mBook->mRect.bottom < (mBook->mSections.back ().mRect.bottom + margin)) + mBook->mRect.bottom = (mBook->mSections.back ().mRect.bottom + margin); + } + } + + void setSectionAlignment (Alignment sectionAlignment) + { + if (mSection != NULL) + mSectionAlignment.back () = sectionAlignment; + mCurrentAlignment = sectionAlignment; + } + + TypesetBook::Ptr complete () + { + int curPageStart = 0; + int curPageStop = 0; + + std::vector ::iterator sa = mSectionAlignment.begin (); + for (Sections::iterator i = mBook->mSections.begin (); i != mBook->mSections.end (); ++i, ++sa) + { + // apply alignment to individual lines... + for (Lines::iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) + { + int width = j->mRect.width (); + int excess = mPageWidth - width; + + switch (*sa) + { + default: + case AlignLeft: j->mRect.left = 0; break; + case AlignCenter: j->mRect.left = excess/2; break; + case AlignRight: j->mRect.left = excess; break; + } + + j->mRect.right = j->mRect.left + width; + } + + if (curPageStop == curPageStart) + { + curPageStart = i->mRect.top; + curPageStop = i->mRect.top; + } + + int spaceLeft = mPageHeight - (curPageStop - curPageStart); + int sectionHeight = i->mRect.height (); + + if (sectionHeight <= mPageHeight) + { + if (sectionHeight > spaceLeft) + { + assert (curPageStart != curPageStop); + + mBook->mPages.push_back (Page (curPageStart, curPageStop)); + + curPageStart = i->mRect.top; + curPageStop = i->mRect.bottom; + } + else + curPageStop = i->mRect.bottom; + } + else + { + //split section + } + } + + if (curPageStart != curPageStop) + mBook->mPages.push_back (Page (curPageStart, curPageStop)); + + return mBook; + } + + void writeImpl (StyleImpl * style, Utf8Stream::Point _begin, Utf8Stream::Point _end) + { + int line_height = style->mFont->getDefaultHeight (); + + Utf8Stream stream (_begin, _end); + + while (!stream.eof ()) + { + if (ucsLineBreak (stream.peek ())) + { + stream.consume (); + mLine = NULL, mRun = NULL; + continue; + } + + int word_width = 0; + int word_height = 0; + int space_width = 0; + int character_count = 0; + + Utf8Stream::Point lead = stream.current (); + + while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) + { + MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); + space_width += gi->advance; + stream.consume (); + } + + Utf8Stream::Point origin = stream.current (); + + while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) + { + MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); + word_width += gi->advance + gi->bearingX; + word_height = line_height; + ++character_count; + stream.consume (); + } + + Utf8Stream::Point extent = stream.current (); + + if (lead == extent) + break; + + int left = mLine ? mLine->mRect.right : 0; + + if (left + space_width + word_width > mPageWidth) + { + mLine = NULL, mRun = NULL; + + append_run (style, origin, extent, extent - origin, word_width, mBook->mRect.bottom + word_height); + } + else + { + int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; + + append_run (style, lead, extent, extent - origin, left + space_width + word_width, top + word_height); + } + } + } + + void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom) + { + if (mSection == NULL) + { + mBook->mSections.push_back (Section ()); + mSection = &mBook->mSections.back (); + mSection->mRect = MyGUI::IntRect (0, mBook->mRect.bottom, 0, mBook->mRect.bottom); + mSectionAlignment.push_back (mCurrentAlignment); + } + + if (mLine == NULL) + { + mSection->mLines.push_back (Line ()); + mLine = &mSection->mLines.back (); + mLine->mRect = MyGUI::IntRect (0, mSection->mRect.bottom, 0, mBook->mRect.bottom); + } + + if (mBook->mRect.right < right) + mBook->mRect.right = right; + + if (mBook->mRect.bottom < bottom) + mBook->mRect.bottom = bottom; + + if (mSection->mRect.right < right) + mSection->mRect.right = right; + + if (mSection->mRect.bottom < bottom) + mSection->mRect.bottom = bottom; + + if (mLine->mRect.right < right) + mLine->mRect.right = right; + + if (mLine->mRect.bottom < bottom) + mLine->mRect.bottom = bottom; + + if (mRun == NULL || mRun->mStyle != style || mRun->mRange.second != begin) + { + int left = mRun ? mRun->mRight : mLine->mRect.left; + + mLine->mRuns.push_back (Run ()); + mRun = &mLine->mRuns.back (); + mRun->mStyle = style; + mRun->mLeft = left; + mRun->mRight = right; + mRun->mRange.first = begin; + mRun->mRange.second = end; + mRun->mPrintableChars = pc; + //Run->Locale = Locale; + } + else + { + mRun->mRight = right; + mRun->mRange.second = end; + mRun->mPrintableChars += pc; + } + } +}; + +BookTypesetter::Ptr BookTypesetter::create (int pageWidth, int pageHeight) +{ + return boost::make_shared (pageWidth, pageHeight); +} + +namespace +{ + struct RenderXform + { + public: + + float clipTop; + float clipLeft; + float clipRight; + float clipBottom; + + float absoluteLeft; + float absoluteTop; + float leftOffset; + float topOffset; + + float pixScaleX; + float pixScaleY; + float hOffset; + float vOffset; + + RenderXform (MyGUI::ICroppedRectangle* croppedParent, MyGUI::RenderTargetInfo const & renderTargetInfo) + { + clipTop = croppedParent->_getMarginTop (); + clipLeft = croppedParent->_getMarginLeft (); + clipRight = croppedParent->getWidth () - croppedParent->_getMarginRight (); + clipBottom = croppedParent->getHeight () - croppedParent->_getMarginBottom (); + + absoluteLeft = croppedParent->getAbsoluteLeft(); + absoluteTop = croppedParent->getAbsoluteTop(); + leftOffset = renderTargetInfo.leftOffset; + topOffset = renderTargetInfo.topOffset; + + pixScaleX = renderTargetInfo.pixScaleX; + pixScaleY = renderTargetInfo.pixScaleY; + hOffset = renderTargetInfo.hOffset; + vOffset = renderTargetInfo.vOffset; + } + + bool clip (MyGUI::FloatRect & vr, MyGUI::FloatRect & tr) + { + if (vr.bottom <= clipTop || vr.right <= clipLeft || + vr.left >= clipRight || vr.top >= clipBottom ) + return false; + + if (vr.top < clipTop) + { + tr.top += tr.height () * (clipTop - vr.top) / vr.height (); + vr.top = clipTop; + } + + if (vr.left < clipLeft) + { + tr.left += tr.width () * (clipLeft - vr.left) / vr.width (); + vr.left = clipLeft; + } + + if (vr.right > clipRight) + { + tr.right -= tr.width () * (vr.right - clipRight) / vr.width (); + vr.right = clipRight; + } + + if (vr.bottom > clipBottom) + { + tr.bottom -= tr.height () * (vr.bottom - clipBottom) / vr.height (); + vr.bottom = clipBottom; + } + + return true; + } + + MyGUI::FloatPoint operator () (MyGUI::FloatPoint pt) + { + pt.left = absoluteLeft - leftOffset + pt.left; + pt.top = absoluteTop - topOffset + pt.top; + + pt.left = +(((pixScaleX * pt.left + hOffset) * 2.0f) - 1.0f); + pt.top = -(((pixScaleY * pt.top + vOffset) * 2.0f) - 1.0f); + + return pt; + } + }; + + struct GlyphStream + { + float mZ; + uint32_t mC; + MyGUI::IFont* mFont; + MyGUI::FloatPoint mOrigin; + MyGUI::FloatPoint mCursor; + MyGUI::Vertex* mVertices; + RenderXform mRenderXform; + MyGUI::VertexColourType mVertexColourType; + + GlyphStream (MyGUI::IFont* font, float left, float top, float Z, + MyGUI::Vertex* vertices, RenderXform const & renderXform) : + mZ(Z), mOrigin (left, top), + mFont (font), mVertices (vertices), + mRenderXform (renderXform) + { + mVertexColourType = MyGUI::RenderManager::getInstance().getVertexFormat(); + } + + ~GlyphStream () + { + } + + MyGUI::Vertex* end () const { return mVertices; } + + void reset (float left, float top, MyGUI::Colour colour) + { + mC = MyGUI::texture_utility::toColourARGB(colour) | 0xFF000000; + MyGUI::texture_utility::convertColour(mC, mVertexColourType); + + mCursor.left = mOrigin.left + left; + mCursor.top = mOrigin.top + top; + } + + void emitGlyph (wchar_t ch) + { + MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + + MyGUI::FloatRect vr; + + vr.left = mCursor.left + gi->bearingX; + vr.top = mCursor.top + gi->bearingY; + vr.right = vr.left + gi->width; + vr.bottom = vr.top + gi->height; + + MyGUI::FloatRect tr = gi->uvRect; + + if (mRenderXform.clip (vr, tr)) + quad (vr, tr); + + mCursor.left += gi->bearingX + gi->advance; + } + + void emitSpace (wchar_t ch) + { + MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + + mCursor.left += gi->bearingX + gi->advance; + } + + private: + + void quad (const MyGUI::FloatRect& vr, const MyGUI::FloatRect& tr) + { + vertex (vr.left, vr.top, tr.left, tr.top); + vertex (vr.right, vr.top, tr.right, tr.top); + vertex (vr.left, vr.bottom, tr.left, tr.bottom); + vertex (vr.right, vr.top, tr.right, tr.top); + vertex (vr.left, vr.bottom, tr.left, tr.bottom); + vertex (vr.right, vr.bottom, tr.right, tr.bottom); + } + + void vertex (float x, float y, float u, float v) + { + MyGUI::FloatPoint pt = mRenderXform (MyGUI::FloatPoint (x, y)); + + mVertices->x = pt.left; + mVertices->y = pt.top ; + mVertices->z = mZ; + mVertices->u = u; + mVertices->v = v; + mVertices->colour = mC; + + ++mVertices; + } + }; +} + +class PageDisplay : public MyGUI::ISubWidgetText +{ + MYGUI_RTTI_DERIVED(PageDisplay) +protected: + + typedef TypesetBookImpl::Section Section; + typedef TypesetBookImpl::Line Line; + typedef TypesetBookImpl::Run Run; + + struct TextFormat : ISubWidget + { + typedef MyGUI::IFont* Id; + + Id mFont; + int mCountVertex; + MyGUI::ITexture* mTexture; + MyGUI::RenderItem* mRenderItem; + PageDisplay * mDisplay; + + TextFormat (MyGUI::IFont* id, PageDisplay * display) : + mFont (id), + mTexture (NULL), + mRenderItem (NULL), + mDisplay (display), + mCountVertex (0) + { + } + + void createDrawItem (MyGUI::ILayerNode* node) + { + assert (mRenderItem == NULL); + + if (mTexture != NULL) + { + mRenderItem = node->addToRenderItem(mTexture, false, false); + mRenderItem->addDrawItem(this, mCountVertex); + } + } + + void destroyDrawItem (MyGUI::ILayerNode* node) + { + assert (mTexture != NULL ? mRenderItem != NULL : mRenderItem == NULL); + + if (mTexture != NULL) + { + mRenderItem->removeDrawItem (this); + mRenderItem = NULL; + } + } + + void doRender() { mDisplay->doRender (*this); } + + // this isn't really a sub-widget, its just a "drawitem" which + // should have its own interface + void createDrawItem(MyGUI::ITexture* _texture, MyGUI::ILayerNode* _node) {} + void destroyDrawItem() {}; + }; + +public: + + typedef TypesetBookImpl::StyleImpl Style; + typedef std::map ActiveTextFormats; + + int mViewTop; + int mViewBottom; + + Style* mFocusItem; + bool mItemActive; + MyGUI::MouseButton mLastDown; + boost::function mLinkClicked; + + + boost::shared_ptr mBook; + size_t mPage; + + MyGUI::ILayerNode* mNode; + ActiveTextFormats mActiveTextFormats; + + PageDisplay () + { + mPage = -1; + } + + void dirtyFocusItem () + { + if (mFocusItem != 0) + { + MyGUI::IFont* Font = mBook->affectedFont (mFocusItem); + + ActiveTextFormats::iterator i = mActiveTextFormats.find (Font); + + mNode->outOfDate (i->second->mRenderItem); + } + } + + void onMouseLostFocus () + { + if (!mBook) + return; + + dirtyFocusItem (); + + mFocusItem = 0; + mItemActive = false; + } + + void onMouseMove (int left, int top) + { + if (!mBook) + return; + + left -= mCroppedParent->getAbsoluteLeft (); + top -= mCroppedParent->getAbsoluteTop (); + + Style * Hit = mBook->hitTest (left, mViewTop + top); + + if (mLastDown == MyGUI::MouseButton::None) + { + if (Hit != mFocusItem) + { + dirtyFocusItem (); + + mFocusItem = Hit; + mItemActive = false; + + dirtyFocusItem (); + } + } + else + if (mFocusItem != 0) + { + bool newItemActive = Hit == mFocusItem; + + if (newItemActive != mItemActive) + { + mItemActive = newItemActive; + + dirtyFocusItem (); + } + } + } + + void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) + { + if (!mBook) + return; + + left -= mCroppedParent->getAbsoluteLeft (); + top -= mCroppedParent->getAbsoluteTop (); + + if (mLastDown == MyGUI::MouseButton::None) + { + mFocusItem = mBook->hitTest (left, mViewTop + top); + mItemActive = true; + + dirtyFocusItem (); + + mLastDown = id; + } + } + + void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) + { + if (!mBook) + return; + + left -= mCroppedParent->getAbsoluteLeft (); + top -= mCroppedParent->getAbsoluteTop (); + + if (mLastDown == id) + { + Style * mItem = mBook->hitTest (left, mViewTop + top); + + bool clicked = mFocusItem == mItem; + + mItemActive = false; + + dirtyFocusItem (); + + mLastDown = MyGUI::MouseButton::None; + + if (clicked && mLinkClicked && mItem && mItem->mInteractiveId != 0) + mLinkClicked (mItem->mInteractiveId); + } + } + + void showPage (TypesetBook::Ptr book, size_t newPage) + { + boost::shared_ptr newBook = boost::dynamic_pointer_cast (book); + + if (mBook != newBook) + { + mFocusItem = nullptr; + mItemActive = 0; + + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + { + if (mNode != NULL) + i->second->destroyDrawItem (mNode); + delete i->second; + } + + mActiveTextFormats.clear (); + + if (newBook != NULL) + { + createActiveFormats (newBook); + + mBook = newBook; + mPage = newPage; + + if (newPage < mBook->mPages.size ()) + { + mViewTop = mBook->mPages [newPage].first; + mViewBottom = mBook->mPages [newPage].second; + } + else + { + mViewTop = 0; + mViewBottom = 0; + } + } + else + { + mBook.reset (); + mPage = -1; + mViewTop = 0; + mViewBottom = 0; + } + } + else + if (mBook && mPage != newPage) + { + if (mNode != NULL) + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + mNode->outOfDate(i->second->mRenderItem); + + mPage = newPage; + + if (newPage < mBook->mPages.size ()) + { + mViewTop = mBook->mPages [newPage].first; + mViewBottom = mBook->mPages [newPage].second; + } + else + { + mViewTop = 0; + mViewBottom = 0; + } + } + } + + struct CreateActiveFormat + { + PageDisplay * this_; + + CreateActiveFormat (PageDisplay * this_) : this_ (this_) {} + + void operator () (Section const & section, Line const & line, Run const & run) const + { + MyGUI::IFont* Font = run.mStyle->mFont; + + ActiveTextFormats::iterator j = this_->mActiveTextFormats.find (Font); + + if (j == this_->mActiveTextFormats.end ()) + { + TextFormat * textFormat = new TextFormat (Font, this_); + + textFormat->mTexture = Font->getTextureFont (); + + j = this_->mActiveTextFormats.insert (std::make_pair (Font, textFormat)).first; + } + + j->second->mCountVertex += run.mPrintableChars * 6; + } + }; + + void createActiveFormats (boost::shared_ptr newBook) + { + newBook->visitRuns (0, 0x7FFFFFFF, CreateActiveFormat (this)); + + if (mNode != NULL) + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + i->second->createDrawItem (mNode); + } + + void setVisible (bool newVisible) + { + if (mVisible == newVisible) + return; + + mVisible = newVisible; + + if (mVisible) + { + // reset input state + mLastDown = MyGUI::MouseButton::None; + mFocusItem = nullptr; + mItemActive = 0; + } + + if (nullptr != mNode) + { + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + mNode->outOfDate(i->second->mRenderItem); + } + } + + void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) + { + //test (); + + mNode = node; + + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + i->second->createDrawItem (node); + } + + struct RenderRun + { + PageDisplay * this_; + GlyphStream &glyphStream; + + RenderRun (PageDisplay * this_, GlyphStream &glyphStream) : + this_(this_), glyphStream (glyphStream) + { + } + + void operator () (Section const & section, Line const & line, Run const & run) const + { + bool isActive = run.mStyle->mInteractiveId && (run.mStyle == this_->mFocusItem); + + MyGUI::Colour colour = isActive ? (this_->mItemActive ? run.mStyle->mActiveColour: run.mStyle->mHotColour) : run.mStyle->mNormalColour; + + glyphStream.reset (section.mRect.left + line.mRect.left + run.mLeft, line.mRect.top, colour); + + Utf8Stream stream (run.mRange); + + while (!stream.eof ()) + { + Utf8Stream::UnicodeChar code_point = stream.consume (); + + if (!ucsSpace (code_point)) + glyphStream.emitGlyph (code_point); + else + glyphStream.emitSpace (code_point); + } + } + }; + + /* + queue up rendering operations for this text format + */ + void doRender(TextFormat & textFormat) + { + if (!mVisible) + return; + + MyGUI::Vertex* vertices = textFormat.mRenderItem->getCurrentVertexBuffer(); + + RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); + + GlyphStream glyphStream (textFormat.mFont, mCoord.left, mCoord.top-mViewTop, + -1 /*mNode->getNodeDepth()*/, vertices, renderXform); + + int visit_top = (std::max) (mViewTop, mViewTop + int (renderXform.clipTop )); + int visit_bottom = (std::min) (mViewBottom, mViewTop + int (renderXform.clipBottom)); + + mBook->visitRuns (visit_top, visit_bottom, textFormat.mFont, RenderRun (this, glyphStream)); + + textFormat.mRenderItem->setLastVertexCount(glyphStream.end () - vertices); + } + + // ISubWidget should not necessarily be a drawitem + // in this case, it is not... + void doRender() { } + + void _updateView () + { + } + + void _correctView() + { + _checkMargin (); + + if (mNode != NULL) + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + mNode->outOfDate (i->second->mRenderItem); + + } + + void destroyDrawItem() + { + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + i->second->destroyDrawItem (mNode); + + mNode = NULL; + } +}; + + +class BookPageImpl : public BookPage +{ +MYGUI_RTTI_DERIVED(BookPage) +public: + + + void showPage (TypesetBook::Ptr book, size_t page) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + pd->showPage (book, page); + else + throw std::runtime_error ("The main sub-widget for a BookPage must be a PageDisplay."); + } + + void adviseLinkClicked (boost::function linkClicked) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->mLinkClicked = linkClicked; + } + } + + void unadviseLinkClicked () + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->mLinkClicked = boost::function (); + } + } + +protected: + void onMouseLostFocus(Widget* _new) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->onMouseLostFocus (); + } + else + Widget::onMouseLostFocus (_new); + } + + void onMouseMove(int left, int top) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->onMouseMove (left, top); + } + else + Widget::onMouseMove (left, top); + } + + void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->onMouseButtonPressed (left, top, id); + } + else + Widget::onMouseButtonPressed (left, top, id); + } + + void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->onMouseButtonReleased (left, top, id); + } + else + Widget::onMouseButtonReleased (left, top, id); + } +}; + +void BookPage::registerMyGUIComponents () +{ + MyGUI::FactoryManager & factory = MyGUI::FactoryManager::getInstance(); + + factory.registerFactory("Widget"); + factory.registerFactory("BasisSkin"); +} + +static bool ucsLineBreak (int codePoint) +{ + return codePoint == '\n'; +} + +static bool ucsSpace (int codePoint) +{ + switch (codePoint) + { + case 0x0020: // SPACE + case 0x00A0: // NO-BREAK SPACE + case 0x1680: // OGHAM SPACE MARK + case 0x180E: // MONGOLIAN VOWEL SEPARATOR + case 0x2000: // EN QUAD + case 0x2001: // EM QUAD + case 0x2002: // EN SPACE + case 0x2003: // EM SPACE + case 0x2004: // THREE-PER-EM SPACE + case 0x2005: // FOUR-PER-EM SPACE + case 0x2006: // SIX-PER-EM SPACE + case 0x2007: // FIGURE SPACE + case 0x2008: // PUNCTUATION SPACE + case 0x2009: // THIN SPACE + case 0x200A: // HAIR SPACE + case 0x200B: // ZERO WIDTH SPACE + case 0x202F: // NARROW NO-BREAK SPACE + case 0x205F: // MEDIUM MATHEMATICAL SPACE + case 0x3000: // IDEOGRAPHIC SPACE + case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE + return true; + default: + return false; + } +} + +static bool ucsBreakingSpace (int codePoint) +{ + switch (codePoint) + { + case 0x0020: // SPACE + //case 0x00A0: // NO-BREAK SPACE + case 0x1680: // OGHAM SPACE MARK + case 0x180E: // MONGOLIAN VOWEL SEPARATOR + case 0x2000: // EN QUAD + case 0x2001: // EM QUAD + case 0x2002: // EN SPACE + case 0x2003: // EM SPACE + case 0x2004: // THREE-PER-EM SPACE + case 0x2005: // FOUR-PER-EM SPACE + case 0x2006: // SIX-PER-EM SPACE + case 0x2007: // FIGURE SPACE + case 0x2008: // PUNCTUATION SPACE + case 0x2009: // THIN SPACE + case 0x200A: // HAIR SPACE + case 0x200B: // ZERO WIDTH SPACE + case 0x202F: // NARROW NO-BREAK SPACE + case 0x205F: // MEDIUM MATHEMATICAL SPACE + case 0x3000: // IDEOGRAPHIC SPACE + //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE + return true; + default: + return false; + } +} + +} diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp new file mode 100644 index 0000000000..28aa371cf3 --- /dev/null +++ b/apps/openmw/mwgui/bookpage.hpp @@ -0,0 +1,122 @@ +#ifndef MWGUI_BOOKPAGE_HPP +#define MWGUI_BOOKPAGE_HPP + +#include "MyGUI_Colour.h" +#include "MyGUI_Widget.h" + +#include +#include +#include +#include + +namespace MWGui +{ + /// A formatted and paginated document to be used with + /// the book page widget. + struct TypesetBook + { + typedef boost::shared_ptr Ptr; + typedef intptr_t InteractiveId; + + /// Returns the number of pages in the document. + virtual size_t pageCount () const = 0; + + /// Return the area covered by the document. The first + /// integer is the maximum with of any line. This is not + /// the largest coordinate of the right edge of any line, + /// it is the largest distance from the left edge to the + /// right edge. The second integer is the height of all + /// text combined prior to pagination. + virtual std::pair getSize () const = 0; + }; + + /// A factory class for creating a typeset book instance. + struct BookTypesetter + { + typedef boost::shared_ptr Ptr; + typedef TypesetBook::InteractiveId InteractiveId; + typedef MyGUI::Colour Colour; + typedef uint8_t const * Utf8Point; + typedef std::pair Utf8Span; + + enum Alignment { + AlignLeft = -1, + AlignCenter = 0, + AlignRight = +1 + }; + + /// Styles are used to control the character level formatting + /// of text added to a typeset book. Their lifetime is equal + /// to the lifetime of the book-typesetter instance that created + /// them. + struct Style; + + /// A factory function for creating the default implementation of a book typesetter + static Ptr create (int pageWidth, int pageHeight); + + /// Create a simple text style consisting of a font and a text color. + virtual Style* createStyle (char const * Font, Colour Colour) = 0; + + /// Create a hyper-link style with a user-defined identifier based on an + /// existing style. The unique flag forces a new instance of this style + /// to be created even if an existing instance is present. + virtual Style* createHotStyle (Style * BaseStyle, Colour NormalColour, Colour HoverColour, Colour ActiveColour, InteractiveId Id, bool Unique = true) = 0; + + /// Insert a line break into the document. Newline characters in the input + /// text have the same affect. The margin parameter adds additional space + /// before the next line of text. + virtual void lineBreak (float margin = 0) = 0; + + /// Insert a section break into the document. This causes a new section + /// to begin when additional text is inserted. Pagination attempts to keep + /// sections together on a single page. The margin parameter adds additional space + /// before the next line of text. + virtual void sectionBreak (float margin = 0) = 0; + + /// Changes the alignment for the current section of text. + virtual void setSectionAlignment (Alignment sectionAlignment) = 0; + + // Layout a block of text with the specified style into the document. + virtual void write (Style * Style, Utf8Span Text) = 0; + + /// Adds a content block to the document without laying it out. An + /// identifier is returned that can be used to refer to it. If select + /// is true, the block is activated to be references by future writes. + virtual intptr_t addContent (Utf8Span Text, bool Select = true) = 0; + + /// Select a previously created content block for future writes. + virtual void selectContent (intptr_t contentHandle) = 0; + + /// Layout a span of the selected content block into the document + /// using the specified style. + virtual void write (Style * Style, size_t Begin, size_t End) = 0; + + /// Finalize the document layout, and return a pointer to it. + virtual TypesetBook::Ptr complete () = 0; + }; + + /// An interface to the BookPage widget. + class BookPage : public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(BookPage) + public: + + typedef TypesetBook::InteractiveId InteractiveId; + typedef boost::function ClickCallback; + + /// Make the widget display the specified page from the specified book. + virtual void showPage (TypesetBook::Ptr Book, size_t Page) = 0; + + /// Set the callback for a clicking a hyper-link in the document. + virtual void adviseLinkClicked (ClickCallback callback) = 0; + + /// Clear the hyper-link click callback. + virtual void unadviseLinkClicked () = 0; + + /// Register the widget and associated sub-widget with MyGUI. Should be + /// called once near the beginning of the program. + static void registerMyGUIComponents (); + }; +} + +#endif // MWGUI_BOOKPAGE_HPP diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 777751069b..3b5deb5acc 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -12,148 +12,187 @@ #include "formatting.hpp" -using namespace MWGui; - -BookWindow::BookWindow (MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_book.layout", parWindowManager) - , mTakeButtonShow(true) - , mTakeButtonAllowed(true) +namespace MWGui { - getWidget(mCloseButton, "CloseButton"); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); - getWidget(mTakeButton, "TakeButton"); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); - - getWidget(mNextPageButton, "NextPageBTN"); - mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); - - getWidget(mPrevPageButton, "PrevPageBTN"); - mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); - - getWidget(mLeftPageNumber, "LeftPageNumber"); - getWidget(mRightPageNumber, "RightPageNumber"); - - getWidget(mLeftPage, "LeftPage"); - getWidget(mRightPage, "RightPage"); - - center(); -} - -void BookWindow::clearPages() -{ - for (std::vector::iterator it=mPages.begin(); - it!=mPages.end(); ++it) + BookWindow::BookWindow () + : WindowBase("openmw_book.layout") + , mTakeButtonShow(true) + , mTakeButtonAllowed(true) { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mPages.clear(); -} + getWidget(mCloseButton, "CloseButton"); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); -void BookWindow::open (MWWorld::Ptr book) -{ - mBook = book; + getWidget(mTakeButton, "TakeButton"); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); - clearPages(); - mCurrentPage = 0; + getWidget(mNextPageButton, "NextPageBTN"); + mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); - MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); + getWidget(mPrevPageButton, "PrevPageBTN"); + mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); - MWWorld::LiveCellRef *ref = mBook.get(); + getWidget(mLeftPageNumber, "LeftPageNumber"); + getWidget(mRightPageNumber, "RightPageNumber"); - BookTextParser parser; - std::vector results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); + getWidget(mLeftPage, "LeftPage"); + getWidget(mRightPage, "RightPage"); - int i=0; - for (std::vector::iterator it=results.begin(); - it!=results.end(); ++it) - { - MyGUI::Widget* parent; - if (i%2 == 0) - parent = mLeftPage; - else - parent = mRightPage; + adjustButton(mCloseButton); + adjustButton(mTakeButton); + adjustButton(mNextPageButton); + adjustButton(mPrevPageButton); - MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); - parser.parse(*it, pageWidget, mLeftPage->getSize().width); - mPages.push_back(pageWidget); - ++i; - } - - updatePages(); - - setTakeButtonShow(true); -} - -void BookWindow::setTakeButtonShow(bool show) -{ - mTakeButtonShow = show; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} - -void BookWindow::setInventoryAllowed(bool allowed) -{ - mTakeButtonAllowed = allowed; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} - -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); -} - -void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); - - MWWorld::ActionTake take(mBook); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - - mWindowManager.removeGuiMode(GM_Book); -} - -void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) -{ - if ((mCurrentPage+1)*2 < mPages.size()) - { - MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); - - ++mCurrentPage; - - updatePages(); - } -} - -void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) -{ - if (mCurrentPage > 0) - { - MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); - - --mCurrentPage; - - updatePages(); - } -} - -void BookWindow::updatePages() -{ - mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); - mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); - - unsigned int i=0; - for (std::vector::iterator it = mPages.begin(); - it != mPages.end(); ++it) - { - if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) - (*it)->setVisible(true); - else + if (mNextPageButton->getSize().width == 64) { - (*it)->setVisible(false); + // english button has a 7 pixel wide strip of garbage on its right edge + mNextPageButton->setSize(64-7, mNextPageButton->getSize().height); + mNextPageButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,mNextPageButton->getSize().height)); } - ++i; + + center(); } + + void BookWindow::clearPages() + { + for (std::vector::iterator it=mPages.begin(); + it!=mPages.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mPages.clear(); + } + + void BookWindow::open (MWWorld::Ptr book) + { + mBook = book; + + clearPages(); + mCurrentPage = 0; + + MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); + + MWWorld::LiveCellRef *ref = mBook.get(); + + BookTextParser parser; + std::vector results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); + + int i=0; + for (std::vector::iterator it=results.begin(); + it!=results.end(); ++it) + { + MyGUI::Widget* parent; + if (i%2 == 0) + parent = mLeftPage; + else + parent = mRightPage; + + MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); + parser.parsePage(*it, pageWidget, mLeftPage->getSize().width); + mPages.push_back(pageWidget); + ++i; + } + + updatePages(); + + setTakeButtonShow(true); + } + + void BookWindow::setTakeButtonShow(bool show) + { + mTakeButtonShow = show; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + void BookWindow::setInventoryAllowed(bool allowed) + { + mTakeButtonAllowed = allowed; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + 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); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + } + + void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + + MWWorld::ActionTake take(mBook); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + } + + void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) + { + if ((mCurrentPage+1)*2 < mPages.size()) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); + + ++mCurrentPage; + + updatePages(); + } + } + + void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) + { + if (mCurrentPage > 0) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); + + --mCurrentPage; + + updatePages(); + } + } + + void BookWindow::updatePages() + { + mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); + mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); + + unsigned int i=0; + for (std::vector::iterator it = mPages.begin(); + it != mPages.end(); ++it) + { + if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) + (*it)->setVisible(true); + else + { + (*it)->setVisible(false); + } + ++i; + } + + //If it is the last page, hide the button "Next Page" + if ( (mCurrentPage+1)*2 == mPages.size() + || (mCurrentPage+1)*2 == mPages.size() + 1) + { + mNextPageButton->setVisible(false); + } else { + mNextPageButton->setVisible(true); + } + //If it is the fist page, hide the button "Prev Page" + if (mCurrentPage == 0) { + mPrevPageButton->setVisible(false); + } else { + mPrevPageButton->setVisible(true); + } + } + + void BookWindow::adjustButton (MWGui::ImageButton* button) + { + MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); + button->setSize(button->getRequestedSize()); + + if (button->getAlign().isRight()) + button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); + } + } diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index a509f131fe..c6ea486d44 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); @@ -27,6 +27,7 @@ namespace MWGui void updatePages(); void clearPages(); + void adjustButton(MWGui::ImageButton* button); private: MWGui::ImageButton* mCloseButton; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 08d024f5c6..fc8c24484e 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -1,17 +1,16 @@ #include "charactercreation.hpp" -#include "text_input.hpp" +#include "textinput.hpp" #include "race.hpp" #include "class.hpp" #include "birth.hpp" #include "review.hpp" -#include "dialogue.hpp" -#include "mode.hpp" #include "inventorywindow.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwworld/fallback.hpp" namespace { @@ -23,14 +22,13 @@ namespace }; const ESM::Class::Specialization mSpecializations[3]={ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}; // The specialization for each answer - Step sGenerateClassSteps(int number) { - MWBase::World *world = MWBase::Environment::get().getWorld(); number++; - Step step = {world->getFallback("Question_"+boost::lexical_cast(number)+"_Question"), - {world->getFallback("Question_"+boost::lexical_cast(number)+"_AnswerOne"), - world->getFallback("Question_"+boost::lexical_cast(number)+"_AnswerTwo"), - world->getFallback("Question_"+boost::lexical_cast(number)+"_AnswerThree")}, + const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); + Step step = {fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_Question"), + {fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerOne"), + fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerTwo"), + fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerThree")}, "vo\\misc\\chargen qa"+boost::lexical_cast(number)+".wav" }; return step; @@ -45,683 +43,685 @@ namespace }; } -using namespace MWGui; - -CharacterCreation::CharacterCreation(MWBase::WindowManager* _wm) - : mNameDialog(0) - , mRaceDialog(0) - , mClassChoiceDialog(0) - , mGenerateClassQuestionDialog(0) - , mGenerateClassResultDialog(0) - , mPickClassDialog(0) - , mCreateClassDialog(0) - , mBirthSignDialog(0) - , mReviewDialog(0) - , mGenerateClassStep(0) - , mWM(_wm) +namespace MWGui { - mCreationStage = CSE_NotStarted; -} -void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - if (mReviewDialog) + CharacterCreation::CharacterCreation() + : mNameDialog(0) + , mRaceDialog(0) + , mClassChoiceDialog(0) + , mGenerateClassQuestionDialog(0) + , mGenerateClassResultDialog(0) + , mPickClassDialog(0) + , mCreateClassDialog(0) + , mBirthSignDialog(0) + , mReviewDialog(0) + , mGenerateClassStep(0) { - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; - - for (int i=0; ids[i]; ++i) - { - if (ids[i]==id) - mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); - } + mCreationStage = CSE_NotStarted; } -} -void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - if (mReviewDialog) + void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) { - if (id == "HBar") + if (mReviewDialog) { - mReviewDialog->setHealth (value); - } - else if (id == "MBar") - { - mReviewDialog->setMagicka (value); - } - else if (id == "FBar") - { - mReviewDialog->setFatigue (value); - } - } -} - -void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) -{ - if (mReviewDialog) - mReviewDialog->setSkillValue(parSkill, value); -} - -void CharacterCreation::configureSkills (const SkillList& major, const SkillList& minor) -{ - if (mReviewDialog) - mReviewDialog->configureSkills(major, minor); -} - -void CharacterCreation::spawnDialog(const char id) -{ - switch (id) - { - case GM_Name: - mWM->removeDialog(mNameDialog); - mNameDialog = 0; - mNameDialog = new TextInputDialog(*mWM); - mNameDialog->setTextLabel(mWM->getGameSettingString("sName", "Name")); - mNameDialog->setTextInput(mPlayerName); - mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); - mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); - mNameDialog->setVisible(true); - break; - - case GM_Race: - mWM->removeDialog(mRaceDialog); - mRaceDialog = 0; - mRaceDialog = new RaceDialog(*mWM); - mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); - mRaceDialog->setRaceId(mPlayerRaceId); - mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); - mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); - mRaceDialog->setVisible(true); - if (mCreationStage < CSE_NameChosen) - mCreationStage = CSE_NameChosen; - break; - - case GM_Class: - mWM->removeDialog(mClassChoiceDialog); - mClassChoiceDialog = 0; - mClassChoiceDialog = new ClassChoiceDialog(*mWM); - mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); - mClassChoiceDialog->setVisible(true); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - break; - - case GM_ClassPick: - mWM->removeDialog(mPickClassDialog); - mPickClassDialog = 0; - mPickClassDialog = new PickClassDialog(*mWM); - mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); - mPickClassDialog->setClassId(mPlayerClass.mName); - mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); - mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); - mPickClassDialog->setVisible(true); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - break; - - case GM_Birth: - mWM->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; - mBirthSignDialog = new BirthDialog(*mWM); - mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); - mBirthSignDialog->setBirthId(mPlayerBirthSignId); - mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); - mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); - mBirthSignDialog->setVisible(true); - if (mCreationStage < CSE_ClassChosen) - mCreationStage = CSE_ClassChosen; - break; - - case GM_ClassCreate: - mWM->removeDialog(mCreateClassDialog); - mCreateClassDialog = 0; - mCreateClassDialog = new CreateClassDialog(*mWM); - mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); - mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); - mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); - mCreateClassDialog->setVisible(true); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - break; - case GM_ClassGenerate: - mGenerateClassStep = 0; - mGenerateClass = ""; - mGenerateClassSpecializations[0] = 0; - mGenerateClassSpecializations[1] = 0; - mGenerateClassSpecializations[2] = 0; - showClassQuestionDialog(); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - break; - case GM_Review: - mWM->removeDialog(mReviewDialog); - mReviewDialog = 0; - mReviewDialog = new ReviewDialog(*mWM); - mReviewDialog->setPlayerName(mPlayerName); - mReviewDialog->setRace(mPlayerRaceId); - mReviewDialog->setClass(mPlayerClass); - mReviewDialog->setBirthSign(mPlayerBirthSignId); - - mReviewDialog->setHealth(mPlayerHealth); - mReviewDialog->setMagicka(mPlayerMagicka); - mReviewDialog->setFatigue(mPlayerFatigue); - + static const char *ids[] = { - std::map > attributes = mWM->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); - it != attributes.end(); ++it) - { - mReviewDialog->setAttribute(static_cast (it->first), it->second); - } + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8", + 0 + }; + + for (int i=0; ids[i]; ++i) + { + if (ids[i]==id) + mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); } + } + } + void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + if (mReviewDialog) + { + if (id == "HBar") { - std::map > skills = mWM->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->setHealth (value); } - - mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); - mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); - mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); - mReviewDialog->setVisible(true); - if (mCreationStage < CSE_BirthSignChosen) - mCreationStage = CSE_BirthSignChosen; - break; - } -} - -void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) -{ - mPlayerHealth = value; -} - -void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) -{ - mPlayerMagicka = value; -} - -void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) -{ - mPlayerFatigue = value; -} - -void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) -{ - mWM->removeDialog(mReviewDialog); - mReviewDialog = 0; - - mWM->popGuiMode(); -} - -void CharacterCreation::onReviewDialogBack() -{ - mWM->removeDialog(mReviewDialog); - mReviewDialog = 0; - - mWM->pushGuiMode(GM_Birth); -} - -void CharacterCreation::onReviewActivateDialog(int parDialog) -{ - mWM->removeDialog(mReviewDialog); - mReviewDialog = 0; - mCreationStage = CSE_ReviewNext; - - mWM->popGuiMode(); - - switch(parDialog) - { - case ReviewDialog::NAME_DIALOG: - mWM->pushGuiMode(GM_Name); - break; - case ReviewDialog::RACE_DIALOG: - mWM->pushGuiMode(GM_Race); - break; - case ReviewDialog::CLASS_DIALOG: - mWM->pushGuiMode(GM_Class); - break; - case ReviewDialog::BIRTHSIGN_DIALOG: - mWM->pushGuiMode(GM_Birth); - }; -} - -void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) -{ - if (mPickClassDialog) - { - const std::string &classId = mPickClassDialog->getClassId(); - if (!classId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); - - const ESM::Class *klass = - MWBase::Environment::get().getWorld()->getStore().get().find(classId); - if (klass) - { - mPlayerClass = *klass; - mWM->setPlayerClass(mPlayerClass); - } - mWM->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); - } - else if (mCreationStage >= CSE_ClassChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); - } - else - { - mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); - } -} - -void CharacterCreation::onPickClassDialogBack() -{ - if (mPickClassDialog) - { - const std::string classId = mPickClassDialog->getClassId(); - if (!classId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); - mWM->removeDialog(mPickClassDialog); - mPickClassDialog = 0; - } - - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} - -void CharacterCreation::onClassChoice(int _index) -{ - mWM->removeDialog(mClassChoiceDialog); - mClassChoiceDialog = 0; - - mWM->popGuiMode(); - - switch(_index) - { - case ClassChoiceDialog::Class_Generate: - mWM->pushGuiMode(GM_ClassGenerate); - break; - case ClassChoiceDialog::Class_Pick: - mWM->pushGuiMode(GM_ClassPick); - break; - case ClassChoiceDialog::Class_Create: - mWM->pushGuiMode(GM_ClassCreate); - break; - case ClassChoiceDialog::Class_Back: - mWM->pushGuiMode(GM_Race); - break; - - }; -} - -void CharacterCreation::onNameDialogDone(WindowBase* parWindow) -{ - if (mNameDialog) - { - mPlayerName = mNameDialog->getTextInput(); - mWM->setValue("name", mPlayerName); - MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); - mWM->removeDialog(mNameDialog); - mNameDialog = 0; - } - - if (mCreationStage == CSE_ReviewNext) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_NameChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Race); - } - else - { - mCreationStage = CSE_NameChosen; - mWM->popGuiMode(); - } -} - -void CharacterCreation::onRaceDialogBack() -{ - if (mRaceDialog) - { - const ESM::NPC &data = mRaceDialog->getResult(); - mPlayerRaceId = data.mRace; - if (!mPlayerRaceId.empty()) { - MWBase::Environment::get().getMechanicsManager()->setPlayerRace( - data.mRace, - data.isMale(), - data.mHead, - data.mHair - ); - } - mWM->removeDialog(mRaceDialog); - mRaceDialog = 0; - } - - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Name); -} - -void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) -{ - if (mRaceDialog) - { - const ESM::NPC &data = mRaceDialog->getResult(); - mPlayerRaceId = data.mRace; - if (!mPlayerRaceId.empty()) { - MWBase::Environment::get().getMechanicsManager()->setPlayerRace( - data.mRace, - data.isMale(), - data.mHead, - data.mHair - ); - } - mWM->getInventoryWindow()->rebuildAvatar(); - - mWM->removeDialog(mRaceDialog); - mRaceDialog = 0; - } - - if (mCreationStage == CSE_ReviewNext) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_RaceChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); - } - else - { - mCreationStage = CSE_RaceChosen; - mWM->popGuiMode(); - } -} - -void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) -{ - if (mBirthSignDialog) - { - mPlayerBirthSignId = mBirthSignDialog->getBirthId(); - if (!mPlayerBirthSignId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); - mWM->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; - } - - if (mCreationStage >= CSE_BirthSignChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else - { - mCreationStage = CSE_BirthSignChosen; - mWM->popGuiMode(); - } -} - -void CharacterCreation::onBirthSignDialogBack() -{ - if (mBirthSignDialog) - { - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); - mWM->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; - } - - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} - -void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) -{ - if (mCreateClassDialog) - { - ESM::Class klass; - klass.mName = mCreateClassDialog->getName(); - klass.mDescription = mCreateClassDialog->getDescription(); - klass.mData.mSpecialization = mCreateClassDialog->getSpecializationId(); - klass.mData.mIsPlayable = 0x1; - - std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); - assert(attributes.size() == 2); - klass.mData.mAttribute[0] = attributes[0]; - klass.mData.mAttribute[1] = attributes[1]; - - std::vector majorSkills = mCreateClassDialog->getMajorSkills(); - std::vector minorSkills = mCreateClassDialog->getMinorSkills(); - assert(majorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); - assert(minorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); - for (size_t i = 0; i < sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]); ++i) - { - klass.mData.mSkills[i][1] = majorSkills[i]; - klass.mData.mSkills[i][0] = minorSkills[i]; - } - - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); - mPlayerClass = klass; - mWM->setPlayerClass(klass); - - mWM->removeDialog(mCreateClassDialog); - mCreateClassDialog = 0; - } - - if (mCreationStage == CSE_ReviewNext) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_ClassChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); - } - else - { - mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); - } -} - -void CharacterCreation::onCreateClassDialogBack() -{ - mWM->removeDialog(mCreateClassDialog); - mCreateClassDialog = 0; - - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} - -void CharacterCreation::onClassQuestionChosen(int _index) -{ - MWBase::Environment::get().getSoundManager()->stopSay(); - - mWM->removeDialog(mGenerateClassQuestionDialog); - mGenerateClassQuestionDialog = 0; - - if (_index < 0 || _index >= 3) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); - return; - } - - ESM::Class::Specialization specialization = mSpecializations[_index]; - if (specialization == ESM::Class::Stealth) - ++mGenerateClassSpecializations[0]; - else if (specialization == ESM::Class::Combat) - ++mGenerateClassSpecializations[1]; - else if (specialization == ESM::Class::Magic) - ++mGenerateClassSpecializations[2]; - ++mGenerateClassStep; - showClassQuestionDialog(); -} - -void CharacterCreation::showClassQuestionDialog() -{ - if (mGenerateClassStep == 10) - { - static boost::array classes = { { - {"Acrobat", {6, 2, 2}}, - {"Agent", {6, 1, 3}}, - {"Archer", {3, 5, 2}}, - {"Archer", {5, 5, 0}}, - {"Assassin", {6, 3, 1}}, - {"Barbarian", {3, 6, 1}}, - {"Bard", {3, 3, 3}}, - {"Battlemage", {1, 3, 6}}, - {"Crusader", {1, 6, 3}}, - {"Healer", {3, 1, 6}}, - {"Knight", {2, 6, 2}}, - {"Monk", {5, 3, 2}}, - {"Nightblade", {4, 2, 4}}, - {"Pilgrim", {5, 2, 3}}, - {"Rogue", {3, 4, 3}}, - {"Rogue", {4, 4, 2}}, - {"Rogue", {5, 4, 1}}, - {"Scout", {2, 5, 3}}, - {"Sorcerer", {2, 2, 6}}, - {"Spellsword", {2, 4, 4}}, - {"Spellsword", {5, 1, 4}}, - {"Witchhunter", {2, 3, 5}}, - {"Witchhunter", {5, 0, 5}} - } }; - - int match = -1; - for (unsigned i = 0; i < classes.size(); ++i) - { - if (mGenerateClassSpecializations[0] == classes[i].points[0] && - mGenerateClassSpecializations[1] == classes[i].points[1] && - mGenerateClassSpecializations[2] == classes[i].points[2]) + else if (id == "MBar") { - match = i; - mGenerateClass = classes[i].id; + mReviewDialog->setMagicka (value); + } + else if (id == "FBar") + { + mReviewDialog->setFatigue (value); + } + } + } + + void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + { + if (mReviewDialog) + mReviewDialog->setSkillValue(parSkill, value); + } + + void CharacterCreation::configureSkills (const SkillList& major, const SkillList& minor) + { + if (mReviewDialog) + mReviewDialog->configureSkills(major, minor); + } + + void CharacterCreation::spawnDialog(const char id) + { + switch (id) + { + case GM_Name: + MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); + mNameDialog = 0; + 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); + mNameDialog->setVisible(true); break; - } - } - if (match == -1) + case GM_Race: + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + mRaceDialog = new RaceDialog(); + mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); + mRaceDialog->setRaceId(mPlayerRaceId); + mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); + mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); + mRaceDialog->setVisible(true); + if (mCreationStage < CSE_NameChosen) + mCreationStage = CSE_NameChosen; + break; + + case GM_Class: + MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; + mClassChoiceDialog = new ClassChoiceDialog(); + mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); + mClassChoiceDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + + case GM_ClassPick: + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); + mPickClassDialog = 0; + mPickClassDialog = new PickClassDialog(); + mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); + mPickClassDialog->setClassId(mPlayerClass.mName); + mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); + mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); + mPickClassDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + + case GM_Birth: + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; + mBirthSignDialog = new BirthDialog(); + mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); + mBirthSignDialog->setBirthId(mPlayerBirthSignId); + mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); + mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); + mBirthSignDialog->setVisible(true); + if (mCreationStage < CSE_ClassChosen) + mCreationStage = CSE_ClassChosen; + break; + + case GM_ClassCreate: + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; + mCreateClassDialog = new CreateClassDialog(); + mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); + mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); + mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); + mCreateClassDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + case GM_ClassGenerate: + mGenerateClassStep = 0; + mGenerateClass = ""; + mGenerateClassSpecializations[0] = 0; + mGenerateClassSpecializations[1] = 0; + mGenerateClassSpecializations[2] = 0; + showClassQuestionDialog(); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + case GM_Review: + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + mReviewDialog = new ReviewDialog(); + mReviewDialog->setPlayerName(mPlayerName); + mReviewDialog->setRace(mPlayerRaceId); + mReviewDialog->setClass(mPlayerClass); + mReviewDialog->setBirthSign(mPlayerBirthSignId); + + mReviewDialog->setHealth(mPlayerHealth); + mReviewDialog->setMagicka(mPlayerMagicka); + mReviewDialog->setFatigue(mPlayerFatigue); + + { + std::map > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); + for (std::map >::iterator it = attributes.begin(); + it != attributes.end(); ++it) + { + mReviewDialog->setAttribute(static_cast (it->first), it->second); + } + } + + { + 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(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills()); + } + + mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); + mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); + mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); + mReviewDialog->setVisible(true); + if (mCreationStage < CSE_BirthSignChosen) + mCreationStage = CSE_BirthSignChosen; + break; + } + } + + void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) + { + mPlayerHealth = value; + } + + void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) + { + mPlayerMagicka = value; + } + + void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) + { + mPlayerFatigue = value; + } + + void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) + { + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + + void CharacterCreation::onReviewDialogBack() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + + void CharacterCreation::onReviewActivateDialog(int parDialog) + { + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + mCreationStage = CSE_ReviewNext; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + + switch(parDialog) { - if (mGenerateClassSpecializations[0] >= 7) - mGenerateClass = "Thief"; - else if (mGenerateClassSpecializations[1] >= 7) - mGenerateClass = "Warrior"; - else if (mGenerateClassSpecializations[2] >= 7) - mGenerateClass = "Mage"; - else + case ReviewDialog::NAME_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); + break; + case ReviewDialog::RACE_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + break; + case ReviewDialog::CLASS_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + break; + case ReviewDialog::BIRTHSIGN_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + }; + } + + void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) + { + if (mPickClassDialog) + { + const std::string &classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); + + const ESM::Class *klass = + MWBase::Environment::get().getWorld()->getStore().get().find(classId); + if (klass) { - std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; - mGenerateClass = "Thief"; + mPlayerClass = *klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); } + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } - mWM->removeDialog(mGenerateClassResultDialog); + //TODO This bit gets repeated a few times; wrap it in a function + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + else + { + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onPickClassDialogBack() + { + if (mPickClassDialog) + { + const std::string classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); + mPickClassDialog = 0; + } + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + + void CharacterCreation::onClassChoice(int _index) + { + MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + + switch(_index) + { + case ClassChoiceDialog::Class_Generate: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassGenerate); + break; + case ClassChoiceDialog::Class_Pick: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassPick); + break; + case ClassChoiceDialog::Class_Create: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassCreate); + break; + case ClassChoiceDialog::Class_Back: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + break; + + }; + } + + void CharacterCreation::onNameDialogDone(WindowBase* parWindow) + { + if (mNameDialog) + { + mPlayerName = mNameDialog->getTextInput(); + MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName); + MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); + MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); + mNameDialog = 0; + } + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_NameChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + } + else + { + mCreationStage = CSE_NameChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onRaceDialogBack() + { + if (mRaceDialog) + { + const ESM::NPC &data = mRaceDialog->getResult(); + mPlayerRaceId = data.mRace; + if (!mPlayerRaceId.empty()) { + MWBase::Environment::get().getMechanicsManager()->setPlayerRace( + data.mRace, + data.isMale(), + data.mHead, + data.mHair + ); + } + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + } + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); + } + + void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) + { + if (mRaceDialog) + { + const ESM::NPC &data = mRaceDialog->getResult(); + mPlayerRaceId = data.mRace; + if (!mPlayerRaceId.empty()) { + MWBase::Environment::get().getMechanicsManager()->setPlayerRace( + data.mRace, + data.isMale(), + data.mHead, + data.mHair + ); + } + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); + + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + } + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_RaceChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + else + { + mCreationStage = CSE_RaceChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) + { + if (mBirthSignDialog) + { + mPlayerBirthSignId = mBirthSignDialog->getBirthId(); + if (!mPlayerBirthSignId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; + } + + if (mCreationStage >= CSE_BirthSignChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else + { + mCreationStage = CSE_BirthSignChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onBirthSignDialogBack() + { + if (mBirthSignDialog) + { + MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; + } + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + + void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) + { + if (mCreateClassDialog) + { + ESM::Class klass; + klass.mName = mCreateClassDialog->getName(); + klass.mDescription = mCreateClassDialog->getDescription(); + klass.mData.mSpecialization = mCreateClassDialog->getSpecializationId(); + klass.mData.mIsPlayable = 0x1; + + std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); + assert(attributes.size() == 2); + klass.mData.mAttribute[0] = attributes[0]; + klass.mData.mAttribute[1] = attributes[1]; + + std::vector majorSkills = mCreateClassDialog->getMajorSkills(); + std::vector minorSkills = mCreateClassDialog->getMinorSkills(); + assert(majorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); + assert(minorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); + for (size_t i = 0; i < sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]); ++i) + { + klass.mData.mSkills[i][1] = majorSkills[i]; + klass.mData.mSkills[i][0] = minorSkills[i]; + } + + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); + mPlayerClass = klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); + + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; + } + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + else + { + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onCreateClassDialogBack() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + + void CharacterCreation::onClassQuestionChosen(int _index) + { + MWBase::Environment::get().getSoundManager()->stopSay(); + + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + + if (_index < 0 || _index >= 3) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + return; + } + + ESM::Class::Specialization specialization = mSpecializations[_index]; + if (specialization == ESM::Class::Stealth) + ++mGenerateClassSpecializations[0]; + else if (specialization == ESM::Class::Combat) + ++mGenerateClassSpecializations[1]; + else if (specialization == ESM::Class::Magic) + ++mGenerateClassSpecializations[2]; + ++mGenerateClassStep; + showClassQuestionDialog(); + } + + void CharacterCreation::showClassQuestionDialog() + { + if (mGenerateClassStep == 10) + { + static boost::array classes = { { + {"Acrobat", {6, 2, 2}}, + {"Agent", {6, 1, 3}}, + {"Archer", {3, 5, 2}}, + {"Archer", {5, 5, 0}}, + {"Assassin", {6, 3, 1}}, + {"Barbarian", {3, 6, 1}}, + {"Bard", {3, 3, 3}}, + {"Battlemage", {1, 3, 6}}, + {"Crusader", {1, 6, 3}}, + {"Healer", {3, 1, 6}}, + {"Knight", {2, 6, 2}}, + {"Monk", {5, 3, 2}}, + {"Nightblade", {4, 2, 4}}, + {"Pilgrim", {5, 2, 3}}, + {"Rogue", {3, 4, 3}}, + {"Rogue", {4, 4, 2}}, + {"Rogue", {5, 4, 1}}, + {"Scout", {2, 5, 3}}, + {"Sorcerer", {2, 2, 6}}, + {"Spellsword", {2, 4, 4}}, + {"Spellsword", {5, 1, 4}}, + {"Witchhunter", {2, 3, 5}}, + {"Witchhunter", {5, 0, 5}} + } }; + + int match = -1; + for (unsigned i = 0; i < classes.size(); ++i) + { + if (mGenerateClassSpecializations[0] == classes[i].points[0] && + mGenerateClassSpecializations[1] == classes[i].points[1] && + mGenerateClassSpecializations[2] == classes[i].points[2]) + { + match = i; + mGenerateClass = classes[i].id; + break; + } + } + + if (match == -1) + { + if (mGenerateClassSpecializations[0] >= 7) + mGenerateClass = "Thief"; + else if (mGenerateClassSpecializations[1] >= 7) + mGenerateClass = "Warrior"; + else if (mGenerateClassSpecializations[2] >= 7) + mGenerateClass = "Mage"; + else + { + std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; + mGenerateClass = "Thief"; + } + } + + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + + mGenerateClassResultDialog = new GenerateClassResultDialog(); + mGenerateClassResultDialog->setClassId(mGenerateClass); + mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); + mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); + mGenerateClassResultDialog->setVisible(true); + return; + } + + if (mGenerateClassStep > 10) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + return; + } + + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + + mGenerateClassQuestionDialog = new InfoBoxDialog(); + + InfoBoxDialog::ButtonList buttons; + mGenerateClassQuestionDialog->setText(sGenerateClassSteps(mGenerateClassStep).mText); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[0]); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[1]); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[2]); + mGenerateClassQuestionDialog->setButtons(buttons); + mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); + mGenerateClassQuestionDialog->setVisible(true); + + MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); + } + + void CharacterCreation::onGenerateClassBack() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); mGenerateClassResultDialog = 0; - mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM); - mGenerateClassResultDialog->setClassId(mGenerateClass); - mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); - mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); - mGenerateClassResultDialog->setVisible(true); - return; + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } - if (mGenerateClassStep > 10) + void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); - return; + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); + + const ESM::Class *klass = + MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); + + mPlayerClass = *klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + else + { + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } } - mWM->removeDialog(mGenerateClassQuestionDialog); - mGenerateClassQuestionDialog = 0; - - mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); - - InfoBoxDialog::ButtonList buttons; - mGenerateClassQuestionDialog->setText(sGenerateClassSteps(mGenerateClassStep).mText); - buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[0]); - buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[1]); - buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[2]); - mGenerateClassQuestionDialog->setButtons(buttons); - mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); - mGenerateClassQuestionDialog->setVisible(true); - - MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); -} - -void CharacterCreation::onGenerateClassBack() -{ - mWM->removeDialog(mGenerateClassResultDialog); - mGenerateClassResultDialog = 0; - - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} - -void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) -{ - mWM->removeDialog(mGenerateClassResultDialog); - mGenerateClassResultDialog = 0; - - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - - const ESM::Class *klass = - MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); - - mPlayerClass = *klass; - mWM->setPlayerClass(mPlayerClass); - - if (mCreationStage == CSE_ReviewNext) + CharacterCreation::~CharacterCreation() { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + delete mNameDialog; + delete mRaceDialog; + delete mClassChoiceDialog; + delete mGenerateClassQuestionDialog; + delete mGenerateClassResultDialog; + delete mPickClassDialog; + delete mCreateClassDialog; + delete mBirthSignDialog; + delete mReviewDialog; } - else if (mCreationStage >= CSE_ClassChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); - } - else - { - mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); - } -} -CharacterCreation::~CharacterCreation() -{ - delete mNameDialog; - delete mRaceDialog; - delete mClassChoiceDialog; - delete mGenerateClassQuestionDialog; - delete mGenerateClassResultDialog; - delete mPickClassDialog; - delete mCreateClassDialog; - delete mBirthSignDialog; - delete mReviewDialog; } 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..ab81038689 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -1,11 +1,6 @@ #include "class.hpp" -#include - #include -#include - -#include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -16,879 +11,873 @@ #undef min #undef max -using namespace MWGui; - -/* GenerateClassResultDialog */ - -GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_generate_class_result.layout", parWindowManager) +namespace MWGui { - // Centre dialog - center(); - setText("ReflectT", mWindowManager.getGameSettingString("sMessageQuestionAnswer1", "")); + /* GenerateClassResultDialog */ - getWidget(mClassImage, "ClassImage"); - getWidget(mClassName, "ClassName"); - - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); -} - -std::string GenerateClassResultDialog::getClassId() const -{ - return mClassName->getCaption(); -} - -void GenerateClassResultDialog::setClassId(const std::string &classId) -{ - mCurrentClassId = classId; - mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); - mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().get().find(mCurrentClassId)->mName); -} - -// widget controls - -void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); -} - -void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -/* PickClassDialog */ - -PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_class.layout", parWindowManager) -{ - // Centre dialog - center(); - - getWidget(mSpecializationName, "SpecializationName"); - - getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); - getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); - mFavoriteAttribute[0]->setWindowManager(&mWindowManager); - mFavoriteAttribute[1]->setWindowManager(&mWindowManager); - - for(int i = 0; i < 5; i++) + GenerateClassResultDialog::GenerateClassResultDialog() + : WindowModal("openmw_chargen_generate_class_result.layout") { - 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); + // Centre dialog + center(); + + setText("ReflectT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sMessageQuestionAnswer1", "")); + + getWidget(mClassImage, "ClassImage"); + getWidget(mClassName, "ClassName"); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); } - getWidget(mClassList, "ClassList"); - mClassList->setScrollVisible(true); - mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - - getWidget(mClassImage, "ClassImage"); - - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); - - updateClasses(); - updateStats(); -} - -void PickClassDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} - -void PickClassDialog::open() -{ - WindowModal::open (); - updateClasses(); - updateStats(); -} - - -void PickClassDialog::setClassId(const std::string &classId) -{ - mCurrentClassId = classId; - mClassList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mClassList->getItemCount(); - for (size_t i = 0; i < count; ++i) + std::string GenerateClassResultDialog::getClassId() const { - if (boost::iequals(*mClassList->getItemDataAt(i), classId)) + return mClassName->getCaption(); + } + + void GenerateClassResultDialog::setClassId(const std::string &classId) + { + mCurrentClassId = classId; + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); + mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().get().find(mCurrentClassId)->mName); + } + + // widget controls + + void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); + } + + void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + /* PickClassDialog */ + + PickClassDialog::PickClassDialog() + : WindowModal("openmw_chargen_class.layout") + { + // Centre dialog + center(); + + getWidget(mSpecializationName, "SpecializationName"); + + getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); + getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); + + for(int i = 0; i < 5; i++) { - mClassList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - break; + char theIndex = '0'+i; + getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); + getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); } + + getWidget(mClassList, "ClassList"); + mClassList->setScrollVisible(true); + mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + + getWidget(mClassImage, "ClassImage"); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); + + updateClasses(); + updateStats(); } - updateStats(); -} - -// widget controls - -void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} - -void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - const std::string *classId = mClassList->getItemDataAt(_index); - if (boost::iequals(mCurrentClassId, *classId)) - return; - - mCurrentClassId = *classId; - updateStats(); -} - -// update widget content - -void PickClassDialog::updateClasses() -{ - mClassList->removeAllItems(); - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - - int index = 0; - MWWorld::Store::iterator it = store.get().begin(); - for (; it != store.get().end(); ++it) + void PickClassDialog::setNextButtonShow(bool shown) { - bool playable = (it->mData.mIsPlayable != 0); - if (!playable) // Only display playable classes - continue; + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); - const std::string &id = it->mId; - mClassList->addItem(it->mName, id); - if (mCurrentClassId.empty()) + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void PickClassDialog::open() + { + WindowModal::open (); + updateClasses(); + updateStats(); + } + + + void PickClassDialog::setClassId(const std::string &classId) + { + mCurrentClassId = classId; + mClassList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mClassList->getItemCount(); + for (size_t i = 0; i < count; ++i) { - mCurrentClassId = id; - mClassList->setIndexSelected(index); + if (boost::iequals(*mClassList->getItemDataAt(i), classId)) + { + mClassList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } } - else if (boost::iequals(id, mCurrentClassId)) - { - mClassList->setIndexSelected(index); - } - ++index; - } -} -void PickClassDialog::updateStats() -{ - if (mCurrentClassId.empty()) - return; - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Class *klass = store.get().search(mCurrentClassId); - if (!klass) - return; - - ESM::Class::Specialization specialization = static_cast(klass->mData.mSpecialization); - - static const char *specIds[3] = { - "sSpecializationCombat", - "sSpecializationMagic", - "sSpecializationStealth" - }; - std::string specName = mWindowManager.getGameSettingString(specIds[specialization], specIds[specialization]); - mSpecializationName->setCaption(specName); - ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); - - mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]); - mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); - ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); - ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); - - for (int i = 0; i < 5; ++i) - { - mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]); - mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]); - ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]); - ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]); + updateStats(); } - mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); -} + // widget controls -/* InfoBoxDialog */ - -void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) -{ - MyGUI::IntCoord inner = widget->getTextRegion(); - MyGUI::IntCoord outer = widget->getCoord(); - MyGUI::IntSize size = widget->getTextSize(); - size.width += outer.width - inner.width; - size.height += outer.height - inner.height; - widget->setSize(size); -} - -void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin) -{ - size_t count = widget->getChildCount(); - int pos = 0; - pos += margin; - int width = 0; - for (unsigned i = 0; i < count; ++i) + void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) { - MyGUI::Widget* child = widget->getChildAt(i); - if (!child->getVisible()) - continue; - - child->setPosition(child->getLeft(), pos); - width = std::max(width, child->getWidth()); - pos += child->getHeight() + margin; - } - width += margin*2; - widget->setSize(width, pos); -} - -InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_infobox.layout", parWindowManager) - , mCurrentButton(-1) -{ - getWidget(mTextBox, "TextBox"); - getWidget(mText, "Text"); - mText->getSubWidgetText()->setWordWrap(true); - getWidget(mButtonBar, "ButtonBar"); - - center(); -} - -void InfoBoxDialog::setText(const std::string &str) -{ - mText->setCaption(str); - mTextBox->setVisible(!str.empty()); - fitToText(mText); -} - -std::string InfoBoxDialog::getText() const -{ - return mText->getCaption(); -} - -void InfoBoxDialog::setButtons(ButtonList &buttons) -{ - for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - this->mButtons.clear(); - mCurrentButton = -1; - - // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget - MyGUI::Button* button; - MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10); - ButtonList::const_iterator end = buttons.end(); - for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) - { - const std::string &text = *it; - button = mButtonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); - button->getSubWidgetText()->setWordWrap(true); - button->setCaption(text); - fitToText(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); - coord.top += button->getHeight(); - this->mButtons.push_back(button); - } -} - -void InfoBoxDialog::open() -{ - WindowModal::open(); - // Fix layout - layoutVertically(mTextBox, 4); - layoutVertically(mButtonBar, 6); - layoutVertically(mMainWidget, 4 + 6); - - center(); -} - -int InfoBoxDialog::getChosenButton() const -{ - return mCurrentButton; -} - -void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender) -{ - std::vector::const_iterator end = mButtons.end(); - int i = 0; - for (std::vector::const_iterator it = mButtons.begin(); it != end; ++it) - { - if (*it == _sender) - { - mCurrentButton = i; - eventButtonSelected(i); + if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) return; - } - ++i; - } -} - -/* ClassChoiceDialog */ - -ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager) - : InfoBoxDialog(parWindowManager) -{ - 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", "")); - setButtons(buttons); -} - -/* CreateClassDialog */ - -CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_create_class.layout", parWindowManager) - , mSpecDialog(NULL) - , mAttribDialog(NULL) - , mSkillDialog(NULL) - , mDescDialog(NULL) -{ - // Centre dialog - center(); - - setText("SpecializationT", mWindowManager.getGameSettingString("sChooseClassMenu1", "Specialization")); - getWidget(mSpecializationName, "SpecializationName"); - mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); - - setText("FavoriteAttributesT", mWindowManager.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", "")); - 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)); - mSkills.push_back(mMajorSkill[i]); - mSkills.push_back(mMinorSkill[i]); + eventDone(this); } - std::vector::const_iterator end = mSkills.end(); - for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) { - (*it)->setWindowManager(&mWindowManager); - (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); + eventBack(); } - setText("LabelT", mWindowManager.getGameSettingString("sName", "")); - getWidget(mEditName, "EditName"); - - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName); - - MyGUI::Button* descriptionButton; - getWidget(descriptionButton, "DescriptionButton"); - descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); - - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked); - - // Set default skills, attributes - - mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength); - mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility); - - mMajorSkill[0]->setSkillId(ESM::Skill::Block); - mMajorSkill[1]->setSkillId(ESM::Skill::Armorer); - mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor); - mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); - mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); - - mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade); - mMinorSkill[1]->setSkillId(ESM::Skill::Axe); - mMinorSkill[2]->setSkillId(ESM::Skill::Spear); - mMinorSkill[3]->setSkillId(ESM::Skill::Athletics); - mMinorSkill[4]->setSkillId(ESM::Skill::Enchant); - - setSpecialization(0); - update(); -} - -CreateClassDialog::~CreateClassDialog() -{ - delete mSpecDialog; - delete mAttribDialog; - delete mSkillDialog; - delete mDescDialog; -} - -void CreateClassDialog::update() -{ - for (int i = 0; i < 5; ++i) + void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) { - ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId()); - ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId()); + if (_index == MyGUI::ITEM_NONE) + return; + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + const std::string *classId = mClassList->getItemDataAt(_index); + if (boost::iequals(mCurrentClassId, *classId)) + return; + + mCurrentClassId = *classId; + updateStats(); } - ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId()); - ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId()); -} + // update widget content -std::string CreateClassDialog::getName() const -{ - return mEditName->getOnlyText(); -} - -std::string CreateClassDialog::getDescription() const -{ - return mDescription; -} - -ESM::Class::Specialization CreateClassDialog::getSpecializationId() const -{ - return mSpecializationId; -} - -std::vector CreateClassDialog::getFavoriteAttributes() const -{ - std::vector v; - v.push_back(mFavoriteAttribute0->getAttributeId()); - v.push_back(mFavoriteAttribute1->getAttributeId()); - return v; -} - -std::vector CreateClassDialog::getMajorSkills() const -{ - std::vector v; - for(int i = 0; i < 5; i++) + void PickClassDialog::updateClasses() { - v.push_back(mMajorSkill[i]->getSkillId()); - } - return v; -} + mClassList->removeAllItems(); -std::vector CreateClassDialog::getMinorSkills() const -{ - std::vector v; - for(int i=0; i < 5; i++) - { - v.push_back(mMinorSkill[i]->getSkillId()); - } - return v; -} + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); -void CreateClassDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} - -// widget controls - -void CreateClassDialog::onDialogCancel() -{ - mWindowManager.removeDialog(mSpecDialog); - mSpecDialog = 0; - - mWindowManager.removeDialog(mAttribDialog); - mAttribDialog = 0; - - mWindowManager.removeDialog(mSkillDialog); - mSkillDialog = 0; - - mWindowManager.removeDialog(mDescDialog); - mDescDialog = 0; -} - -void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender) -{ - delete mSpecDialog; - mSpecDialog = new SelectSpecializationDialog(mWindowManager); - mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); - mSpecDialog->setVisible(true); -} - -void CreateClassDialog::onSpecializationSelected() -{ - mSpecializationId = mSpecDialog->getSpecializationId(); - setSpecialization(mSpecializationId); - - mWindowManager.removeDialog(mSpecDialog); - mSpecDialog = 0; -} - -void CreateClassDialog::setSpecialization(int id) -{ - mSpecializationId = (ESM::Class::Specialization) id; - static const char *specIds[3] = { - "sSpecializationCombat", - "sSpecializationMagic", - "sSpecializationStealth" - }; - std::string specName = mWindowManager.getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); - mSpecializationName->setCaption(specName); - ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); -} - -void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) -{ - delete mAttribDialog; - mAttribDialog = new SelectAttributeDialog(mWindowManager); - mAffectedAttribute = _sender; - mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); - mAttribDialog->setVisible(true); -} - -void CreateClassDialog::onAttributeSelected() -{ - ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); - if (mAffectedAttribute == mFavoriteAttribute0) - { - if (mFavoriteAttribute1->getAttributeId() == id) - mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); - } - else if (mAffectedAttribute == mFavoriteAttribute1) - { - if (mFavoriteAttribute0->getAttributeId() == id) - mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); - } - mAffectedAttribute->setAttributeId(id); - mWindowManager.removeDialog(mAttribDialog); - mAttribDialog = 0; - - update(); -} - -void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) -{ - delete mSkillDialog; - mSkillDialog = new SelectSkillDialog(mWindowManager); - mAffectedSkill = _sender; - mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); - mSkillDialog->setVisible(true); -} - -void CreateClassDialog::onSkillSelected() -{ - ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); - - // Avoid duplicate skills by swapping any skill field that matches the selected one - std::vector::const_iterator end = mSkills.end(); - for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) - { - if (*it == mAffectedSkill) - continue; - if ((*it)->getSkillId() == id) + int index = 0; + MWWorld::Store::iterator it = store.get().begin(); + for (; it != store.get().end(); ++it) { - (*it)->setSkillId(mAffectedSkill->getSkillId()); - break; + bool playable = (it->mData.mIsPlayable != 0); + if (!playable) // Only display playable classes + continue; + + const std::string &id = it->mId; + mClassList->addItem(it->mName, id); + if (mCurrentClassId.empty()) + { + mCurrentClassId = id; + mClassList->setIndexSelected(index); + } + else if (boost::iequals(id, mCurrentClassId)) + { + mClassList->setIndexSelected(index); + } + ++index; } } - mAffectedSkill->setSkillId(mSkillDialog->getSkillId()); - mWindowManager.removeDialog(mSkillDialog); - mSkillDialog = 0; - update(); -} + void PickClassDialog::updateStats() + { + if (mCurrentClassId.empty()) + return; + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Class *klass = store.get().search(mCurrentClassId); + if (!klass) + return; -void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) -{ - mDescDialog = new DescriptionDialog(mWindowManager); - mDescDialog->setTextInput(mDescription); - mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); - mDescDialog->setVisible(true); -} + ESM::Class::Specialization specialization = static_cast(klass->mData.mSpecialization); -void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) -{ - mDescription = mDescDialog->getTextInput(); - mWindowManager.removeDialog(mDescDialog); - mDescDialog = 0; -} + static const char *specIds[3] = { + "sSpecializationCombat", + "sSpecializationMagic", + "sSpecializationStealth" + }; + std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[specialization], specIds[specialization]); + mSpecializationName->setCaption(specName); + ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); -void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(getName().size() <= 0) - return; - eventDone(this); -} + mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]); + mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); + ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); -void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + for (int i = 0; i < 5; ++i) + { + mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]); + mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]); + ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]); + ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]); + } -/* SelectSpecializationDialog */ + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); + } -SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_specialization.layout", parWindowManager) -{ - // Centre dialog - center(); + /* InfoBoxDialog */ - setText("LabelT", mWindowManager.getGameSettingString("sSpecializationMenu1", "")); + void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) + { + MyGUI::IntCoord inner = widget->getTextRegion(); + MyGUI::IntCoord outer = widget->getCoord(); + MyGUI::IntSize size = widget->getTextSize(); + size.width += outer.width - inner.width; + size.height += outer.height - inner.height; + widget->setSize(size); + } - 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], ""); + void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin) + { + size_t count = widget->getChildCount(); + int pos = 0; + pos += margin; + int width = 0; + for (unsigned i = 0; i < count; ++i) + { + MyGUI::Widget* child = widget->getChildAt(i); + if (!child->getVisible()) + continue; - mSpecialization0->setCaption(combat); - mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecialization1->setCaption(magic); - mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecialization2->setCaption(stealth); - mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecializationId = ESM::Class::Combat; + child->setPosition(child->getLeft(), pos); + width = std::max(width, child->getWidth()); + pos += child->getHeight() + margin; + } + width += margin*2; + widget->setSize(width, pos); + } - ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat); - ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic); - ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth); + InfoBoxDialog::InfoBoxDialog() + : WindowModal("openmw_infobox.layout") + , mCurrentButton(-1) + { + getWidget(mTextBox, "TextBox"); + getWidget(mText, "Text"); + mText->getSubWidgetText()->setWordWrap(true); + getWidget(mButtonBar, "ButtonBar"); - MyGUI::Button* cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); -} + center(); + } -SelectSpecializationDialog::~SelectSpecializationDialog() -{ -} + void InfoBoxDialog::setText(const std::string &str) + { + mText->setCaption(str); + mTextBox->setVisible(!str.empty()); + fitToText(mText); + } -// widget controls + std::string InfoBoxDialog::getText() const + { + return mText->getCaption(); + } -void SelectSpecializationDialog::onSpecializationClicked(MyGUI::Widget* _sender) -{ - if (_sender == mSpecialization0) + void InfoBoxDialog::setButtons(ButtonList &buttons) + { + for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + this->mButtons.clear(); + mCurrentButton = -1; + + // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget + MyGUI::Button* button; + MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10); + ButtonList::const_iterator end = buttons.end(); + for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) + { + const std::string &text = *it; + button = mButtonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); + button->getSubWidgetText()->setWordWrap(true); + button->setCaption(text); + fitToText(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); + coord.top += button->getHeight(); + this->mButtons.push_back(button); + } + } + + void InfoBoxDialog::open() + { + WindowModal::open(); + // Fix layout + layoutVertically(mTextBox, 4); + layoutVertically(mButtonBar, 6); + layoutVertically(mMainWidget, 4 + 6); + + center(); + } + + int InfoBoxDialog::getChosenButton() const + { + return mCurrentButton; + } + + void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender) + { + std::vector::const_iterator end = mButtons.end(); + int i = 0; + for (std::vector::const_iterator it = mButtons.begin(); it != end; ++it) + { + if (*it == _sender) + { + mCurrentButton = i; + eventButtonSelected(i); + return; + } + ++i; + } + } + + /* ClassChoiceDialog */ + + ClassChoiceDialog::ClassChoiceDialog() + : InfoBoxDialog() + { + setText(""); + ButtonList buttons; + 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() + : WindowModal("openmw_chargen_create_class.layout") + , mSpecDialog(NULL) + , mAttribDialog(NULL) + , mSkillDialog(NULL) + , mDescDialog(NULL) + { + // Centre dialog + center(); + + setText("SpecializationT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu1", "Specialization")); + getWidget(mSpecializationName, "SpecializationName"); + mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); + + setText("FavoriteAttributesT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); + getWidget(mFavoriteAttribute0, "FavoriteAttribute0"); + getWidget(mFavoriteAttribute1, "FavoriteAttribute1"); + mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + + 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; + getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); + getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); + mSkills.push_back(mMajorSkill[i]); + mSkills.push_back(mMinorSkill[i]); + } + + std::vector::const_iterator end = mSkills.end(); + for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + { + (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); + } + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "")); + getWidget(mEditName, "EditName"); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName); + + MyGUI::Button* descriptionButton; + getWidget(descriptionButton, "DescriptionButton"); + descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked); + + // Set default skills, attributes + + mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength); + mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility); + + mMajorSkill[0]->setSkillId(ESM::Skill::Block); + mMajorSkill[1]->setSkillId(ESM::Skill::Armorer); + mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor); + mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); + mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); + + mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade); + mMinorSkill[1]->setSkillId(ESM::Skill::Axe); + mMinorSkill[2]->setSkillId(ESM::Skill::Spear); + mMinorSkill[3]->setSkillId(ESM::Skill::Athletics); + mMinorSkill[4]->setSkillId(ESM::Skill::Enchant); + + setSpecialization(0); + update(); + } + + CreateClassDialog::~CreateClassDialog() + { + delete mSpecDialog; + delete mAttribDialog; + delete mSkillDialog; + delete mDescDialog; + } + + void CreateClassDialog::update() + { + for (int i = 0; i < 5; ++i) + { + ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId()); + ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId()); + } + + ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId()); + } + + std::string CreateClassDialog::getName() const + { + return mEditName->getOnlyText(); + } + + std::string CreateClassDialog::getDescription() const + { + return mDescription; + } + + ESM::Class::Specialization CreateClassDialog::getSpecializationId() const + { + return mSpecializationId; + } + + std::vector CreateClassDialog::getFavoriteAttributes() const + { + std::vector v; + v.push_back(mFavoriteAttribute0->getAttributeId()); + v.push_back(mFavoriteAttribute1->getAttributeId()); + return v; + } + + std::vector CreateClassDialog::getMajorSkills() const + { + std::vector v; + for(int i = 0; i < 5; i++) + { + v.push_back(mMajorSkill[i]->getSkillId()); + } + return v; + } + + std::vector CreateClassDialog::getMinorSkills() const + { + std::vector v; + for(int i=0; i < 5; i++) + { + v.push_back(mMinorSkill[i]->getSkillId()); + } + return v; + } + + void CreateClassDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + // widget controls + + void CreateClassDialog::onDialogCancel() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); + mSpecDialog = 0; + + MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); + mAttribDialog = 0; + + MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); + mSkillDialog = 0; + + MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); + mDescDialog = 0; + } + + void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender) + { + delete mSpecDialog; + mSpecDialog = new SelectSpecializationDialog(); + mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); + mSpecDialog->setVisible(true); + } + + void CreateClassDialog::onSpecializationSelected() + { + mSpecializationId = mSpecDialog->getSpecializationId(); + setSpecialization(mSpecializationId); + + MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); + mSpecDialog = 0; + } + + void CreateClassDialog::setSpecialization(int id) + { + mSpecializationId = (ESM::Class::Specialization) id; + static const char *specIds[3] = { + "sSpecializationCombat", + "sSpecializationMagic", + "sSpecializationStealth" + }; + std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); + mSpecializationName->setCaption(specName); + ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); + } + + void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) + { + delete mAttribDialog; + mAttribDialog = new SelectAttributeDialog(); + mAffectedAttribute = _sender; + mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); + mAttribDialog->setVisible(true); + } + + void CreateClassDialog::onAttributeSelected() + { + ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); + if (mAffectedAttribute == mFavoriteAttribute0) + { + if (mFavoriteAttribute1->getAttributeId() == id) + mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); + } + else if (mAffectedAttribute == mFavoriteAttribute1) + { + if (mFavoriteAttribute0->getAttributeId() == id) + mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); + } + mAffectedAttribute->setAttributeId(id); + MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); + mAttribDialog = 0; + + update(); + } + + void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) + { + delete mSkillDialog; + mSkillDialog = new SelectSkillDialog(); + mAffectedSkill = _sender; + mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); + mSkillDialog->setVisible(true); + } + + void CreateClassDialog::onSkillSelected() + { + ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); + + // Avoid duplicate skills by swapping any skill field that matches the selected one + std::vector::const_iterator end = mSkills.end(); + for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + { + if (*it == mAffectedSkill) + continue; + if ((*it)->getSkillId() == id) + { + (*it)->setSkillId(mAffectedSkill->getSkillId()); + break; + } + } + + mAffectedSkill->setSkillId(mSkillDialog->getSkillId()); + MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); + mSkillDialog = 0; + update(); + } + + void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) + { + mDescDialog = new DescriptionDialog(); + mDescDialog->setTextInput(mDescription); + mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); + mDescDialog->setVisible(true); + } + + void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) + { + mDescription = mDescDialog->getTextInput(); + MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); + mDescDialog = 0; + } + + void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(getName().size() <= 0) + return; + eventDone(this); + } + + void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + /* SelectSpecializationDialog */ + + SelectSpecializationDialog::SelectSpecializationDialog() + : WindowModal("openmw_chargen_select_specialization.layout") + { + // Centre dialog + center(); + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMenu1", "")); + + getWidget(mSpecialization0, "Specialization0"); + getWidget(mSpecialization1, "Specialization1"); + getWidget(mSpecialization2, "Specialization2"); + 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); + mSpecialization1->setCaption(magic); + mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + mSpecialization2->setCaption(stealth); + mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); mSpecializationId = ESM::Class::Combat; - else if (_sender == mSpecialization1) - mSpecializationId = ESM::Class::Magic; - else if (_sender == mSpecialization2) - mSpecializationId = ESM::Class::Stealth; - else - return; - eventItemSelected(); -} + ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat); + ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic); + ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth); -void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} - -/* SelectAttributeDialog */ - -SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_attribute.layout", parWindowManager) -{ - // Centre dialog - center(); - - setText("LabelT", mWindowManager.getGameSettingString("sAttributesMenu1", "")); - - for (int i = 0; i < 8; ++i) - { - Widgets::MWAttributePtr attribute; - 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()); + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); } - MyGUI::Button* cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); -} - -SelectAttributeDialog::~SelectAttributeDialog() -{ -} - -// widget controls - -void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) -{ - // TODO: Change MWAttribute to set and get AttributeID enum instead of int - mAttributeId = static_cast(_sender->getAttributeId()); - eventItemSelected(); -} - -void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} - - -/* SelectSkillDialog */ - -SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_skill.layout", parWindowManager) -{ - // Centre dialog - center(); - - setText("LabelT", mWindowManager.getGameSettingString("sSkillsMenu1", "")); - setText("CombatLabelT", mWindowManager.getGameSettingString("sSpecializationCombat", "")); - setText("MagicLabelT", mWindowManager.getGameSettingString("sSpecializationMagic", "")); - setText("StealthLabelT", mWindowManager.getGameSettingString("sSpecializationStealth", "")); - - for(int i = 0; i < 9; i++) + SelectSpecializationDialog::~SelectSpecializationDialog() { - char theIndex = '0'+i; - getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); - getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); - getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); } - struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = { + // widget controls + + void SelectSpecializationDialog::onSpecializationClicked(MyGUI::Widget* _sender) + { + if (_sender == mSpecialization0) + mSpecializationId = ESM::Class::Combat; + else if (_sender == mSpecialization1) + mSpecializationId = ESM::Class::Magic; + else if (_sender == mSpecialization2) + mSpecializationId = ESM::Class::Stealth; + else + return; + + eventItemSelected(); + } + + void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } + + /* SelectAttributeDialog */ + + SelectAttributeDialog::SelectAttributeDialog() + : WindowModal("openmw_chargen_select_attribute.layout") + { + // Centre dialog + center(); + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sAttributesMenu1", "")); + + for (int i = 0; i < 8; ++i) { - {mCombatSkill[0], ESM::Skill::Block}, - {mCombatSkill[1], ESM::Skill::Armorer}, - {mCombatSkill[2], ESM::Skill::MediumArmor}, - {mCombatSkill[3], ESM::Skill::HeavyArmor}, - {mCombatSkill[4], ESM::Skill::BluntWeapon}, - {mCombatSkill[5], ESM::Skill::LongBlade}, - {mCombatSkill[6], ESM::Skill::Axe}, - {mCombatSkill[7], ESM::Skill::Spear}, - {mCombatSkill[8], ESM::Skill::Athletics} - }, - { - {mMagicSkill[0], ESM::Skill::Enchant}, - {mMagicSkill[1], ESM::Skill::Destruction}, - {mMagicSkill[2], ESM::Skill::Alteration}, - {mMagicSkill[3], ESM::Skill::Illusion}, - {mMagicSkill[4], ESM::Skill::Conjuration}, - {mMagicSkill[5], ESM::Skill::Mysticism}, - {mMagicSkill[6], ESM::Skill::Restoration}, - {mMagicSkill[7], ESM::Skill::Alchemy}, - {mMagicSkill[8], ESM::Skill::Unarmored} - }, - { - {mStealthSkill[0], ESM::Skill::Security}, - {mStealthSkill[1], ESM::Skill::Sneak}, - {mStealthSkill[2], ESM::Skill::Acrobatics}, - {mStealthSkill[3], ESM::Skill::LightArmor}, - {mStealthSkill[4], ESM::Skill::ShortBlade}, - {mStealthSkill[5] ,ESM::Skill::Marksman}, - {mStealthSkill[6] ,ESM::Skill::Mercantile}, - {mStealthSkill[7] ,ESM::Skill::Speechcraft}, - {mStealthSkill[8] ,ESM::Skill::HandToHand} + Widgets::MWAttributePtr attribute; + char theIndex = '0'+i; + + getWidget(attribute, std::string("Attribute").append(1, theIndex)); + attribute->setAttributeId(ESM::Attribute::sAttributeIds[i]); + attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); + ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId()); } - }; - for (int spec = 0; spec < 3; ++spec) - { - 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()); - } + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); + } + + SelectAttributeDialog::~SelectAttributeDialog() + { + } + + // widget controls + + void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) + { + // TODO: Change MWAttribute to set and get AttributeID enum instead of int + mAttributeId = static_cast(_sender->getAttributeId()); + eventItemSelected(); + } + + void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } + + + /* SelectSkillDialog */ + + SelectSkillDialog::SelectSkillDialog() + : WindowModal("openmw_chargen_select_skill.layout") + { + // Centre dialog + center(); + + 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++) + { + char theIndex = '0'+i; + getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); + getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); + getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); + } + + struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = { + { + {mCombatSkill[0], ESM::Skill::Block}, + {mCombatSkill[1], ESM::Skill::Armorer}, + {mCombatSkill[2], ESM::Skill::MediumArmor}, + {mCombatSkill[3], ESM::Skill::HeavyArmor}, + {mCombatSkill[4], ESM::Skill::BluntWeapon}, + {mCombatSkill[5], ESM::Skill::LongBlade}, + {mCombatSkill[6], ESM::Skill::Axe}, + {mCombatSkill[7], ESM::Skill::Spear}, + {mCombatSkill[8], ESM::Skill::Athletics} + }, + { + {mMagicSkill[0], ESM::Skill::Enchant}, + {mMagicSkill[1], ESM::Skill::Destruction}, + {mMagicSkill[2], ESM::Skill::Alteration}, + {mMagicSkill[3], ESM::Skill::Illusion}, + {mMagicSkill[4], ESM::Skill::Conjuration}, + {mMagicSkill[5], ESM::Skill::Mysticism}, + {mMagicSkill[6], ESM::Skill::Restoration}, + {mMagicSkill[7], ESM::Skill::Alchemy}, + {mMagicSkill[8], ESM::Skill::Unarmored} + }, + { + {mStealthSkill[0], ESM::Skill::Security}, + {mStealthSkill[1], ESM::Skill::Sneak}, + {mStealthSkill[2], ESM::Skill::Acrobatics}, + {mStealthSkill[3], ESM::Skill::LightArmor}, + {mStealthSkill[4], ESM::Skill::ShortBlade}, + {mStealthSkill[5] ,ESM::Skill::Marksman}, + {mStealthSkill[6] ,ESM::Skill::Mercantile}, + {mStealthSkill[7] ,ESM::Skill::Speechcraft}, + {mStealthSkill[8] ,ESM::Skill::HandToHand} + } + }; + + for (int spec = 0; spec < 3; ++spec) + { + for (int i = 0; i < 9; ++i) + { + 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()); + } + } + + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); + } + + SelectSkillDialog::~SelectSkillDialog() + { + } + + // widget controls + + void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) + { + mSkillId = _sender->getSkillId(); + eventItemSelected(); + } + + void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } + + /* DescriptionDialog */ + + DescriptionDialog::DescriptionDialog() + : WindowModal("openmw_chargen_class_description.layout") + { + // Centre dialog + center(); + + getWidget(mTextEdit, "TextEdit"); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sInputMenu1", "")); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + } + + DescriptionDialog::~DescriptionDialog() + { + } + + // widget controls + + void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); } - MyGUI::Button* cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); -} - -SelectSkillDialog::~SelectSkillDialog() -{ -} - -// widget controls - -void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) -{ - mSkillId = _sender->getSkillId(); - eventItemSelected(); -} - -void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} - -/* DescriptionDialog */ - -DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_class_description.layout", parWindowManager) -{ - // Centre dialog - center(); - - getWidget(mTextEdit, "TextEdit"); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); - okButton->setCaption(mWindowManager.getGameSettingString("sInputMenu1", "")); - - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} - -DescriptionDialog::~DescriptionDialog() -{ -} - -// widget controls - -void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); } 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/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp new file mode 100644 index 0000000000..3212ed7018 --- /dev/null +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -0,0 +1,34 @@ +#include "companionitemmodel.hpp" + +#include "../mwmechanics/npcstats.hpp" +#include "../mwworld/class.hpp" + +namespace MWGui +{ + CompanionItemModel::CompanionItemModel(const MWWorld::Ptr &actor) + : InventoryItemModel(actor) + { + } + + void CompanionItemModel::copyItem (const ItemStack& item, size_t count) + { + if (mActor.getTypeName() == typeid(ESM::NPC).name()) + { + MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); + stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); + } + + InventoryItemModel::copyItem(item, count); + } + + void CompanionItemModel::removeItem (const ItemStack& item, size_t count) + { + if (mActor.getTypeName() == typeid(ESM::NPC).name()) + { + MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); + stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); + } + + InventoryItemModel::removeItem(item, count); + } +} diff --git a/apps/openmw/mwgui/companionitemmodel.hpp b/apps/openmw/mwgui/companionitemmodel.hpp new file mode 100644 index 0000000000..c11e11c325 --- /dev/null +++ b/apps/openmw/mwgui/companionitemmodel.hpp @@ -0,0 +1,22 @@ +#ifndef MWGUI_COMPANION_ITEM_MODEL_H +#define MWGUI_COMPANION_ITEM_MODEL_H + +#include "inventoryitemmodel.hpp" + +namespace MWGui +{ + + /// @brief The companion item model keeps track of the companion's profit by + /// monitoring which items are being added to and removed from the model. + class CompanionItemModel : public InventoryItemModel + { + public: + CompanionItemModel (const MWWorld::Ptr& actor); + + virtual void copyItem (const ItemStack& item, size_t count); + virtual void removeItem (const ItemStack& item, size_t count); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp new file mode 100644 index 0000000000..9698608d69 --- /dev/null +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -0,0 +1,152 @@ +#include "companionwindow.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/dialoguemanager.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "../mwworld/class.hpp" + +#include "messagebox.hpp" +#include "itemview.hpp" +#include "sortfilteritemmodel.hpp" +#include "companionitemmodel.hpp" +#include "container.hpp" +#include "countdialog.hpp" + +namespace MWGui +{ + +CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* manager) + : WindowBase("openmw_companion_window.layout") + , mDragAndDrop(dragAndDrop) + , mMessageBoxManager(manager) + , mSelectedItem(-1) + , mModel(NULL) + , mSortModel(NULL) +{ + getWidget(mCloseButton, "CloseButton"); + getWidget(mProfitLabel, "ProfitLabel"); + getWidget(mEncumbranceBar, "EncumbranceBar"); + getWidget(mItemView, "ItemView"); + mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &CompanionWindow::onBackgroundSelected); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &CompanionWindow::onItemSelected); + + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CompanionWindow::onCloseButtonClicked); + + setCoord(200,0,600,300); +} + +void CompanionWindow::onItemSelected(int index) +{ + if (mDragAndDrop->mIsOnDragAndDrop) + { + mDragAndDrop->drop(mModel, mItemView); + updateEncumbranceBar(); + return; + } + + const ItemStack& item = mSortModel->getItem(index); + + MWWorld::Ptr object = item.mBase; + int count = item.mCount; + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); + if (MyGUI::InputManager::getInstance().isControlPressed()) + count = 1; + + mSelectedItem = mSortModel->mapToSource(index); + + if (count > 1 && !shift) + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + dialog->open(MWWorld::Class::get(object).getName(object), "#{sTake}", count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); + } + else + dragItem (NULL, count); +} + +void CompanionWindow::dragItem(MyGUI::Widget* sender, int count) +{ + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); +} + +void CompanionWindow::onBackgroundSelected() +{ + if (mDragAndDrop->mIsOnDragAndDrop) + { + mDragAndDrop->drop(mModel, mItemView); + updateEncumbranceBar(); + } +} + +void CompanionWindow::open(const MWWorld::Ptr& npc) +{ + mPtr = npc; + setTitle(MWWorld::Class::get(npc).getName(npc)); + updateEncumbranceBar(); + + mModel = new CompanionItemModel(npc); + mSortModel = new SortFilterItemModel(mModel); + mItemView->setModel(mSortModel); +} + +void CompanionWindow::onFrame() +{ + updateEncumbranceBar(); +} + +void CompanionWindow::updateEncumbranceBar() +{ + if (mPtr.isEmpty()) + return; + float capacity = MWWorld::Class::get(mPtr).getCapacity(mPtr); + float encumbrance = MWWorld::Class::get(mPtr).getEncumbrance(mPtr); + mEncumbranceBar->setValue(encumbrance, capacity); + + if (mPtr.getTypeName() != typeid(ESM::NPC).name()) + mProfitLabel->setCaption(""); + else + { + MWMechanics::NpcStats& stats = MWWorld::Class::get(mPtr).getNpcStats(mPtr); + mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + boost::lexical_cast(stats.getProfit())); + } +} + +void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender) +{ + if (mPtr.getTypeName() == typeid(ESM::NPC).name() && MWWorld::Class::get(mPtr).getNpcStats(mPtr).getProfit() < 0) + { + std::vector buttons; + buttons.push_back("#{sCompanionWarningButtonOne}"); + buttons.push_back("#{sCompanionWarningButtonTwo}"); + mMessageBoxManager->createInteractiveMessageBox("#{sCompanionWarningMessage}", buttons); + mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked); + } + else + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); +} + +void CompanionWindow::onMessageBoxButtonClicked(int button) +{ + if (button == 0) + { + mPtr.getRefData().getLocals().setVarByInt(MWWorld::Class::get(mPtr).getScript(mPtr), + "minimumProfit", MWWorld::Class::get(mPtr).getNpcStats(mPtr).getProfit()); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); + MWBase::Environment::get().getDialogueManager()->startDialogue (mPtr); + } +} + +void CompanionWindow::onReferenceUnavailable() +{ + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); +} + + + +} diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp new file mode 100644 index 0000000000..7fdfc069f3 --- /dev/null +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -0,0 +1,52 @@ +#ifndef OPENMW_MWGUI_COMPANIONWINDOW_H +#define OPENMW_MWGUI_COMPANIONWINDOW_H + +#include "widgets.hpp" +#include "windowbase.hpp" +#include "referenceinterface.hpp" + +namespace MWGui +{ + class MessageBoxManager; + class ItemView; + class DragAndDrop; + class SortFilterItemModel; + class CompanionItemModel; + + class CompanionWindow : public WindowBase, public ReferenceInterface + { + public: + CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager); + + void open(const MWWorld::Ptr& npc); + void onFrame (); + + private: + ItemView* mItemView; + SortFilterItemModel* mSortModel; + CompanionItemModel* mModel; + size_t mSelectedItem; + + DragAndDrop* mDragAndDrop; + + MyGUI::Button* mCloseButton; + MyGUI::TextBox* mProfitLabel; + Widgets::MWDynamicStat* mEncumbranceBar; + MessageBoxManager* mMessageBoxManager; + + void onItemSelected(int index); + void onBackgroundSelected(); + void dragItem(MyGUI::Widget* sender, int count); + + void onMessageBoxButtonClicked(int button); + + void updateEncumbranceBar(); + + void onCloseButtonClicked(MyGUI::Widget* _sender); + + virtual void onReferenceUnavailable(); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 31f4989e07..f431f2f64c 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -1,14 +1,9 @@ #include "confirmationdialog.hpp" -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - 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/console.cpp b/apps/openmw/mwgui/console.cpp index 1aebe57da2..b69d2d6af9 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -1,13 +1,7 @@ - #include "console.hpp" -#include -#include - #include -#include "../mwworld/esmstore.hpp" - #include "../mwscript/extensions.hpp" #include "../mwbase/environment.hpp" @@ -143,7 +137,7 @@ namespace MWGui void Console::disable() { setVisible(false); - setSelectedObject(MWWorld::Ptr()); + // Remove keyboard focus from the console input whenever the // console is turned off MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); @@ -284,16 +278,15 @@ namespace MWGui std::string Console::complete( std::string input, std::vector &matches ) { - using namespace std; - string output=input; - string tmp=input; + std::string output = input; + std::string tmp = input; bool has_front_quote = false; /* Does the input string contain things that don't have to be completed? If yes erase them. */ /* Are there quotation marks? */ - if( tmp.find('"') != string::npos ) { + if( tmp.find('"') != std::string::npos ) { int numquotes=0; - for(string::iterator it=tmp.begin(); it < tmp.end(); ++it) { + for(std::string::iterator it=tmp.begin(); it < tmp.end(); ++it) { if( *it == '"' ) numquotes++; } @@ -305,7 +298,7 @@ namespace MWGui } else { size_t pos; - if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) { + if( ( ((pos = tmp.rfind(' ')) != std::string::npos ) ) && ( pos > tmp.rfind('"') ) ) { tmp.erase( 0, tmp.rfind(' ')+1); } else { @@ -317,7 +310,7 @@ namespace MWGui /* No quotation marks. Are there spaces?*/ else { size_t rpos; - if( (rpos=tmp.rfind(' ')) != string::npos ) { + if( (rpos=tmp.rfind(' ')) != std::string::npos ) { if( rpos == 0 ) { tmp.clear(); } @@ -336,7 +329,7 @@ namespace MWGui } /* Iterate through the vector. */ - for(vector::iterator it=mNames.begin(); it < mNames.end();++it) { + for(std::vector::iterator it=mNames.begin(); it < mNames.end();++it) { bool string_different=false; /* Is the string shorter than the input string? If yes skip it. */ @@ -344,7 +337,7 @@ namespace MWGui continue; /* Is the beginning of the string different from the input string? If yes skip it. */ - for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { + for( std::string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { if( tolower(*iter) != tolower(*iter2) ) { string_different=true; break; @@ -367,24 +360,24 @@ namespace MWGui /* Only one match. We're done. */ if( matches.size() == 1 ) { /* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/ - if( ( matches.front().find(' ') != string::npos ) ) { + if( ( matches.front().find(' ') != std::string::npos ) ) { if( !has_front_quote ) - output.append(string("\"")); - return output.append(matches.front() + string("\" ")); + output.append(std::string("\"")); + return output.append(matches.front() + std::string("\" ")); } else if( has_front_quote ) { - return output.append(matches.front() + string("\" ")); + return output.append(matches.front() + std::string("\" ")); } else { - return output.append(matches.front() + string(" ")); + return output.append(matches.front() + std::string(" ")); } } /* Check if all matching strings match further than input. If yes complete to this match. */ int i = tmp.length(); - for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { - for(vector::iterator it=matches.begin(); it < matches.end();++it) { + for(std::string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { + for(std::vector::iterator it=matches.begin(); it < matches.end();++it) { if( tolower((*it)[i]) != tolower(*iter) ) { /* Append the longest match to the end of the output string*/ output.append(matches.front().substr( 0, i)); @@ -404,11 +397,24 @@ namespace MWGui void Console::setSelectedObject(const MWWorld::Ptr& object) { - mPtr = object; - if (!mPtr.isEmpty()) - setTitle("#{sConsoleTitle} (" + mPtr.getCellRef().mRefID + ")"); + if (!object.isEmpty()) + { + if (object == mPtr) + { + setTitle("#{sConsoleTitle}"); + mPtr=MWWorld::Ptr(); + } + else + { + setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")"); + mPtr = object; + } + } else + { setTitle("#{sConsoleTitle}"); + mPtr = MWWorld::Ptr(); + } MyGUI::InputManager::getInstance().setKeyFocusWidget(command); } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 2b80003127..85c2cf5fb1 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -1,11 +1,5 @@ #include "container.hpp" -#include -#include -#include -#include -#include - #include #include "../mwbase/environment.hpp" @@ -13,717 +7,269 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/containerstore.hpp" -#include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwclass/container.hpp" - -#include "widgets.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" #include "inventorywindow.hpp" -using namespace MWGui; -using namespace Widgets; - +#include "itemview.hpp" +#include "inventoryitemmodel.hpp" +#include "sortfilteritemmodel.hpp" +#include "pickpocketitemmodel.hpp" namespace { - bool compareType(std::string type1, std::string type2) + std::string getCountString(const int count) { - // this defines the sorting order of types. types that are first in the vector, appear before other types. - std::vector mapping; - mapping.push_back( typeid(ESM::Weapon).name() ); - mapping.push_back( typeid(ESM::Armor).name() ); - mapping.push_back( typeid(ESM::Clothing).name() ); - mapping.push_back( typeid(ESM::Potion).name() ); - mapping.push_back( typeid(ESM::Ingredient).name() ); - mapping.push_back( typeid(ESM::Apparatus).name() ); - mapping.push_back( typeid(ESM::Book).name() ); - mapping.push_back( typeid(ESM::Light).name() ); - mapping.push_back( typeid(ESM::Miscellaneous).name() ); - mapping.push_back( typeid(ESM::Tool).name() ); - mapping.push_back( typeid(ESM::Repair).name() ); - mapping.push_back( typeid(ESM::Probe).name() ); + if (count == 1) + return ""; + if (count > 9999) + return boost::lexical_cast(int(count/1000.f)) + "k"; + else + return boost::lexical_cast(count); + } +} - assert( std::find(mapping.begin(), mapping.end(), type1) != mapping.end() ); - assert( std::find(mapping.begin(), mapping.end(), type2) != mapping.end() ); +namespace MWGui +{ - return std::find(mapping.begin(), mapping.end(), type1) < std::find(mapping.begin(), mapping.end(), type2); + void DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count) + { + mItem = sourceModel->getItem(index); + mDraggedCount = count; + mSourceModel = sourceModel; + mSourceView = sourceView; + mSourceSortModel = sortModel; + mIsOnDragAndDrop = true; + mDragAndDropWidget->setVisible(true); + + std::string sound = MWWorld::Class::get(mItem.mBase).getUpSoundId(mItem.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + if (mSourceSortModel) + { + mSourceSortModel->clearDragItems(); + mSourceSortModel->addDragItem(mItem.mBase, count); + } + + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(mItem.mBase).getInventoryIcon(mItem.mBase); + MyGUI::ImageBox* baseWidget = mDragAndDropWidget->createWidget + ("ImageBox", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default); + mDraggedWidget = baseWidget; + MyGUI::ImageBox* image = baseWidget->createWidget("ImageBox", + MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture(path); + image->setNeedMouseFocus(false); + + // text widget that shows item count + MyGUI::TextBox* text = image->createWidget("SandBrightText", + MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); + text->setTextAlign(MyGUI::Align::Right); + text->setNeedMouseFocus(false); + text->setTextShadow(true); + text->setTextShadowColour(MyGUI::Colour(0,0,0)); + text->setCaption(getCountString(count)); + + sourceView->update(); + + MWBase::Environment::get().getWindowManager()->setDragDrop(true); } - bool sortItems(MWWorld::Ptr left, MWWorld::Ptr right) + void DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView) { - if (left.getTypeName() == right.getTypeName()) + std::string sound = MWWorld::Class::get(mItem.mBase).getDownSoundId(mItem.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + mDragAndDropWidget->setVisible(false); + + targetModel->copyItem(mItem, mDraggedCount); + mSourceModel->removeItem(mItem, mDraggedCount); + + mSourceModel->update(); + + finish(); + targetView->update(); + } + + void DragAndDrop::finish() + { + mIsOnDragAndDrop = false; + mSourceSortModel->clearDragItems(); + + MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); + mDraggedWidget = 0; + MWBase::Environment::get().getWindowManager()->setDragDrop(false); + } + + + ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) + : WindowBase("openmw_container_window.layout") + , mDragAndDrop(dragAndDrop) + , mSelectedItem(-1) + , mModel(NULL) + , mSortModel(NULL) + { + getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); + getWidget(mTakeButton, "TakeButton"); + getWidget(mCloseButton, "CloseButton"); + + getWidget(mItemView, "ItemView"); + mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &ContainerWindow::onBackgroundSelected); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &ContainerWindow::onItemSelected); + + mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); + + setCoord(200,0,600,300); + } + + void ContainerWindow::onItemSelected(int index) + { + if (mDragAndDrop->mIsOnDragAndDrop) { - int cmp = MWWorld::Class::get(left).getName(left).compare( - MWWorld::Class::get(right).getName(right)); - return cmp < 0; + if (!dynamic_cast(mModel)) + dropItem(); + return; + } + + const ItemStack& item = mSortModel->getItem(index); + + MWWorld::Ptr object = item.mBase; + int count = item.mCount; + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); + if (MyGUI::InputManager::getInstance().isControlPressed()) + count = 1; + + mSelectedItem = mSortModel->mapToSource(index); + + if (count > 1 && !shift) + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + dialog->open(MWWorld::Class::get(object).getName(object), "#{sTake}", count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } else - { - return compareType(left.getTypeName(), right.getTypeName()); - } + dragItem (NULL, count); } -} - -ContainerBase::ContainerBase(DragAndDrop* dragAndDrop) - : mDragAndDrop(dragAndDrop) - , mFilter(ContainerBase::Filter_All) - , mDisplayEquippedItems(true) - , mHighlightEquippedItems(true) -{ -} - -void ContainerBase::setWidgets(MyGUI::Widget* containerWidget, MyGUI::ScrollView* itemView) -{ - mContainerWidget = containerWidget; - mItemView = itemView; - - mContainerWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerBase::onContainerClicked); - mContainerWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerWindow::onMouseWheel); -} - -ContainerBase::~ContainerBase() -{ -} - -void ContainerBase::onSelectedItem(MyGUI::Widget* _sender) -{ - mSelectedItem = _sender; - - if (mDragAndDrop && !isTrading()) + void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) { - if(!mDragAndDrop->mIsOnDragAndDrop) - { - MWWorld::Ptr object = (*_sender->getUserData()); - int count = object.getRefData().getCount(); - - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) - { - startDragItem(_sender, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - startDragItem(_sender, 1); - } - else - { - std::string message = "#{sTake}"; - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::startDragItem); - } - } - else - onContainerClicked(mContainerWidget); + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } - else if (isTrading()) + + void ContainerWindow::dropItem() { - MWWorld::Ptr object = (*_sender->getUserData()); - int count = object.getRefData().getCount(); - - if (isInventory()) + if (mPtr.getTypeName() == typeid(ESM::Container).name()) { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - // the player is trying to sell an item, check if the merchant accepts it - // also, don't allow selling gold (let's be better than Morrowind at this, can we?) - if (!MWBase::Environment::get().getWindowManager()->getTradeWindow()->npcAcceptsItem(object) || - MWWorld::Class::get(object).getName(object) == gmst.find("sGold")->getString()) + // check that we don't exceed container capacity + MWWorld::Ptr item = mDragAndDrop->mItem.mBase; + float weight = MWWorld::Class::get(item).getWeight(item) * mDragAndDrop->mDraggedCount; + if (MWWorld::Class::get(mPtr).getCapacity(mPtr) < MWWorld::Class::get(mPtr).getEncumbrance(mPtr) + weight) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); + return; + } + + // check container organic flag + MWWorld::LiveCellRef* ref = mPtr.get(); + if (ref->mBase->mFlags & ESM::Container::Organic) { - // user notification "i don't buy this item" MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog4}", std::vector()); + messageBox("#{sContentsMessage2}"); return; } } - bool buying = isTradeWindow(); // buying or selling? - std::string message = buying ? "#{sQuanityMenuMessage02}" : "#{sQuanityMenuMessage01}"; + mDragAndDrop->drop(mModel, mItemView); + } - if (std::find(mBoughtItems.begin(), mBoughtItems.end(), object) != mBoughtItems.end()) + void ContainerWindow::onBackgroundSelected() + { + if (mDragAndDrop->mIsOnDragAndDrop && !dynamic_cast(mModel)) + dropItem(); + } + + void ContainerWindow::open(const MWWorld::Ptr& container, bool loot) + { + mPtr = container; + + if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) { - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) - { - sellAlreadyBoughtItem(NULL, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - sellAlreadyBoughtItem(NULL, 1); - } - else - { - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::sellAlreadyBoughtItem); - } + // we are stealing stuff + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + mModel = new PickpocketItemModel(player, new InventoryItemModel(container)); } else + mModel = new InventoryItemModel(container); + + mDisposeCorpseButton->setVisible(loot); + + setTitle(MWWorld::Class::get(container).getName(container)); + + mSortModel = new SortFilterItemModel(mModel); + + mItemView->setModel (mSortModel); + } + + void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) + { + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) - { - sellItem(NULL, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - sellItem(NULL, 1); - } - else - { - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::sellItem); - } + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } } - else + + void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) { - onSelectedItemImpl(*_sender->getUserData()); - } -} - -void ContainerBase::sellAlreadyBoughtItem(MyGUI::Widget* _sender, int count) -{ - MWWorld::Ptr object = *mSelectedItem->getUserData(); - - if (isInventory()) - { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, true); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); - } - else - { - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, true); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); - } - - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - drawItems(); -} - -void ContainerBase::sellItem(MyGUI::Widget* _sender, int count) -{ - MWWorld::Ptr object = *mSelectedItem->getUserData(); - - if (isInventory()) - { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, false); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); - } - else - { - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, false); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); - } - - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - drawItems(); -} - -void ContainerBase::startDragItem(MyGUI::Widget* _sender, int count) -{ - mDragAndDrop->mIsOnDragAndDrop = true; - mSelectedItem->detachFromWidget(); - mSelectedItem->attachToWidget(mDragAndDrop->mDragAndDropWidget); - - MWWorld::Ptr object = *mSelectedItem->getUserData(); - _unequipItem(object); - - mDragAndDrop->mDraggedCount = count; - - mDragAndDrop->mDraggedFrom = this; - - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - mDragAndDrop->mDraggedWidget = mSelectedItem; - static_cast(mSelectedItem)->setImageTexture(""); // remove the background texture (not visible during drag) - static_cast(mSelectedItem->getChildAt(0)->getChildAt(0))->setCaption( - getCountString(mDragAndDrop->mDraggedCount)); - - drawItems(); - - MWBase::Environment::get().getWindowManager()->setDragDrop(true); -} - -void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) -{ - if (mDragAndDrop == NULL) return; - - if(mDragAndDrop->mIsOnDragAndDrop) //drop item here - { - MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - if (mDragAndDrop->mDraggedFrom != this) + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { - assert(object.getContainerStore() && "Item is not in a container!"); - - // check the container's Organic flag (if this is a container). container with Organic flag doesn't allow putting items inside - if (mPtr.getTypeName() == typeid(ESM::Container).name()) + // transfer everything into the player's inventory + ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); + mModel->update(); + for (size_t i=0; igetItemCount(); ++i) { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mFlags & ESM::Container::Organic) + if (i==0) { - // user notification - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sContentsMessage2}", std::vector()); - - return; + // play the sound of the first object + MWWorld::Ptr item = mModel->getItem(i).mBase; + std::string sound = MWWorld::Class::get(item).getUpSoundId(item); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); } + + playerModel->copyItem(mModel->getItem(i), mModel->getItem(i).mCount); + mModel->removeItem(mModel->getItem(i), mModel->getItem(i).mCount); } - int origCount = object.getRefData().getCount(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); + } + } - // check that we don't exceed the allowed weight (only for containers, not for inventory) - if (!isInventory()) - { - float capacity = MWWorld::Class::get(mPtr).getCapacity(mPtr); + void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) + { + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + { + onTakeAllButtonClicked(mTakeButton); - // try adding the item, and if weight is exceeded, just remove it again. - object.getRefData().setCount(mDragAndDrop->mDraggedCount); - MWWorld::ContainerStoreIterator it = containerStore.add(object); - - float curWeight = MWWorld::Class::get(mPtr).getEncumbrance(mPtr); - if (curWeight > capacity) - { - it->getRefData().setCount(0); - object.getRefData().setCount(origCount); - // user notification - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sContentsMessage3}", std::vector()); - - return; - } - else - { - object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); - } - } + if (MWWorld::Class::get(mPtr).isPersistent(mPtr)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); else - { - object.getRefData().setCount (mDragAndDrop->mDraggedCount); - containerStore.add(object); - object.getRefData().setCount (origCount - mDragAndDrop->mDraggedCount); - } - } + MWBase::Environment::get().getWorld()->deleteObject(mPtr); - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - drawItems(); - mDragAndDrop->mDraggedFrom->drawItems(); - - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - } -} - -void ContainerBase::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mItemView->getViewOffset().left + _rel*0.3 > 0) - mItemView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mItemView->setViewOffset(MyGUI::IntPoint(mItemView->getViewOffset().left + _rel*0.3, 0)); -} - -void ContainerBase::setFilter(ContainerBase::Filter filter) -{ - mFilter = filter; - drawItems(); -} - -void ContainerBase::openContainer(MWWorld::Ptr container) -{ - mPtr = container; -} - -void ContainerBase::drawItems() -{ - while (mContainerWidget->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mContainerWidget->getChildAt(0)); - } - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - int x = 0; - int y = 0; - int maxHeight = mItemView->getSize().height - 58; - - bool onlyMagic = false; - int categories = 0; - if (mFilter == Filter_All) - categories = MWWorld::ContainerStore::Type_All; - else if (mFilter == Filter_Weapon) - categories = MWWorld::ContainerStore::Type_Weapon; - else if (mFilter == Filter_Apparel) - categories = MWWorld::ContainerStore::Type_Clothing + MWWorld::ContainerStore::Type_Armor; - else if (mFilter == Filter_Magic) - { - categories = MWWorld::ContainerStore::Type_Clothing + MWWorld::ContainerStore::Type_Armor - + MWWorld::ContainerStore::Type_Weapon + MWWorld::ContainerStore::Type_Book - + MWWorld::ContainerStore::Type_Potion; - onlyMagic = true; - } - else if (mFilter == Filter_Misc) - { - categories = MWWorld::ContainerStore::Type_Miscellaneous + MWWorld::ContainerStore::Type_Book - + MWWorld::ContainerStore::Type_Ingredient + MWWorld::ContainerStore::Type_Repair - + MWWorld::ContainerStore::Type_Lockpick + MWWorld::ContainerStore::Type_Light - + MWWorld::ContainerStore::Type_Apparatus + MWWorld::ContainerStore::Type_Probe; - } - else if (mFilter == Filter_Ingredients) - categories = MWWorld::ContainerStore::Type_Ingredient; - - /// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them - - std::vector< std::pair > items; - - std::vector equippedItems = getEquippedItems(); - - // add bought items (always at the beginning) - std::vector boughtItems; - for (MWWorld::ContainerStoreIterator it (mBoughtItems.begin()); it!=mBoughtItems.end(); ++it) - { - boughtItems.push_back(*it); - } - std::sort(boughtItems.begin(), boughtItems.end(), sortItems); - - for (std::vector::iterator it=boughtItems.begin(); - it != boughtItems.end(); ++it) - { - items.push_back( std::make_pair(*it, ItemState_Barter) ); - } - - // filter out the equipped items of categories we don't want - std::vector unwantedItems = equippedItems; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin(categories)); iter!=containerStore.end(); ++iter) - { - std::vector::iterator found = std::find(unwantedItems.begin(), unwantedItems.end(), *iter); - if (found != unwantedItems.end()) - { - unwantedItems.erase(found); - } - } - // now erase everything that's still in unwantedItems. - for (std::vector::iterator it=unwantedItems.begin(); - it != unwantedItems.end(); ++it) - { - std::vector::iterator found = std::find(equippedItems.begin(), equippedItems.end(), *it); - assert(found != equippedItems.end()); - equippedItems.erase(found); - } - // and add the items that are left (= have the correct category) - if (mDisplayEquippedItems && mHighlightEquippedItems) - { - for (std::vector::const_iterator it=equippedItems.begin(); - it != equippedItems.end(); ++it) - { - items.push_back( std::make_pair(*it, ItemState_Equipped) ); + mPtr = MWWorld::Ptr(); } } - std::vector ignoreItems = itemsToIgnore(); - - // now add the regular items - std::vector regularItems; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin(categories)); iter!=containerStore.end(); ++iter) - { - if ( (std::find(equippedItems.begin(), equippedItems.end(), *iter) == equippedItems.end() - || (!mHighlightEquippedItems && mDisplayEquippedItems)) - && std::find(ignoreItems.begin(), ignoreItems.end(), *iter) == ignoreItems.end() - && std::find(mBoughtItems.begin(), mBoughtItems.end(), *iter) == mBoughtItems.end()) - regularItems.push_back(*iter); - } - - // sort them and add - std::sort(regularItems.begin(), regularItems.end(), sortItems); - for (std::vector::const_iterator it=regularItems.begin(); it!=regularItems.end(); ++it) - { - items.push_back( std::make_pair(*it, ItemState_Normal) ); - } - - for (std::vector< std::pair >::const_iterator it=items.begin(); - it != items.end(); ++it) - { - const MWWorld::Ptr* iter = &((*it).first); - - int displayCount = iter->getRefData().getCount(); - if (mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop && *iter == *mDragAndDrop->mDraggedWidget->getUserData()) - { - displayCount -= mDragAndDrop->mDraggedCount; - } - if(displayCount > 0 && !(onlyMagic && it->second != ItemState_Barter && MWWorld::Class::get(*iter).getEnchantment(*iter) == "" && iter->getTypeName() != typeid(ESM::Potion).name())) - { - std::string path = std::string("icons\\"); - path += MWWorld::Class::get(*iter).getInventoryIcon(*iter); - - // background widget (for the "equipped" frame and magic item background image) - bool isMagic = (MWWorld::Class::get(*iter).getEnchantment(*iter) != ""); - MyGUI::ImageBox* backgroundWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); - backgroundWidget->setUserString("ToolTipType", "ItemPtr"); - backgroundWidget->setUserData(*iter); - - std::string backgroundTex = "textures\\menu_icon"; - if (isMagic) - backgroundTex += "_magic"; - if (it->second == ItemState_Normal) - { - if (!isMagic) - backgroundTex = ""; - } - else if (it->second == ItemState_Equipped) - { - backgroundTex += "_equip"; - } - else if (it->second == ItemState_Barter) - { - backgroundTex += "_barter"; - } - if (backgroundTex != "") - backgroundTex += ".dds"; - - backgroundWidget->setImageTexture(backgroundTex); - if (it->second == ItemState_Barter && !isMagic) - backgroundWidget->setProperty("ImageCoord", "2 2 42 42"); - else - backgroundWidget->setProperty("ImageCoord", "0 0 42 42"); - backgroundWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerBase::onSelectedItem); - backgroundWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerBase::onMouseWheel); - - // image - MyGUI::ImageBox* image = backgroundWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); - int pos = path.rfind("."); - path.erase(pos); - path.append(".dds"); - image->setImageTexture(path); - image->setNeedMouseFocus(false); - - // text widget that shows item count - MyGUI::TextBox* text = image->createWidget("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); - text->setTextAlign(MyGUI::Align::Right); - text->setNeedMouseFocus(false); - text->setTextShadow(true); - text->setTextShadowColour(MyGUI::Colour(0,0,0)); - text->setCaption(getCountString(displayCount)); - - y += 42; - if (y > maxHeight) - { - x += 42; - y = 0; - } - - } - } - - MyGUI::IntSize size = MyGUI::IntSize(std::max(mItemView->getSize().width, x+42), mItemView->getSize().height); - mItemView->setCanvasSize(size); - mContainerWidget->setSize(size); - - notifyContentChanged(); -} - -std::string ContainerBase::getCountString(const int count) -{ - if (count == 1) - return ""; - if (count > 9999) - return boost::lexical_cast(int(count/1000.f)) + "k"; - else - return boost::lexical_cast(count); -} - -void ContainerBase::addBarteredItem(MWWorld::Ptr item, int count) -{ - int origCount = item.getRefData().getCount(); - item.getRefData().setCount(count); - MWWorld::ContainerStoreIterator it = mBoughtItems.add(item); - item.getRefData().setCount(origCount - count); -} - -void ContainerBase::addItem(MWWorld::Ptr item, int count) -{ - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - int origCount = item.getRefData().getCount(); - - item.getRefData().setCount(count); - MWWorld::ContainerStoreIterator it = containerStore.add(item); - - item.getRefData().setCount(origCount - count); -} - -void ContainerBase::transferBoughtItems() -{ - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) - { - containerStore.add(*it); - } -} - -void ContainerBase::returnBoughtItems(MWWorld::ContainerStore& store) -{ - for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) - { - store.add(*it); - } -} - -std::vector ContainerBase::getEquippedItems() -{ - if (mPtr.getTypeName() != typeid(ESM::NPC).name()) - return std::vector(); - - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - std::vector items; - - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); - if (it != invStore.end()) - { - items.push_back(*it); - } - } - - return items; -} - -MWWorld::ContainerStore& ContainerBase::getContainerStore() -{ - MWWorld::ContainerStore& store = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - return store; -} - -// ------------------------------------------------------------------------------------------------ - -ContainerWindow::ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) - : ContainerBase(dragAndDrop) - , WindowBase("openmw_container_window.layout", parWindowManager) -{ - getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); - getWidget(mTakeButton, "TakeButton"); - getWidget(mCloseButton, "CloseButton"); - - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); - - mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); - - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ContainerWindow::onWindowResize); - - setCoord(200,0,600,300); -} - -ContainerWindow::~ContainerWindow() -{ -} - -void ContainerWindow::onWindowResize(MyGUI::Window* window) -{ - drawItems(); -} - -void ContainerWindow::open(MWWorld::Ptr container, bool loot) -{ - mDisplayEquippedItems = true; - mHighlightEquippedItems = false; - if (container.getTypeName() == typeid(ESM::NPC).name() && !loot) - { - // we are stealing stuff - mDisplayEquippedItems = false; - } - - mDisposeCorpseButton->setVisible(loot); - - openContainer(container); - setTitle(MWWorld::Class::get(container).getName(container)); - drawItems(); -} - -void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + void ContainerWindow::onReferenceUnavailable() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } -} - -void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) - { - // transfer everything into the player's inventory - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::ContainerStore& playerStore = MWWorld::Class::get(player).getContainerStore(player); - - int i=0; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin()); iter!=containerStore.end(); ++iter) - { - playerStore.add(*iter); - - if (i==0) - { - // play the sound of the first object - std::string sound = MWWorld::Class::get(*iter).getUpSoundId(*iter); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - } - - ++i; - } - - containerStore.clear(); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); - } -} - -void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) - { - onTakeAllButtonClicked(mTakeButton); - - /// \todo I don't think this is the correct flag to check - if (MWWorld::Class::get(mPtr).isEssential(mPtr)) - mWindowManager.messageBox("#{sDisposeCorpseFail}"); - else - MWBase::Environment::get().getWorld()->deleteObject(mPtr); - - mPtr = MWWorld::Ptr(); - } -} - -void ContainerWindow::onReferenceUnavailable() -{ - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); + } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 3c8127b26c..243f77aa56 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -1,15 +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" - +#include "itemmodel.hpp" namespace MWWorld { @@ -26,7 +21,8 @@ namespace MWGui { class WindowManager; class ContainerWindow; - class ContainerBase; + class ItemView; + class SortFilterItemModel; } @@ -38,109 +34,41 @@ namespace MWGui bool mIsOnDragAndDrop; MyGUI::Widget* mDraggedWidget; MyGUI::Widget* mDragAndDropWidget; - ContainerBase* mDraggedFrom; + ItemModel* mSourceModel; + ItemView* mSourceView; + SortFilterItemModel* mSourceSortModel; + ItemStack mItem; int mDraggedCount; + + void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); + void drop (ItemModel* targetModel, ItemView* targetView); + + void finish(); }; - class ContainerBase : public ReferenceInterface + class ContainerWindow : public WindowBase, public ReferenceInterface { public: - ContainerBase(DragAndDrop* dragAndDrop); - virtual ~ContainerBase(); + ContainerWindow(DragAndDrop* dragAndDrop); - enum Filter - { - Filter_All = 0x01, - Filter_Weapon = 0x02, - Filter_Apparel = 0x03, - Filter_Magic = 0x04, - Filter_Misc = 0x05, - - Filter_Ingredients = 0x06 - }; - - enum ItemState - { - ItemState_Normal = 0x01, - ItemState_Equipped = 0x02, - ItemState_Barter = 0x03 - }; - - void setWidgets(MyGUI::Widget* containerWidget, MyGUI::ScrollView* itemView); ///< only call once - - void addBarteredItem(MWWorld::Ptr item, int count); - void addItem(MWWorld::Ptr item, int count); - - void transferBoughtItems(); ///< transfer bought items into the inventory - void returnBoughtItems(MWWorld::ContainerStore& store); ///< return bought items into the specified ContainerStore - - MWWorld::ContainerStore& getContainerStore(); - MWWorld::ContainerStore& getBoughtItems() { return mBoughtItems; } - - void openContainer(MWWorld::Ptr container); - void setFilter(Filter filter); ///< set category filter - void drawItems(); - - protected: - bool mDisplayEquippedItems; - bool mHighlightEquippedItems; - - MyGUI::ScrollView* mItemView; - MyGUI::Widget* mContainerWidget; - - MyGUI::Widget* mSelectedItem; + void open(const MWWorld::Ptr& container, bool loot=false); + private: DragAndDrop* mDragAndDrop; - Filter mFilter; + MWGui::ItemView* mItemView; + SortFilterItemModel* mSortModel; + ItemModel* mModel; + size_t mSelectedItem; - // bought items are put in a separate ContainerStore so that they don't stack with other (not bought) items. - MWWorld::ContainerStore mBoughtItems; - - void onSelectedItem(MyGUI::Widget* _sender); - void onContainerClicked(MyGUI::Widget* _sender); - void onMouseWheel(MyGUI::Widget* _sender, int _rel); - - /// start dragging an item (drag & drop) - void startDragItem(MyGUI::Widget* _sender, int count); - - /// sell an item from this container - void sellItem(MyGUI::Widget* _sender, int count); - - /// sell an item from this container, that was previously just bought - void sellAlreadyBoughtItem(MyGUI::Widget* _sender, int count); - - std::string getCountString(const int count); - - virtual bool isTradeWindow() { return false; } - virtual bool isInventory() { return false; } - virtual std::vector getEquippedItems(); - virtual void _unequipItem(MWWorld::Ptr item) { ; } - - virtual bool isTrading() { return false; } - - virtual void onSelectedItemImpl(MWWorld::Ptr item) { ; } - - virtual std::vector itemsToIgnore() { return std::vector(); } - - virtual void notifyContentChanged() { ; } - }; - - class ContainerWindow : public ContainerBase, public WindowBase - { - public: - ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); - - virtual ~ContainerWindow(); - - void open(MWWorld::Ptr container, bool loot=false); - - protected: MyGUI::Button* mDisposeCorpseButton; MyGUI::Button* mTakeButton; MyGUI::Button* mCloseButton; - void onWindowResize(MyGUI::Window* window); + void onItemSelected(int index); + void onBackgroundSelected(); + void dragItem(MyGUI::Widget* sender, int count); + void dropItem(); void onCloseButtonClicked(MyGUI::Widget* _sender); void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp new file mode 100644 index 0000000000..79351c6ca0 --- /dev/null +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -0,0 +1,113 @@ +#include "containeritemmodel.hpp" + +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" + +namespace MWGui +{ + +ContainerItemModel::ContainerItemModel(const std::vector& itemSources) + : mItemSources(itemSources) +{ + assert (mItemSources.size()); +} + +ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) +{ + mItemSources.push_back(source); +} + +ItemStack ContainerItemModel::getItem (ModelIndex index) +{ + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; +} + +size_t ContainerItemModel::getItemCount() +{ + return mItems.size(); +} + +ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item) +{ + size_t i = 0; + for (std::vector::iterator it = mItems.begin(); it != mItems.end(); ++it) + { + if (*it == item) + return i; + ++i; + } + return -1; +} + +void ContainerItemModel::copyItem (const ItemStack& item, size_t count) +{ + const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; + int origCount = item.mBase.getRefData().getCount(); + item.mBase.getRefData().setCount(count); + MWWorld::ContainerStoreIterator it = MWWorld::Class::get(source).getContainerStore(source).add(item.mBase); + if (*it != item.mBase) + item.mBase.getRefData().setCount(origCount); + else + item.mBase.getRefData().setCount(origCount + count); // item copied onto itself +} + +void ContainerItemModel::removeItem (const ItemStack& item, size_t count) +{ + int toRemove = count; + + for (std::vector::iterator source = mItemSources.begin(); source != mItemSources.end(); ++source) + { + MWWorld::ContainerStore& store = MWWorld::Class::get(*source).getContainerStore(*source); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure + if (*it == item.mBase || (store.stacks(*it, item.mBase) && item.mBase.getContainerStore()->stacks(*it, item.mBase))) + { + int refCount = it->getRefData().getCount(); + it->getRefData().setCount(std::max(0, refCount - toRemove)); + toRemove -= refCount; + if (toRemove <= 0) + return; + } + } + } + throw std::runtime_error("Not enough items to remove could be found"); +} + +void ContainerItemModel::update() +{ + mItems.clear(); + for (std::vector::iterator source = mItemSources.begin(); source != mItemSources.end(); ++source) + { + MWWorld::ContainerStore& store = MWWorld::Class::get(*source).getContainerStore(*source); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + std::vector::iterator itemStack = mItems.begin(); + for (; itemStack != mItems.end(); ++itemStack) + { + // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure + if (store.stacks(itemStack->mBase, *it) && it->getContainerStore()->stacks(itemStack->mBase, *it)) + { + // we already have an item stack of this kind, add to it + itemStack->mCount += it->getRefData().getCount(); + break; + } + } + + if (itemStack == mItems.end()) + { + // no stack yet, create one + ItemStack newItem (*it, this, it->getRefData().getCount()); + mItems.push_back(newItem); + } + } + } +} + +} diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp new file mode 100644 index 0000000000..2f4c708bae --- /dev/null +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -0,0 +1,37 @@ +#ifndef MWGUI_CONTAINER_ITEM_MODEL_H +#define MWGUI_CONTAINER_ITEM_MODEL_H + +#include "itemmodel.hpp" + +namespace MWGui +{ + + /// @brief The container item model supports multiple item sources, which are needed for + /// making NPCs sell items from containers owned by them + class ContainerItemModel : public ItemModel + { + public: + ContainerItemModel (const std::vector& itemSources); + ///< @note The order of elements \a itemSources matters here. The first element has the highest priority for removal, + /// while the last element will be used to add new items to. + + ContainerItemModel (const MWWorld::Ptr& source); + + virtual ItemStack getItem (ModelIndex index); + virtual ModelIndex getIndex (ItemStack item); + virtual size_t getItemCount(); + + virtual void copyItem (const ItemStack& item, size_t count); + virtual void removeItem (const ItemStack& item, size_t count); + + virtual void update(); + + private: + std::vector mItemSources; + + std::vector mItems; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 61c3c358a2..354de561d9 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -2,13 +2,10 @@ #include -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - 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"); @@ -21,6 +18,8 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CountDialog::onOkButtonClicked); mItemEdit->eventEditTextChange += MyGUI::newDelegate(this, &CountDialog::onEditTextChange); mSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &CountDialog::onSliderMoved); + // make sure we read the enter key being pressed to accept multiple items + mItemEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &CountDialog::onEnterKeyPressed); } void CountDialog::open(const std::string& item, const std::string& message, const int maxCount) @@ -40,6 +39,7 @@ namespace MWGui width, mMainWidget->getHeight()); + // by default, the text edit field has the focus of the keyboard MyGUI::InputManager::getInstance().setKeyFocusWidget(mItemEdit); mSlider->setScrollPosition(maxCount-1); @@ -57,7 +57,16 @@ namespace MWGui setVisible(false); } - + + // essentially duplicating what the OK button does if user presses + // Enter key + void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) + { + eventOkClicked(NULL, mSlider->getScrollPosition()+1); + + setVisible(false); + } + void CountDialog::onEditTextChange(MyGUI::EditBox* _sender) { if (_sender->getCaption() == "") diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 80da6eea01..82a833bb7d 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; @@ -25,11 +25,12 @@ namespace MWGui MyGUI::TextBox* mLabelText; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; - + void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); void onEditTextChange(MyGUI::EditBox* _sender); void onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position); + void onEnterKeyPressed(MyGUI::EditBox* _sender); }; } diff --git a/apps/openmw/mwgui/cursor.cpp b/apps/openmw/mwgui/cursor.cpp index b0d164bedf..c069eca15a 100644 --- a/apps/openmw/mwgui/cursor.cpp +++ b/apps/openmw/mwgui/cursor.cpp @@ -2,13 +2,11 @@ #include #include -#include #include #include #include - namespace MWGui { 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 cdcbfc4d18..2eacd973e8 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -1,513 +1,607 @@ #include "dialogue.hpp" -#include -#include - -#include +#include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" -#include "../mwbase/dialoguemanager.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwworld/class.hpp" + #include "../mwdialogue/dialoguemanagerimp.hpp" -#include "dialogue_history.hpp" #include "widgets.hpp" #include "list.hpp" #include "tradewindow.hpp" #include "spellbuyingwindow.hpp" #include "inventorywindow.hpp" #include "travelwindow.hpp" +#include "bookpage.hpp" -using namespace MWGui; -using namespace Widgets; -/** -*Copied from the internet. -*/ - -namespace { - -std::string lower_string(const std::string& str) +namespace { - std::string lowerCase = Misc::StringUtils::lowerCase (str); - - return lowerCase; -} - -std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos) -{ - return lower_string(str).find(lower_string(substr),pos); -} - -bool sortByLength (const std::string& left, const std::string& right) -{ - return left.size() > right.size(); -} -} - - - -PersuasionDialog::PersuasionDialog(MWBase::WindowManager &parWindowManager) - : WindowModal("openmw_persuasion_dialog.layout", parWindowManager) -{ - getWidget(mCancelButton, "CancelButton"); - getWidget(mAdmireButton, "AdmireButton"); - getWidget(mIntimidateButton, "IntimidateButton"); - getWidget(mTauntButton, "TauntButton"); - getWidget(mBribe10Button, "Bribe10Button"); - getWidget(mBribe100Button, "Bribe100Button"); - getWidget(mBribe1000Button, "Bribe1000Button"); - getWidget(mGoldLabel, "GoldLabel"); - - mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel); - mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); -} - -void PersuasionDialog::onCancel(MyGUI::Widget *sender) -{ - setVisible(false); -} - -void PersuasionDialog::onPersuade(MyGUI::Widget *sender) -{ - MWBase::MechanicsManager::PersuasionType type; - if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; - else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; - else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; - else if (sender == mBribe10Button) + MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) { - mWindowManager.getTradeWindow()->addOrRemoveGold(-10); - type = MWBase::MechanicsManager::PT_Bribe10; + typedef MWGui::BookTypesetter::Utf8Point point; + + point begin = reinterpret_cast (text); + + return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); } - else if (sender == mBribe100Button) +} + +namespace MWGui +{ + + PersuasionDialog::PersuasionDialog() + : WindowModal("openmw_persuasion_dialog.layout") { - mWindowManager.getTradeWindow()->addOrRemoveGold(-100); - type = MWBase::MechanicsManager::PT_Bribe100; - } - else /*if (sender == mBribe1000Button)*/ - { - mWindowManager.getTradeWindow()->addOrRemoveGold(-1000); - type = MWBase::MechanicsManager::PT_Bribe1000; + getWidget(mCancelButton, "CancelButton"); + getWidget(mAdmireButton, "AdmireButton"); + getWidget(mIntimidateButton, "IntimidateButton"); + getWidget(mTauntButton, "TauntButton"); + getWidget(mBribe10Button, "Bribe10Button"); + getWidget(mBribe100Button, "Bribe100Button"); + getWidget(mBribe1000Button, "Bribe1000Button"); + getWidget(mGoldLabel, "GoldLabel"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel); + mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); } - MWBase::Environment::get().getDialogueManager()->persuade(type); - - setVisible(false); -} - -void PersuasionDialog::open() -{ - WindowModal::open(); - center(); - - int playerGold = mWindowManager.getInventoryWindow()->getPlayerGold(); - - mBribe10Button->setEnabled (playerGold >= 10); - mBribe100Button->setEnabled (playerGold >= 100); - mBribe1000Button->setEnabled (playerGold >= 1000); - - mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); -} - -// -------------------------------------------------------------------------------------------------- - -DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_dialogue_window.layout", parWindowManager) - , mPersuasionDialog(parWindowManager) - , mEnabled(false) - , mServices(0) -{ - // Centre dialog - center(); - - mPersuasionDialog.setVisible(false); - - //History view - getWidget(mHistory, "History"); - mHistory->setOverflowToTheLeft(true); - mHistory->setMaxTextLength(1000000); - MyGUI::Widget* eventbox; - - //An EditBox cannot receive mouse click events, so we use an - //invisible widget on top of the editbox to receive them - getWidget(eventbox, "EventBox"); - eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked); - eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); - - //Topics list - getWidget(mTopicsList, "TopicsList"); - mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); - - MyGUI::Button* byeButton; - getWidget(byeButton, "ByeButton"); - byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); - - getWidget(mDispositionBar, "Disposition"); - getWidget(mDispositionText,"DispositionText"); - - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); -} - -void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) -{ - MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText(); - if(t == NULL) - return; - - const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); - - size_t cursorPosition = t->getCursorPosition(lastPressed); - MyGUI::UString color = mHistory->getColorAtPos(cursorPosition); - - if (!mEnabled && color == "#572D21") - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); - - if(color != "#B29154") + void PersuasionDialog::onCancel(MyGUI::Widget *sender) { - MyGUI::UString key = mHistory->getColorTextAt(cursorPosition); + setVisible(false); + } - if(color == "#686EBA") + void PersuasionDialog::onPersuade(MyGUI::Widget *sender) + { + MWBase::MechanicsManager::PersuasionType type; + if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; + else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; + else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; + else if (sender == mBribe10Button) { - std::map::iterator i = mHyperLinks.upper_bound(cursorPosition); - if( !mHyperLinks.empty() ) - { - --i; + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-10); + type = MWBase::MechanicsManager::PT_Bribe10; + } + else if (sender == mBribe100Button) + { + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-100); + type = MWBase::MechanicsManager::PT_Bribe100; + } + else /*if (sender == mBribe1000Button)*/ + { + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-1000); + type = MWBase::MechanicsManager::PT_Bribe1000; + } - if( i->first + i->second.mLength > cursorPosition) - { - MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue); - } + MWBase::Environment::get().getDialogueManager()->persuade(type); + + setVisible(false); + } + + void PersuasionDialog::open() + { + WindowModal::open(); + center(); + + int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold(); + + mBribe10Button->setEnabled (playerGold >= 10); + mBribe100Button->setEnabled (playerGold >= 100); + mBribe1000Button->setEnabled (playerGold >= 1000); + + mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); + } + + // -------------------------------------------------------------------------------------------------- + + Response::Response(const std::string &text, const std::string &title) + : mTitle(title) + { + mText = text; + } + + void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const + { + BookTypesetter::Style* title = typesetter->createStyle("EB Garamond", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f)); + typesetter->sectionBreak(9); + if (mTitle != "") + typesetter->write(title, to_utf8_span(mTitle.c_str())); + typesetter->sectionBreak(9); + + typedef std::pair Range; + std::map hyperLinks; + + // We need this copy for when @# hyperlinks are replaced + std::string text = mText; + + size_t pos_begin, pos_end; + for(;;) + { + pos_begin = text.find('@'); + if (pos_begin != std::string::npos) + pos_end = text.find('#', pos_begin); + + if (pos_begin != std::string::npos && pos_end != std::string::npos) + { + std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); + const char specialPseudoAsteriskCharacter = 127; + std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*'); + std::string topicName = MWBase::Environment::get().getWindowManager()-> + getTranslationDataStorage().topicStandardForm(link); + + std::string displayName = link; + while (displayName[displayName.size()-1] == '*') + displayName.erase(displayName.size()-1, 1); + + text.replace(pos_begin, pos_end+1-pos_begin, displayName); + + if (topicLinks.find(Misc::StringUtils::lowerCase(topicName)) != topicLinks.end()) + hyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = intptr_t(topicLinks[Misc::StringUtils::lowerCase(topicName)]); } else + break; + } + + typesetter->addContent(to_utf8_span(text.c_str())); + + if (hyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) + { + BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f)); + size_t formatted = 0; // points to the first character that is not laid out yet + for (std::map::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it) { - // the link was colored, but it is not in mHyperLinks. - // It means that those liunks are not marked with @# and found - // by topic name search - MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key)); + intptr_t topicId = it->second; + const MyGUI::Colour linkHot (143/255.f, 155/255.f, 218/255.f); + const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/255.f); + const MyGUI::Colour linkActive (175/255.f, 184/255.f, 228/255.f); + BookTypesetter::Style* hotStyle = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId); + if (formatted < it->first.first) + typesetter->write(style, formatted, it->first.first); + typesetter->write(hotStyle, it->first.first, it->first.second); + formatted = it->first.second; } - } - - if(color == "#572D21") - MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); - } -} - -void DialogueWindow::onWindowResize(MyGUI::Window* _sender) -{ - mTopicsList->adjustSize(); -} - -void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mHistory->getVScrollPosition() - _rel*0.3 < 0) - mHistory->setVScrollPosition(0); - else - mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3); -} - -void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); -} - -void DialogueWindow::onSelectTopic(const std::string& topic, int id) -{ - if (!mEnabled) return; - - int separatorPos = mTopicsList->getItemCount(); - for (unsigned int i=0; igetItemCount(); ++i) - { - if (mTopicsList->getItemNameAt(i) == "") - separatorPos = i; - } - - if (id > separatorPos) - MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); - else - { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (topic == gmst.find("sBarter")->getString()) - { - /// \todo check if the player is allowed to trade with this actor (e.g. faction rank high enough)? - mWindowManager.pushGuiMode(GM_Barter); - mWindowManager.getTradeWindow()->startTrade(mPtr); - } - if (topic == gmst.find("sPersuasion")->getString()) - { - mPersuasionDialog.setVisible(true); - } - else if (topic == gmst.find("sSpells")->getString()) - { - mWindowManager.pushGuiMode(GM_SpellBuying); - mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); - } - else if (topic == gmst.find("sTravel")->getString()) - { - mWindowManager.pushGuiMode(GM_Travel); - mWindowManager.getTravelWindow()->startTravel(mPtr); - } - else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) - { - mWindowManager.pushGuiMode(GM_SpellCreation); - mWindowManager.startSpellMaking (mPtr); - } - else if (topic == gmst.find("sEnchanting")->getString()) - { - mWindowManager.pushGuiMode(GM_Enchanting); - mWindowManager.startEnchanting (mPtr); - } - else if (topic == gmst.find("sServiceTrainingTitle")->getString()) - { - mWindowManager.pushGuiMode(GM_Training); - mWindowManager.startTraining (mPtr); - } - } -} - -void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) -{ - mEnabled = true; - mPtr = actor; - mTopicsList->setEnabled(true); - setTitle(npcName); - - mTopicsList->clear(); - mHyperLinks.clear(); - mHistory->setCaption(""); - updateOptions(); -} - -void DialogueWindow::setKeywords(std::list keyWords) -{ - mTopicsList->clear(); - - bool anyService = mServices > 0; - - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addItem(gmst.find("sPersuasion")->getString()); - - if (mServices & Service_Trade) - mTopicsList->addItem(gmst.find("sBarter")->getString()); - - if (mServices & Service_BuySpells) - mTopicsList->addItem(gmst.find("sSpells")->getString()); - - if (mServices & Service_Travel) - mTopicsList->addItem(gmst.find("sTravel")->getString()); - - if (mServices & Service_CreateSpells) - mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); - -// if (mServices & Service_Enchant) -// mTopicsList->addItem(gmst.find("sEnchanting")->getString()); - - if (mServices & Service_Training) - mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); - - if (anyService || mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addSeparator(); - - for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) - { - mTopicsList->addItem(*it); - } - mTopicsList->adjustSize(); -} - -void DialogueWindow::removeKeyword(std::string keyWord) -{ - if(mTopicsList->hasItem(keyWord)) - { - mTopicsList->removeItem(keyWord); - } - mTopicsList->adjustSize(); -} - -void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2) -{ - size_t pos = 0; - while((pos = find_str_ci(str,keyword, pos)) != std::string::npos) - { - // do not add color if this portion of text is already colored. - { - MyGUI::TextIterator iterator (str); - MyGUI::UString colour; - while(iterator.moveNext()) - { - size_t iteratorPos = iterator.getPosition(); - iterator.getTagColour(colour); - if (iteratorPos == pos) - break; - } - - if (colour == color1) - return; - } - - str.insert(pos,color1); - pos += color1.length(); - pos += keyword.length(); - str.insert(pos,color2); - pos+= color2.length(); - } -} - -std::string DialogueWindow::parseText(const std::string& text) -{ - bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored) - - std::vector topics; - - for(unsigned int i = 0;igetItemCount();i++) - { - std::string keyWord = mTopicsList->getItemNameAt(i); - if (separatorReached) - topics.push_back(keyWord); - else if (keyWord == "") - separatorReached = true; - } - - // sort by length to make sure longer topics are replaced first - std::sort(topics.begin(), topics.end(), sortByLength); - - std::vector hypertext = MWDialogue::ParseHyperText(text); - - size_t historySize = 0; - if(mHistory->getClient()->getSubWidgetText() != NULL) - { - historySize = mHistory->getOnlyText().size(); - } - - std::string result; - size_t hypertextPos = 0; - for (size_t i = 0; i < hypertext.size(); ++i) - { - if (hypertext[i].mLink) - { - size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText); - std::string standardForm = hypertext[i].mText; - for(; asterisk_count > 0; --asterisk_count) - standardForm.append("*"); - - standardForm = - MWBase::Environment::get().getWindowManager()-> - getTranslationDataStorage().topicStandardForm(standardForm); - - if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() ) - { - result.append("#686EBA").append(hypertext[i].mText).append("#B29154"); - - mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length(); - mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm); - } - else - result += hypertext[i].mText; + if (formatted < text.size()) + typesetter->write(style, formatted, text.size()); } else { - if( !mWindowManager.getTranslationDataStorage().hasTranslation() ) + std::string::const_iterator i = text.begin (); + KeywordSearchT::Match match; + while (i != text.end () && keywordSearch->search (i, text.end (), match)) { - for(std::vector::const_iterator it = topics.begin(); it != topics.end(); ++it) - { - addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154"); - } + if (i != match.mBeg) + addTopicLink (typesetter, 0, i - text.begin (), match.mBeg - text.begin ()); + + addTopicLink (typesetter, match.mValue, match.mBeg - text.begin (), match.mEnd - text.begin ()); + + i = match.mEnd; } - result += hypertext[i].mText; + if (i != text.end ()) + addTopicLink (typesetter, 0, i - text.begin (), text.size ()); + } + } + + void Response::addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const + { + BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f)); + + const MyGUI::Colour linkHot (143/255.f, 155/255.f, 218/255.f); + const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/255.f); + const MyGUI::Colour linkActive (175/255.f, 184/255.f, 228/255.f); + + if (topicId) + style = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId); + typesetter->write (style, begin, end); + } + + Message::Message(const std::string& text) + { + mText = text; + } + + void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const + { + BookTypesetter::Style* title = typesetter->createStyle("EB Garamond", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f)); + typesetter->sectionBreak(9); + typesetter->write(title, to_utf8_span(mText.c_str())); + } + + // -------------------------------------------------------------------------------------------------- + + void Choice::activated() + { + MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId); + } + + void Topic::activated() + { + MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId)); + } + + void Goodbye::activated() + { + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + } + + // -------------------------------------------------------------------------------------------------- + + DialogueWindow::DialogueWindow() + : WindowBase("openmw_dialogue_window.layout") + , mPersuasionDialog() + , mEnabled(false) + , mServices(0) + , mGoodbye(false) + { + // Centre dialog + center(); + + mPersuasionDialog.setVisible(false); + + //History view + getWidget(mHistory, "History"); + + //Topics list + getWidget(mTopicsList, "TopicsList"); + mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); + + MyGUI::Button* byeButton; + getWidget(byeButton, "ByeButton"); + byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); + + getWidget(mDispositionBar, "Disposition"); + getWidget(mDispositionText,"DispositionText"); + getWidget(mScrollBar, "VScroll"); + + mScrollBar->eventScrollChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onScrollbarMoved); + mHistory->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); + + BookPage::ClickCallback callback = boost::bind (&DialogueWindow::notifyLinkClicked, this, _1); + mHistory->adviseLinkClicked(callback); + + static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); + } + + void DialogueWindow::onWindowResize(MyGUI::Window* _sender) + { + mTopicsList->adjustSize(); + updateHistory(); + } + + void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (!mScrollBar->getVisible()) + return; + mScrollBar->setScrollPosition(std::min(static_cast(mScrollBar->getScrollRange()-1), + std::max(0, static_cast(mScrollBar->getScrollPosition() - _rel*0.3)))); + onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition()); + } + + void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + } + + void DialogueWindow::onSelectTopic(const std::string& topic, int id) + { + if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) + return; + + int separatorPos = 0; + for (unsigned int i=0; igetItemCount(); ++i) + { + if (mTopicsList->getItemNameAt(i) == "") + separatorPos = i; } - hypertextPos += MyGUI::UString(hypertext[i].mText).length(); + if (id >= separatorPos) + MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic)); + else + { + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (topic == gmst.find("sPersuasion")->getString()) + { + mPersuasionDialog.setVisible(true); + } + else if (topic == gmst.find("sCompanionShare")->getString()) + { + 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()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->startTrade(mPtr); + } + else if (topic == gmst.find("sSpells")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying); + MWBase::Environment::get().getWindowManager()->getSpellBuyingWindow()->startSpellBuying(mPtr); + } + else if (topic == gmst.find("sTravel")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->getTravelWindow()->startTravel(mPtr); + } + else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr); + } + else if (topic == gmst.find("sEnchanting")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); + MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr); + } + else if (topic == gmst.find("sServiceTrainingTitle")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training); + MWBase::Environment::get().getWindowManager()->startTraining (mPtr); + } + else if (topic == gmst.find("sRepair")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair); + MWBase::Environment::get().getWindowManager()->startRepair (mPtr); + } + } + } } - return result; -} - -void DialogueWindow::addText(std::string text) -{ - mHistory->addDialogText("#B29154"+parseText(text)+"#B29154"); -} - -void DialogueWindow::addMessageBox(const std::string& text) -{ - mHistory->addDialogText("\n#FFFFFF"+text+"#B29154"); -} - -void DialogueWindow::addTitle(std::string text) -{ - // This is called from the dialogue manager, so text is - // case-smashed - thus we have to retrieve the correct case - // of the text through the topic list. - for (size_t i=0; igetItemCount(); ++i) + void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) { - std::string item = mTopicsList->getItemNameAt(i); - if (lower_string(item) == text) - text = item; + mGoodbye = false; + mEnabled = true; + mPtr = actor; + mTopicsList->setEnabled(true); + setTitle(npcName); + + mTopicsList->clear(); + + for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) + delete (*it); + mHistoryContents.clear(); + + for (std::vector::iterator it = mLinks.begin(); it != mLinks.end(); ++it) + delete (*it); + mLinks.clear(); + + updateOptions(); } - mHistory->addDialogHeading(text); -} - -void DialogueWindow::askQuestion(std::string question) -{ - mHistory->addDialogText("#572D21"+question+"#B29154"+" "); -} - -void DialogueWindow::updateOptions() -{ - //Clear the list of topics - mTopicsList->clear(); - mHyperLinks.clear(); - mHistory->eraseText(0, mHistory->getTextLength()); - - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + void DialogueWindow::setKeywords(std::list keyWords) { - mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); - mDispositionText->eraseText(0, mDispositionText->getTextLength()); - mDispositionText->addText("#B29154"+boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + mTopicsList->clear(); + for (std::map::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it) + delete it->second; + mTopicLinks.clear(); + mKeywordSearch.clear(); + + bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty() + && mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion"); + + bool anyService = mServices > 0 || isCompanion || mPtr.getTypeName() == typeid(ESM::NPC).name(); + + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + mTopicsList->addItem(gmst.find("sPersuasion")->getString()); + + if (mServices & Service_Trade) + mTopicsList->addItem(gmst.find("sBarter")->getString()); + + if (mServices & Service_BuySpells) + mTopicsList->addItem(gmst.find("sSpells")->getString()); + + if (mServices & Service_Travel) + mTopicsList->addItem(gmst.find("sTravel")->getString()); + + if (mServices & Service_CreateSpells) + mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); + + if (mServices & Service_Enchant) + mTopicsList->addItem(gmst.find("sEnchanting")->getString()); + + if (mServices & Service_Training) + mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); + + if (mServices & Service_Repair) + mTopicsList->addItem(gmst.find("sRepair")->getString()); + + if (isCompanion) + mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); + + if (anyService) + mTopicsList->addSeparator(); + + + for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) + { + mTopicsList->addItem(*it); + + Topic* t = new Topic(*it); + mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t; + + mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); + } + mTopicsList->adjustSize(); + + updateHistory(); } -} -void DialogueWindow::goodbye() -{ - mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString()); - mTopicsList->setEnabled(false); - mEnabled = false; -} - -void DialogueWindow::onReferenceUnavailable() -{ - mWindowManager.removeGuiMode(GM_Dialogue); -} - -void DialogueWindow::onFrame() -{ - if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) + void DialogueWindow::updateHistory(bool scrollbar) { - int disp = std::max(0, std::min(100, - MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) - + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); - mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(disp); - mDispositionText->eraseText(0, mDispositionText->getTextLength()); - mDispositionText->addText("#B29154"+boost::lexical_cast(disp)+std::string("/100")+"#B29154"); + if (!scrollbar && mScrollBar->getVisible()) + { + mHistory->setSize(mHistory->getSize()+MyGUI::IntSize(mScrollBar->getWidth(),0)); + mScrollBar->setVisible(false); + } + if (scrollbar && !mScrollBar->getVisible()) + { + mHistory->setSize(mHistory->getSize()-MyGUI::IntSize(mScrollBar->getWidth(),0)); + mScrollBar->setVisible(true); + } + + BookTypesetter::Ptr typesetter = BookTypesetter::create (mHistory->getWidth(), std::numeric_limits().max()); + + for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) + (*it)->write(typesetter, &mKeywordSearch, mTopicLinks); + + + BookTypesetter::Style* body = typesetter->createStyle("EB Garamond", MyGUI::Colour::White); + + // choices + const MyGUI::Colour linkHot (223/255.f, 201/255.f, 159/255.f); + const MyGUI::Colour linkNormal (150/255.f, 50/255.f, 30/255.f); + const MyGUI::Colour linkActive (243/255.f, 237/255.f, 221/255.f); + for (std::map::iterator it = mChoices.begin(); it != mChoices.end(); ++it) + { + Choice* link = new Choice(it->second); + mLinks.push_back(link); + + typesetter->lineBreak(); + BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive, + TypesetBook::InteractiveId(link)); + typesetter->write(questionStyle, to_utf8_span(it->first.c_str())); + } + + if (mGoodbye) + { + std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString(); + BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive, + TypesetBook::InteractiveId(mLinks.back())); + typesetter->lineBreak(); + typesetter->write(questionStyle, to_utf8_span(goodbye.c_str())); + } + + TypesetBook::Ptr book = typesetter->complete(); + mHistory->showPage(book, 0); + size_t viewHeight = mHistory->getParent()->getHeight(); + if (!scrollbar && book->getSize().second > viewHeight) + updateHistory(true); + else if (scrollbar) + { + mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second)); + size_t range = book->getSize().second - viewHeight; + mScrollBar->setScrollRange(range); + mScrollBar->setScrollPosition(range-1); + mScrollBar->setTrackSize(viewHeight / static_cast(book->getSize().second) * mScrollBar->getLineSize()); + onScrollbarMoved(mScrollBar, range-1); + } + else + { + // no scrollbar + onScrollbarMoved(mScrollBar, 0); + } + } + + void DialogueWindow::notifyLinkClicked (TypesetBook::InteractiveId link) + { + reinterpret_cast(link)->activated(); + } + + void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) + { + mHistory->setPosition(0,-pos); + } + + void DialogueWindow::addResponse(const std::string &text, const std::string &title) + { + // This is called from the dialogue manager, so text is + // case-smashed - thus we have to retrieve the correct case + // of the title through the topic list. + std::string realTitle = title; + if (realTitle != "") + { + for (size_t i=0; igetItemCount(); ++i) + { + std::string item = mTopicsList->getItemNameAt(i); + if (Misc::StringUtils::lowerCase(item) == title) + { + realTitle = item; + break; + } + } + } + + mHistoryContents.push_back(new Response(text, realTitle)); + updateHistory(); + } + + void DialogueWindow::addMessageBox(const std::string& text) + { + mHistoryContents.push_back(new Message(text)); + updateHistory(); + } + + void DialogueWindow::addChoice(const std::string& choice, int id) + { + mChoices[choice] = id; + updateHistory(); + } + + void DialogueWindow::clearChoices() + { + mChoices.clear(); + updateHistory(); + } + + void DialogueWindow::updateOptions() + { + //Clear the list of topics + mTopicsList->clear(); + + if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + } + } + + void DialogueWindow::goodbye() + { + mLinks.push_back(new Goodbye()); + mGoodbye = true; + mTopicsList->setEnabled(false); + mEnabled = false; + updateHistory(); + } + + void DialogueWindow::onReferenceUnavailable() + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + } + + void DialogueWindow::onFrame() + { + if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + int disp = std::max(0, std::min(100, + MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(disp); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+boost::lexical_cast(disp)+std::string("/100")+"#B29154"); + } } } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index a8e0a6d174..befbd6eeeb 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -1,11 +1,12 @@ #ifndef MWGUI_DIALOGE_H #define MWGUI_DIALOGE_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" -#include -#include "../mwworld/ptr.hpp" +#include "bookpage.hpp" + +#include "keywordsearch.hpp" namespace MWGui { @@ -24,12 +25,13 @@ namespace MWGui namespace MWGui { - class DialogueHistory; + class DialogueHistoryViewModel; + class BookPage; class PersuasionDialog : public WindowModal { public: - PersuasionDialog(MWBase::WindowManager& parWindowManager); + PersuasionDialog(); virtual void open(); @@ -47,27 +49,75 @@ namespace MWGui void onPersuade (MyGUI::Widget* sender); }; + + struct Link + { + virtual ~Link() {} + virtual void activated () = 0; + }; + + struct Topic : Link + { + Topic(const std::string& id) : mTopicId(id) {} + std::string mTopicId; + virtual void activated (); + }; + + struct Choice : Link + { + Choice(int id) : mChoiceId(id) {} + int mChoiceId; + virtual void activated (); + }; + + struct Goodbye : Link + { + virtual void activated (); + }; + + typedef KeywordSearch KeywordSearchT; + + struct DialogueText + { + virtual ~DialogueText() {} + virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const = 0; + std::string mText; + }; + + struct Response : DialogueText + { + Response(const std::string& text, const std::string& title = ""); + virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const; + void addTopicLink (BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const; + std::string mTitle; + }; + + struct Message : DialogueText + { + Message(const std::string& text); + virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const; + }; + class DialogueWindow: public WindowBase, public ReferenceInterface { public: - DialogueWindow(MWBase::WindowManager& parWindowManager); + DialogueWindow(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; - /** Event : Dialog finished, OK button clicked.\n - signature : void method()\n - */ - EventHandle_Void eventBye; + void notifyLinkClicked (TypesetBook::InteractiveId link); void startDialogue(MWWorld::Ptr actor, std::string npcName); - void stopDialogue(); void setKeywords(std::list keyWord); - void removeKeyword(std::string keyWord); - void addText(std::string text); + + void addResponse (const std::string& text, const std::string& title=""); + void addMessageBox(const std::string& text); - void addTitle(std::string text); - void askQuestion(std::string question); + + void addChoice(const std::string& choice, int id); + void clearChoices(); + void goodbye(); void onFrame(); @@ -81,43 +131,46 @@ namespace MWGui Service_CreateSpells = 0x04, Service_Enchant = 0x08, Service_Training = 0x10, - Service_Travel = 0x20 + Service_Travel = 0x20, + Service_Repair = 0x40 }; protected: void onSelectTopic(const std::string& topic, int id); void onByeClicked(MyGUI::Widget* _sender); - void onHistoryClicked(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onWindowResize(MyGUI::Window* _sender); - virtual void onReferenceUnavailable(); + void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos); - struct HyperLink - { - size_t mLength; - std::string mTrueValue; - }; + void updateHistory(bool scrollbar=false); + + virtual void onReferenceUnavailable(); private: void updateOptions(); - /** - *Helper function that add topic keyword in blue in a text. - */ - std::string parseText(const std::string& text); int mServices; bool mEnabled; - DialogueHistory* mHistory; + bool mGoodbye; + + std::vector mHistoryContents; + std::map mChoices; + + std::vector mLinks; + std::map mTopicLinks; + + KeywordSearchT mKeywordSearch; + + BookPage* mHistory; Widgets::MWList* mTopicsList; + MyGUI::ScrollBar* mScrollBar; MyGUI::ProgressPtr mDispositionBar; MyGUI::EditBox* mDispositionText; PersuasionDialog mPersuasionDialog; - - std::map mHyperLinks; }; } #endif diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialogue_history.cpp deleted file mode 100644 index 13f72545e2..0000000000 --- a/apps/openmw/mwgui/dialogue_history.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "dialogue_history.hpp" - -#include "../mwbase/windowmanager.hpp" - -#include "widgets.hpp" - -#include "../mwworld/esmstore.hpp" - -#include -#include - -#include -#include - -using namespace MWGui; -using namespace Widgets; - -MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos) -{ - MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); - MyGUI::TextIterator iterator(getCaption()); - while(iterator.moveNext()) - { - size_t pos = iterator.getPosition(); - iterator.getTagColour(colour); - if (pos < _pos) - continue; - else if (pos == _pos) - break; - } - return colour; -} - -MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos) -{ - bool breakOnNext = false; - MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); - MyGUI::UString colour2 = colour; - MyGUI::TextIterator iterator(getCaption()); - MyGUI::TextIterator col_start = iterator; - while(iterator.moveNext()) - { - size_t pos = iterator.getPosition(); - iterator.getTagColour(colour); - if(colour != colour2) - { - if(breakOnNext) - { - return getOnlyText().substr(col_start.getPosition(), iterator.getPosition()-col_start.getPosition()); - } - col_start = iterator; - colour2 = colour; - } - if (pos < _pos) - continue; - else if (pos == _pos) - { - breakOnNext = true; - } - } - return ""; -} - -void DialogueHistory::addDialogHeading(const MyGUI::UString& parText) -{ - MyGUI::UString head("\n#D8C09A"); - head.append(parText); - head.append("#B29154\n"); - addText(head); -} - -void DialogueHistory::addDialogText(const MyGUI::UString& parText) -{ - addText(parText); - addText("\n"); -} diff --git a/apps/openmw/mwgui/dialogue_history.hpp b/apps/openmw/mwgui/dialogue_history.hpp deleted file mode 100644 index c37504af77..0000000000 --- a/apps/openmw/mwgui/dialogue_history.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef MWGUI_DIALOGE_HISTORY_H -#define MWGUI_DIALOGE_HISTORY_H -#include - -namespace MWGui -{ - class DialogueHistory : public MyGUI::EditBox - { - MYGUI_RTTI_DERIVED( DialogueHistory ) - public: - Widget* getClient() { return mClient; } - MyGUI::UString getColorAtPos(size_t _pos); - MyGUI::UString getColorTextAt(size_t _pos); - void addDialogHeading(const MyGUI::UString& parText); - void addDialogText(const MyGUI::UString& parText); - }; -} -#endif - diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 3bd67ade63..799a89ab5a 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -1,43 +1,305 @@ #include "enchantingdialog.hpp" +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" + +#include "itemselection.hpp" +#include "container.hpp" +#include "inventorywindow.hpp" + +#include "sortfilteritemmodel.hpp" 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) { + getWidget(mName, "NameEdit"); getWidget(mCancelButton, "CancelButton"); getWidget(mAvailableEffectsList, "AvailableEffects"); getWidget(mUsedEffectsView, "UsedEffects"); + getWidget(mItemBox, "ItemBox"); + getWidget(mSoulBox, "SoulBox"); + getWidget(mEnchantmentPoints, "Enchantment"); + getWidget(mCastCost, "CastCost"); + getWidget(mCharge, "Charge"); + getWidget(mTypeButton, "TypeButton"); + getWidget(mBuyButton, "BuyButton"); + getWidget(mPrice, "PriceLabel"); + getWidget(mPriceText, "PriceTextLabel"); setWidgets(mAvailableEffectsList, mUsedEffectsView); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onCancelButtonClicked); + mItemBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectItem); + mSoulBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectSoul); + mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked); + mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked); + } + + EnchantingDialog::~EnchantingDialog() + { + delete mItemSelectionDialog; } void EnchantingDialog::open() { center(); + onRemoveItem(NULL); + onRemoveSoul(NULL); + } + + void EnchantingDialog::updateLabels() + { + std::stringstream enchantCost; + enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantCost(); + mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast(mEnchanting.getMaxEnchantValue())); + + mCharge->setCaption(boost::lexical_cast(mEnchanting.getGemCharge())); + + mCastCost->setCaption(boost::lexical_cast(mEnchanting.getEnchantCost())); + + mPrice->setCaption(boost::lexical_cast(mEnchanting.getEnchantPrice())); + + switch(mEnchanting.getEnchantType()) + { + case 0: + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once")); + mAddEffectDialog.constantEffect=false; + break; + case 1: + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes")); + mAddEffectDialog.constantEffect=false; + break; + case 2: + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used")); + mAddEffectDialog.constantEffect=false; + break; + case 3: + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant")); + mAddEffectDialog.constantEffect=true; + break; + } } void EnchantingDialog::startEnchanting (MWWorld::Ptr actor) { + mEnchanting.setSelfEnchanting(false); + mEnchanting.setEnchanter(actor); + mPtr = actor; startEditing (); } + void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) + { + 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}"); + mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onItemSelected); + mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onItemCancel); + mItemSelectionDialog->setVisible(true); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyEnchantable); + } + + void EnchantingDialog::onItemSelected(MWWorld::Ptr item) + { + mItemSelectionDialog->setVisible(false); + + while (mItemBox->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mItemBox->getChildAt(0)); + + MyGUI::ImageBox* image = mItemBox->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->setUserString ("ToolTipType", "ItemPtr"); + image->setUserData(item); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveItem); + + mEnchanting.setOldItem(item); + mEnchanting.nextEnchantType(); + updateLabels(); + } + + void EnchantingDialog::onRemoveItem(MyGUI::Widget *sender) + { + while (mItemBox->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mItemBox->getChildAt(0)); + mEnchanting.setOldItem(MWWorld::Ptr()); + updateLabels(); + } + + void EnchantingDialog::onItemCancel() + { + mItemSelectionDialog->setVisible(false); + } + + void EnchantingDialog::onSoulSelected(MWWorld::Ptr item) + { + mItemSelectionDialog->setVisible(false); + mEnchanting.setSoulGem(item); + + if(mEnchanting.getGemCharge()==0) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage32}"); + return; + } + + while (mSoulBox->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mSoulBox->getChildAt(0)); + + 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(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->setUserString ("ToolTipType", "ItemPtr"); + image->setUserData(item); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveSoul); + updateLabels(); + } + + void EnchantingDialog::onRemoveSoul(MyGUI::Widget *sender) + { + while (mSoulBox->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mSoulBox->getChildAt(0)); + mEnchanting.setSoulGem(MWWorld::Ptr()); + updateLabels(); + } + + void EnchantingDialog::onSoulCancel() + { + mItemSelectionDialog->setVisible(false); + } + + void EnchantingDialog::onSelectSoul(MyGUI::Widget *sender) + { + delete mItemSelectionDialog; + mItemSelectionDialog = new ItemSelectionDialog("#{sSoulGemsWithSouls}"); + 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->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones); + + //MWBase::Environment::get().getWindowManager()->messageBox("#{sInventorySelectNoSoul}"); + } + + void EnchantingDialog::notifyEffectsChanged () + { + mEffectList.mList = mEffects; + mEnchanting.setEffect(mEffectList); + updateLabels(); + } + + void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender) + { + mEnchanting.nextEnchantType(); + updateLabels(); + } + + void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) + { + if (mEffects.size() <= 0) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); + return; + } + + if (mName->getCaption ().empty()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage10}"); + return; + } + + if (mEnchanting.soulEmpty()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage52}"); + return; + } + + if (mEnchanting.itemEmpty()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage11}"); + return; + } + + if (mEnchanting.getEnchantCost() > mEnchanting.getMaxEnchantValue()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}"); + return; + } + + mEnchanting.setNewItemName(mName->getCaption()); + mEnchanting.setEffect(mEffectList); + + 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 0415c9d8df..8bad60c8e6 100644 --- a/apps/openmw/mwgui/enchantingdialog.hpp +++ b/apps/openmw/mwgui/enchantingdialog.hpp @@ -1,29 +1,63 @@ #ifndef MWGUI_ENCHANTINGDIALOG_H #define MWGUI_ENCHANTINGDIALOG_H -#include "window_base.hpp" -#include "referenceinterface.hpp" #include "spellcreationdialog.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwmechanics/enchanting.hpp" + namespace MWGui { + class ItemSelectionDialog; + class EnchantingDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase { public: - EnchantingDialog(MWBase::WindowManager& parWindowManager); + EnchantingDialog(); + virtual ~EnchantingDialog(); virtual void open(); void startEnchanting(MWWorld::Ptr actor); + void startSelfEnchanting(MWWorld::Ptr soulgem); protected: virtual void onReferenceUnavailable(); + virtual void notifyEffectsChanged (); void onCancelButtonClicked(MyGUI::Widget* sender); + void onSelectItem (MyGUI::Widget* sender); + void onSelectSoul (MyGUI::Widget* sender); + void onRemoveItem (MyGUI::Widget* sender); + void onRemoveSoul (MyGUI::Widget* sender); + + void onItemSelected(MWWorld::Ptr item); + void onItemCancel(); + void onSoulSelected(MWWorld::Ptr item); + void onSoulCancel(); + void onBuyButtonClicked(MyGUI::Widget* sender); + void updateLabels(); + void onTypeButtonClicked(MyGUI::Widget* sender); + + ItemSelectionDialog* mItemSelectionDialog; MyGUI::Button* mCancelButton; + MyGUI::ImageBox* mItemBox; + MyGUI::ImageBox* mSoulBox; + + MyGUI::Button* mTypeButton; + MyGUI::Button* mBuyButton; + + MyGUI::TextBox* mName; + MyGUI::TextBox* mEnchantmentPoints; + MyGUI::TextBox* mCastCost; + MyGUI::TextBox* mCharge; + MyGUI::TextBox* mPrice; + MyGUI::TextBox* mPriceText; + + MWMechanics::Enchanting mEnchanting; + ESM::EffectList mEffectList; }; } diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 7f28e9e17d..aebaf16a23 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -3,15 +3,15 @@ #include #include "../mwscript/interpretercontext.hpp" -#include "../mwworld/ptr.hpp" #include #include +#include #include +#include +#include #include -using namespace MWGui; - namespace { int convertFromHex(std::string hex) @@ -77,289 +77,333 @@ namespace Ogre::UTFString string(s); return string.getChar(0); } + + bool is_not_empty(const std::string s) { + std::string temp = s; + boost::algorithm::trim(temp); + return !temp.empty(); + } } -std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) +namespace MWGui { - using Ogre::UTFString; - std::vector result; - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); - - boost::algorithm::replace_all(utf8Text, "\n", ""); - boost::algorithm::replace_all(utf8Text, "
", "\n"); - boost::algorithm::replace_all(utf8Text, "

", "\n\n"); - - UTFString text(utf8Text); - const int spacing = 48; - - const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); - const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); - const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); - - while (!text.empty()) + std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) { - // read in characters until we have exceeded the size, or run out of text - int currentWidth = 0; - int currentHeight = 0; + using Ogre::UTFString; + std::vector result; - size_t currentWordStart = 0; - size_t index = 0; - while (currentHeight <= height - spacing && index < text.size()) + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); + + boost::algorithm::replace_all(utf8Text, "\n", ""); + boost::algorithm::replace_all(utf8Text, "\r", ""); + boost::algorithm::replace_all(utf8Text, "
", "\n"); + boost::algorithm::replace_all(utf8Text, "

", "\n\n"); + + UTFString text(utf8Text); + const int spacing = 48; + + const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); + const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); + const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); + + while (!text.empty()) { - const UTFString::unicode_char ch = text.getChar(index); - if (ch == LEFT_ANGLE) - { - const size_t tagStart = index + 1; - const size_t tagEnd = text.find('>', tagStart); - if (tagEnd == UTFString::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8(); + // read in characters until we have exceeded the size, or run out of text + int currentWidth = 0; + int currentHeight = 0; - if (boost::algorithm::starts_with(tag, "IMG")) + size_t currentWordStart = 0; + size_t index = 0; + + { + std::string texToTrim = text.asUTF8(); + boost::algorithm::trim( texToTrim ); + text = UTFString(texToTrim); + } + + + while (currentHeight <= height - spacing && index < text.size()) + { + const UTFString::unicode_char ch = text.getChar(index); + if (ch == LEFT_ANGLE) { - const int h = mHeight; - parseImage(tag, false); - currentHeight += (mHeight - h); - currentWidth = 0; - } - else if (boost::algorithm::starts_with(tag, "FONT")) - { - parseFont(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - currentWidth = 0; - } - currentWidth = 0; - } - else if (boost::algorithm::starts_with(tag, "DIV")) - { - parseDiv(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - currentWidth = 0; - } - } - index = tagEnd; - } - else if (ch == NEWLINE) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - currentWordStart = index; - } - else if (ch == SPACE) - { - currentWidth += 3; // keep this in sync with the font's SpaceWidth property - currentWordStart = index; - } - else - { - currentWidth += widthForCharGlyph(ch); - } - - if (currentWidth > width) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - // add size of the current word - UTFString word = text.substr(currentWordStart, index - currentWordStart); - for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it) - currentWidth += widthForCharGlyph(it.getCharacter()); - } - index += UTFString::_utf16_char_length(ch); - } - const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) - ? currentWordStart : index; - - result.push_back(text.substr(0, pageEnd).asUTF8()); - text.erase(0, pageEnd); - } - - return result; -} - -float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const -{ - std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName) - ->getGlyphInfo(unicodeChar)->width; -} - -float BookTextParser::currentFontHeight() const -{ - std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); -} - -MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) -{ - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); - - mParent = parent; - mWidth = width; - mHeight = 0; - - assert(mParent); - while (mParent->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); - } - - boost::algorithm::replace_all(text, "\n", ""); - boost::algorithm::replace_all(text, "
", "\n"); - boost::algorithm::replace_all(text, "

", "\n\n"); - - // remove leading newlines -// while (text[0] == '\n') -// text.erase(0); - - // remove trailing " - if (text[text.size()-1] == '\"') - text.erase(text.size()-1); - - parseSubText(text); - return MyGUI::IntSize(mWidth, mHeight); -} - -void BookTextParser::parseImage(std::string tag, bool createWidget) -{ - int src_start = tag.find("SRC=")+5; - std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start); - - // fix texture extension to .dds - if (image.size() > 4) - { - image[image.size()-3] = 'd'; - image[image.size()-2] = 'd'; - image[image.size()-1] = 's'; - } - - int width_start = tag.find("WIDTH=")+7; - int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); - - int height_start = tag.find("HEIGHT=")+8; - int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); - - if (createWidget) - { - MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", - MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - box->setImageTexture("bookart\\" + image); - box->setProperty("NeedMouse", "false"); - } - - mWidth = std::max(mWidth, width); - mHeight += height; -} - -void BookTextParser::parseDiv(std::string tag) -{ - if (tag.find("ALIGN=") == std::string::npos) - return; - - int align_start = tag.find("ALIGN=")+7; - std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); - if (align == "CENTER") - mTextStyle.mTextAlign = MyGUI::Align::HCenter; - else if (align == "LEFT") - mTextStyle.mTextAlign = MyGUI::Align::Left; -} - -void BookTextParser::parseFont(std::string tag) -{ - if (tag.find("COLOR=") != std::string::npos) - { - int color_start = tag.find("COLOR=")+7; - std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); - - mTextStyle.mColour = MyGUI::Colour( - convertFromHex(color.substr(0, 2))/255.0, - convertFromHex(color.substr(2, 2))/255.0, - convertFromHex(color.substr(4, 2))/255.0); - } - if (tag.find("FACE=") != std::string::npos) - { - int face_start = tag.find("FACE=")+6; - std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); - - if (face != "Magic Cards") - mTextStyle.mFont = face; - } - if (tag.find("SIZE=") != std::string::npos) - { - /// \todo - } -} - -void BookTextParser::parseSubText(std::string text) -{ - if (text[0] == '<') - { - const size_t tagStart = 1; - const size_t tagEnd = text.find('>', tagStart); - if (tagEnd == std::string::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - const std::string tag = text.substr(tagStart, tagEnd - tagStart); - - if (boost::algorithm::starts_with(tag, "IMG")) - parseImage(tag); - if (boost::algorithm::starts_with(tag, "FONT")) - parseFont(tag); - if (boost::algorithm::starts_with(tag, "DOV")) - parseDiv(tag); - - text.erase(0, tagEnd + 1); - } - - size_t tagStart = std::string::npos; - std::string realText; // real text, without tags - for (size_t i = 0; i= text.size()) + const size_t tagStart = index + 1; + const size_t tagEnd = text.find('>', tagStart); + if (tagEnd == UTFString::npos) throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - ++i; - c = text[i]; + const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8(); + + if (boost::algorithm::starts_with(tag, "IMG")) + { + const int h = mHeight; + parseImage(tag, false); + currentHeight += (mHeight - h); + currentWidth = 0; + } + else if (boost::algorithm::starts_with(tag, "FONT")) + { + parseFont(tag); + if (currentWidth != 0) { + currentHeight += currentFontHeight(); + currentWidth = 0; + } + currentWidth = 0; + } + else if (boost::algorithm::starts_with(tag, "DIV")) + { + parseDiv(tag); + if (currentWidth != 0) { + currentHeight += currentFontHeight(); + currentWidth = 0; + } + } + index = tagEnd; + } + else if (ch == NEWLINE) + { + currentHeight += currentFontHeight(); + currentWidth = 0; + currentWordStart = index; + } + else if (ch == SPACE) + { + currentWidth += 3; // keep this in sync with the font's SpaceWidth property + currentWordStart = index; + } + else + { + currentWidth += widthForCharGlyph(ch); + } + + if (currentWidth > width) + { + currentHeight += currentFontHeight(); + currentWidth = 0; + // add size of the current word + UTFString word = text.substr(currentWordStart, index - currentWordStart); + for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it) + currentWidth += widthForCharGlyph(it.getCharacter()); + } + index += UTFString::_utf16_char_length(ch); + } + const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) + ? currentWordStart : index; + + result.push_back(text.substr(0, pageEnd).asUTF8()); + text.erase(0, pageEnd); + } + + std::vector nonEmptyPages; + boost::copy(result | boost::adaptors::filtered(is_not_empty), std::back_inserter(nonEmptyPages)); + return nonEmptyPages; + } + + float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const + { + std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName) + ->getGlyphInfo(unicodeChar)->width; + } + + float BookTextParser::currentFontHeight() const + { + std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); + } + + MyGUI::IntSize BookTextParser::parsePage(std::string text, MyGUI::Widget* parent, const int width) + { + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + text = Interpreter::fixDefinesBook(text, interpreterContext); + + mParent = parent; + mWidth = width; + mHeight = 0; + + assert(mParent); + while (mParent->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + } + + // remove trailing " + if (text[text.size()-1] == '\"') + text.erase(text.size()-1); + + parseSubText(text); + return MyGUI::IntSize(mWidth, mHeight); + } + + MyGUI::IntSize BookTextParser::parseScroll(std::string text, MyGUI::Widget* parent, const int width) + { + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + text = Interpreter::fixDefinesBook(text, interpreterContext); + + mParent = parent; + mWidth = width; + mHeight = 0; + + assert(mParent); + while (mParent->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + } + + boost::algorithm::replace_all(text, "\n", "\n"); + boost::algorithm::replace_all(text, "\r", "\r"); + boost::algorithm::replace_all(text, "
", "\n\n"); + boost::algorithm::replace_all(text, "

", "\n\n"); // tweaking by adding another newline to see if that spaces out better + boost::algorithm::trim_left(text); + + // remove trailing " + if (text[text.size()-1] == '\"') + text.erase(text.size()-1); + + parseSubText(text); + return MyGUI::IntSize(mWidth, mHeight); + } + + + void BookTextParser::parseImage(std::string tag, bool createWidget) + { + int src_start = tag.find("SRC=")+5; + std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start); + + // fix texture extension to .dds + if (image.size() > 4) + { + image[image.size()-3] = 'd'; + image[image.size()-2] = 'd'; + image[image.size()-1] = 's'; + } + + int width_start = tag.find("WIDTH=")+7; + int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); + + int height_start = tag.find("HEIGHT=")+8; + int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); + + if (createWidget) + { + MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", + MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, + mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + box->setImageTexture("bookart\\" + image); + box->setProperty("NeedMouse", "false"); + } + + mWidth = std::max(mWidth, width); + mHeight += height; + } + + void BookTextParser::parseDiv(std::string tag) + { + if (tag.find("ALIGN=") == std::string::npos) + return; + + int align_start = tag.find("ALIGN=")+7; + std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); + if (align == "CENTER") + mTextStyle.mTextAlign = MyGUI::Align::HCenter; + else if (align == "LEFT") + mTextStyle.mTextAlign = MyGUI::Align::Left; + } + + void BookTextParser::parseFont(std::string tag) + { + if (tag.find("COLOR=") != std::string::npos) + { + int color_start = tag.find("COLOR=")+7; + std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); + + mTextStyle.mColour = MyGUI::Colour( + convertFromHex(color.substr(0, 2))/255.0, + convertFromHex(color.substr(2, 2))/255.0, + convertFromHex(color.substr(4, 2))/255.0); + } + if (tag.find("FACE=") != std::string::npos) + { + int face_start = tag.find("FACE=")+6; + std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); + + if (face != "Magic Cards") + mTextStyle.mFont = face; + } + if (tag.find("SIZE=") != std::string::npos) + { + /// \todo + } + } + + void BookTextParser::parseSubText(std::string text) + { + if (text[0] == '<') + { + const size_t tagStart = 1; + const size_t tagEnd = text.find('>', tagStart); + if (tagEnd == std::string::npos) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + const std::string tag = text.substr(tagStart, tagEnd - tagStart); + + if (boost::algorithm::starts_with(tag, "IMG")) + parseImage(tag); + if (boost::algorithm::starts_with(tag, "FONT")) + parseFont(tag); + if (boost::algorithm::starts_with(tag, "DIV")) + parseDiv(tag); + + text.erase(0, tagEnd + 1); + } + + size_t tagStart = std::string::npos; + std::string realText; // real text, without tags + for (size_t i = 0; i= text.size()) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + ++i; + c = text[i]; + } + continue; + } + else + { + tagStart = i; + break; } - continue; } else - { - tagStart = i; - break; - } + realText += c; + } + + MyGUI::EditBox* box = mParent->createWidget("NormalText", + MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, + mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + box->setProperty("Static", "true"); + box->setProperty("MultiLine", "true"); + box->setProperty("WordWrap", "true"); + box->setProperty("NeedMouse", "false"); + box->setMaxTextLength(realText.size()); + box->setTextAlign(mTextStyle.mTextAlign); + box->setTextColour(mTextStyle.mColour); + box->setFontName(mTextStyle.mFont); + box->setCaption(realText); + box->setSize(box->getSize().width, box->getTextSize().height); + mHeight += box->getTextSize().height; + + if (tagStart != std::string::npos) + { + parseSubText(text.substr(tagStart, text.size())); } - else - realText += c; } - MyGUI::EditBox* box = mParent->createWidget("NormalText", - MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - box->setProperty("Static", "true"); - box->setProperty("MultiLine", "true"); - box->setProperty("WordWrap", "true"); - box->setProperty("NeedMouse", "false"); - box->setMaxTextLength(realText.size()); - box->setTextAlign(mTextStyle.mTextAlign); - box->setTextColour(mTextStyle.mColour); - box->setFontName(mTextStyle.mFont); - box->setCaption(realText); - box->setSize(box->getSize().width, box->getTextSize().height); - mHeight += box->getTextSize().height; - - if (tagStart != std::string::npos) - { - parseSubText(text.substr(tagStart, text.size())); - } } diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index ab1ee3af4d..a32d98fe58 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -32,7 +32,16 @@ namespace MWGui * @param maximum width * @return size of the created widgets */ - MyGUI::IntSize parse(std::string text, MyGUI::Widget* parent, const int width); + MyGUI::IntSize parsePage(std::string text, MyGUI::Widget* parent, const int width); + + /** + * Parse markup as MyGUI widgets + * @param markup to parse + * @param parent for the created widgets + * @param maximum width + * @return size of the created widgets + */ + MyGUI::IntSize parseScroll(std::string text, MyGUI::Widget* parent, const int width); /** * Split the specified text into pieces that fit in the area specified by width and height parameters diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 0a31a428b8..469c45936f 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -1,548 +1,540 @@ #include "hud.hpp" -#include - -#include -#include - #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" - -#include "../mwgui/widgets.hpp" +#include "../mwworld/class.hpp" #include "inventorywindow.hpp" -#include "container.hpp" #include "console.hpp" #include "spellicons.hpp" +#include "itemmodel.hpp" +#include "container.hpp" -using namespace MWGui; - -HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) - : Layout("openmw_hud.layout") - , mHealth(NULL) - , mMagicka(NULL) - , mStamina(NULL) - , mWeapImage(NULL) - , mSpellImage(NULL) - , mWeapStatus(NULL) - , mSpellStatus(NULL) - , mEffectBox(NULL) - , mMinimap(NULL) - , mCompass(NULL) - , mCrosshair(NULL) - , mFpsBox(NULL) - , mFpsCounter(NULL) - , mTriangleCounter(NULL) - , mBatchCounter(NULL) - , mHealthManaStaminaBaseLeft(0) - , mWeapBoxBaseLeft(0) - , mSpellBoxBaseLeft(0) - , mEffectBoxBaseRight(0) - , mMinimapBoxBaseRight(0) - , mDragAndDrop(dragAndDrop) - , mCellNameTimer(0.0f) - , mCellNameBox(NULL) - , mMapVisible(true) - , mWeaponVisible(true) - , mSpellVisible(true) - , mWorldMouseOver(false) +namespace MWGui { - setCoord(0,0, width, height); - // Energy bars - getWidget(mHealthFrame, "HealthFrame"); - getWidget(mHealth, "Health"); - getWidget(mMagicka, "Magicka"); - getWidget(mStamina, "Stamina"); - mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); - - MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; - getWidget(healthFrame, "HealthFrame"); - getWidget(magickaFrame, "MagickaFrame"); - getWidget(fatigueFrame, "FatigueFrame"); - healthFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // Item and spell images and status bars - getWidget(mWeapBox, "WeapBox"); - getWidget(mWeapImage, "WeapImage"); - getWidget(mWeapStatus, "WeapStatus"); - mWeapBoxBaseLeft = mWeapBox->getLeft(); - mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); - - getWidget(mSpellBox, "SpellBox"); - getWidget(mSpellImage, "SpellImage"); - getWidget(mSpellStatus, "SpellStatus"); - mSpellBoxBaseLeft = mSpellBox->getLeft(); - mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); - - getWidget(mEffectBox, "EffectBox"); - mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); - - getWidget(mMinimapBox, "MiniMapBox"); - mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); - getWidget(mMinimap, "MiniMap"); - getWidget(mCompass, "Compass"); - getWidget(mMinimapButton, "MiniMapButton"); - mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); - - getWidget(mCellNameBox, "CellName"); - getWidget(mWeaponSpellBox, "WeaponSpellName"); - - getWidget(mCrosshair, "Crosshair"); - - setFpsLevel(fpsLevel); - - getWidget(mTriangleCounter, "TriangleCounter"); - getWidget(mBatchCounter, "BatchCounter"); - - LocalMapBase::init(mMinimap, mCompass, this); - - mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); - mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); - mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus); - - mSpellIcons = new SpellIcons(); -} - -HUD::~HUD() -{ - delete mSpellIcons; -} - -void HUD::setFpsLevel(int level) -{ - mFpsCounter = 0; - - MyGUI::Widget* fps; - getWidget(fps, "FPSBoxAdv"); - fps->setVisible(false); - getWidget(fps, "FPSBox"); - fps->setVisible(false); - - if (level == 2) + HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) + : Layout("openmw_hud.layout") + , mHealth(NULL) + , mMagicka(NULL) + , mStamina(NULL) + , mWeapImage(NULL) + , mSpellImage(NULL) + , mWeapStatus(NULL) + , mSpellStatus(NULL) + , mEffectBox(NULL) + , mMinimap(NULL) + , mCompass(NULL) + , mCrosshair(NULL) + , mFpsBox(NULL) + , mFpsCounter(NULL) + , mTriangleCounter(NULL) + , mBatchCounter(NULL) + , mHealthManaStaminaBaseLeft(0) + , mWeapBoxBaseLeft(0) + , mSpellBoxBaseLeft(0) + , mEffectBoxBaseRight(0) + , mMinimapBoxBaseRight(0) + , mDragAndDrop(dragAndDrop) + , mCellNameTimer(0.0f) + , mCellNameBox(NULL) + , mMapVisible(true) + , mWeaponVisible(true) + , mSpellVisible(true) + , mWorldMouseOver(false) { - getWidget(mFpsBox, "FPSBoxAdv"); - mFpsBox->setVisible(true); - getWidget(mFpsCounter, "FPSCounterAdv"); + setCoord(0,0, width, height); + + // Energy bars + getWidget(mHealthFrame, "HealthFrame"); + getWidget(mHealth, "Health"); + getWidget(mMagicka, "Magicka"); + getWidget(mStamina, "Stamina"); + mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); + + MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; + getWidget(healthFrame, "HealthFrame"); + getWidget(magickaFrame, "MagickaFrame"); + getWidget(fatigueFrame, "FatigueFrame"); + healthFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // Item and spell images and status bars + getWidget(mWeapBox, "WeapBox"); + getWidget(mWeapImage, "WeapImage"); + getWidget(mWeapStatus, "WeapStatus"); + mWeapBoxBaseLeft = mWeapBox->getLeft(); + mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); + + getWidget(mSpellBox, "SpellBox"); + getWidget(mSpellImage, "SpellImage"); + getWidget(mSpellStatus, "SpellStatus"); + mSpellBoxBaseLeft = mSpellBox->getLeft(); + mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); + + getWidget(mEffectBox, "EffectBox"); + mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); + + getWidget(mMinimapBox, "MiniMapBox"); + mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); + getWidget(mMinimap, "MiniMap"); + getWidget(mCompass, "Compass"); + getWidget(mMinimapButton, "MiniMapButton"); + mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); + + getWidget(mCellNameBox, "CellName"); + getWidget(mWeaponSpellBox, "WeaponSpellName"); + + getWidget(mCrosshair, "Crosshair"); + + setFpsLevel(fpsLevel); + + getWidget(mTriangleCounter, "TriangleCounter"); + getWidget(mBatchCounter, "BatchCounter"); + + LocalMapBase::init(mMinimap, mCompass, this); + + mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); + mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); + mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus); + + mSpellIcons = new SpellIcons(); } - else if (level == 1) + + HUD::~HUD() { - getWidget(mFpsBox, "FPSBox"); - mFpsBox->setVisible(true); - getWidget(mFpsCounter, "FPSCounter"); + delete mSpellIcons; } -} -void HUD::setFPS(float fps) -{ - if (mFpsCounter) - mFpsCounter->setCaption(boost::lexical_cast((int)fps)); -} - -void HUD::setTriangleCount(unsigned int count) -{ - mTriangleCounter->setCaption(boost::lexical_cast(count)); -} - -void HUD::setBatchCount(unsigned int count) -{ - mBatchCounter->setCaption(boost::lexical_cast(count)); -} - -void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) -{ - static const char *ids[] = + void HUD::setFpsLevel(int level) { - "HBar", "MBar", "FBar", 0 - }; + mFpsCounter = 0; - for (int i=0; ids[i]; ++i) - if (ids[i]==id) + MyGUI::Widget* fps; + getWidget(fps, "FPSBoxAdv"); + fps->setVisible(false); + getWidget(fps, "FPSBox"); + fps->setVisible(false); + + if (level == 2) { - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - switch (i) - { - case 0: - mHealth->setProgressRange (value.getModified()); - mHealth->setProgressPosition (value.getCurrent()); - getWidget(w, "HealthFrame"); - w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); - break; - case 1: - mMagicka->setProgressRange (value.getModified()); - mMagicka->setProgressPosition (value.getCurrent()); - getWidget(w, "MagickaFrame"); - w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); - break; - case 2: - mStamina->setProgressRange (value.getModified()); - mStamina->setProgressPosition (value.getCurrent()); - getWidget(w, "FatigueFrame"); - w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); - break; - } + getWidget(mFpsBox, "FPSBoxAdv"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounterAdv"); + } + else if (level == 1) + { + getWidget(mFpsBox, "FPSBox"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounter"); } -} - -void HUD::onWorldClicked(MyGUI::Widget* _sender) -{ - if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) - return; - - if (mDragAndDrop->mIsOnDragAndDrop) - { - // drop item into the gameworld - MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); - - MWBase::World* world = MWBase::Environment::get().getWorld(); - - MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); - float mouseX = cursorPosition.left / float(viewSize.width); - float mouseY = cursorPosition.top / float(viewSize.height); - - int origCount = object.getRefData().getCount(); - object.getRefData().setCount(mDragAndDrop->mDraggedCount); - - if (world->canPlaceObject(mouseX, mouseY)) - world->placeObject(object, mouseX, mouseY); - else - world->dropObjectOnGround(world->getPlayer().getPlayer(), object); - - MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - // remove object from the container it was coming from - object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); - - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - mDragAndDrop->mDraggedWidget = 0; - - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); } - else - { - GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); - if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) + void HUD::setFPS(float fps) + { + if (mFpsCounter) + mFpsCounter->setCaption(boost::lexical_cast((int)fps)); + } + + void HUD::setTriangleCount(unsigned int count) + { + mTriangleCounter->setCaption(boost::lexical_cast(count)); + } + + void HUD::setBatchCount(unsigned int count) + { + mBatchCounter->setCaption(boost::lexical_cast(count)); + } + + void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) + { + static const char *ids[] = + { + "HBar", "MBar", "FBar", 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) + { + MyGUI::Widget* w; + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + switch (i) + { + case 0: + mHealth->setProgressRange (value.getModified()); + mHealth->setProgressPosition (value.getCurrent()); + getWidget(w, "HealthFrame"); + w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + break; + case 1: + mMagicka->setProgressRange (value.getModified()); + mMagicka->setProgressPosition (value.getCurrent()); + getWidget(w, "MagickaFrame"); + w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + break; + case 2: + mStamina->setProgressRange (value.getModified()); + mStamina->setProgressPosition (value.getCurrent()); + getWidget(w, "FatigueFrame"); + w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + break; + } + } + } + + void HUD::onWorldClicked(MyGUI::Widget* _sender) + { + if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) return; - MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); - - if (mode == GM_Console) - MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); - else if ((mode == GM_Container) || (mode == GM_Inventory)) + if (mDragAndDrop->mIsOnDragAndDrop) { - // pick up object - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); - } - } -} + // drop item into the gameworld + MWWorld::Ptr object = mDragAndDrop->mItem.mBase; -void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) -{ - if (mDragAndDrop->mIsOnDragAndDrop) - { - mWorldMouseOver = false; + MWBase::World* world = MWBase::Environment::get().getWorld(); - MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); - float mouseX = cursorPosition.left / float(viewSize.width); - float mouseY = cursorPosition.top / float(viewSize.height); + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); + float mouseX = cursorPosition.left / float(viewSize.width); + float mouseY = cursorPosition.top / float(viewSize.height); - MWBase::World* world = MWBase::Environment::get().getWorld(); + int origCount = object.getRefData().getCount(); + object.getRefData().setCount(mDragAndDrop->mDraggedCount); - // if we can't drop the object at the wanted position, show the "drop on ground" cursor. - bool canDrop = world->canPlaceObject(mouseX, mouseY); + if (world->canPlaceObject(mouseX, mouseY)) + world->placeObject(object, mouseX, mouseY); + else + world->dropObjectOnGround(world->getPlayer().getPlayer(), object); - if (!canDrop) - MWBase::Environment::get().getWindowManager()->changePointer("drop_ground"); - else MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + std::string sound = MWWorld::Class::get(object).getDownSoundId(object); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + object.getRefData().setCount(origCount); + + // remove object from the container it was coming from + mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); + mDragAndDrop->finish(); + } + else + { + GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + + if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) + return; + + MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); + + if (mode == GM_Console) + MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); + else if ((mode == GM_Container) || (mode == GM_Inventory)) + { + // pick up object + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); + } + } } - else + + void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) + { + if (mDragAndDrop->mIsOnDragAndDrop) + { + mWorldMouseOver = false; + + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); + float mouseX = cursorPosition.left / float(viewSize.width); + float mouseY = cursorPosition.top / float(viewSize.height); + + MWBase::World* world = MWBase::Environment::get().getWorld(); + + // if we can't drop the object at the wanted position, show the "drop on ground" cursor. + bool canDrop = world->canPlaceObject(mouseX, mouseY); + + if (!canDrop) + MWBase::Environment::get().getWindowManager()->changePointer("drop_ground"); + else + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + + } + else + { + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + mWorldMouseOver = true; + } + } + + void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new) { MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - mWorldMouseOver = true; - } -} - -void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new) -{ - MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - mWorldMouseOver = false; -} - -void HUD::onHMSClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); -} - -void HUD::onMapClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); -} - -void HUD::onWeaponClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); -} - -void HUD::onMagicClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); -} - -void HUD::setCellName(const std::string& cellName) -{ - if (mCellName != cellName) - { - mCellNameTimer = 5.0f; - mCellName = cellName; - - mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); - mCellNameBox->setVisible(mMapVisible); - } -} - -void HUD::onFrame(float dt) -{ - mCellNameTimer -= dt; - mWeaponSpellTimer -= dt; - if (mCellNameTimer < 0) - mCellNameBox->setVisible(false); - if (mWeaponSpellTimer < 0) - mWeaponSpellBox->setVisible(false); -} - -void HUD::onResChange(int width, int height) -{ - setCoord(0, 0, width, height); -} - -void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) -{ - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - - std::string spellName = spell->mName; - if (spellName != mSpellName && mSpellVisible) - { - mWeaponSpellTimer = 5.0f; - mSpellName = spellName; - mWeaponSpellBox->setCaption(mSpellName); - mWeaponSpellBox->setVisible(true); + mWorldMouseOver = false; } - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(successChancePercent); - - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - - mSpellBox->setUserString("ToolTipType", "Spell"); - mSpellBox->setUserString("Spell", spellId); - - // use the icon of the first effect - const ESM::MagicEffect* effect = - MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); - - std::string icon = effect->mIcon; - int slashPos = icon.find("\\"); - icon.insert(slashPos+1, "b_"); - icon = std::string("icons\\") + icon; - Widgets::fixTexturePath(icon); - mSpellImage->setImageTexture(icon); -} - -void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) -{ - std::string itemName = MWWorld::Class::get(item).getName(item); - if (itemName != mSpellName && mSpellVisible) + void HUD::onHMSClicked(MyGUI::Widget* _sender) { - mWeaponSpellTimer = 5.0f; - mSpellName = itemName; - mWeaponSpellBox->setCaption(mSpellName); - mWeaponSpellBox->setVisible(true); + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); } - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(chargePercent); - - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - - mSpellBox->setUserString("ToolTipType", "ItemPtr"); - mSpellBox->setUserData(item); - - mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) - , MyGUI::Align::Stretch); - - std::string path = std::string("icons\\"); - path+=MWWorld::Class::get(item).getInventoryIcon(item); - Widgets::fixTexturePath(path); - itemBox->setImageTexture(path); - itemBox->setNeedMouseFocus(false); -} - -void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) -{ - std::string itemName = MWWorld::Class::get(item).getName(item); - if (itemName != mWeaponName && mWeaponVisible) + void HUD::onMapClicked(MyGUI::Widget* _sender) { - mWeaponSpellTimer = 5.0f; - mWeaponName = itemName; - mWeaponSpellBox->setCaption(mWeaponName); - mWeaponSpellBox->setVisible(true); + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); } - mWeapBox->setUserString("ToolTipType", "ItemPtr"); - mWeapBox->setUserData(item); - - mWeapStatus->setProgressRange(100); - mWeapStatus->setProgressPosition(durabilityPercent); - - if (mWeapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); - - std::string path = std::string("icons\\"); - path+=MWWorld::Class::get(item).getInventoryIcon(item); - Widgets::fixTexturePath(path); - - if (MWWorld::Class::get(item).getEnchantment(item) != "") + void HUD::onWeaponClicked(MyGUI::Widget* _sender) { - mWeapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = mWeapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); + } + + void HUD::onMagicClicked(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); + } + + void HUD::setCellName(const std::string& cellName) + { + if (mCellName != cellName) + { + mCellNameTimer = 5.0f; + mCellName = cellName; + + mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); + mCellNameBox->setVisible(mMapVisible); + } + } + + void HUD::onFrame(float dt) + { + mCellNameTimer -= dt; + mWeaponSpellTimer -= dt; + if (mCellNameTimer < 0) + mCellNameBox->setVisible(false); + if (mWeaponSpellTimer < 0) + mWeaponSpellBox->setVisible(false); + } + + void HUD::onResChange(int width, int height) + { + setCoord(0, 0, width, height); + } + + void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) + { + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + + std::string spellName = spell->mName; + if (spellName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(successChancePercent); + + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + + mSpellBox->setUserString("ToolTipType", "Spell"); + mSpellBox->setUserString("Spell", spellId); + + // use the icon of the first effect + const ESM::MagicEffect* effect = + MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); + + std::string icon = effect->mIcon; + int slashPos = icon.find("\\"); + icon.insert(slashPos+1, "b_"); + icon = std::string("icons\\") + icon; + Widgets::fixTexturePath(icon); + mSpellImage->setImageTexture(icon); + } + + void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) + { + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = itemName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(chargePercent); + + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + + mSpellBox->setUserString("ToolTipType", "ItemPtr"); + mSpellBox->setUserData(item); + + mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) , MyGUI::Align::Stretch); + + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); itemBox->setImageTexture(path); itemBox->setNeedMouseFocus(false); } - else - mWeapImage->setImageTexture(path); -} -void HUD::unsetSelectedSpell() -{ - std::string spellName = "#{sNone}"; - if (spellName != mSpellName && mSpellVisible) + void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) { - mWeaponSpellTimer = 5.0f; - mSpellName = spellName; - mWeaponSpellBox->setCaptionWithReplacing(mSpellName); - mWeaponSpellBox->setVisible(true); + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mWeaponName && mWeaponVisible) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaption(mWeaponName); + mWeaponSpellBox->setVisible(true); + } + + mWeapBox->setUserString("ToolTipType", "ItemPtr"); + mWeapBox->setUserData(item); + + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(durabilityPercent); + + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); + + if (MWWorld::Class::get(item).getEnchantment(item) != "") + { + mWeapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mWeapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + , MyGUI::Align::Stretch); + itemBox->setImageTexture(path); + itemBox->setNeedMouseFocus(false); + } + else + mWeapImage->setImageTexture(path); } - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(0); - mSpellImage->setImageTexture(""); - mSpellBox->clearUserStrings(); -} - -void HUD::unsetSelectedWeapon() -{ - std::string itemName = "#{sSkillHandtohand}"; - if (itemName != mWeaponName && mWeaponVisible) + void HUD::unsetSelectedSpell() { - mWeaponSpellTimer = 5.0f; - mWeaponName = itemName; - mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); - mWeaponSpellBox->setVisible(true); + std::string spellName = "#{sNone}"; + if (spellName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaptionWithReplacing(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(0); + mSpellImage->setImageTexture(""); + mSpellBox->clearUserStrings(); + } + + void HUD::unsetSelectedWeapon() + { + std::string itemName = "#{sSkillHandtohand}"; + if (itemName != mWeaponName && mWeaponVisible) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); + mWeaponSpellBox->setVisible(true); + } + + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(0); + mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); + mWeapBox->clearUserStrings(); + } + + void HUD::setCrosshairVisible(bool visible) + { + mCrosshair->setVisible (visible); + } + + void HUD::setHmsVisible(bool visible) + { + mHealth->setVisible(visible); + mMagicka->setVisible(visible); + mStamina->setVisible(visible); + updatePositions(); + } + + void HUD::setWeapVisible(bool visible) + { + mWeapBox->setVisible(visible); + updatePositions(); + } + + void HUD::setSpellVisible(bool visible) + { + mSpellBox->setVisible(visible); + updatePositions(); + } + + void HUD::setEffectVisible(bool visible) + { + mEffectBox->setVisible (visible); + updatePositions(); + } + + void HUD::setMinimapVisible(bool visible) + { + mMinimapBox->setVisible (visible); + updatePositions(); + } + + void HUD::updatePositions() + { + int weapDx = 0, spellDx = 0; + if (!mHealth->getVisible()) + spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; + + if (!mWeapBox->getVisible()) + spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; + + mWeaponVisible = mWeapBox->getVisible(); + mSpellVisible = mSpellBox->getVisible(); + if (!mWeaponVisible && !mSpellVisible) + mWeaponSpellBox->setVisible(false); + + mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); + mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); + + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // effect box can have variable width -> variable left coordinate + int effectsDx = 0; + if (!mMinimapBox->getVisible ()) + effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); + + mMapVisible = mMinimapBox->getVisible (); + mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); + } + + void HUD::update() + { + mSpellIcons->updateWidgets(mEffectBox, true); } - if (mWeapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); - mWeapStatus->setProgressRange(100); - mWeapStatus->setProgressPosition(0); - mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); - mWeapBox->clearUserStrings(); -} - -void HUD::setCrosshairVisible(bool visible) -{ - mCrosshair->setVisible (visible); -} - -void HUD::setHmsVisible(bool visible) -{ - mHealth->setVisible(visible); - mMagicka->setVisible(visible); - mStamina->setVisible(visible); - updatePositions(); -} - -void HUD::setWeapVisible(bool visible) -{ - mWeapBox->setVisible(visible); - updatePositions(); -} - -void HUD::setSpellVisible(bool visible) -{ - mSpellBox->setVisible(visible); - updatePositions(); -} - -void HUD::setEffectVisible(bool visible) -{ - mEffectBox->setVisible (visible); - updatePositions(); -} - -void HUD::setMinimapVisible(bool visible) -{ - mMinimapBox->setVisible (visible); - updatePositions(); -} - -void HUD::updatePositions() -{ - int weapDx = 0, spellDx = 0; - if (!mHealth->getVisible()) - spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; - - if (!mWeapBox->getVisible()) - spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; - - mWeaponVisible = mWeapBox->getVisible(); - mSpellVisible = mSpellBox->getVisible(); - if (!mWeaponVisible && !mSpellVisible) - mWeaponSpellBox->setVisible(false); - - mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); - mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); - - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // effect box can have variable width -> variable left coordinate - int effectsDx = 0; - if (!mMinimapBox->getVisible ()) - effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); - - mMapVisible = mMinimapBox->getVisible (); - mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); -} - -void HUD::update() -{ - mSpellIcons->updateWidgets(mEffectBox, true); } 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/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp new file mode 100644 index 0000000000..c78bc2c009 --- /dev/null +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -0,0 +1,97 @@ +#include "inventoryitemmodel.hpp" + +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" + +namespace MWGui +{ + +InventoryItemModel::InventoryItemModel(const MWWorld::Ptr &actor) + : mActor(actor) +{ +} + +ItemStack InventoryItemModel::getItem (ModelIndex index) +{ + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; +} + +size_t InventoryItemModel::getItemCount() +{ + return mItems.size(); +} + +ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item) +{ + size_t i = 0; + for (std::vector::iterator it = mItems.begin(); it != mItems.end(); ++it) + { + if (*it == item) + return i; + ++i; + } + return -1; +} + +void InventoryItemModel::copyItem (const ItemStack& item, size_t count) +{ + int origCount = item.mBase.getRefData().getCount(); + item.mBase.getRefData().setCount(count); + MWWorld::ContainerStoreIterator it = MWWorld::Class::get(mActor).getContainerStore(mActor).add(item.mBase); + if (*it != item.mBase) + item.mBase.getRefData().setCount(origCount); + else + item.mBase.getRefData().setCount(origCount + count); // item copied onto itself +} + + +void InventoryItemModel::removeItem (const ItemStack& item, size_t count) +{ + MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (*it == item.mBase) + { + if (it->getRefData().getCount() < static_cast(count)) + throw std::runtime_error("Not enough items in the stack to remove"); + it->getRefData().setCount(it->getRefData().getCount() - count); + return; + } + } + throw std::runtime_error("Item to remove not found in container store"); +} + +void InventoryItemModel::update() +{ + MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor); + + mItems.clear(); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + ItemStack newItem (*it, this, it->getRefData().getCount()); + + if (mActor.getTypeName() == typeid(ESM::NPC).name()) + { + MWWorld::InventoryStore& store = MWWorld::Class::get(mActor).getInventoryStore(mActor); + for (int slot=0; slot mItems; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ab7615c0ef..9fa87c4b85 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1,48 +1,47 @@ #include "inventorywindow.hpp" -#include -#include -#include -#include +#include #include -#include - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/containerstore.hpp" -#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/actiontake.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/action.hpp" -#include "widgets.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" #include "spellwindow.hpp" +#include "itemview.hpp" +#include "inventoryitemmodel.hpp" +#include "sortfilteritemmodel.hpp" +#include "tradeitemmodel.hpp" +#include "countdialog.hpp" +#include "tradewindow.hpp" +#include "container.hpp" namespace MWGui { - InventoryWindow::InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) - : ContainerBase(dragAndDrop) - , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) + InventoryWindow::InventoryWindow(DragAndDrop* dragAndDrop) + : WindowPinnableBase("openmw_inventory_window.layout") , mTrading(false) , mLastXSize(0) , mLastYSize(0) , mPreview(MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()) + , mPreviewDirty(true) + , mDragAndDrop(dragAndDrop) { static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); getWidget(mAvatar, "Avatar"); getWidget(mAvatarImage, "AvatarImage"); getWidget(mEncumbranceBar, "EncumbranceBar"); - getWidget(mEncumbranceText, "EncumbranceBarT"); getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); getWidget(mFilterApparel, "ApparelButton"); @@ -50,14 +49,15 @@ namespace MWGui getWidget(mFilterMisc, "MiscButton"); getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); + getWidget(mArmorRating, "ArmorRating"); mAvatar->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); + getWidget(mItemView, "ItemView"); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected); + mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &InventoryWindow::onBackgroundSelected); + + updatePlayer(); mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); @@ -68,23 +68,137 @@ namespace MWGui mFilterAll->setStateSelected(true); setCoord(0, 342, 498, 258); + onWindowResize(static_cast(mMainWidget)); mPreview.setup(); + } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - openContainer(player); + void InventoryWindow::updatePlayer() + { + mPtr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); + mSortModel = new SortFilterItemModel(mTradeModel); + mItemView->setModel(mSortModel); + mPreview = MWRender::InventoryPreview(mPtr); + mPreview.setup(); + } + + TradeItemModel* InventoryWindow::getTradeModel() + { + return mTradeModel; + } + + ItemModel* InventoryWindow::getModel() + { + return mTradeModel; + } + + void InventoryWindow::onBackgroundSelected() + { + if (mDragAndDrop->mIsOnDragAndDrop) + mDragAndDrop->drop(mTradeModel, mItemView); + } + + void InventoryWindow::onItemSelected (int index) + { + onItemSelectedFromSourceModel (mSortModel->mapToSource(index)); + } + + void InventoryWindow::onItemSelectedFromSourceModel (int index) + { + if (mDragAndDrop->mIsOnDragAndDrop) + { + mDragAndDrop->drop(mTradeModel, mItemView); + return; + } + + const ItemStack& item = mTradeModel->getItem(index); + + unequipItem(item.mBase); + + MWWorld::Ptr object = item.mBase; + int count = item.mCount; + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); + if (MyGUI::InputManager::getInstance().isControlPressed()) + count = 1; + + if (mTrading) + { + // check if merchant accepts item + int services = MWBase::Environment::get().getWindowManager()->getTradeWindow()->getMerchantServices(); + if (!MWWorld::Class::get(object).canSell(object, services)) + { + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sBarterDialog4}"); + return; + } + } + + if (count > 1 && !shift) + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}"; + dialog->open(MWWorld::Class::get(object).getName(object), message, count); + dialog->eventOkClicked.clear(); + if (mTrading) + dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem); + else + dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dragItem); + mSelectedItem = index; + } + else + { + mSelectedItem = index; + if (mTrading) + sellItem (NULL, count); + else + dragItem (NULL, count); + } + + // item might have been unequipped + notifyContentChanged(); + } + + void InventoryWindow::dragItem(MyGUI::Widget* sender, int count) + { + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count); + } + + void InventoryWindow::sellItem(MyGUI::Widget* sender, int count) + { + const ItemStack& item = mTradeModel->getItem(mSelectedItem); + std::string sound = MWWorld::Class::get(item.mBase).getDownSoundId(item.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + if (item.mType == ItemStack::Type_Barter) + { + // this was an item borrowed to us by the merchant + MWBase::Environment::get().getWindowManager()->getTradeWindow()->returnItem(mSelectedItem, count); + mTradeModel->returnItemBorrowedToUs(mSelectedItem, count); + } + else + { + // borrow item to the merchant + MWBase::Environment::get().getWindowManager()->getTradeWindow()->borrowItem(mSelectedItem, count); + mTradeModel->borrowItemFromUs(mSelectedItem, count); + } + + mItemView->update(); + } + + void InventoryWindow::updateItemView() + { + mItemView->update(); + mPreviewDirty = true; } void InventoryWindow::open() { updateEncumbranceBar(); - mTrading = false; + mItemView->update(); - mBoughtItems.clear(); - - onWindowResize(static_cast(mMainWidget)); - drawItems(); + notifyContentChanged(); } void InventoryWindow::onWindowResize(MyGUI::Window* _sender) @@ -98,24 +212,24 @@ namespace MWGui if (mMainWidget->getSize().width != mLastXSize || mMainWidget->getSize().height != mLastYSize) { - drawItems(); mLastXSize = mMainWidget->getSize().width; mLastYSize = mMainWidget->getSize().height; + mPreviewDirty = true; } } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) - setFilter(ContainerBase::Filter_All); + mSortModel->setCategory(SortFilterItemModel::Category_All); else if (_sender == mFilterWeapon) - setFilter(ContainerBase::Filter_Weapon); + mSortModel->setCategory(SortFilterItemModel::Category_Weapon); else if (_sender == mFilterApparel) - setFilter(ContainerBase::Filter_Apparel); + mSortModel->setCategory(SortFilterItemModel::Category_Apparel); else if (_sender == mFilterMagic) - setFilter(ContainerBase::Filter_Magic); + mSortModel->setCategory(SortFilterItemModel::Category_Magic); else if (_sender == mFilterMisc) - setFilter(ContainerBase::Filter_Misc); + mSortModel->setCategory(SortFilterItemModel::Category_Misc); mFilterAll->setStateSelected(false); mFilterWeapon->setStateSelected(false); @@ -123,35 +237,38 @@ namespace MWGui mFilterMagic->setStateSelected(false); mFilterMisc->setStateSelected(false); + mItemView->update(); + static_cast(_sender)->setStateSelected(true); } void InventoryWindow::onPinToggled() { - mWindowManager.setWeaponVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned); } void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender) { if (mDragAndDrop->mIsOnDragAndDrop) { - MWWorld::Ptr ptr = *mDragAndDrop->mDraggedWidget->getUserData(); + MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase; + mDragAndDrop->finish(); - if (mDragAndDrop->mDraggedFrom != this) + if (mDragAndDrop->mSourceModel != mTradeModel) { // add item to the player's inventory MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); MWWorld::ContainerStoreIterator it = invStore.begin(); int origCount = ptr.getRefData().getCount(); - ptr.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); + ptr.getRefData().setCount(mDragAndDrop->mDraggedCount); it = invStore.add(ptr); - (*it).getRefData().setCount(mDragAndDrop->mDraggedCount); + ptr.getRefData().setCount(origCount); + + mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); ptr = *it; } - /// \todo scripts - boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); @@ -160,15 +277,10 @@ 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); - - drawItems(); + mItemView->update(); notifyContentChanged(); } @@ -183,16 +295,15 @@ namespace MWGui if (itemSelected.isEmpty ()) return; - for (unsigned int i=0; i < mContainerWidget->getChildCount (); ++i) + for (size_t i=0; i < mTradeModel->getItemCount (); ++i) { - MyGUI::Widget* w = mContainerWidget->getChildAt (i); - - if (*w->getUserData() == itemSelected) + if (mTradeModel->getItem(i).mBase == itemSelected) { - onSelectedItem(w); + onItemSelectedFromSourceModel(i); return; } } + throw std::runtime_error("Can't find clicked item"); } } @@ -211,7 +322,7 @@ namespace MWGui return MWWorld::Ptr(); } - void InventoryWindow::_unequipItem(MWWorld::Ptr item) + void InventoryWindow::unequipItem(const MWWorld::Ptr& item) { MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); @@ -222,11 +333,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; } } @@ -238,9 +349,7 @@ namespace MWGui float capacity = MWWorld::Class::get(player).getCapacity(player); float encumbrance = MWWorld::Class::get(player).getEncumbrance(player); - mEncumbranceBar->setProgressRange(capacity); - mEncumbranceBar->setProgressPosition(encumbrance); - mEncumbranceText->setCaption( boost::lexical_cast(int(encumbrance)) + "/" + boost::lexical_cast(int(capacity)) ); + mEncumbranceBar->setValue(encumbrance, capacity); } void InventoryWindow::onFrame() @@ -264,36 +373,46 @@ namespace MWGui return 0; } - void InventoryWindow::startTrade() + void InventoryWindow::setTrading(bool trading) { - mTrading = true; + mTrading = trading; + } + + 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); - MyGUI::IntSize size = mAvatar->getSize(); + mPreviewDirty = true; - 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"); + mArmorRating->setCaptionWithReplacing ("#{sArmor}: " + + boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) { - /// \todo scripts - // make sure the object is of a type that can be picked up std::string type = object.getTypeName(); if ( (type != typeid(ESM::Apparatus).name()) @@ -303,7 +422,7 @@ namespace MWGui && (type != typeid(ESM::Ingredient).name()) && (type != typeid(ESM::Light).name()) && (type != typeid(ESM::Miscellaneous).name()) - && (type != typeid(ESM::Tool).name()) + && (type != typeid(ESM::Lockpick).name()) && (type != typeid(ESM::Probe).name()) && (type != typeid(ESM::Repair).name()) && (type != typeid(ESM::Weapon).name()) @@ -313,11 +432,9 @@ namespace MWGui if (MWWorld::Class::get(object).getName(object) == "") // objects without name presented to user can never be picked up return; - // sound - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound(sound, 1, 1); - int count = object.getRefData().getCount(); + if (object.getCellRef().mGoldValue > 1) + count = object.getCellRef().mGoldValue; // add to player inventory // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object @@ -326,31 +443,17 @@ namespace MWGui // remove from world MWBase::Environment::get().getWorld()->deleteObject (object); - mDragAndDrop->mIsOnDragAndDrop = true; - mDragAndDrop->mDraggedCount = count; - - std::string path = std::string("icons\\"); - path += MWWorld::Class::get(newObject).getInventoryIcon(newObject); - MyGUI::ImageBox* baseWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default); - baseWidget->detachFromWidget(); - baseWidget->attachToWidget(mDragAndDrop->mDragAndDropWidget); - baseWidget->setUserData(newObject); - mDragAndDrop->mDraggedWidget = baseWidget; - MyGUI::ImageBox* image = baseWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); - int pos = path.rfind("."); - path.erase(pos); - path.append(".dds"); - image->setImageTexture(path); - image->setNeedMouseFocus(false); - - // text widget that shows item count - MyGUI::TextBox* text = image->createWidget("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); - text->setTextAlign(MyGUI::Align::Right); - text->setNeedMouseFocus(false); - text->setTextShadow(true); - text->setTextShadowColour(MyGUI::Colour(0,0,0)); - text->setCaption(getCountString(count)); - mDragAndDrop->mDraggedFrom = this; + // get ModelIndex to the item + mTradeModel->update(); + size_t i=0; + for (; igetItemCount(); ++i) + { + if (mTradeModel->getItem(i).mBase == newObject) + break; + } + if (i == mTradeModel->getItemCount()) + throw std::runtime_error("Added item not found"); + mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count); } MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 7c59bab506..13c1189130 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -3,20 +3,28 @@ #include "../mwrender/characterpreview.hpp" -#include "container.hpp" -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" +#include "widgets.hpp" namespace MWGui { - class InventoryWindow : public ContainerBase, public WindowPinnableBase + class ItemView; + class SortFilterItemModel; + class TradeItemModel; + class DragAndDrop; + class ItemModel; + + class InventoryWindow : 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(); + void setTrading(bool trading); void onFrame(); @@ -32,12 +40,29 @@ namespace MWGui mPreview.rebuild(); } - protected: + TradeItemModel* getTradeModel(); + ItemModel* getModel(); + + void updateItemView(); + + void updatePlayer(); + + private: + DragAndDrop* mDragAndDrop; + + bool mPreviewDirty; + size_t mSelectedItem; + + MWWorld::Ptr mPtr; + + MWGui::ItemView* mItemView; + SortFilterItemModel* mSortModel; + TradeItemModel* mTradeModel; + MyGUI::Widget* mAvatar; MyGUI::ImageBox* mAvatarImage; MyGUI::TextBox* mArmorRating; - MyGUI::ProgressBar* mEncumbranceBar; - MyGUI::TextBox* mEncumbranceText; + Widgets::MWDynamicStat* mEncumbranceBar; MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; @@ -55,20 +80,22 @@ namespace MWGui bool mTrading; + void onItemSelected(int index); + void onItemSelectedFromSourceModel(int index); + + void onBackgroundSelected(); + + void sellItem(MyGUI::Widget* sender, int count); + void dragItem(MyGUI::Widget* sender, int count); + void onWindowResize(MyGUI::Window* _sender); void onFilterChanged(MyGUI::Widget* _sender); void onAvatarClicked(MyGUI::Widget* _sender); void onPinToggled(); + void unequipItem(const MWWorld::Ptr& item); void updateEncumbranceBar(); - - virtual bool isTrading() { return mTrading; } - virtual bool isInventory() { return true; } - virtual void _unequipItem(MWWorld::Ptr item); - - virtual void onReferenceUnavailable() { ; } - - virtual void notifyContentChanged(); + void notifyContentChanged(); }; } diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp new file mode 100644 index 0000000000..0dc2bb62c4 --- /dev/null +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -0,0 +1,98 @@ +#include "itemmodel.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" + +namespace MWGui +{ + + ItemStack::ItemStack(const MWWorld::Ptr &base, ItemModel *creator, size_t count) + : mCreator(creator) + , mCount(count) + , mFlags(0) + , mType(Type_Normal) + , mBase(base) + { + assert(base.getContainerStore()); + + if (MWWorld::Class::get(base).getEnchantment(base) != "") + mFlags |= Flag_Enchanted; + } + + ItemStack::ItemStack() + : mCreator(NULL) + , mCount(0) + , mFlags(0) + , mType(Type_Normal) + { + } + + bool ItemStack::stacks(const ItemStack &other) + { + if(mBase == other.mBase) + return true; + return mBase.getContainerStore()->stacks(mBase, other.mBase) + && other.mBase.getContainerStore()->stacks(mBase, other.mBase); + } + + bool operator == (const ItemStack& left, const ItemStack& right) + { + if (left.mType != right.mType) + return false; + if(left.mBase == right.mBase) + return true; + return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase) + && right.mBase.getContainerStore()->stacks(left.mBase, right.mBase); + } + + ItemModel::ItemModel() + { + } + + + ProxyItemModel::~ProxyItemModel() + { + delete mSourceModel; + } + + void ProxyItemModel::copyItem (const ItemStack& item, size_t count) + { + // no need to use mapToSource since itemIndex refers to an index in the sourceModel + mSourceModel->copyItem (item, count); + } + + void ProxyItemModel::removeItem (const ItemStack& item, size_t count) + { + mSourceModel->removeItem (item, count); + } + + ItemModel::ModelIndex ProxyItemModel::mapToSource (ModelIndex index) + { + const ItemStack& itemToSearch = getItem(index); + for (size_t i=0; igetItemCount(); ++i) + { + const ItemStack& item = mSourceModel->getItem(i); + if (item == itemToSearch) + return i; + } + return -1; + } + + ItemModel::ModelIndex ProxyItemModel::mapFromSource (ModelIndex index) + { + const ItemStack& itemToSearch = mSourceModel->getItem(index); + for (size_t i=0; igetIndex(item); + } + +} diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp new file mode 100644 index 0000000000..47aaf79e85 --- /dev/null +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -0,0 +1,84 @@ +#ifndef MWGUI_ITEM_MODEL_H +#define MWGUI_ITEM_MODEL_H + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + + class ItemModel; + + /// @brief A single item stack managed by an item model + struct ItemStack + { + ItemStack (const MWWorld::Ptr& base, ItemModel* creator, size_t count); + ItemStack(); + bool stacks (const ItemStack& other); + ///< like operator==, only without checking mType + + enum Type + { + Type_Barter, + Type_Equipped, + Type_Normal + }; + Type mType; + + enum Flags + { + Flag_Enchanted = (1<<0) + }; + int mFlags; + + ItemModel* mCreator; + size_t mCount; + MWWorld::Ptr mBase; + }; + + bool operator == (const ItemStack& left, const ItemStack& right); + + + /// @brief The base class that all item models should derive from. + class ItemModel + { + public: + ItemModel(); + virtual ~ItemModel() {} + + typedef int ModelIndex; + + virtual ItemStack getItem (ModelIndex index) = 0; + ///< throws for invalid index + virtual size_t getItemCount() = 0; + + virtual ModelIndex getIndex (ItemStack item) = 0; + + virtual void update() = 0; + + virtual void copyItem (const ItemStack& item, size_t count) = 0; + virtual void removeItem (const ItemStack& item, size_t count) = 0; + + private: + ItemModel(const ItemModel&); + ItemModel& operator=(const ItemModel&); + }; + + /// @brief A proxy item model can be used to filter or rearrange items from a source model (or even add new items to it). + /// The neat thing is that this does not actually alter the source model. + class ProxyItemModel : public ItemModel + { + public: + virtual ~ProxyItemModel(); + virtual void copyItem (const ItemStack& item, size_t count); + virtual void removeItem (const ItemStack& item, size_t count); + virtual ModelIndex getIndex (ItemStack item); + + ModelIndex mapToSource (ModelIndex index); + ModelIndex mapFromSource (ModelIndex index); + protected: + ItemModel* mSourceModel; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 14b1cf8ee5..a57c6d81f1 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -1,19 +1,17 @@ #include "itemselection.hpp" +#include "itemview.hpp" +#include "inventoryitemmodel.hpp" +#include "sortfilteritemmodel.hpp" + namespace MWGui { - ItemSelectionDialog::ItemSelectionDialog(const std::string &label, ContainerBase::Filter filter, MWBase::WindowManager& parWindowManager) - : ContainerBase(NULL) - , WindowModal("openmw_itemselection_dialog.layout", parWindowManager) + ItemSelectionDialog::ItemSelectionDialog(const std::string &label) + : WindowModal("openmw_itemselection_dialog.layout") { - mFilter = filter; - - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); + getWidget(mItemView, "ItemView"); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &ItemSelectionDialog::onSelectedItem); MyGUI::TextBox* l; getWidget(l, "Label"); @@ -26,9 +24,29 @@ namespace MWGui center(); } - void ItemSelectionDialog::onSelectedItemImpl(MWWorld::Ptr item) + void ItemSelectionDialog::openContainer(const MWWorld::Ptr& container) { - eventItemSelected(item); + mModel = new InventoryItemModel(container); + mSortModel = new SortFilterItemModel(mModel); + mItemView->setModel(mSortModel); + } + + void ItemSelectionDialog::setCategory(int category) + { + mSortModel->setCategory(category); + mItemView->update(); + } + + void ItemSelectionDialog::setFilter(int filter) + { + mSortModel->setFilter(filter); + mItemView->update(); + } + + void ItemSelectionDialog::onSelectedItem(int index) + { + ItemStack item = mSortModel->getItem(index); + eventItemSelected(item.mBase); } void ItemSelectionDialog::onCancelButtonClicked(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp index cab125f1f8..d6d19d9e1a 100644 --- a/apps/openmw/mwgui/itemselection.hpp +++ b/apps/openmw/mwgui/itemselection.hpp @@ -1,14 +1,15 @@ #include "container.hpp" -#include "../mwworld/ptr.hpp" - namespace MWGui { + class ItemView; + class SortFilterItemModel; + class InventoryItemModel; - class ItemSelectionDialog : public ContainerBase, public WindowModal + class ItemSelectionDialog : public WindowModal { public: - ItemSelectionDialog(const std::string& label, ContainerBase::Filter filter, MWBase::WindowManager& parWindowManager); + ItemSelectionDialog(const std::string& label); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Item; @@ -16,11 +17,16 @@ namespace MWGui EventHandle_Item eventItemSelected; EventHandle_Void eventDialogCanceled; + void openContainer (const MWWorld::Ptr& container); + void setCategory(int category); + void setFilter(int filter); private: - virtual void onReferenceUnavailable() { ; } + ItemView* mItemView; + SortFilterItemModel* mSortModel; + InventoryItemModel* mModel; - virtual void onSelectedItemImpl(MWWorld::Ptr item); + void onSelectedItem(int index); void onCancelButtonClicked(MyGUI::Widget* sender); }; diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp new file mode 100644 index 0000000000..5155d27120 --- /dev/null +++ b/apps/openmw/mwgui/itemview.cpp @@ -0,0 +1,200 @@ +#include "itemview.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include "../mwworld/class.hpp" + +#include "itemmodel.hpp" + +namespace +{ + std::string getCountString(const int count) + { + if (count == 1) + return ""; + if (count > 9999) + return boost::lexical_cast(int(count/1000.f)) + "k"; + else + return boost::lexical_cast(count); + } +} + + +namespace MWGui +{ + +ItemView::ItemView() + : mModel(NULL) +{ +} + +ItemView::~ItemView() +{ + delete mModel; +} + +void ItemView::setModel(ItemModel *model) +{ + delete mModel; + mModel = model; + update(); +} + +void ItemView::initialiseOverride() +{ + Base::initialiseOverride(); + + assignWidget(mScrollView, "ScrollView"); + if (mScrollView == NULL) + throw std::runtime_error("Item view needs a scroll view"); + + mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); +} + +void ItemView::update() +{ + while (mScrollView->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); + + if (!mModel) + return; + + int x = 0; + int y = 0; + int maxHeight = mScrollView->getSize().height - 58; + + mModel->update(); + + MyGUI::Widget* dragArea = mScrollView->createWidget("",0,0,mScrollView->getWidth(),mScrollView->getHeight(), + MyGUI::Align::Stretch); + dragArea->setNeedMouseFocus(true); + dragArea->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedBackground); + dragArea->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheel); + + for (ItemModel::ModelIndex i=0; i(mModel->getItemCount()); ++i) + { + const ItemStack& item = mModel->getItem(i); + + /// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item.mBase).getInventoryIcon(item.mBase); + + // background widget (for the "equipped" frame and magic item background image) + bool isMagic = (item.mFlags & ItemStack::Flag_Enchanted); + MyGUI::ImageBox* backgroundWidget = dragArea->createWidget("ImageBox", + MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); + backgroundWidget->setUserString("ToolTipType", "ItemModelIndex"); + backgroundWidget->setUserData(std::make_pair(i, mModel)); + + std::string backgroundTex = "textures\\menu_icon"; + if (isMagic) + backgroundTex += "_magic"; + if (item.mType == ItemStack::Type_Normal) + { + if (!isMagic) + backgroundTex = ""; + } + else if (item.mType == ItemStack::Type_Equipped) + backgroundTex += "_equip"; + else if (item.mType == ItemStack::Type_Barter) + backgroundTex += "_barter"; + + if (backgroundTex != "") + backgroundTex += ".dds"; + + backgroundWidget->setImageTexture(backgroundTex); + if ((item.mType == ItemStack::Type_Barter) && !isMagic) + backgroundWidget->setProperty("ImageCoord", "2 2 42 42"); + else + backgroundWidget->setProperty("ImageCoord", "0 0 42 42"); + backgroundWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedItem); + backgroundWidget->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheel); + + // image + MyGUI::ImageBox* image = backgroundWidget->createWidget("ImageBox", + MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture(path); + image->setNeedMouseFocus(false); + + // text widget that shows item count + MyGUI::TextBox* text = image->createWidget("SandBrightText", + MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); + text->setTextAlign(MyGUI::Align::Right); + text->setNeedMouseFocus(false); + text->setTextShadow(true); + text->setTextShadowColour(MyGUI::Colour(0,0,0)); + text->setCaption(getCountString(item.mCount)); + + y += 42; + if (y > maxHeight) + { + x += 42; + y = 0; + } + + } + x += 42; + MyGUI::IntSize size = MyGUI::IntSize(std::max(mScrollView->getSize().width, x), mScrollView->getSize().height); + mScrollView->setCanvasSize(size); + dragArea->setSize(size); +} + +void ItemView::onSelectedItem(MyGUI::Widget *sender) +{ + ItemModel::ModelIndex index = (*sender->getUserData >()).first; + eventItemClicked(index); +} + +void ItemView::onSelectedBackground(MyGUI::Widget *sender) +{ + eventBackgroundClicked(); +} + +void ItemView::onMouseWheel(MyGUI::Widget *_sender, int _rel) +{ + if (mScrollView->getViewOffset().left + _rel*0.3 > 0) + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mScrollView->setViewOffset(MyGUI::IntPoint(mScrollView->getViewOffset().left + _rel*0.3, 0)); +} + +void ItemView::setSize(const MyGUI::IntSize &_value) +{ + Base::setSize(_value); + update(); +} + +void ItemView::setSize(int _width, int _height) +{ + Base::setSize(_width, _height); + update(); +} + +void ItemView::setCoord(const MyGUI::IntCoord &_value) +{ + Base::setCoord(_value); + update(); +} + +void ItemView::setCoord(int _left, int _top, int _width, int _height) +{ + Base::setCoord(_left, _top, _width, _height); + update(); +} + +void ItemView::registerComponents() +{ + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); +} + +} diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp new file mode 100644 index 0000000000..17f609f2b8 --- /dev/null +++ b/apps/openmw/mwgui/itemview.hpp @@ -0,0 +1,52 @@ +#ifndef MWGUI_ITEMVIEW_H +#define MWGUI_ITEMVIEW_H + +#include + +#include "itemmodel.hpp" + +namespace MWGui +{ + + class ItemView : public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(ItemView) + public: + ItemView(); + virtual ~ItemView(); + + /// Register needed components with MyGUI's factory manager + static void registerComponents (); + + /// Takes ownership of \a model + void setModel (ItemModel* model); + + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ModelIndex; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; + /// Fired when an item was clicked + EventHandle_ModelIndex eventItemClicked; + /// Fired when the background was clicked (useful for drag and drop) + EventHandle_Void eventBackgroundClicked; + + void update(); + + private: + virtual void initialiseOverride(); + + virtual void setSize(const MyGUI::IntSize& _value); + virtual void setCoord(const MyGUI::IntCoord& _value); + void setSize(int _width, int _height); + void setCoord(int _left, int _top, int _width, int _height); + + void onSelectedItem (MyGUI::Widget* sender); + void onSelectedBackground (MyGUI::Widget* sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + + ItemModel* mModel; + MyGUI::ScrollView* mScrollView; + + }; + +} + +#endif diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp new file mode 100644 index 0000000000..273985e3e7 --- /dev/null +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -0,0 +1,326 @@ +#include "journalbooks.hpp" + +namespace +{ + MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) + { + typedef MWGui::BookTypesetter::Utf8Point point; + + point begin = reinterpret_cast (text); + + return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); + } + + const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f); + const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f); + const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f); + + struct AddContent + { + MWGui::BookTypesetter::Ptr mTypesetter; + MWGui::BookTypesetter::Style* mBodyStyle; + + AddContent (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : + mTypesetter (typesetter), mBodyStyle (body_style) + { + } + }; + + struct AddSpan : AddContent + { + AddSpan (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : + AddContent (typesetter, body_style) + { + } + + void operator () (intptr_t topicId, size_t begin, size_t end) + { + MWGui::BookTypesetter::Style* style = mBodyStyle; + + if (topicId) + style = mTypesetter->createHotStyle (mBodyStyle, linkNormal, linkHot, linkActive, topicId); + + mTypesetter->write (style, begin, end); + } + }; + + struct AddEntry + { + MWGui::BookTypesetter::Ptr mTypesetter; + MWGui::BookTypesetter::Style* mBodyStyle; + + AddEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : + mTypesetter (typesetter), mBodyStyle (body_style) + { + } + + void operator () (MWGui::JournalViewModel::Entry const & entry) + { + mTypesetter->addContent (entry.body ()); + + entry.visitSpans (AddSpan (mTypesetter, mBodyStyle)); + } + }; + + struct AddJournalEntry : AddEntry + { + bool mAddHeader; + MWGui::BookTypesetter::Style* mHeaderStyle; + + AddJournalEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style, + MWGui::BookTypesetter::Style* header_style, bool add_header) : + AddEntry (typesetter, body_style), + mHeaderStyle (header_style), + mAddHeader (add_header) + { + } + + void operator () (MWGui::JournalViewModel::JournalEntry const & entry) + { + if (mAddHeader) + { + mTypesetter->write (mHeaderStyle, entry.timestamp ()); + mTypesetter->lineBreak (); + } + + AddEntry::operator () (entry); + + mTypesetter->sectionBreak (10); + } + }; + + struct AddTopicEntry : AddEntry + { + intptr_t mContentId; + MWGui::BookTypesetter::Style* mHeaderStyle; + + AddTopicEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style, + MWGui::BookTypesetter::Style* header_style, intptr_t contentId) : + AddEntry (typesetter, body_style), mHeaderStyle (header_style), mContentId (contentId) + { + } + + void operator () (MWGui::JournalViewModel::TopicEntry const & entry) + { + mTypesetter->write (mBodyStyle, entry.source ()); + mTypesetter->write (mBodyStyle, 0, 3);// begin + + AddEntry::operator() (entry); + + mTypesetter->selectContent (mContentId); + mTypesetter->write (mBodyStyle, 2, 3);// end quote + + mTypesetter->sectionBreak (10); + } + }; + + struct AddTopicName : AddContent + { + AddTopicName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : + AddContent (typesetter, style) + { + } + + void operator () (MWGui::JournalViewModel::Utf8Span topicName) + { + mTypesetter->write (mBodyStyle, topicName); + mTypesetter->sectionBreak (10); + } + }; + + struct AddQuestName : AddContent + { + AddQuestName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : + AddContent (typesetter, style) + { + } + + void operator () (MWGui::JournalViewModel::Utf8Span topicName) + { + mTypesetter->write (mBodyStyle, topicName); + mTypesetter->sectionBreak (10); + } + }; + + struct AddTopicLink : AddContent + { + AddTopicLink (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : + AddContent (typesetter, style) + { + } + + void operator () (MWGui::JournalViewModel::TopicId topicId, MWGui::JournalViewModel::Utf8Span name) + { + MWGui::BookTypesetter::Style* link = mTypesetter->createHotStyle (mBodyStyle, MyGUI::Colour::Black, linkHot, linkActive, topicId); + + mTypesetter->write (link, name); + mTypesetter->lineBreak (); + } + }; + + struct AddQuestLink : AddContent + { + AddQuestLink (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : + AddContent (typesetter, style) + { + } + + void operator () (MWGui::JournalViewModel::QuestId id, MWGui::JournalViewModel::Utf8Span name) + { + MWGui::BookTypesetter::Style* style = mTypesetter->createHotStyle (mBodyStyle, MyGUI::Colour::Black, linkHot, linkActive, id); + + mTypesetter->write (style, name); + mTypesetter->lineBreak (); + } + }; +} + +namespace MWGui +{ + +typedef TypesetBook::Ptr book; + +JournalBooks::JournalBooks (JournalViewModel::Ptr model) : + mModel (model) +{ +} + +book JournalBooks::createEmptyJournalBook () +{ + BookTypesetter::Ptr typesetter = createTypesetter (); + + BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + + typesetter->write (header, to_utf8_span ("You have no journal entries!")); + typesetter->lineBreak (); + typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest.")); + + BookTypesetter::Style* big = typesetter->createStyle ("EB Garamond 24", MyGUI::Colour::Black); + BookTypesetter::Style* test = typesetter->createStyle ("MonoFont", MyGUI::Colour::Blue); + + typesetter->sectionBreak (20); + typesetter->write (body, to_utf8_span ( + "The layout engine doesn't currently support aligning fonts to " + "their baseline within a single line so the following text looks " + "funny. In order to properly implement it, a stupidly simple " + "change is needed in MyGUI to report the where the baseline is for " + "a particular font" + )); + + typesetter->sectionBreak (20); + typesetter->write (big, to_utf8_span ("big text g")); + typesetter->write (body, to_utf8_span (" проверяем только в дебаге")); + typesetter->write (body, to_utf8_span (" normal g")); + typesetter->write (big, to_utf8_span (" done g")); + + typesetter->sectionBreak (20); + typesetter->write (test, to_utf8_span ( + "int main (int argc,\n" + " char ** argv)\n" + "{\n" + " print (\"hello world!\\n\");\n" + " return 0;\n" + "}\n" + )); + + return typesetter->complete (); +} + +book JournalBooks::createJournalBook () +{ + BookTypesetter::Ptr typesetter = createTypesetter (); + + BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + + mModel->visitJournalEntries (0, AddJournalEntry (typesetter, body, header, true)); + + return typesetter->complete (); +} + +book JournalBooks::createTopicBook (uintptr_t topicId) +{ + BookTypesetter::Ptr typesetter = createTypesetter (); + + BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + + mModel->visitTopicName (topicId, AddTopicName (typesetter, header)); + + intptr_t contentId = typesetter->addContent (to_utf8_span (", \"")); + + mModel->visitTopicEntries (topicId, AddTopicEntry (typesetter, body, header, contentId)); + + return typesetter->complete (); +} + +book JournalBooks::createQuestBook (uintptr_t questId) +{ + BookTypesetter::Ptr typesetter = createTypesetter (); + + BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + + mModel->visitQuestName (questId, AddQuestName (typesetter, header)); + + mModel->visitJournalEntries (questId, AddJournalEntry (typesetter, body, header, false)); + + return typesetter->complete (); +} + +book JournalBooks::createTopicIndexBook () +{ + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + + typesetter->setSectionAlignment (BookTypesetter::AlignCenter); + + BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + + for (int i = 0; i < 26; ++i) + { + char ch = 'A' + i; + + char buffer [32]; + + sprintf (buffer, "( %c )", ch); + + BookTypesetter::Style* style = typesetter->createHotStyle (body, MyGUI::Colour::Black, linkHot, linkActive, ch); + + if (i == 13) + typesetter->sectionBreak (); + + typesetter->write (style, to_utf8_span (buffer)); + typesetter->lineBreak (); + } + + return typesetter->complete (); +} + +book JournalBooks::createTopicIndexBook (char character) +{ + BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF); + BookTypesetter::Style* style = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + + mModel->visitTopicNamesStartingWith (character, AddTopicLink (typesetter, style)); + + return typesetter->complete (); +} + +book JournalBooks::createQuestIndexBook (bool activeOnly) +{ + BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF); + BookTypesetter::Style* base = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + + mModel->visitQuestNames (activeOnly, AddQuestLink (typesetter, base)); + + return typesetter->complete (); +} + +BookTypesetter::Ptr JournalBooks::createTypesetter () +{ + //TODO: determine page size from layout... + return BookTypesetter::create (240, 300); +} + +} diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp new file mode 100644 index 0000000000..09d3cf1a85 --- /dev/null +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -0,0 +1,29 @@ +#ifndef MWGUI_JOURNALBOOKS_HPP +#define MWGUI_JOURNALBOOKS_HPP + +#include "bookpage.hpp" +#include "journalviewmodel.hpp" + +namespace MWGui +{ + struct JournalBooks + { + typedef TypesetBook::Ptr Book; + JournalViewModel::Ptr mModel; + + JournalBooks (JournalViewModel::Ptr model); + + Book createEmptyJournalBook (); + Book createJournalBook (); + Book createTopicBook (uintptr_t topicId); + Book createQuestBook (uintptr_t questId); + Book createTopicIndexBook (); + Book createTopicIndexBook (char character); + Book createQuestIndexBook (bool showAll); + + private: + BookTypesetter::Ptr createTypesetter (); + }; +} + +#endif // MWGUI_JOURNALBOOKS_HPP diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp new file mode 100644 index 0000000000..79a77070ae --- /dev/null +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -0,0 +1,389 @@ +#include "journalviewmodel.hpp" + +#include +#include +#include + +#include + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwdialogue/journalentry.hpp" + +#include "keywordsearch.hpp" + +namespace MWGui { + +struct JournalViewModelImpl; + +static void injectMonthName (std::ostream & os, int month); + +struct JournalViewModelImpl : JournalViewModel +{ + typedef KeywordSearch KeywordSearchT; + + mutable bool mKeywordSearchLoaded; + mutable KeywordSearchT mKeywordSearch; + + std::locale mLocale; + + JournalViewModelImpl () + { + mKeywordSearchLoaded = false; + } + + virtual ~JournalViewModelImpl () + { + } + + /// \todo replace this nasty BS + static Utf8Span toUtf8Span (std::string const & str) + { + if (str.size () == 0) + return Utf8Span (Utf8Point (NULL), Utf8Point (NULL)); + + Utf8Point point = reinterpret_cast (str.c_str ()); + + return Utf8Span (point, point + str.size ()); + } + + void load () + { + } + + void unload () + { + mKeywordSearch.clear (); + mKeywordSearchLoaded = false; + } + + void ensureKeyWordSearchLoaded () const + { + if (!mKeywordSearchLoaded) + { + MWBase::Journal * journal = MWBase::Environment::get().getJournal(); + + for(MWBase::Journal::TTopicIter i = journal->topicBegin(); i != journal->topicEnd (); ++i) + mKeywordSearch.seed (i->first, intptr_t (&i->second)); + + mKeywordSearchLoaded = true; + } + } + + wchar_t tolower (wchar_t ch) const { return std::tolower (ch, mLocale); } + + bool isEmpty () const + { + MWBase::Journal * journal = MWBase::Environment::get().getJournal(); + + return journal->begin () == journal->end (); + } + + template + struct BaseEntry : Interface + { + typedef t_iterator iterator_t; + + iterator_t itr; + JournalViewModelImpl const * mModel; + + BaseEntry (JournalViewModelImpl const * model, iterator_t itr) : + mModel (model), itr (itr), loaded (false) + {} + + virtual ~BaseEntry () {} + + mutable bool loaded; + mutable std::string utf8text; + + typedef std::pair Range; + + // hyperlinks in @link# notation + mutable std::map mHyperLinks; + + virtual std::string getText () const = 0; + + void ensureLoaded () const + { + if (!loaded) + { + mModel->ensureKeyWordSearchLoaded (); + + utf8text = getText (); + + size_t pos_begin, pos_end; + for(;;) + { + pos_begin = utf8text.find('@'); + if (pos_begin != std::string::npos) + pos_end = utf8text.find('#', pos_begin); + + if (pos_begin != std::string::npos && pos_end != std::string::npos) + { + std::string link = utf8text.substr(pos_begin + 1, pos_end - pos_begin - 1); + const char specialPseudoAsteriskCharacter = 127; + std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*'); + std::string topicName = MWBase::Environment::get().getWindowManager()-> + getTranslationDataStorage().topicStandardForm(link); + + std::string displayName = link; + while (displayName[displayName.size()-1] == '*') + displayName.erase(displayName.size()-1, 1); + + utf8text.replace(pos_begin, pos_end+1-pos_begin, displayName); + + intptr_t value; + if (mModel->mKeywordSearch.containsKeyword(topicName, value)) + mHyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = value; + } + else + break; + } + + loaded = true; + } + } + + Utf8Span body () const + { + ensureLoaded (); + + return toUtf8Span (utf8text); + } + + void visitSpans (boost::function < void (TopicId, size_t, size_t)> visitor) const + { + ensureLoaded (); + mModel->ensureKeyWordSearchLoaded (); + + if (mHyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) + { + size_t formatted = 0; // points to the first character that is not laid out yet + for (std::map::const_iterator it = mHyperLinks.begin(); it != mHyperLinks.end(); ++it) + { + intptr_t topicId = it->second; + if (formatted < it->first.first) + visitor (0, formatted, it->first.first); + visitor (topicId, it->first.first, it->first.second); + formatted = it->first.second; + } + if (formatted < utf8text.size()) + visitor (0, formatted, utf8text.size()); + } + else + { + std::string::const_iterator i = utf8text.begin (); + + KeywordSearchT::Match match; + + while (i != utf8text.end () && mModel->mKeywordSearch.search (i, utf8text.end (), match)) + { + if (i != match.mBeg) + visitor (0, i - utf8text.begin (), match.mBeg - utf8text.begin ()); + + visitor (match.mValue, match.mBeg - utf8text.begin (), match.mEnd - utf8text.begin ()); + + i = match.mEnd; + } + + if (i != utf8text.end ()) + visitor (0, i - utf8text.begin (), utf8text.size ()); + } + } + + }; + + void visitQuestNames (bool active_only, boost::function visitor) const + { + MWBase::Journal * journal = MWBase::Environment::get ().getJournal (); + + for (MWBase::Journal::TQuestIter i = journal->questBegin (); i != journal->questEnd (); ++i) + { + if (active_only && i->second.isFinished ()) + continue; + + /// \todo quest.getName() is broken? returns empty string + //const MWDialogue::Quest& quest = i->second; + + visitor (reinterpret_cast (&i->second), toUtf8Span (i->first)); + } + } + + void visitQuestName (QuestId questId, boost::function visitor) const + { + MWDialogue::Quest const * quest = reinterpret_cast (questId); + + std::string name = quest->getName (); + + visitor (toUtf8Span (name)); + } + + template + struct JournalEntryImpl : BaseEntry + { + using BaseEntry ::itr; + + mutable std::string timestamp_buffer; + + JournalEntryImpl (JournalViewModelImpl const * model, iterator_t itr) : + BaseEntry (model, itr) + {} + + std::string getText () const + { + return itr->getText(MWBase::Environment::get().getWorld()->getStore()); + } + + Utf8Span timestamp () const + { + if (timestamp_buffer.empty ()) + { + std::ostringstream os; + + os << itr->mDayOfMonth << ' '; + + injectMonthName (os, itr->mMonth); + + const std::string& dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}"); + os << " (" << dayStr << " " << (itr->mDay + 1) << ')'; + + timestamp_buffer = os.str (); + } + + return toUtf8Span (timestamp_buffer); + } + }; + + void visitJournalEntries (QuestId questId, boost::function visitor) const + { + MWBase::Journal * journal = MWBase::Environment::get().getJournal(); + + if (questId != 0) + { + MWDialogue::Quest const * quest = reinterpret_cast (questId); + + for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i) + { + for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j) + { + if (i->mInfoId == *j) + visitor (JournalEntryImpl (this, i)); + } + } + } + else + { + for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i) + visitor (JournalEntryImpl (this, i)); + } + } + + void visitTopics (boost::function visitor) const + { + throw std::runtime_error ("not implemented"); + } + + void visitTopicName (TopicId topicId, boost::function visitor) const + { + MWDialogue::Topic const & topic = * reinterpret_cast (topicId); + // This is to get the correct case for the topic + const std::string& name = MWBase::Environment::get().getWorld()->getStore().get().find(topic.getName())->mId; + visitor (toUtf8Span (name)); + } + + void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const + { + MWBase::Journal * journal = MWBase::Environment::get().getJournal(); + + for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i) + { + if (i->first [0] != std::tolower (character, mLocale)) + continue; + + // This is to get the correct case for the topic + const std::string& name = MWBase::Environment::get().getWorld()->getStore().get().find(i->first)->mId; + + visitor (TopicId (&i->second), toUtf8Span (name)); + } + + } + + struct TopicEntryImpl : BaseEntry + { + MWDialogue::Topic const & mTopic; + + mutable std::string source_buffer; + + TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) : + BaseEntry (model, itr), mTopic (topic) + {} + + std::string getText () const + { + /// \todo defines are not replaced (%PCName etc). should probably be done elsewhere though since we need the actor + return mTopic.getEntry (*itr).getText(MWBase::Environment::get().getWorld()->getStore()); + + } + + Utf8Span source () const + { + if (source_buffer.empty ()) + source_buffer = "someone"; + return toUtf8Span (source_buffer); + } + + }; + + void visitTopicEntries (TopicId topicId, boost::function visitor) const + { + typedef MWDialogue::Topic::TEntryIter iterator_t; + + MWDialogue::Topic const & topic = * reinterpret_cast (topicId); + + for (iterator_t i = topic.begin (); i != topic.end (); ++i) + visitor (TopicEntryImpl (this, topic, i)); + } +}; + +static void injectMonthName (std::ostream & os, int month) +{ + MyGUI::LanguageManager& lm = MyGUI::LanguageManager::getInstance(); + + if (month == 0) + os << lm.replaceTags ("#{sMonthMorningstar}"); + else if (month == 1) + os << lm.replaceTags ("#{sMonthSunsdawn}"); + else if (month == 2) + os << lm.replaceTags ("#{sMonthFirstseed}"); + else if (month == 3) + os << lm.replaceTags ("#{sMonthRainshand}"); + else if (month == 4) + os << lm.replaceTags ("#{sMonthSecondseed}"); + else if (month == 5) + os << lm.replaceTags ("#{sMonthMidyear}"); + else if (month == 6) + os << lm.replaceTags ("#{sMonthSunsheight}"); + else if (month == 7) + os << lm.replaceTags ("#{sMonthLastseed}"); + else if (month == 8) + os << lm.replaceTags ("#{sMonthHeartfire}"); + else if (month == 9) + os << lm.replaceTags ("#{sMonthFrostfall}"); + else if (month == 10) + os << lm.replaceTags ("#{sMonthSunsdusk}"); + else if (month == 11) + os << lm.replaceTags ("#{sMonthEveningstar}"); + else + os << month; +} + +JournalViewModel::Ptr JournalViewModel::create () +{ + return boost::make_shared (); +} + +} diff --git a/apps/openmw/mwgui/journalviewmodel.hpp b/apps/openmw/mwgui/journalviewmodel.hpp new file mode 100644 index 0000000000..21c3a11785 --- /dev/null +++ b/apps/openmw/mwgui/journalviewmodel.hpp @@ -0,0 +1,93 @@ +#ifndef MWGUI_JOURNALVIEWMODEL_HPP +#define MWGUI_JOURNALVIEWMODEL_HPP + +#include +#include +#include +#include +#include +#include + +namespace MWGui +{ + /// View-Model for the journal GUI + /// + /// This interface defines an abstract data model suited + /// specifically to the needs of the journal GUI. It isolates + /// the journal GUI from the implementation details of the + /// game data store. + struct JournalViewModel + { + typedef boost::shared_ptr Ptr; + + typedef intptr_t QuestId; + typedef intptr_t TopicId; + typedef uint8_t const * Utf8Point; + typedef std::pair Utf8Span; + + /// The base interface for both journal entries and topics. + struct Entry + { + /// returns the body text for the journal entry + /// + /// This function returns a borrowed reference to the body of the + /// journal entry. The returned reference becomes invalid when the + /// entry is destroyed. + virtual Utf8Span body () const = 0; + + /// Visits each subset of text in the body, delivering the beginning + /// and end of the span relative to the body, and a valid topic ID if + /// the span represents a keyword, or zero if not. + virtual void visitSpans (boost::function visitor) const = 0; + }; + + /// An interface to topic data. + struct TopicEntry : Entry + { + /// Returns a pre-formatted span of UTF8 encoded text representing + /// the name of the NPC this portion of dialog was heard from. + virtual Utf8Span source () const = 0; + }; + + /// An interface to journal data. + struct JournalEntry : Entry + { + /// Returns a pre-formatted span of UTF8 encoded text representing + /// the in-game date this entry was added to the journal. + virtual Utf8Span timestamp () const = 0; + }; + + + /// called prior to journal opening + virtual void load () = 0; + + /// called prior to journal closing + virtual void unload () = 0; + + /// returns true if their are no journal entries to display + virtual bool isEmpty () const = 0; + + /// provides access to the name of the quest with the specified identifier + virtual void visitQuestName (TopicId topicId, boost::function visitor) const = 0; + + /// walks the active and optionally completed, quests providing the quest id and name + virtual void visitQuestNames (bool active_only, boost::function visitor) const = 0; + + /// walks over the journal entries related to the specified quest identified by its id + virtual void visitJournalEntries (QuestId questId, boost::function visitor) const = 0; + + /// provides the name of the topic specified by its id + virtual void visitTopicName (TopicId topicId, boost::function visitor) const = 0; + + /// walks over the topics whose names start with the specified character providing the topics id and name + virtual void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const = 0; + + /// walks over the topic entries for the topic specified by its identifier + virtual void visitTopicEntries (TopicId topicId, boost::function visitor) const = 0; + + // create an instance of the default journal view model implementation + static Ptr create (); + }; +} + +#endif // MWGUI_JOURNALVIEWMODEL_HPP diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index ba39b01012..5b84c99a5f 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -1,201 +1,475 @@ #include "journalwindow.hpp" #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/journal.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "list.hpp" -#include "../mwdialogue/journalentry.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "boost/lexical_cast.hpp" + +#include "bookpage.hpp" +#include "windowbase.hpp" +#include "imagebutton.hpp" +#include "journalviewmodel.hpp" +#include "journalbooks.hpp" namespace { - struct book + static char const OptionsOverlay [] = "OptionsOverlay"; + static char const OptionsBTN [] = "OptionsBTN"; + static char const PrevPageBTN [] = "PrevPageBTN"; + static char const NextPageBTN [] = "NextPageBTN"; + static char const CloseBTN [] = "CloseBTN"; + static char const JournalBTN [] = "JournalBTN"; + static char const TopicsBTN [] = "TopicsBTN"; + static char const QuestsBTN [] = "QuestsBTN"; + static char const CancelBTN [] = "CancelBTN"; + static char const ShowAllBTN [] = "ShowAllBTN"; + static char const ShowActiveBTN [] = "ShowActiveBTN"; + static char const PageOneNum [] = "PageOneNum"; + static char const PageTwoNum [] = "PageTwoNum"; + static char const TopicsList [] = "TopicsList"; + static char const TopicsPage [] = "TopicsPage"; + static char const QuestsList [] = "QuestsList"; + static char const QuestsPage [] = "QuestsPage"; + static char const LeftBookPage [] = "LeftBookPage"; + static char const RightBookPage [] = "RightBookPage"; + static char const LeftTopicIndex [] = "LeftTopicIndex"; + static char const RightTopicIndex [] = "RightTopicIndex"; + + struct JournalWindowImpl : MWGui::WindowBase, MWGui::JournalBooks, MWGui::JournalWindow { - int endLine; - std::list pages; + struct DisplayState + { + unsigned int mPage; + Book mBook; + }; + + typedef std::stack DisplayStateStack; + + DisplayStateStack mStates; + Book mTopicIndexBook; + bool mQuestMode; + bool mAllQuests; + + template + T * getWidget (char const * name) + { + T * widget; + WindowBase::getWidget (widget, name); + return widget; + } + + template + void setText (char const * name, value_type const & value) + { + getWidget (name) -> + setCaption (boost::lexical_cast (value)); + } + + void setVisible (char const * name, bool visible) + { + getWidget (name) -> + setVisible (visible); + } + + void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender)) + { + getWidget (name) -> + eventMouseButtonClick += newDelegate(this, Handler); + } + + MWGui::BookPage* getPage (char const * name) + { + return getWidget (name); + } + + JournalWindowImpl (MWGui::JournalViewModel::Ptr Model) + : WindowBase("openmw_journal.layout"), JournalBooks (Model) + { + mMainWidget->setVisible(false); + center(); + + adviseButtonClick (OptionsBTN, &JournalWindowImpl::notifyOptions ); + adviseButtonClick (PrevPageBTN, &JournalWindowImpl::notifyPrevPage ); + adviseButtonClick (NextPageBTN, &JournalWindowImpl::notifyNextPage ); + adviseButtonClick (CloseBTN, &JournalWindowImpl::notifyClose ); + adviseButtonClick (JournalBTN, &JournalWindowImpl::notifyJournal ); + + adviseButtonClick (TopicsBTN, &JournalWindowImpl::notifyTopics ); + adviseButtonClick (QuestsBTN, &JournalWindowImpl::notifyQuests ); + adviseButtonClick (CancelBTN, &JournalWindowImpl::notifyCancel ); + + adviseButtonClick (ShowAllBTN, &JournalWindowImpl::notifyShowAll ); + adviseButtonClick (ShowActiveBTN, &JournalWindowImpl::notifyShowActive); + + { + MWGui::BookPage::ClickCallback callback; + + callback = boost::bind (&JournalWindowImpl::notifyTopicClicked, this, _1); + + getPage (TopicsPage)->adviseLinkClicked (callback); + getPage (LeftBookPage)->adviseLinkClicked (callback); + getPage (RightBookPage)->adviseLinkClicked (callback); + } + + { + MWGui::BookPage::ClickCallback callback; + + callback = boost::bind (&JournalWindowImpl::notifyIndexLinkClicked, this, _1); + + getPage (LeftTopicIndex)->adviseLinkClicked (callback); + getPage (RightTopicIndex)->adviseLinkClicked (callback); + } + + { + MWGui::BookPage::ClickCallback callback; + + callback = boost::bind (&JournalWindowImpl::notifyQuestClicked, this, _1); + + getPage (QuestsPage)->adviseLinkClicked (callback); + } + + adjustButton(OptionsBTN); + adjustButton(PrevPageBTN); + adjustButton(NextPageBTN); + adjustButton(CloseBTN); + adjustButton(CancelBTN); + adjustButton(ShowAllBTN); + adjustButton(ShowActiveBTN); + adjustButton(JournalBTN); + + MWGui::ImageButton* nextButton = getWidget(NextPageBTN); + if (nextButton->getSize().width == 64) + { + // english button has a 7 pixel wide strip of garbage on its right edge + nextButton->setSize(64-7, nextButton->getSize().height); + nextButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,nextButton->getSize().height)); + } + + adjustButton(TopicsBTN); + adjustButton(QuestsBTN); + int width = getWidget(TopicsBTN)->getSize().width + getWidget(QuestsBTN)->getSize().width; + int topicsWidth = getWidget(TopicsBTN)->getSize().width; + int pageWidth = getWidget(RightBookPage)->getSize().width; + + getWidget(TopicsBTN)->setPosition((pageWidth - width)/2, getWidget(TopicsBTN)->getPosition().top); + getWidget(QuestsBTN)->setPosition((pageWidth - width)/2 + topicsWidth, getWidget(QuestsBTN)->getPosition().top); + + mQuestMode = false; + mAllQuests = false; + } + + void adjustButton (char const * name) + { + MWGui::ImageButton* button = getWidget(name); + + MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); + button->setSize(button->getRequestedSize()); + + if (button->getAlign().isRight()) + button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); + } + + void open() + { + if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) + { + MWBase::Environment::get().getWindowManager()->popGuiMode (); + } + mModel->load (); + + setBookMode (); + + /// \todo Wiping the whole book layout each time the journal is opened is probably too costly for a large journal (eg 300+ pages). + /// There should be a way to keep the existing layout and append new entries to the end of it. + /// However, that still leaves the problem of having to add links to previously unknown, but now known topics, so + /// we maybe need to find another way to speed things up. + Book journalBook; + if (mModel->isEmpty ()) + journalBook = createEmptyJournalBook (); + else + journalBook = createJournalBook (); + + pushBook (journalBook, 0); + + // fast forward to the last page + if (!mStates.empty ()) + { + unsigned int & page = mStates.top ().mPage; + page = mStates.top().mBook->pageCount()-1; + if (page%2) + --page; + } + updateShowingPages(); + } + + void close() + { + mModel->unload (); + + getPage (LeftBookPage)->showPage (Book (), 0); + getPage (RightBookPage)->showPage (Book (), 0); + + while (!mStates.empty ()) + mStates.pop (); + + mTopicIndexBook.reset (); + } + + void setVisible (bool newValue) + { + WindowBase::setVisible (newValue); + } + + void setBookMode () + { + setVisible (OptionsBTN, true); + setVisible (OptionsOverlay, false); + + updateShowingPages (); + updateCloseJournalButton (); + } + + void setOptionsMode () + { + setVisible (OptionsBTN, false); + setVisible (OptionsOverlay, true); + + setVisible (PrevPageBTN, false); + setVisible (NextPageBTN, false); + setVisible (CloseBTN, false); + setVisible (JournalBTN, false); + + setVisible (TopicsList, false); + setVisible (QuestsList, mQuestMode); + setVisible (LeftTopicIndex, !mQuestMode); + setVisible (RightTopicIndex, !mQuestMode); + setVisible (ShowAllBTN, mQuestMode && !mAllQuests); + setVisible (ShowActiveBTN, mQuestMode && mAllQuests); + + //TODO: figure out how to make "options" page overlay book page + // correctly, so that text may show underneath + getPage (RightBookPage)->showPage (Book (), 0); + } + + void pushBook (Book book, unsigned int page) + { + DisplayState bs; + bs.mPage = page; + bs.mBook = book; + mStates.push (bs); + updateShowingPages (); + updateCloseJournalButton (); + } + + void replaceBook (Book book, unsigned int page) + { + assert (!mStates.empty ()); + mStates.top ().mBook = book; + mStates.top ().mPage = page; + updateShowingPages (); + } + + void popBook () + { + mStates.pop (); + updateShowingPages (); + updateCloseJournalButton (); + } + + void updateCloseJournalButton () + { + setVisible (CloseBTN, mStates.size () < 2); + setVisible (JournalBTN, mStates.size () >= 2); + } + + void updateShowingPages () + { + Book book; + unsigned int page; + unsigned int relPages; + + if (!mStates.empty ()) + { + book = mStates.top ().mBook; + page = mStates.top ().mPage; + relPages = book->pageCount () - page; + } + else + { + page = 0; + relPages = 0; + } + + setVisible (PrevPageBTN, page > 0); + setVisible (NextPageBTN, relPages > 2); + + setVisible (PageOneNum, relPages > 0); + setVisible (PageTwoNum, relPages > 1); + + getPage (LeftBookPage)->showPage ((relPages > 0) ? book : Book (), page+0); + getPage (RightBookPage)->showPage ((relPages > 0) ? book : Book (), page+1); + + setText (PageOneNum, page + 1); + setText (PageTwoNum, page + 2); + } + + void notifyTopicClicked (intptr_t linkId) + { + Book topicBook = createTopicBook (linkId); + + if (mStates.size () > 1) + replaceBook (topicBook, 0); + else + pushBook (topicBook, 0); + + setVisible (OptionsOverlay, false); + setVisible (OptionsBTN, true); + setVisible (JournalBTN, true); + } + + void notifyQuestClicked (intptr_t questId) + { + Book book = createQuestBook (questId); + + if (mStates.size () > 1) + replaceBook (book, 0); + else + pushBook (book, 0); + + setVisible (OptionsOverlay, false); + setVisible (OptionsBTN, true); + setVisible (JournalBTN, true); + } + + void notifyOptions(MyGUI::Widget* _sender) + { + setOptionsMode (); + + if (!mTopicIndexBook) + mTopicIndexBook = createTopicIndexBook (); + + getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); + getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); + } + + void notifyJournal(MyGUI::Widget* _sender) + { + assert (mStates.size () > 1); + popBook (); + } + + void showList (char const * listId, char const * pageId, Book book) + { + std::pair size = book->getSize (); + + getPage (pageId)->showPage (book, 0); + + getWidget (listId)->setCanvasSize (size.first, size.second); + } + + void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId character) + { + setVisible (LeftTopicIndex, false); + setVisible (RightTopicIndex, false); + setVisible (TopicsList, true); + + showList (TopicsList, TopicsPage, createTopicIndexBook ((char)character)); + } + + void notifyTopics(MyGUI::Widget* _sender) + { + mQuestMode = false; + setVisible (LeftTopicIndex, true); + setVisible (RightTopicIndex, true); + setVisible (TopicsList, false); + setVisible (QuestsList, false); + setVisible (ShowAllBTN, false); + setVisible (ShowActiveBTN, false); + } + + void notifyQuests(MyGUI::Widget* _sender) + { + mQuestMode = true; + setVisible (LeftTopicIndex, false); + setVisible (RightTopicIndex, false); + setVisible (TopicsList, false); + setVisible (QuestsList, true); + setVisible (ShowAllBTN, !mAllQuests); + setVisible (ShowActiveBTN, mAllQuests); + + showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests)); + } + + void notifyShowAll(MyGUI::Widget* _sender) + { + mAllQuests = true; + setVisible (ShowAllBTN, !mAllQuests); + setVisible (ShowActiveBTN, mAllQuests); + showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests)); + } + + void notifyShowActive(MyGUI::Widget* _sender) + { + mAllQuests = false; + setVisible (ShowAllBTN, !mAllQuests); + setVisible (ShowActiveBTN, mAllQuests); + showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests)); + } + + void notifyCancel(MyGUI::Widget* _sender) + { + setBookMode (); + } + + void notifyClose(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); + + MWBase::Environment::get().getWindowManager ()->popGuiMode (); + } + + void notifyNextPage(MyGUI::Widget* _sender) + { + if (!mStates.empty ()) + { + unsigned int & page = mStates.top ().mPage; + Book book = mStates.top ().mBook; + + if (page < book->pageCount () - 2) + { + page += 2; + updateShowingPages (); + } + } + } + + void notifyPrevPage(MyGUI::Widget* _sender) + { + if (!mStates.empty ()) + { + unsigned int & page = mStates.top ().mPage; + + if(page > 0) + { + page -= 2; + updateShowingPages (); + } + } + } }; } -book formatText(std::string text,book mBook,int maxLine, int lineSize) +// glue the implementation to the interface +MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model) { - //stringList.push_back(""); - - int cLineSize = 0; - int cLine = mBook.endLine +1; - std::string cString; - - if(mBook.pages.empty()) - { - cString = ""; - cLine = 0; - } - else - { - cString = mBook.pages.back() + std::string("\n"); - mBook.pages.pop_back(); - } - - //std::string::iterator wordBegin = text.begin(); - //std::string::iterator wordEnd; - - std::string cText = text; - - while(cText.length() != 0) - { - size_t firstSpace = cText.find_first_of(' '); - if(firstSpace == std::string::npos) - { - cString = cString + cText; - mBook.pages.push_back(cString); - //TODO:finnish this - break; - } - if(static_cast (firstSpace) + cLineSize <= lineSize) - { - cLineSize = firstSpace + cLineSize; - cString = cString + cText.substr(0,firstSpace +1); - } - else - { - cLineSize = firstSpace; - if(cLine +1 <= maxLine) - { - cLine = cLine + 1; - cString = cString + std::string("\n") + cText.substr(0,firstSpace +1); - } - else - { - cLine = 0; - mBook.pages.push_back(cString); - cString = cText.substr(0,firstSpace +1); - } - } - //std::cout << cText << "\n"; - //std::cout << cText.length(); - cText = cText.substr(firstSpace +1,cText.length() - firstSpace -1); - } - mBook.endLine = cLine; - return mBook; - //std::string -} - - -MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_journal.layout", parWindowManager) - , mPageNumber(0) -{ - mMainWidget->setVisible(false); - //setCoord(0,0,498, 342); - center(); - - getWidget(mLeftTextWidget, "LeftText"); - getWidget(mRightTextWidget, "RightText"); - getWidget(mPrevBtn, "PrevPageBTN"); - mPrevBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyPrevPage); - getWidget(mNextBtn, "NextPageBTN"); - mNextBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyNextPage); - //MyGUI::ItemBox* list = new MyGUI::ItemBox(); - //list->addItem("qaq","aqzazaz"); - //mScrollerWidget->addChildItem(list); - //mScrollerWidget->addItem("dserzt",MyGUI::UString("fedgdfg")); - //mEditWidget->addText("ljblsxdvdsfvgedfvdfvdkjfghldfjgn sdv,nhsxl;vvn lklksbvlksb lbsdflkbdSLKJGBLskdhbvlshow(); - //mEditWidget->setEditStatic(true); - mLeftTextWidget->addText("left texxxt "); - mLeftTextWidget->setEditReadOnly(true); - mRightTextWidget->setEditReadOnly(true); - mRightTextWidget->setEditStatic(true); - mLeftTextWidget->setEditStatic(true); - mRightTextWidget->addText("Right texxt "); - - //std::list list = formatText("OpenMW rgh dsfg sqef srg ZT uzql n ZLIEHRF LQSJH GLOIjf qjfmj hslkdgn jlkdjhg qlr isgli shli uhs fiuh qksf cg ksjnf lkqsnbf ksbf sbfkl zbf kuyzflkj sbgdfkj zlfh ozhjfmo hzmfh lizuf rty qzt ezy tkyEZT RYYJ DG fgh is an open-source implementation of the game engine found in the game Morrowind. This is a dumb test text msodjbg smojg smoig fiiinnn"); - //std::list list = formatText(); - //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; - journal.endLine = 0; - - for(std::deque::const_iterator it = MWBase::Environment::get().getJournal()->begin();it!=MWBase::Environment::get().getJournal()->end();++it) - { - std::string a = it->getText(MWBase::Environment::get().getWorld()->getStore()); - journal = formatText(a,journal,10,17); - journal.endLine = journal.endLine +1; - journal.pages.back() = journal.pages.back() + std::string("\n"); - } - //std::string a = MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore()); - //std::list journal = formatText(a,10,20,1); - bool left = true; - for(std::list::iterator it = journal.pages.begin(); it != journal.pages.end();++it) - { - if(left) - { - mLeftPages.push_back(*it); - } - else - { - mRightPages.push_back(*it); - } - left = !left; - } - if(!left) mRightPages.push_back(""); - - mPageNumber = mLeftPages.size()-1; - displayLeftText(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); - - } - else - { - //std::cout << MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore()); - } -} - -void MWGui::JournalWindow::displayLeftText(std::string text) -{ - mLeftTextWidget->eraseText(0,mLeftTextWidget->getTextLength()); - mLeftTextWidget->addText(text); -} - -void MWGui::JournalWindow::displayRightText(std::string text) -{ - mRightTextWidget->eraseText(0,mRightTextWidget->getTextLength()); - mRightTextWidget->addText(text); -} - - -void MWGui::JournalWindow::notifyNextPage(MyGUI::Widget* _sender) -{ - if(mPageNumber < int(mLeftPages.size())-1) - { - std::string nextSound = "book page2"; - MWBase::Environment::get().getSoundManager()->playSound (nextSound, 1.0, 1.0); - mPageNumber = mPageNumber + 1; - displayLeftText(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); - } -} - -void MWGui::JournalWindow::notifyPrevPage(MyGUI::Widget* _sender) -{ - if(mPageNumber > 0) - { - std::string prevSound = "book page"; - MWBase::Environment::get().getSoundManager()->playSound (prevSound, 1.0, 1.0); - mPageNumber = mPageNumber - 1; - displayLeftText(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); - } + return new JournalWindowImpl (Model); } diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index cd1ff7ebbd..63770ec1aa 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -1,43 +1,26 @@ #ifndef MWGUI_JOURNAL_H #define MWGUI_JOURNAL_H -#include -#include -#include -#include +#include +#include -#include "window_base.hpp" -#include "imagebutton.hpp" +namespace MWBase { class WindowManager; } namespace MWGui { - class JournalWindow : public WindowBase + struct JournalViewModel; + + struct JournalWindow { - public: - JournalWindow(MWBase::WindowManager& parWindowManager); - virtual void open(); - virtual void close(); + /// construct a new instance of the one JournalWindow implementation + static JournalWindow * create (boost::shared_ptr Model); - private: - void displayLeftText(std::string text); - void displayRightText(std::string text); + /// destroy this instance of the JournalWindow implementation + virtual ~JournalWindow () {}; - - /** - *Called when next/prev button is used. - */ - void notifyNextPage(MyGUI::Widget* _sender); - void notifyPrevPage(MyGUI::Widget* _sender); - - MyGUI::EditBox* mLeftTextWidget; - MyGUI::EditBox* mRightTextWidget; - MWGui::ImageButton* mPrevBtn; - MWGui::ImageButton* mNextBtn; - std::vector mLeftPages; - std::vector mRightPages; - int mPageNumber; //store the number of the current left page + /// show/hide the journal window + virtual void setVisible (bool newValue) = 0; }; - } #endif diff --git a/apps/openmw/mwgui/keywordsearch.cpp b/apps/openmw/mwgui/keywordsearch.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/openmw/mwgui/keywordsearch.hpp b/apps/openmw/mwgui/keywordsearch.hpp new file mode 100644 index 0000000000..13d3318e59 --- /dev/null +++ b/apps/openmw/mwgui/keywordsearch.hpp @@ -0,0 +1,199 @@ +#ifndef MWGUI_KEYWORDSEARCH_H +#define MWGUI_KEYWORDSEARCH_H + +#include +#include +#include +#include +#include // std::reverse + +#include + +namespace MWGui +{ + +template +class KeywordSearch +{ +public: + + typedef typename string_t::const_iterator Point; + + struct Match + { + Point mBeg; + Point mEnd; + value_t mValue; + }; + + void seed (string_t keyword, value_t value) + { + seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot); + } + + void clear () + { + mRoot.mChildren.clear (); + mRoot.mKeyword.clear (); + } + + bool containsKeyword (string_t keyword, value_t& value) + { + typename Entry::childen_t::iterator current; + typename Entry::childen_t::iterator next; + + current = mRoot.mChildren.find (std::tolower (*keyword.begin(), mLocale)); + if (current == mRoot.mChildren.end()) + return false; + else if (current->second.mKeyword.size() && Misc::StringUtils::ciEqual(current->second.mKeyword, keyword)) + { + value = current->second.mValue; + return true; + } + + for (Point i = ++keyword.begin(); i != keyword.end(); ++i) + { + next = current->second.mChildren.find(std::tolower (*i, mLocale)); + if (next == current->second.mChildren.end()) + return false; + if (Misc::StringUtils::ciEqual(next->second.mKeyword, keyword)) + { + value = next->second.mValue; + return true; + } + current = next; + } + return false; + } + + bool search (Point beg, Point end, Match & match) + { + for (Point i = beg; i != end; ++i) + { + // check first character + typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (std::tolower (*i, mLocale)); + + // no match, on to next character + if (candidate == mRoot.mChildren.end ()) + continue; + + // see how far the match goes + Point j = i; + + // some keywords might be longer variations of other keywords, so we definitely need a list of candidates + // the first element in the pair is length of the match, i.e. depth from the first character on + std::vector< typename std::pair > candidates; + + while ((j + 1) != end) + { + typename Entry::childen_t::iterator next = candidate->second.mChildren.find (std::tolower (*++j, mLocale)); + + if (next == candidate->second.mChildren.end ()) + { + if (candidate->second.mKeyword.size() > 0) + candidates.push_back(std::make_pair((j-i), candidate)); + break; + } + + candidate = next; + + if (candidate->second.mKeyword.size() > 0) + candidates.push_back(std::make_pair((j-i), candidate)); + } + + if (!candidates.size()) + continue; // didn't match enough to disambiguate, on to next character + + // shorter candidates will be added to the vector first. however, we want to check against longer candidates first + std::reverse(candidates.begin(), candidates.end()); + + for (typename std::vector< std::pair >::iterator it = candidates.begin(); + it != candidates.end(); ++it) + { + candidate = it->second; + // try to match the rest of the keyword + Point k = i + it->first; + typename string_t::const_iterator t = candidate->second.mKeyword.begin () + (k - i); + + + while (k != end && t != candidate->second.mKeyword.end ()) + { + if (std::tolower (*k, mLocale) != std::tolower (*t, mLocale)) + break; + + ++k, ++t; + } + + // didn't match full keyword, try the next candidate + if (t != candidate->second.mKeyword.end ()) + continue; + + // we did it, report the good news + match.mValue = candidate->second.mValue; + match.mBeg = i; + match.mEnd = k; + + return true; + } + } + + // no match in range, report the bad news + return false; + } + +private: + + struct Entry + { + typedef std::map childen_t; + + string_t mKeyword; + value_t mValue; + childen_t mChildren; + }; + + void seed_impl (string_t keyword, value_t value, size_t depth, Entry & entry) + { + int ch = tolower (keyword.at (depth), mLocale); + + typename Entry::childen_t::iterator j = entry.mChildren.find (ch); + + if (j == entry.mChildren.end ()) + { + entry.mChildren [ch].mValue = /*std::move*/ (value); + entry.mChildren [ch].mKeyword = /*std::move*/ (keyword); + } + else + { + if (j->second.mKeyword.size () > 0) + { + if (keyword == j->second.mKeyword) + throw std::runtime_error ("duplicate keyword inserted"); + + value_t pushValue = /*std::move*/ (j->second.mValue); + string_t pushKeyword = /*std::move*/ (j->second.mKeyword); + + if (depth >= pushKeyword.size ()) + throw std::runtime_error ("unexpected"); + + if (depth+1 < pushKeyword.size()) + { + seed_impl (/*std::move*/ (pushKeyword), /*std::move*/ (pushValue), depth+1, j->second); + j->second.mKeyword.clear (); + } + } + if (depth+1 == keyword.size()) + j->second.mKeyword = value; + else // depth+1 < keyword.size() + seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), depth+1, j->second); + } + + } + + Entry mRoot; + std::locale mLocale; +}; + +} + +#endif diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 45890b89fe..5857db4d2e 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -8,21 +8,22 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/esmstore.hpp" +#include "../mwworld/fallback.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/stat.hpp" 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"); getWidget(mLevelText, "LevelText"); + getWidget(mLevelDescription, "LevelDescription"); + getWidget(mCoinBox, "Coins"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onOkButtonClicked); @@ -80,11 +81,13 @@ namespace MWGui void LevelupDialog::resetCoins () { - int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5; + int curX = 0; for (int i=0; i<3; ++i) { MyGUI::ImageBox* image = mCoins[i]; - image->setCoord(MyGUI::IntCoord(curX,250,16,16)); + image->detachFromWidget(); + image->attachToWidget(mCoinBox); + image->setCoord(MyGUI::IntCoord(curX,0,16,16)); curX += 24+2; } } @@ -95,6 +98,9 @@ namespace MWGui for (unsigned int i=0; idetachFromWidget(); + image->attachToWidget(mMainWidget); + int attribute = mSpentAttributes[i]; int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 30; @@ -113,8 +119,6 @@ namespace MWGui MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); - center(); - mSpentAttributes.clear(); resetCoins(); @@ -128,16 +132,25 @@ namespace MWGui mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); - /// \todo replace this with INI-imported texts int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); + std::string levelupdescription; + if(level>20) + levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default"); + else + levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast(level)); + + mLevelDescription->setCaption (levelupdescription); + for (int i=0; i<8; ++i) { MyGUI::TextBox* text = mAttributeMultipliers[i]; int mult = pcStats.getLevelupAttributeMultiplier (i); text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast(mult)); } + + center(); } void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) @@ -147,7 +160,7 @@ namespace MWGui MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); if (mSpentAttributes.size() < 3) - MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}", std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}"); else { // increase attributes @@ -168,7 +181,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 f5b24530d1..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(); @@ -17,6 +17,9 @@ namespace MWGui MyGUI::Button* mOkButton; MyGUI::ImageBox* mClassImage; MyGUI::TextBox* mLevelText; + MyGUI::EditBox* mLevelDescription; + + MyGUI::Widget* mCoinBox; std::vector mAttributes; std::vector mAttributeValues; diff --git a/apps/openmw/mwgui/list.cpp b/apps/openmw/mwgui/list.cpp index c7b7207305..8dda041ca6 100644 --- a/apps/openmw/mwgui/list.cpp +++ b/apps/openmw/mwgui/list.cpp @@ -5,159 +5,165 @@ #include #include -using namespace MWGui; -using namespace MWGui::Widgets; - -MWList::MWList() : - mClient(0) - , mScrollView(0) - , mItemHeight(0) +namespace MWGui { -} -void MWList::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mClient, "Client"); - if (mClient == 0) - mClient = this; - - mScrollView = mClient->createWidgetReal( - "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), - MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); -} - -void MWList::addItem(const std::string& name) -{ - mItems.push_back(name); -} - -void MWList::addSeparator() -{ - mItems.push_back(""); -} - -void MWList::adjustSize() -{ - redraw(); -} - -void MWList::redraw(bool scrollbarShown) -{ - const int _scrollBarWidth = 24; // fetch this from skin? - const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; - const int spacing = 3; - size_t scrollbarPosition = mScrollView->getScrollPosition(); - - while (mScrollView->getChildCount()) + namespace Widgets { - MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); - } - mItemHeight = 0; - int i=0; - for (std::vector::const_iterator it=mItems.begin(); - it!=mItems.end(); ++it) - { - if (*it != "") + MWList::MWList() : + mClient(0) + , mScrollView(0) + , mItemHeight(0) { - MyGUI::Button* button = mScrollView->createWidget( - "MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), - MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it)); - button->setCaption((*it)); - button->getSubWidgetText()->setWordWrap(true); - button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); - button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); - - int height = button->getTextSize().height; - button->setSize(MyGUI::IntSize(button->getSize().width, height)); - button->setUserData(i); - - mItemHeight += height + spacing; } - else + + void MWList::initialiseOverride() { - MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", - MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - separator->setNeedMouseFocus(false); + Base::initialiseOverride(); - mItemHeight += 18 + spacing; + assignWidget(mClient, "Client"); + if (mClient == 0) + mClient = this; + + mScrollView = mClient->createWidgetReal( + "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), + MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); } - ++i; + + void MWList::addItem(const std::string& name) + { + mItems.push_back(name); + } + + void MWList::addSeparator() + { + mItems.push_back(""); + } + + void MWList::adjustSize() + { + redraw(); + } + + void MWList::redraw(bool scrollbarShown) + { + const int _scrollBarWidth = 24; // fetch this from skin? + const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; + const int spacing = 3; + size_t scrollbarPosition = mScrollView->getScrollPosition(); + + while (mScrollView->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); + } + + mItemHeight = 0; + int i=0; + for (std::vector::const_iterator it=mItems.begin(); + it!=mItems.end(); ++it) + { + if (*it != "") + { + MyGUI::Button* button = mScrollView->createWidget( + "MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), + MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it)); + button->setCaption((*it)); + button->getSubWidgetText()->setWordWrap(true); + button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); + button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); + + int height = button->getTextSize().height; + button->setSize(MyGUI::IntSize(button->getSize().width, height)); + button->setUserData(i); + + mItemHeight += height + spacing; + } + else + { + MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", + MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + separator->setNeedMouseFocus(false); + + mItemHeight += 18 + spacing; + } + ++i; + } + mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); + + if (!scrollbarShown && mItemHeight > mClient->getSize().height) + redraw(true); + + size_t scrollbarRange = mScrollView->getScrollRange(); + if(scrollbarPosition > scrollbarRange) + scrollbarPosition = scrollbarRange; + mScrollView->setScrollPosition(scrollbarPosition); + } + + bool MWList::hasItem(const std::string& name) + { + return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); + } + + unsigned int MWList::getItemCount() + { + return mItems.size(); + } + + std::string MWList::getItemNameAt(unsigned int at) + { + assert(at < mItems.size() && "List item out of bounds"); + return mItems[at]; + } + + void MWList::removeItem(const std::string& name) + { + assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() ); + mItems.erase( std::find(mItems.begin(), mItems.end(), name) ); + } + + void MWList::clear() + { + mItems.clear(); + } + + void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + //NB view offset is negative + if (mScrollView->getViewOffset().top + _rel*0.3 > 0) + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3)); + } + + void MWList::onItemSelected(MyGUI::Widget* _sender) + { + std::string name = static_cast(_sender)->getCaption(); + int id = *_sender->getUserData(); + eventItemSelected(name, id); + eventWidgetSelected(_sender); + } + + MyGUI::Widget* MWList::getItemWidget(const std::string& name) + { + return mScrollView->findWidget (getName() + "_item_" + name); + } + + size_t MWScrollView::getScrollPosition() + { + return getVScroll()->getScrollPosition(); + } + + void MWScrollView::setScrollPosition(size_t position) + { + getVScroll()->setScrollPosition(position); + } + size_t MWScrollView::getScrollRange() + { + return getVScroll()->getScrollRange(); + } + } - mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); - - if (!scrollbarShown && mItemHeight > mClient->getSize().height) - redraw(true); - - size_t scrollbarRange = mScrollView->getScrollRange(); - if(scrollbarPosition > scrollbarRange) - scrollbarPosition = scrollbarRange; - mScrollView->setScrollPosition(scrollbarPosition); -} - -bool MWList::hasItem(const std::string& name) -{ - return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); -} - -unsigned int MWList::getItemCount() -{ - return mItems.size(); -} - -std::string MWList::getItemNameAt(unsigned int at) -{ - assert(at < mItems.size() && "List item out of bounds"); - return mItems[at]; -} - -void MWList::removeItem(const std::string& name) -{ - assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() ); - mItems.erase( std::find(mItems.begin(), mItems.end(), name) ); -} - -void MWList::clear() -{ - mItems.clear(); -} - -void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - //NB view offset is negative - if (mScrollView->getViewOffset().top + _rel*0.3 > 0) - mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3)); -} - -void MWList::onItemSelected(MyGUI::Widget* _sender) -{ - std::string name = static_cast(_sender)->getCaption(); - int id = *_sender->getUserData(); - eventItemSelected(name, id); - eventWidgetSelected(_sender); -} - -MyGUI::Widget* MWList::getItemWidget(const std::string& name) -{ - return mScrollView->findWidget (getName() + "_item_" + name); -} - -size_t MWScrollView::getScrollPosition() -{ - return getVScroll()->getScrollPosition(); -} - -void MWScrollView::setScrollPosition(size_t position) -{ - getVScroll()->setScrollPosition(position); -} -size_t MWScrollView::getScrollRange() -{ - return getVScroll()->getScrollRange(); } 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..018f51feb8 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,36 +1,28 @@ #include "loadingscreen.hpp" #include -#include #include #include -#include - - -#include #include #include "../mwbase/environment.hpp" -#include "../mwbase/inputmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include - - 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) , mFirstLoad(true) + , mTotalRefsLoading(0) { getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); @@ -195,12 +187,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 +203,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/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 5402d35428..88227c7512 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -3,9 +3,11 @@ #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/dialoguemanager.hpp" namespace MWGui { @@ -30,7 +32,7 @@ namespace MWGui std::vector buttons; buttons.push_back("return"); - //buttons.push_back("newgame"); + buttons.push_back("newgame"); //buttons.push_back("loadgame"); //buttons.push_back("savegame"); buttons.push_back("options"); @@ -73,6 +75,13 @@ namespace MWGui MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); else if (sender == mButtons["exitgame"]) Ogre::Root::getSingleton ().queueEndRendering (); + else if (sender == mButtons["newgame"]) + { + MWBase::Environment::get().getWorld()->startNewGame(); + MWBase::Environment::get().getWindowManager()->setNewGame(true); + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + } } } diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp deleted file mode 100644 index 52b108f850..0000000000 --- a/apps/openmw/mwgui/map_window.cpp +++ /dev/null @@ -1,446 +0,0 @@ -#include "map_window.hpp" - -#include - -#include -#include -#include - -#include - -#include "../mwbase/windowmanager.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" -#include "../mwworld/player.hpp" - -#include "../mwrender/globalmap.hpp" - -#include "widgets.hpp" - -using namespace MWGui; - -LocalMapBase::LocalMapBase() - : mCurX(0) - , mCurY(0) - , mInterior(false) - , mFogOfWar(true) - , mLocalMap(NULL) - , mMapDragAndDrop(false) - , mPrefix() - , mChanged(true) - , mLayout(NULL) - , mLastPositionX(0.0f) - , mLastPositionY(0.0f) - , mLastDirectionX(0.0f) - , mLastDirectionY(0.0f) - , mCompass(NULL) -{ -} - -void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) -{ - mLocalMap = widget; - mLayout = layout; - mMapDragAndDrop = mapDragAndDrop; - mCompass = compass; - - // create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each - const int widgetSize = 512; - for (int mx=0; mx<3; ++mx) - { - for (int my=0; my<3; ++my) - { - MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", - MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), - MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my)); - - MyGUI::ImageBox* fog = map->createWidget("ImageBox", - MyGUI::IntCoord(0, 0, widgetSize, widgetSize), - MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my) + "_fog"); - - if (!mMapDragAndDrop) - { - map->setNeedMouseFocus(false); - fog->setNeedMouseFocus(false); - } - - mMapWidgets.push_back(map); - mFogWidgets.push_back(fog); - } - } -} - -void LocalMapBase::setCellPrefix(const std::string& prefix) -{ - mPrefix = prefix; - mChanged = true; -} - -void LocalMapBase::toggleFogOfWar() -{ - mFogOfWar = !mFogOfWar; - applyFogOfWar(); -} - -void LocalMapBase::applyFogOfWar() -{ - for (int mx=0; mx<3; ++mx) - { - for (int my=0; my<3; ++my) - { - std::string name = "Map_" + boost::lexical_cast(mx) + "_" - + boost::lexical_cast(my); - - std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" - + boost::lexical_cast(mCurY + (-1*(my-1))); - MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; - fog->setImageTexture(mFogOfWar ? - ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" - : "black.png" ) - : ""); - } - } - notifyMapChanged (); -} - -void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) -{ - applyFogOfWar (); -} - -void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) -{ - applyFogOfWar (); -} - -void LocalMapBase::setActiveCell(const int x, const int y, bool interior) -{ - if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell - - // clear all previous markers - for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) - { - if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") - { - MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); - } - } - - for (int mx=0; mx<3; ++mx) - { - for (int my=0; my<3; ++my) - { - // map - std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" - + boost::lexical_cast(y + (-1*(my-1))); - - std::string name = "Map_" + boost::lexical_cast(mx) + "_" - + boost::lexical_cast(my); - - MyGUI::ImageBox* box = mMapWidgets[my + 3*mx]; - - if (MyGUI::RenderManager::getInstance().getTexture(image) != 0) - box->setImageTexture(image); - else - box->setImageTexture("black.png"); - - - // door markers - - // interior map only consists of one cell, so handle the markers only once - if (interior && (mx != 2 || my != 2)) - continue; - - MWWorld::CellStore* cell; - if (interior) - cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); - else - cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); - - std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); - - for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) - { - MWBase::World::DoorMarker marker = *it; - - // convert world coordinates to normalized cell coordinates - MyGUI::IntCoord widgetCoord; - float nX,nY; - int cellDx, cellDy; - if (!interior) - { - const int cellSize = 8192; - - nX = (marker.x - cellSize * (x+mx-1)) / cellSize; - nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); - } - else - { - Ogre::Vector2 position (marker.x, marker.y); - MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); - } - - static int counter = 0; - ++counter; - MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); - markerWidget->setImageResource("DoorMarker"); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTip"); - markerWidget->setUserString("Caption_Text", marker.name); - markerWidget->setUserString("IsMarker", "true"); - markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); - markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); - - MarkerPosition markerPos; - markerPos.interior = interior; - markerPos.cellX = interior ? cellDx : x + mx - 1; - markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); - markerPos.nX = nX; - markerPos.nY = nY; - - markerWidget->setUserData(markerPos); - } - - - } - } - mInterior = interior; - mCurX = x; - mCurY = y; - mChanged = false; - - // fog of war - applyFogOfWar(); - - // set the compass texture again, because MyGUI determines sorting of ImageBox widgets - // based on the last setImageTexture call - std::string tex = "textures\\compass.dds"; - mCompass->setImageTexture(""); - mCompass->setImageTexture(tex); -} - - -void LocalMapBase::setPlayerPos(const float x, const float y) -{ - if (x == mLastPositionX && y == mLastPositionY) - return; - - notifyPlayerUpdate (); - - MyGUI::IntSize size = mLocalMap->getCanvasSize(); - MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); - MyGUI::IntCoord viewsize = mLocalMap->getCoord(); - MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); - mLocalMap->setViewOffset(pos); - - mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16)); - mLastPositionX = x; - mLastPositionY = y; -} - -void LocalMapBase::setPlayerDir(const float x, const float y) -{ - if (x == mLastDirectionX && y == mLastDirectionY) - return; - - notifyPlayerUpdate (); - - MyGUI::ISubWidget* main = mCompass->getSubWidgetMain(); - MyGUI::RotatingSkin* rotatingSubskin = main->castType(); - rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); - float angle = std::atan2(x,y); - rotatingSubskin->setAngle(angle); - - mLastDirectionX = x; - mLastDirectionY = y; -} - -// ------------------------------------------------------------------------------------------ - -MapWindow::MapWindow(MWBase::WindowManager& parWindowManager, const std::string& cacheDir) - : MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager) - , mGlobal(false) -{ - setCoord(500,0,320,300); - - mGlobalMapRender = new MWRender::GlobalMap(cacheDir); - mGlobalMapRender->render(); - - getWidget(mLocalMap, "LocalMap"); - getWidget(mGlobalMap, "GlobalMap"); - getWidget(mGlobalMapImage, "GlobalMapImage"); - getWidget(mGlobalMapOverlay, "GlobalMapOverlay"); - getWidget(mPlayerArrowLocal, "CompassLocal"); - getWidget(mPlayerArrowGlobal, "CompassGlobal"); - - mGlobalMapImage->setImageTexture("GlobalMap.png"); - mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); - - mGlobalMap->setVisible (false); - - getWidget(mButton, "WorldButton"); - mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); - mButton->setCaptionWithReplacing("#{sWorld}"); - - getWidget(mEventBoxGlobal, "EventBoxGlobal"); - mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); - mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); - getWidget(mEventBoxLocal, "EventBoxLocal"); - mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); - mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); - - LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); -} - -MapWindow::~MapWindow() -{ - delete mGlobalMapRender; -} - -void MapWindow::setCellName(const std::string& cellName) -{ - setTitle("#{sCell=" + cellName + "}"); -} - -void MapWindow::addVisitedLocation(const std::string& name, int x, int y) -{ - float worldX, worldY; - mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); - - MyGUI::IntCoord widgetCoord( - worldX * mGlobalMapRender->getWidth()+6, - worldY * mGlobalMapRender->getHeight()+6, - 12, 12); - - - static int _counter=0; - MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); - markerWidget->setImageResource("DoorMarker"); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTip"); - markerWidget->setUserString("Caption_Text", name); - ++_counter; - - markerWidget = mEventBoxGlobal->createWidget("", - widgetCoord, MyGUI::Align::Default); - markerWidget->setNeedMouseFocus (true); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTip"); - markerWidget->setUserString("Caption_Text", name); -} - -void MapWindow::cellExplored(int x, int y) -{ - mGlobalMapRender->exploreCell(x,y); -} - -void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) -{ - if (_id!=MyGUI::MouseButton::Left) return; - mLastDragPos = MyGUI::IntPoint(_left, _top); -} - -void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) -{ - if (_id!=MyGUI::MouseButton::Left) return; - - MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; - - if (!mGlobal) - mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); - else - mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); - - - mLastDragPos = MyGUI::IntPoint(_left, _top); -} - -void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) -{ - mGlobal = !mGlobal; - mGlobalMap->setVisible(mGlobal); - mLocalMap->setVisible(!mGlobal); - - mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : - "#{sWorld}"); - - if (mGlobal) - globalMapUpdatePlayer (); -} - -void MapWindow::onPinToggled() -{ - mWindowManager.setMinimapVisibility(!mPinned); -} - -void MapWindow::open() -{ - mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - - for (unsigned int i=0; igetChildCount (); ++i) - { - if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") - mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); - } - - globalMapUpdatePlayer(); - - mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); -} - -void MapWindow::globalMapUpdatePlayer () -{ - Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); - Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); - Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); - - float worldX, worldY; - mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); - worldX *= mGlobalMapRender->getWidth(); - worldY *= mGlobalMapRender->getHeight(); - - - // for interiors, we have no choice other than using the last position & direction. - /// \todo save this last position in the savegame? - if (MWBase::Environment::get().getWorld ()->isCellExterior ()) - { - mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); - - MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); - MyGUI::RotatingSkin* rotatingSubskin = main->castType(); - rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); - float angle = std::atan2(dir.x, dir.y); - rotatingSubskin->setAngle(angle); - - // set the view offset so that player is in the center - MyGUI::IntSize viewsize = mGlobalMap->getSize(); - MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); - mGlobalMap->setViewOffset(viewoffs); - } -} - -void MapWindow::notifyPlayerUpdate () -{ - globalMapUpdatePlayer (); -} - -void MapWindow::notifyMapChanged () -{ - // workaround to prevent the map from drawing on top of the button - MyGUI::IntCoord oldCoord = mButton->getCoord (); - MyGUI::Gui::getInstance().destroyWidget (mButton); - mButton = mMainWidget->createWidget("MW_Button", - oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right); - mButton->setProperty ("ExpandDirection", "Left"); - - mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); - mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : - "#{sWorld}"); -} diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp new file mode 100644 index 0000000000..4a78238ce0 --- /dev/null +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -0,0 +1,445 @@ +#include "mapwindow.hpp" + +#include + +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/player.hpp" + +#include "../mwrender/globalmap.hpp" + +#include "widgets.hpp" + +namespace MWGui +{ + + LocalMapBase::LocalMapBase() + : mCurX(0) + , mCurY(0) + , mInterior(false) + , mFogOfWar(true) + , mLocalMap(NULL) + , mMapDragAndDrop(false) + , mPrefix() + , mChanged(true) + , mLayout(NULL) + , mLastPositionX(0.0f) + , mLastPositionY(0.0f) + , mLastDirectionX(0.0f) + , mLastDirectionY(0.0f) + , mCompass(NULL) + { + } + + void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) + { + mLocalMap = widget; + mLayout = layout; + mMapDragAndDrop = mapDragAndDrop; + mCompass = compass; + + // create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each + const int widgetSize = 512; + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", + MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), + MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my)); + + MyGUI::ImageBox* fog = map->createWidget("ImageBox", + MyGUI::IntCoord(0, 0, widgetSize, widgetSize), + MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my) + "_fog"); + + if (!mMapDragAndDrop) + { + map->setNeedMouseFocus(false); + fog->setNeedMouseFocus(false); + } + + mMapWidgets.push_back(map); + mFogWidgets.push_back(fog); + } + } + } + + void LocalMapBase::setCellPrefix(const std::string& prefix) + { + mPrefix = prefix; + mChanged = true; + } + + void LocalMapBase::toggleFogOfWar() + { + mFogOfWar = !mFogOfWar; + applyFogOfWar(); + } + + void LocalMapBase::applyFogOfWar() + { + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + std::string name = "Map_" + boost::lexical_cast(mx) + "_" + + boost::lexical_cast(my); + + std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" + + boost::lexical_cast(mCurY + (-1*(my-1))); + MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; + fog->setImageTexture(mFogOfWar ? + ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" + : "black.png" ) + : ""); + } + } + notifyMapChanged (); + } + + void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) + { + applyFogOfWar (); + } + + void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) + { + applyFogOfWar (); + } + + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) + { + if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell + + // clear all previous markers + for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) + { + if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + { + MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); + } + } + + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + // map + std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" + + boost::lexical_cast(y + (-1*(my-1))); + + std::string name = "Map_" + boost::lexical_cast(mx) + "_" + + boost::lexical_cast(my); + + MyGUI::ImageBox* box = mMapWidgets[my + 3*mx]; + + if (MyGUI::RenderManager::getInstance().getTexture(image) != 0) + box->setImageTexture(image); + else + box->setImageTexture("black.png"); + + + // door markers + + // interior map only consists of one cell, so handle the markers only once + if (interior && (mx != 2 || my != 2)) + continue; + + MWWorld::CellStore* cell; + if (interior) + cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); + else + cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); + + std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); + + for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) + { + MWBase::World::DoorMarker marker = *it; + + // convert world coordinates to normalized cell coordinates + MyGUI::IntCoord widgetCoord; + float nX,nY; + int cellDx, cellDy; + if (!interior) + { + const int cellSize = 8192; + + nX = (marker.x - cellSize * (x+mx-1)) / cellSize; + nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; + + widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); + } + else + { + Ogre::Vector2 position (marker.x, marker.y); + MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); + + widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); + } + + static int counter = 0; + ++counter; + MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", marker.name); + markerWidget->setUserString("IsMarker", "true"); + markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); + markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); + + MarkerPosition markerPos; + markerPos.interior = interior; + markerPos.cellX = interior ? cellDx : x + mx - 1; + markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); + markerPos.nX = nX; + markerPos.nY = nY; + + markerWidget->setUserData(markerPos); + } + + + } + } + mInterior = interior; + mCurX = x; + mCurY = y; + mChanged = false; + + // fog of war + applyFogOfWar(); + + // set the compass texture again, because MyGUI determines sorting of ImageBox widgets + // based on the last setImageTexture call + std::string tex = "textures\\compass.dds"; + mCompass->setImageTexture(""); + mCompass->setImageTexture(tex); + } + + + void LocalMapBase::setPlayerPos(const float x, const float y) + { + if (x == mLastPositionX && y == mLastPositionY) + return; + + notifyPlayerUpdate (); + + MyGUI::IntSize size = mLocalMap->getCanvasSize(); + MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); + MyGUI::IntCoord viewsize = mLocalMap->getCoord(); + MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); + mLocalMap->setViewOffset(pos); + + mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16)); + mLastPositionX = x; + mLastPositionY = y; + } + + void LocalMapBase::setPlayerDir(const float x, const float y) + { + if (x == mLastDirectionX && y == mLastDirectionY) + return; + + notifyPlayerUpdate (); + + MyGUI::ISubWidget* main = mCompass->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(x,y); + rotatingSubskin->setAngle(angle); + + mLastDirectionX = x; + mLastDirectionY = y; + } + + // ------------------------------------------------------------------------------------------ + + MapWindow::MapWindow(const std::string& cacheDir) + : MWGui::WindowPinnableBase("openmw_map_window.layout") + , mGlobal(false) + { + setCoord(500,0,320,300); + + mGlobalMapRender = new MWRender::GlobalMap(cacheDir); + mGlobalMapRender->render(); + + getWidget(mLocalMap, "LocalMap"); + getWidget(mGlobalMap, "GlobalMap"); + getWidget(mGlobalMapImage, "GlobalMapImage"); + getWidget(mGlobalMapOverlay, "GlobalMapOverlay"); + getWidget(mPlayerArrowLocal, "CompassLocal"); + getWidget(mPlayerArrowGlobal, "CompassGlobal"); + + mGlobalMapImage->setImageTexture("GlobalMap.png"); + mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); + + mGlobalMap->setVisible (false); + + getWidget(mButton, "WorldButton"); + mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); + mButton->setCaptionWithReplacing("#{sWorld}"); + + getWidget(mEventBoxGlobal, "EventBoxGlobal"); + mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + getWidget(mEventBoxLocal, "EventBoxLocal"); + mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + + LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); + } + + MapWindow::~MapWindow() + { + delete mGlobalMapRender; + } + + void MapWindow::setCellName(const std::string& cellName) + { + setTitle("#{sCell=" + cellName + "}"); + } + + void MapWindow::addVisitedLocation(const std::string& name, int x, int y) + { + float worldX, worldY; + mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); + + MyGUI::IntCoord widgetCoord( + worldX * mGlobalMapRender->getWidth()+6, + worldY * mGlobalMapRender->getHeight()+6, + 12, 12); + + + static int _counter=0; + MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", name); + ++_counter; + + markerWidget = mEventBoxGlobal->createWidget("", + widgetCoord, MyGUI::Align::Default); + markerWidget->setNeedMouseFocus (true); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", name); + } + + void MapWindow::cellExplored(int x, int y) + { + mGlobalMapRender->exploreCell(x,y); + } + + void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + if (_id!=MyGUI::MouseButton::Left) return; + mLastDragPos = MyGUI::IntPoint(_left, _top); + } + + void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + if (_id!=MyGUI::MouseButton::Left) return; + + MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; + + if (!mGlobal) + mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); + else + mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); + + + mLastDragPos = MyGUI::IntPoint(_left, _top); + } + + void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) + { + mGlobal = !mGlobal; + mGlobalMap->setVisible(mGlobal); + mLocalMap->setVisible(!mGlobal); + + mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : + "#{sWorld}"); + + if (mGlobal) + globalMapUpdatePlayer (); + } + + void MapWindow::onPinToggled() + { + MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned); + } + + void MapWindow::open() + { + mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + + for (unsigned int i=0; igetChildCount (); ++i) + { + if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") + mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); + } + + globalMapUpdatePlayer(); + + mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); + } + + void MapWindow::globalMapUpdatePlayer () + { + Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); + Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); + Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); + + float worldX, worldY; + mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); + worldX *= mGlobalMapRender->getWidth(); + worldY *= mGlobalMapRender->getHeight(); + + + // for interiors, we have no choice other than using the last position & direction. + /// \todo save this last position in the savegame? + if (MWBase::Environment::get().getWorld ()->isCellExterior ()) + { + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); + + MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(dir.x, dir.y); + rotatingSubskin->setAngle(angle); + + // set the view offset so that player is in the center + MyGUI::IntSize viewsize = mGlobalMap->getSize(); + MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); + mGlobalMap->setViewOffset(viewoffs); + } + } + + void MapWindow::notifyPlayerUpdate () + { + globalMapUpdatePlayer (); + } + + void MapWindow::notifyMapChanged () + { + // workaround to prevent the map from drawing on top of the button + MyGUI::IntCoord oldCoord = mButton->getCoord (); + MyGUI::Gui::getInstance().destroyWidget (mButton); + mButton = mMainWidget->createWidget("MW_Button", + oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right); + mButton->setProperty ("ExpandDirection", "Left"); + + mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); + mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : + "#{sWorld}"); + } + +} 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 new file mode 100644 index 0000000000..530594ddaa --- /dev/null +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -0,0 +1,132 @@ +#include "merchantrepair.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" + +#include "inventorywindow.hpp" +#include "tradewindow.hpp" + +namespace MWGui +{ + +MerchantRepair::MerchantRepair() + : WindowBase("openmw_merchantrepair.layout") +{ + getWidget(mList, "RepairView"); + getWidget(mOkButton, "OkButton"); + getWidget(mGoldLabel, "PlayerGold"); + + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onOkButtonClick); +} + +void MerchantRepair::startRepair(const MWWorld::Ptr &actor) +{ + mActor = actor; + + while (mList->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mList->getChildAt(0)); + + int currentY = 0; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + 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)) + { + int maxDurability = MWWorld::Class::get(*iter).getItemMaxHealth(*iter); + int durability = (iter->getCellRef().mCharge == -1) ? maxDurability : iter->getCellRef().mCharge; + if (maxDurability == durability) + continue; + + int basePrice = MWWorld::Class::get(*iter).getValue(*iter); + float fRepairMult = MWBase::Environment::get().getWorld()->getStore().get() + .find("fRepairMult")->getFloat(); + + float p = std::max(1, basePrice); + float r = std::max(1, static_cast(maxDurability / p)); + + int x = ((maxDurability - durability) / r); + x = (fRepairMult * x); + + int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true); + + + std::string name = MWWorld::Class::get(*iter).getName(*iter) + + " - " + boost::lexical_cast(price) + + MWBase::Environment::get().getWorld()->getStore().get() + .find("sgp")->getString();; + + + MyGUI::Button* button = + mList->createWidget( + (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + 0, + currentY, + 0, + 18, + MyGUI::Align::Default + ); + + currentY += 18; + + button->setEnabled(price<=MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()); + button->setUserString("Price", boost::lexical_cast(price)); + button->setUserData(*iter); + button->setCaptionWithReplacing(name); + button->setSize(button->getTextSize().width,18); + button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel); + button->setUserString("ToolTipType", "ItemPtr"); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick); + } + } + mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY))); + + mGoldLabel->setCaptionWithReplacing("#{sGold}: " + + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); +} + +void MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel) +{ + if (mList->getViewOffset().top + _rel*0.3 > 0) + mList->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mList->setViewOffset(MyGUI::IntPoint(0, mList->getViewOffset().top + _rel*0.3)); +} + +void MerchantRepair::open() +{ + center(); +} + +void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) +{ + // repair + MWWorld::Ptr item = *sender->getUserData(); + item.getCellRef().mCharge = MWWorld::Class::get(item).getItemMaxHealth(item); + + MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); + + int price = boost::lexical_cast(sender->getUserString("Price")); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-price); + + startRepair(mActor); +} + +void MerchantRepair::onOkButtonClick(MyGUI::Widget *sender) +{ + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_MerchantRepair); +} + +} diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp new file mode 100644 index 0000000000..4cb39fe012 --- /dev/null +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -0,0 +1,37 @@ +#ifndef OPENMW_MWGUI_MERCHANTREPAIR_H +#define OPENMW_MWGUI_MERCHANTREPAIR_H + +#include "windowbase.hpp" +#include "../mwworld/ptr.hpp" + + + +namespace MWGui +{ + +class MerchantRepair : public WindowBase +{ +public: + MerchantRepair(); + + virtual void open(); + + void startRepair(const MWWorld::Ptr& actor); + +private: + MyGUI::ScrollView* mList; + MyGUI::Button* mOkButton; + MyGUI::TextBox* mGoldLabel; + + MWWorld::Ptr mActor; + +protected: + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void onRepairButtonClick(MyGUI::Widget* sender); + void onOkButtonClick(MyGUI::Widget* sender); + +}; + +} + +#endif diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index b8a34c457e..2fc50f257c 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -3,411 +3,426 @@ #include "messagebox.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/inputmanager.hpp" -using namespace MWGui; - -MessageBoxManager::MessageBoxManager (MWBase::WindowManager *windowManager) +namespace MWGui { - mWindowManager = windowManager; - // defines - mMessageBoxSpeed = 0.1; - mInterMessageBoxe = NULL; -} -void MessageBoxManager::onFrame (float frameDuration) -{ - std::vector::iterator it; - for(it = mTimers.begin(); it != mTimers.end();) + MessageBoxManager::MessageBoxManager () { - // if this messagebox is already deleted, remove the timer and move on - if (std::find(mMessageBoxes.begin(), mMessageBoxes.end(), it->messageBox) == mMessageBoxes.end()) - { - it = mTimers.erase(it); - continue; - } + // defines + mMessageBoxSpeed = 0.1; + mInterMessageBoxe = NULL; + mStaticMessageBox = NULL; + } - it->current += frameDuration; - if(it->current >= it->max) + void MessageBoxManager::onFrame (float frameDuration) + { + std::vector::iterator it; + for(it = mTimers.begin(); it != mTimers.end();) { - it->messageBox->mMarkedToDelete = true; - - if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one + // if this messagebox is already deleted, remove the timer and move on + if (std::find(mMessageBoxes.begin(), mMessageBoxes.end(), it->messageBox) == mMessageBoxes.end()) { - // collect all with mMarkedToDelete and delete them. - // and place the other messageboxes on the right position - int height = 0; - std::vector::iterator it2 = mMessageBoxes.begin(); - while(it2 != mMessageBoxes.end()) + it = mTimers.erase(it); + continue; + } + + it->current += frameDuration; + if(it->current >= it->max) + { + it->messageBox->mMarkedToDelete = true; + + if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one { - if((*it2)->mMarkedToDelete) + // collect all with mMarkedToDelete and delete them. + // and place the other messageboxes on the right position + int height = 0; + std::vector::iterator it2 = mMessageBoxes.begin(); + while(it2 != mMessageBoxes.end()) { - delete (*it2); - it2 = mMessageBoxes.erase(it2); - } - else { - (*it2)->update(height); - height += (*it2)->getHeight(); - it2++; + if((*it2)->mMarkedToDelete) + { + delete (*it2); + it2 = mMessageBoxes.erase(it2); + } + else { + (*it2)->update(height); + height += (*it2)->getHeight(); + it2++; + } } } + it = mTimers.erase(it); + } + else + { + it++; } - it = mTimers.erase(it); } - else - { - it++; + + if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { + delete mInterMessageBoxe; + mInterMessageBoxe = NULL; + MWBase::Environment::get().getInputManager()->changeInputMode( + MWBase::Environment::get().getWindowManager()->isGuiMode()); } } - if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { - delete mInterMessageBoxe; - mInterMessageBoxe = NULL; - mWindowManager->removeGuiMode(GM_InterMessageBox); - } -} - -void MessageBoxManager::createMessageBox (const std::string& message) -{ - MessageBox *box = new MessageBox(*this, message); - - removeMessageBox(message.length()*mMessageBoxSpeed, box); - - mMessageBoxes.push_back(box); - std::vector::iterator it; - - if(mMessageBoxes.size() > 3) { - delete *mMessageBoxes.begin(); - mMessageBoxes.erase(mMessageBoxes.begin()); - } - - int height = 0; - for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + void MessageBoxManager::createMessageBox (const std::string& message, bool stat) { - (*it)->update(height); - height += (*it)->getHeight(); - } -} + MessageBox *box = new MessageBox(*this, message); -bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) -{ - /// \todo Don't write this kind of error message to cout. Either discard the old message box - /// silently or throw an exception. - if(mInterMessageBoxe != NULL) { - std::cout << "there is a MessageBox already" << std::endl; + if(stat) + mStaticMessageBox = box; + else + removeMessageBox(message.length()*mMessageBoxSpeed, box); + + mMessageBoxes.push_back(box); + std::vector::iterator it; + + if(mMessageBoxes.size() > 3) { + delete *mMessageBoxes.begin(); + mMessageBoxes.erase(mMessageBoxes.begin()); + } + + int height = 0; + for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + { + (*it)->update(height); + height += (*it)->getHeight(); + } + } + + void MessageBoxManager::removeStaticMessageBox () + { + removeMessageBox(mStaticMessageBox); + mStaticMessageBox = NULL; + } + + bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) + { + if(mInterMessageBoxe != NULL) { + throw std::runtime_error("There is a message box already"); + } + + mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); + + return true; + } + + bool MessageBoxManager::isInteractiveMessageBox () + { + return mInterMessageBoxe != NULL; + } + + void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox) + { + MessageBoxManagerTimer timer; + timer.current = 0; + timer.max = time; + timer.messageBox = msgbox; + + mTimers.insert(mTimers.end(), timer); + } + + bool MessageBoxManager::removeMessageBox (MessageBox *msgbox) + { + std::vector::iterator it; + for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + { + if((*it) == msgbox) + { + delete (*it); + mMessageBoxes.erase(it); + return true; + } + } return false; } - mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); - - return true; -} - -bool MessageBoxManager::isInteractiveMessageBox () -{ - return mInterMessageBoxe != NULL; -} - -void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox) -{ - MessageBoxManagerTimer timer; - timer.current = 0; - timer.max = time; - timer.messageBox = msgbox; - - mTimers.insert(mTimers.end(), timer); -} - -bool MessageBoxManager::removeMessageBox (MessageBox *msgbox) -{ - std::vector::iterator it; - for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + void MessageBoxManager::setMessageBoxSpeed (int speed) { - if((*it) == msgbox) + mMessageBoxSpeed = speed; + } + + void MessageBoxManager::enterPressed () + { + if(mInterMessageBoxe != NULL) + mInterMessageBoxe->enterPressed(); + } + + int MessageBoxManager::readPressedButton () + { + if(mInterMessageBoxe != NULL) { - delete (*it); - mMessageBoxes.erase(it); - return true; + return mInterMessageBoxe->readPressedButton(); } + return -1; } - return false; -} -void MessageBoxManager::setMessageBoxSpeed (int speed) -{ - mMessageBoxSpeed = speed; -} -void MessageBoxManager::enterPressed () -{ - mInterMessageBoxe->enterPressed(); -} -int MessageBoxManager::readPressedButton () -{ - if(mInterMessageBoxe != NULL) + + MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) + : Layout("openmw_messagebox.layout") + , mMessageBoxManager(parMessageBoxManager) + , mMessage(message) { - return mInterMessageBoxe->readPressedButton(); + // defines + mFixedWidth = 300; + mBottomPadding = 20; + mNextBoxPadding = 20; + mMarkedToDelete = false; + + getWidget(mMessageWidget, "message"); + + mMessageWidget->setOverflowToTheLeft(true); + mMessageWidget->setCaptionWithReplacing(mMessage); + + MyGUI::IntSize size; + size.width = mFixedWidth; + size.height = 100; // dummy + + MyGUI::IntCoord coord; + coord.left = 10; // dummy + coord.top = 10; // dummy + + mMessageWidget->setSize(size); + + MyGUI::IntSize textSize = mMessageWidget->getTextSize(); + + size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box + + mMainWidget->setSize(size); + size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0") + mMessageWidget->setSize(size); } - return -1; -} - - - -MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) - : Layout("openmw_messagebox.layout") - , mMessageBoxManager(parMessageBoxManager) - , mMessage(message) -{ - // defines - mFixedWidth = 300; - mBottomPadding = 20; - mNextBoxPadding = 20; - mMarkedToDelete = false; - - getWidget(mMessageWidget, "message"); - - mMessageWidget->setOverflowToTheLeft(true); - mMessageWidget->setCaptionWithReplacing(mMessage); - - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = 100; // dummy - - MyGUI::IntCoord coord; - coord.left = 10; // dummy - coord.top = 10; // dummy - - mMessageWidget->setSize(size); - - MyGUI::IntSize textSize = mMessageWidget->getTextSize(); - - size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box - - mMainWidget->setSize(size); - size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0") - mMessageWidget->setSize(size); -} - -void MessageBox::update (int height) -{ - MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntCoord coord; - coord.left = (gameWindowSize.width - mFixedWidth)/2; - coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding); - - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = mHeight; - - mMainWidget->setCoord(coord); - mMainWidget->setSize(size); - mMainWidget->setVisible(true); -} - -int MessageBox::getHeight () -{ - return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox -} - - - -InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) - : Layout("openmw_interactive_messagebox.layout") - , mMessageBoxManager(parMessageBoxManager) - , mButtonPressed(-1) -{ - int fixedWidth = 500; - int textPadding = 10; // padding between text-widget and main-widget - int textButtonPadding = 20; // padding between the text-widget und the button-widget - int buttonLeftPadding = 10; // padding between the buttons if horizontal - int buttonTopPadding = 5; // ^-- if vertical - int buttonPadding = 5; // padding between button label and button itself - int buttonMainPadding = 10; // padding between buttons and bottom of the main widget - - mMarkedToDelete = false; - - - getWidget(mMessageWidget, "message"); - getWidget(mButtonsWidget, "buttons"); - - mMessageWidget->setOverflowToTheLeft(true); - mMessageWidget->addText(message); - - MyGUI::IntSize textSize = mMessageWidget->getTextSize(); - - MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); - - int biggestButtonWidth = 0; - int buttonWidth = 0; - int buttonsWidth = 0; - int buttonHeight = 0; - MyGUI::IntCoord dummyCoord(0, 0, 0, 0); - - std::vector::const_iterator it; - for(it = buttons.begin(); it != buttons.end(); ++it) + void MessageBox::update (int height) { - MyGUI::Button* button = mButtonsWidget->createWidget( - MyGUI::WidgetStyle::Child, - std::string("MW_Button"), - dummyCoord, - MyGUI::Align::Default); - button->setCaption(*it); + MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntCoord coord; + coord.left = (gameWindowSize.width - mFixedWidth)/2; + coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); + MyGUI::IntSize size; + size.width = mFixedWidth; + size.height = mHeight; - mButtons.push_back(button); + mMainWidget->setCoord(coord); + mMainWidget->setSize(size); + mMainWidget->setVisible(true); + } - buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding; - buttonsWidth += buttonWidth; - buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding; + int MessageBox::getHeight () + { + return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox + } - if(buttonWidth > biggestButtonWidth) + + + InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) + : WindowModal("openmw_interactive_messagebox.layout") + , mMessageBoxManager(parMessageBoxManager) + , mButtonPressed(-1) + { + WindowModal::open(); + + int fixedWidth = 500; + int textPadding = 10; // padding between text-widget and main-widget + int textButtonPadding = 20; // padding between the text-widget und the button-widget + int buttonLeftPadding = 10; // padding between the buttons if horizontal + int buttonTopPadding = 5; // ^-- if vertical + int buttonPadding = 5; // padding between button label and button itself + int buttonMainPadding = 10; // padding between buttons and bottom of the main widget + + mMarkedToDelete = false; + + + getWidget(mMessageWidget, "message"); + getWidget(mButtonsWidget, "buttons"); + + mMessageWidget->setOverflowToTheLeft(true); + mMessageWidget->setCaptionWithReplacing(message); + + MyGUI::IntSize textSize = mMessageWidget->getTextSize(); + + MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); + + int biggestButtonWidth = 0; + int buttonWidth = 0; + int buttonsWidth = 0; + int buttonHeight = 0; + MyGUI::IntCoord dummyCoord(0, 0, 0, 0); + + std::vector::const_iterator it; + for(it = buttons.begin(); it != buttons.end(); ++it) { - biggestButtonWidth = buttonWidth; + MyGUI::Button* button = mButtonsWidget->createWidget( + MyGUI::WidgetStyle::Child, + std::string("MW_Button"), + dummyCoord, + MyGUI::Align::Default); + button->setCaptionWithReplacing(*it); + + button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); + + mButtons.push_back(button); + + buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding; + buttonsWidth += buttonWidth; + buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding; + + if(buttonWidth > biggestButtonWidth) + { + biggestButtonWidth = buttonWidth; + } } - } - buttonsWidth += buttonLeftPadding; + buttonsWidth += buttonLeftPadding; - MyGUI::IntSize mainWidgetSize; - if(buttonsWidth < fixedWidth) - { - // on one line - if(textSize.width + 2*textPadding < buttonsWidth) + MyGUI::IntSize mainWidgetSize; + if(buttonsWidth < fixedWidth) { - mainWidgetSize.width = buttonsWidth; + // on one line + if(textSize.width + 2*textPadding < buttonsWidth) + { + mainWidgetSize.width = buttonsWidth; + } + else + { + mainWidgetSize.width = textSize.width + 3*textPadding; + } + mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; + + MyGUI::IntCoord absCoord; + absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + + mMainWidget->setCoord(absCoord); + mMainWidget->setSize(mainWidgetSize); + + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + mMessageWidget->setCoord(messageWidgetCoord); + + mMessageWidget->setSize(textSize); + + MyGUI::IntCoord buttonCord; + MyGUI::IntSize buttonSize(0, buttonHeight); + int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding; + + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + buttonCord.left = left; + buttonCord.top = textSize.height + textButtonPadding; + + buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding; + buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding; + + (*button)->setCoord(buttonCord); + (*button)->setSize(buttonSize); + + left += buttonSize.width + buttonLeftPadding; + } } else { - mainWidgetSize.width = textSize.width + 3*textPadding; + // among each other + if(biggestButtonWidth > textSize.width) { + mainWidgetSize.width = biggestButtonWidth + buttonTopPadding; + } + else { + mainWidgetSize.width = textSize.width + 3*textPadding; + } + mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; + + mMainWidget->setSize(mainWidgetSize); + + MyGUI::IntCoord absCoord; + absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + + mMainWidget->setCoord(absCoord); + mMainWidget->setSize(mainWidgetSize); + + + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + mMessageWidget->setCoord(messageWidgetCoord); + + mMessageWidget->setSize(textSize); + + MyGUI::IntCoord buttonCord; + MyGUI::IntSize buttonSize(0, buttonHeight); + + int top = textButtonPadding + buttonTopPadding + textSize.height; + + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; + buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; + + buttonCord.top = top; + buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/ + + (*button)->setCoord(buttonCord); + (*button)->setSize(buttonSize); + + top += buttonSize.height + 2*buttonTopPadding; + } + } - mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; + } - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; - - mMainWidget->setCoord(absCoord); - mMainWidget->setSize(mainWidgetSize); - - MyGUI::IntCoord messageWidgetCoord; - messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; - messageWidgetCoord.top = textPadding; - mMessageWidget->setCoord(messageWidgetCoord); - - mMessageWidget->setSize(textSize); - - MyGUI::IntCoord buttonCord; - MyGUI::IntSize buttonSize(0, buttonHeight); - int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding; + 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) { - buttonCord.left = left; - buttonCord.top = textSize.height + textButtonPadding; - - buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding; - buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding; - - (*button)->setCoord(buttonCord); - (*button)->setSize(buttonSize); - - left += buttonSize.width + buttonLeftPadding; + if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) + { + buttonActivated(*button); + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); + break; + } } + } - else + + void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) { - // among each other - if(biggestButtonWidth > textSize.width) { - mainWidgetSize.width = biggestButtonWidth + buttonTopPadding; - } - else { - mainWidgetSize.width = textSize.width + 3*textPadding; - } - mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; - - mMainWidget->setSize(mainWidgetSize); - - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; - - mMainWidget->setCoord(absCoord); - mMainWidget->setSize(mainWidgetSize); - - - MyGUI::IntCoord messageWidgetCoord; - messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; - messageWidgetCoord.top = textPadding; - mMessageWidget->setCoord(messageWidgetCoord); - - mMessageWidget->setSize(textSize); - - MyGUI::IntCoord buttonCord; - MyGUI::IntSize buttonSize(0, buttonHeight); - - int top = textButtonPadding + buttonTopPadding + textSize.height; + buttonActivated (pressed); + } + void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed) + { + mMarkedToDelete = true; + int index = 0; std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { - buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; - buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; - - buttonCord.top = top; - buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/ - - (*button)->setCoord(buttonCord); - (*button)->setSize(buttonSize); - - top += buttonSize.height + 2*buttonTopPadding; + if(*button == pressed) + { + mButtonPressed = index; + mMessageBoxManager.onButtonPressed(mButtonPressed); + return; + } + index++; } - } -} -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) + int InteractiveMessageBox::readPressedButton () { - if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) - { - buttonActivated(*button); - MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); - break; - } + int pressed = mButtonPressed; + mButtonPressed = -1; + return pressed; } } - -void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) -{ - buttonActivated (pressed); -} - -void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed) -{ - mMarkedToDelete = true; - int index = 0; - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) - { - if(*button == pressed) - { - mButtonPressed = index; - return; - } - index++; - } -} - -int InteractiveMessageBox::readPressedButton () -{ - int pressed = mButtonPressed; - mButtonPressed = -1; - return pressed; -} diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 149aa7e7f1..0e47b0323b 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,24 +29,31 @@ namespace MWGui class MessageBoxManager { public: - MessageBoxManager (MWBase::WindowManager* windowManager); + MessageBoxManager (); void onFrame (float frameDuration); - void createMessageBox (const std::string& message); + void createMessageBox (const std::string& message, bool stat = false); + void removeStaticMessageBox (); bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); bool isInteractiveMessageBox (); void removeMessageBox (float time, MessageBox *msgbox); bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - + void enterPressed(); int readPressedButton (); - MWBase::WindowManager *mWindowManager; + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; + + // Note: this delegate unassigns itself after it was fired, i.e. works once. + EventHandle_Int eventButtonPressed; + + void onButtonPressed(int button) { eventButtonPressed(button); eventButtonPressed.clear(); } private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; + MessageBox* mStaticMessageBox; std::vector mTimers; float mMessageBoxSpeed; }; @@ -73,7 +78,7 @@ namespace MWGui int mNextBoxPadding; }; - class InteractiveMessageBox : public OEngine::GUI::Layout + class InteractiveMessageBox : public WindowModal { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); @@ -85,7 +90,7 @@ namespace MWGui private: void buttonActivated (MyGUI::Widget* _widget); - + MessageBoxManager& mMessageBoxManager; MyGUI::EditBox* mMessageWidget; MyGUI::Widget* mButtonsWidget; diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 3195372978..879fcb483f 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -5,9 +5,11 @@ namespace MWGui { enum GuiMode { + GM_None, GM_Settings, // Settings window GM_Inventory, // Inventory mode GM_Container, + GM_Companion, GM_MainMenu, // Main menu mode GM_Console, // Console mode @@ -16,6 +18,7 @@ namespace MWGui GM_Scroll, // Read scroll GM_Book, // Read book GM_Alchemy, // Make potions + GM_Repair, GM_Dialogue, // NPC interaction GM_Barter, @@ -26,6 +29,7 @@ namespace MWGui GM_SpellCreation, GM_Enchanting, GM_Training, + GM_MerchantRepair, GM_Levelup, @@ -39,9 +43,6 @@ namespace MWGui GM_ClassCreate, GM_Review, - // interactive MessageBox - GM_InterMessageBox, - GM_Loading, GM_LoadingWallpaper, diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp new file mode 100644 index 0000000000..16be5f6cca --- /dev/null +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -0,0 +1,55 @@ +#include "pickpocketitemmodel.hpp" + +#include "../mwmechanics/npcstats.hpp" +#include "../mwworld/class.hpp" + +namespace MWGui +{ + + PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& thief, ItemModel *sourceModel) + { + mSourceModel = sourceModel; + int chance = MWWorld::Class::get(thief).getNpcStats(thief).getSkill(ESM::Skill::Sneak).getModified(); + + mSourceModel->update(); + for (size_t i = 0; igetItemCount(); ++i) + { + if (std::rand() / static_cast(RAND_MAX) * 100 > chance) + mHiddenItems.push_back(mSourceModel->getItem(i)); + } + } + + ItemStack PickpocketItemModel::getItem (ModelIndex index) + { + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; + } + + size_t PickpocketItemModel::getItemCount() + { + return mItems.size(); + } + + void PickpocketItemModel::update() + { + mSourceModel->update(); + mItems.clear(); + for (size_t i = 0; igetItemCount(); ++i) + { + const ItemStack& item = mSourceModel->getItem(i); + if (std::find(mHiddenItems.begin(), mHiddenItems.end(), item) == mHiddenItems.end() + && item.mType != ItemStack::Type_Equipped) + mItems.push_back(item); + } + } + + void PickpocketItemModel::removeItem (const ItemStack &item, size_t count) + { + ProxyItemModel::removeItem(item, count); + /// \todo check if player is detected + } + +} diff --git a/apps/openmw/mwgui/pickpocketitemmodel.hpp b/apps/openmw/mwgui/pickpocketitemmodel.hpp new file mode 100644 index 0000000000..090d48d0e7 --- /dev/null +++ b/apps/openmw/mwgui/pickpocketitemmodel.hpp @@ -0,0 +1,26 @@ +#ifndef MWGUI_PICKPOCKET_ITEM_MODEL_H +#define MWGUI_PICKPOCKET_ITEM_MODEL_H + +#include "itemmodel.hpp" + +namespace MWGui +{ + + /// @brief The pickpocket item model randomly hides item stacks based on a specified chance. Equipped items are always hidden. + class PickpocketItemModel : public ProxyItemModel + { + public: + PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel); + virtual ItemStack getItem (ModelIndex index); + virtual size_t getItemCount(); + virtual void update(); + virtual void removeItem (const ItemStack& item, size_t count); + + private: + std::vector mHiddenItems; + std::vector mItems; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 6d51420f01..877f7b7003 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -2,13 +2,9 @@ #include -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" #include "../mwgui/inventorywindow.hpp" #include "../mwgui/bookwindow.hpp" @@ -43,8 +39,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 +105,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,13 +120,12 @@ namespace MWGui { if (!mItemSelectionDialog ) { - mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}", ContainerBase::Filter_All, mWindowManager); + mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}"); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel); } mItemSelectionDialog->setVisible(true); mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - mItemSelectionDialog->drawItems (); mAssignDialog->setVisible (false); } @@ -139,7 +134,7 @@ namespace MWGui { if (!mMagicSelectionDialog ) { - mMagicSelectionDialog = new MagicSelectionDialog(mWindowManager, this); + mMagicSelectionDialog = new MagicSelectionDialog(this); } mMagicSelectionDialog->setVisible(true); @@ -281,7 +276,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) { @@ -291,7 +286,7 @@ namespace MWGui if (item.getRefData ().getCount() == 0) { MWBase::Environment::get().getWindowManager ()->messageBox ( - "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item), std::vector()); + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); return; } @@ -303,11 +298,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()->updateItemView(); } else if (type == Type_MagicItem) { @@ -317,7 +312,7 @@ namespace MWGui if (item.getRefData ().getCount() == 0) { MWBase::Environment::get().getWindowManager ()->messageBox ( - "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item), std::vector()); + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); return; } @@ -341,19 +336,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()->updateItemView(); } 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 +394,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 1436995c53..9065333f5c 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -1,404 +1,385 @@ #include "race.hpp" -#include -#include - #include #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "widgets.hpp" #include "tooltips.hpp" -using namespace MWGui; -using namespace Widgets; - -RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_race.layout", parWindowManager) - , mGenderIndex(0) - , mFaceIndex(0) - , mHairIndex(0) - , mFaceCount(10) - , mHairCount(14) - , mCurrentAngle(0) +namespace { - // Centre dialog - center(); - - setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); - getWidget(mPreviewImage, "PreviewImage"); - - getWidget(mHeadRotate, "HeadRotate"); - mHeadRotate->setScrollRange(50); - mHeadRotate->setScrollPosition(25); - mHeadRotate->setScrollViewPage(10); - mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); - - // Set up next/previous buttons - MyGUI::Button *prevButton, *nextButton; - - setText("GenderChoiceT", mWindowManager.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")); - 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")); - 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")); - 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")); - getWidget(mSkillList, "SkillList"); - setText("SpellPowerT", mWindowManager.getGameSettingString("sRaceMenu7", "Specials")); - getWidget(mSpellPowerList, "SpellPowerList"); - - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); - - updateRaces(); - updateSkills(); - updateSpellPowers(); -} - -void RaceDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} - -void RaceDialog::open() -{ - WindowModal::open(); - - updateRaces(); - updateSkills(); - updateSpellPowers(); - - mPreview = new MWRender::RaceSelectionPreview(); - mPreview->setup(); - mPreview->update (0); - - 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; - - index = proto.mHair.substr(proto.mHair.size() - 2, 2); - mHairIndex = boost::lexical_cast(index) - 1; - - mPreviewImage->setImageTexture ("CharacterHeadPreview"); -} - - -void RaceDialog::setRaceId(const std::string &raceId) -{ - mCurrentRaceId = raceId; - mRaceList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mRaceList->getItemCount(); - for (size_t i = 0; i < count; ++i) + int wrap(int index, int max) { - if (boost::iequals(*mRaceList->getItemDataAt(i), raceId)) + if (index < 0) + return max - 1; + else if (index >= max) + return 0; + else + return index; + } +} + +namespace MWGui +{ + + RaceDialog::RaceDialog() + : WindowModal("openmw_chargen_race.layout") + , mGenderIndex(0) + , mFaceIndex(0) + , mHairIndex(0) + , mCurrentAngle(0) + { + // Centre dialog + center(); + + setText("AppearanceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu1", "Appearance")); + getWidget(mPreviewImage, "PreviewImage"); + + getWidget(mHeadRotate, "HeadRotate"); + mHeadRotate->setScrollRange(50); + mHeadRotate->setScrollPosition(25); + mHeadRotate->setScrollViewPage(10); + mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); + + // Set up next/previous buttons + MyGUI::Button *prevButton, *nextButton; + + 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", 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", 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", 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", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); + getWidget(mSkillList, "SkillList"); + setText("SpellPowerT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials")); + getWidget(mSpellPowerList, "SpellPowerList"); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); + + updateRaces(); + updateSkills(); + updateSpellPowers(); + } + + void RaceDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void RaceDialog::open() + { + WindowModal::open(); + + updateRaces(); + updateSkills(); + updateSpellPowers(); + + mPreview = new MWRender::RaceSelectionPreview(); + mPreview->setup(); + mPreview->update (0); + + 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; + + index = proto.mHair.substr(proto.mHair.size() - 2, 2); + mHairIndex = boost::lexical_cast(index) - 1; + + mPreviewImage->setImageTexture ("CharacterHeadPreview"); + } + + + void RaceDialog::setRaceId(const std::string &raceId) + { + mCurrentRaceId = raceId; + mRaceList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mRaceList->getItemCount(); + for (size_t i = 0; i < count; ++i) { - mRaceList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - break; + if (boost::iequals(*mRaceList->getItemDataAt(i), raceId)) + { + mRaceList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } + } + + updateSkills(); + updateSpellPowers(); + } + + void RaceDialog::close() + { + delete mPreview; + mPreview = 0; + } + + // widget controls + + void RaceDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + + void RaceDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) + { + float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; + float diff = angle - mCurrentAngle; + mPreview->update (diff); + mCurrentAngle += diff; + } + + void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) + { + mGenderIndex = wrap(mGenderIndex - 1, 2); + + recountParts(); + updatePreview(); + } + + void RaceDialog::onSelectNextGender(MyGUI::Widget*) + { + mGenderIndex = wrap(mGenderIndex + 1, 2); + + recountParts(); + updatePreview(); + } + + void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) + { + mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size()); + updatePreview(); + } + + void RaceDialog::onSelectNextFace(MyGUI::Widget*) + { + mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size()); + updatePreview(); + } + + void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) + { + mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size()); + updatePreview(); + } + + void RaceDialog::onSelectNextHair(MyGUI::Widget*) + { + mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size()); + updatePreview(); + } + + void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + const std::string *raceId = mRaceList->getItemDataAt(_index); + if (boost::iequals(mCurrentRaceId, *raceId)) + return; + + mCurrentRaceId = *raceId; + + recountParts(); + + updatePreview(); + updateSkills(); + updateSpellPowers(); + } + + void RaceDialog::getBodyParts (int part, std::vector& out) + { + out.clear(); + const MWWorld::Store &store = + MWBase::Environment::get().getWorld()->getStore().get(); + + for (MWWorld::Store::iterator it = store.begin(); it != store.end(); ++it) + { + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) + continue; + if (bodypart.mData.mPart != static_cast(part)) + continue; + if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson) + continue; + if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId)) + out.push_back(bodypart.mId); } } - updateSkills(); - updateSpellPowers(); -} + void RaceDialog::recountParts() + { + getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs); + getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads); -int wrap(int index, int max) -{ - if (index < 0) - return max - 1; - else if (index >= max) - return 0; - else - return index; -} - -int countParts(const std::string &part, const std::string &race, bool male) -{ - const MWWorld::Store &store = - MWBase::Environment::get().getWorld()->getStore().get(); - - std::string prefix = - "b_n_" + race + ((male) ? "_m_" : "_f_") + part; - - std::string suffix; - suffix.reserve(prefix.size() + 3); - - int count = -1; - do { - ++count; - suffix = "_" + (boost::format("%02d") % (count + 1)).str(); + mFaceIndex = 0; + mHairIndex = 0; } - while (store.search(prefix + suffix) != 0); - if (count == 0 && part == "hair") { - count = -1; - do { - ++count; - suffix = (boost::format("%02d") % (count + 1)).str(); + // update widget content + + void RaceDialog::updatePreview() + { + ESM::NPC record = mPreview->getPrototype(); + record.mRace = mCurrentRaceId; + record.setIsMale(mGenderIndex == 0); + + record.mHead = mAvailableHeads[mFaceIndex]; + record.mHair = mAvailableHairs[mHairIndex]; + + mPreview->setPrototype(record); + } + + void RaceDialog::updateRaces() + { + mRaceList->removeAllItems(); + + const MWWorld::Store &races = + MWBase::Environment::get().getWorld()->getStore().get(); + + + int index = 0; + MWWorld::Store::iterator it = races.begin(); + for (; it != races.end(); ++it) + { + bool playable = it->mData.mFlags & ESM::Race::Playable; + if (!playable) // Only display playable races + continue; + + mRaceList->addItem(it->mName, it->mId); + if (boost::iequals(it->mId, mCurrentRaceId)) + mRaceList->setIndexSelected(index); + ++index; } - while (store.search(prefix + suffix) != 0); } - return count; -} -void RaceDialog::close() -{ - delete mPreview; - mPreview = 0; -} - -// widget controls - -void RaceDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} - -void RaceDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) -{ - float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; - float diff = angle - mCurrentAngle; - mPreview->update (diff); - mCurrentAngle += diff; -} - -void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) -{ - mGenderIndex = wrap(mGenderIndex - 1, 2); - - recountParts(); - updatePreview(); -} - -void RaceDialog::onSelectNextGender(MyGUI::Widget*) -{ - mGenderIndex = wrap(mGenderIndex + 1, 2); - - recountParts(); - updatePreview(); -} - -void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) -{ - mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); - updatePreview(); -} - -void RaceDialog::onSelectNextFace(MyGUI::Widget*) -{ - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); - updatePreview(); -} - -void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) -{ - mHairIndex = wrap(mHairIndex - 1, mHairCount); - updatePreview(); -} - -void RaceDialog::onSelectNextHair(MyGUI::Widget*) -{ - mHairIndex = wrap(mHairIndex + 1, mHairCount); - updatePreview(); -} - -void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - const std::string *raceId = mRaceList->getItemDataAt(_index); - if (boost::iequals(mCurrentRaceId, *raceId)) - return; - - mCurrentRaceId = *raceId; - - recountParts(); - - updatePreview(); - updateSkills(); - updateSpellPowers(); -} - -void RaceDialog::recountParts() -{ - mFaceIndex = 0; - mHairIndex = 0; - - mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); - mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); -} - -// update widget content - -void RaceDialog::updatePreview() -{ - ESM::NPC record = mPreview->getPrototype(); - record.mRace = mCurrentRaceId; - record.setIsMale(mGenderIndex == 0); - - std::string prefix = - "b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); - - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - record.mHead = prefix + "head_" + headIndex; - record.mHair = prefix + "hair_" + hairIndex; - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(record.mHair) == 0) { - record.mHair = prefix + "hair" + hairIndex; - } - mPreview->setPrototype(record); -} - -void RaceDialog::updateRaces() -{ - mRaceList->removeAllItems(); - - const MWWorld::Store &races = - MWBase::Environment::get().getWorld()->getStore().get(); - - - int index = 0; - MWWorld::Store::iterator it = races.begin(); - for (; it != races.end(); ++it) + void RaceDialog::updateSkills() { - bool playable = it->mData.mFlags & ESM::Race::Playable; - if (!playable) // Only display playable races - continue; + for (std::vector::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillItems.clear(); - mRaceList->addItem(it->mName, it->mId); - if (boost::iequals(it->mId, mCurrentRaceId)) - mRaceList->setIndexSelected(index); - ++index; - } -} - -void RaceDialog::updateSkills() -{ - for (std::vector::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSkillItems.clear(); - - if (mCurrentRaceId.empty()) - return; - - MWSkillPtr skillWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Race *race = store.get().find(mCurrentRaceId); - int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? - for (int i = 0; i < count; ++i) - { - int skillId = race->mData.mBonus[i].mSkill; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; - - 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); - - - mSkillItems.push_back(skillWidget); - - coord1.top += lineHeight; - } -} - -void RaceDialog::updateSpellPowers() -{ - for (std::vector::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSpellPowerItems.clear(); - - if (mCurrentRaceId.empty()) - return; - - MWSpellPtr spellPowerWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Race *race = store.get().find(mCurrentRaceId); - - std::vector::const_iterator it = race->mPowers.mList.begin(); - std::vector::const_iterator end = race->mPowers.mList.end(); - for (int i = 0; it != end; ++it) - { - 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); - - mSpellPowerItems.push_back(spellPowerWidget); - - coord.top += lineHeight; - ++i; + if (mCurrentRaceId.empty()) + return; + + Widgets::MWSkillPtr skillWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mCurrentRaceId); + int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? + for (int i = 0; i < count; ++i) + { + int skillId = race->mData.mBonus[i].mSkill; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + + skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, + std::string("Skill") + boost::lexical_cast(i)); + skillWidget->setSkillNumber(skillId); + skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(race->mData.mBonus[i].mBonus)); + ToolTips::createSkillToolTip(skillWidget, skillId); + + + mSkillItems.push_back(skillWidget); + + coord1.top += lineHeight; + } + } + + void RaceDialog::updateSpellPowers() + { + for (std::vector::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSpellPowerItems.clear(); + + if (mCurrentRaceId.empty()) + return; + + Widgets::MWSpellPtr spellPowerWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mCurrentRaceId); + + std::vector::const_iterator it = race->mPowers.mList.begin(); + std::vector::const_iterator end = race->mPowers.mList.end(); + for (int i = 0; it != end; ++it) + { + const std::string &spellpower = *it; + spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); + spellPowerWidget->setSpellId(spellpower); + spellPowerWidget->setUserString("ToolTipType", "Spell"); + spellPowerWidget->setUserString("Spell", spellpower); + + mSpellPowerItems.push_back(spellPowerWidget); + + coord.top += lineHeight; + ++i; + } } } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index efd08f4395..1d48c67cdf 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 { @@ -81,6 +76,11 @@ namespace MWGui void updatePreview(); void recountParts(); + void getBodyParts (int part, std::vector& out); + + std::vector mAvailableHeads; + std::vector mAvailableHairs; + MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; MyGUI::ScrollBar* mHeadRotate; @@ -92,7 +92,6 @@ namespace MWGui std::vector mSpellPowerItems; int mGenderIndex, mFaceIndex, mHairIndex; - int mFaceCount, mHairCount; std::string mCurrentRaceId; diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index b1f7affb6f..86a85be18e 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -18,15 +18,18 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - if (mPtr.isEmpty()) - return; - MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); // check if player has changed cell, or count of the reference has become 0 if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) - || mPtr.getRefData().getCount() == 0) - onReferenceUnavailable(); + || (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)) + { + if (!mPtr.isEmpty()) + { + mPtr = MWWorld::Ptr(); + onReferenceUnavailable(); + } + } mCurrentPlayerCell = playerCell; } diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp new file mode 100644 index 0000000000..0bd4b0995f --- /dev/null +++ b/apps/openmw/mwgui/repair.cpp @@ -0,0 +1,157 @@ +#include "repair.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" + +#include "widgets.hpp" + +namespace MWGui +{ + +Repair::Repair() + : WindowBase("openmw_repair.layout") +{ + getWidget(mRepairBox, "RepairBox"); + getWidget(mRepairView, "RepairView"); + getWidget(mToolBox, "ToolBox"); + getWidget(mToolIcon, "ToolIcon"); + getWidget(mUsesLabel, "UsesLabel"); + getWidget(mQualityLabel, "QualityLabel"); + getWidget(mCancelButton, "CancelButton"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onCancel); +} + +void Repair::open() +{ + center(); +} + +void Repair::startRepairItem(const MWWorld::Ptr &item) +{ + mRepair.setTool(item); + + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + mToolIcon->setImageTexture (path); + mToolIcon->setUserString("ToolTipType", "ItemPtr"); + mToolIcon->setUserData(item); + + updateRepairView(); +} + +void Repair::updateRepairView() +{ + MWWorld::LiveCellRef *ref = + mRepair.getTool().get(); + + int uses = (mRepair.getTool().getCellRef().mCharge != -1) ? mRepair.getTool().getCellRef().mCharge : ref->mBase->mData.mUses; + + float quality = ref->mBase->mData.mQuality; + + std::stringstream qualityStr; + qualityStr << std::setprecision(3) << quality; + + mUsesLabel->setCaptionWithReplacing("#{sUses} " + boost::lexical_cast(uses)); + mQualityLabel->setCaptionWithReplacing("#{sQuality} " + qualityStr.str()); + + bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0); + mToolBox->setVisible(toolBoxVisible); + + bool toolBoxWasVisible = (mRepairBox->getPosition().top != mToolBox->getPosition().top); + + if (toolBoxVisible && !toolBoxWasVisible) + { + // shrink + mRepairBox->setPosition(mRepairBox->getPosition() + MyGUI::IntPoint(0,mToolBox->getSize().height)); + mRepairBox->setSize(mRepairBox->getSize() - MyGUI::IntSize(0,mToolBox->getSize().height)); + } + else if (!toolBoxVisible && toolBoxWasVisible) + { + // expand + mRepairBox->setPosition(MyGUI::IntPoint (mRepairBox->getPosition().left, mToolBox->getPosition().top)); + mRepairBox->setSize(mRepairBox->getSize() + MyGUI::IntSize(0,mToolBox->getSize().height)); + } + + while (mRepairView->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mRepairView->getChildAt(0)); + + int currentY = 0; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + 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)) + { + int maxDurability = MWWorld::Class::get(*iter).getItemMaxHealth(*iter); + int durability = (iter->getCellRef().mCharge == -1) ? maxDurability : iter->getCellRef().mCharge; + if (maxDurability == durability) + continue; + + MyGUI::TextBox* text = mRepairView->createWidget ( + "SandText", MyGUI::IntCoord(8, currentY, mRepairView->getWidth()-8, 18), MyGUI::Align::Default); + text->setCaption(MWWorld::Class::get(*iter).getName(*iter)); + text->setNeedMouseFocus(false); + currentY += 19; + + MyGUI::ImageBox* icon = mRepairView->createWidget ( + "ImageBox", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(*iter).getInventoryIcon(*iter); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + icon->setImageTexture (path); + icon->setUserString("ToolTipType", "ItemPtr"); + icon->setUserData(*iter); + icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onRepairItem); + icon->eventMouseWheel += MyGUI::newDelegate(this, &Repair::onMouseWheel); + + Widgets::MWDynamicStatPtr chargeWidget = mRepairView->createWidget + ("MW_ChargeBar", MyGUI::IntCoord(72, currentY+2, 199, 20), MyGUI::Align::Default); + chargeWidget->setValue(durability, maxDurability); + chargeWidget->setNeedMouseFocus(false); + + currentY += 32 + 4; + } + } + mRepairView->setCanvasSize (MyGUI::IntSize(mRepairView->getWidth(), std::max(mRepairView->getHeight(), currentY))); +} + +void Repair::onCancel(MyGUI::Widget *sender) +{ + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Repair); +} + +void Repair::onRepairItem(MyGUI::Widget *sender) +{ + if (!mRepair.getTool().getRefData().getCount()) + return; + + mRepair.repair(*sender->getUserData()); + + updateRepairView(); +} + +void Repair::onMouseWheel(MyGUI::Widget* _sender, int _rel) +{ + if (mRepairView->getViewOffset().top + _rel*0.3 > 0) + mRepairView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mRepairView->setViewOffset(MyGUI::IntPoint(0, mRepairView->getViewOffset().top + _rel*0.3)); +} + +} diff --git a/apps/openmw/mwgui/repair.hpp b/apps/openmw/mwgui/repair.hpp new file mode 100644 index 0000000000..d0f5c54c4b --- /dev/null +++ b/apps/openmw/mwgui/repair.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_MWGUI_REPAIR_H +#define OPENMW_MWGUI_REPAIR_H + +#include "windowbase.hpp" + +#include "../mwmechanics/repair.hpp" + +namespace MWGui +{ + +class Repair : public WindowBase +{ +public: + Repair(); + + virtual void open(); + + void startRepairItem (const MWWorld::Ptr& item); + +protected: + MyGUI::Widget* mRepairBox; + MyGUI::ScrollView* mRepairView; + + MyGUI::Widget* mToolBox; + + MyGUI::ImageBox* mToolIcon; + + MyGUI::TextBox* mUsesLabel; + MyGUI::TextBox* mQualityLabel; + + MyGUI::Button* mCancelButton; + + MWMechanics::Repair mRepair; + + void updateRepairView(); + + void onRepairItem (MyGUI::Widget* sender); + void onCancel (MyGUI::Widget* sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + +}; + +} + +#endif diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 50508cc5f0..824929b67e 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -1,374 +1,369 @@ #include "review.hpp" -#include - -#include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "widgets.hpp" #include "tooltips.hpp" #undef min #undef max -using namespace MWGui; -using namespace Widgets; - -const int ReviewDialog::sLineHeight = 18; - -ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_review.layout", parWindowManager) - , mLastPos(0) +namespace MWGui { - // Centre dialog - center(); - // Setup static stats - MyGUI::Button* button; - getWidget(mNameWidget, "NameText"); - getWidget(button, "NameButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);; + const int ReviewDialog::sLineHeight = 18; - getWidget(mRaceWidget, "RaceText"); - getWidget(button, "RaceButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);; - - getWidget(mClassWidget, "ClassText"); - getWidget(button, "ClassButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);; - - getWidget(mBirthSignWidget, "SignText"); - getWidget(button, "SignButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);; - - // Setup dynamic stats - getWidget(mHealth, "Health"); - mHealth->setTitle(mWindowManager.getGameSettingString("sHealth", "")); - mHealth->setValue(45, 45); - - getWidget(mMagicka, "Magicka"); - mMagicka->setTitle(mWindowManager.getGameSettingString("sMagic", "")); - mMagicka->setValue(50, 50); - - getWidget(mFatigue, "Fatigue"); - mFatigue->setTitle(mWindowManager.getGameSettingString("sFatigue", "")); - mFatigue->setValue(160, 160); - - // Setup attributes - - MWAttributePtr attribute; - for (int idx = 0; idx < ESM::Attribute::Length; ++idx) + ReviewDialog::ReviewDialog() + : WindowModal("openmw_chargen_review.layout") + , mLastPos(0) { - 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)); - } + // Centre dialog + center(); - // Setup skills - getWidget(mSkillView, "SkillView"); - mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + // Setup static stats + MyGUI::Button* button; + getWidget(mNameWidget, "NameText"); + getWidget(button, "NameButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);; - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); - mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); - } + getWidget(mRaceWidget, "RaceText"); + getWidget(button, "RaceButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);; - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); + getWidget(mClassWidget, "ClassText"); + getWidget(button, "ClassButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);; - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); -} + getWidget(mBirthSignWidget, "SignText"); + getWidget(button, "SignButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);; -void ReviewDialog::open() -{ - WindowModal::open(); - updateSkillArea(); -} + // Setup dynamic stats + getWidget(mHealth, "Health"); + mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sHealth", "")); + mHealth->setValue(45, 45); -void ReviewDialog::setPlayerName(const std::string &name) -{ - mNameWidget->setCaption(name); -} + getWidget(mMagicka, "Magicka"); + mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sMagic", "")); + mMagicka->setValue(50, 50); -void ReviewDialog::setRace(const std::string &raceId) -{ - mRaceId = raceId; + getWidget(mFatigue, "Fatigue"); + mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFatigue", "")); + mFatigue->setValue(160, 160); - const ESM::Race *race = - MWBase::Environment::get().getWorld()->getStore().get().search(mRaceId); - if (race) - { - ToolTips::createRaceToolTip(mRaceWidget, race); - mRaceWidget->setCaption(race->mName); - } -} + // Setup attributes -void ReviewDialog::setClass(const ESM::Class& class_) -{ - mKlass = class_; - mClassWidget->setCaption(mKlass.mName); - ToolTips::createClassToolTip(mClassWidget, mKlass); -} - -void ReviewDialog::setBirthSign(const std::string& signId) -{ - mBirthSignId = signId; - - const ESM::BirthSign *sign = - MWBase::Environment::get().getWorld()->getStore().get().search(mBirthSignId); - if (sign) - { - mBirthSignWidget->setCaption(sign->mName); - ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); - } -} - -void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) -{ - mHealth->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); -} - -void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) -{ - mMagicka->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); -} - -void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) -{ - mFatigue->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); -} - -void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) -{ - std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); - if (attr == mAttributeWidgets.end()) - return; - - attr->second->setAttributeValue(value); -} - -void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) -{ - mSkillValues[skillId] = value; - MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; - if (widget) - { - float modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(std::floor(modified)); - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - - widget->setCaption(text); - widget->_setWidgetState(state); - } - -} - -void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) -{ - mMajorSkills = major; - mMinorSkills = minor; - - // Update misc skills with the remaining skills not in major or minor - std::set skillSet; - std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); - std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); - boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); - mMiscSkills.clear(); - for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) - { - int skill = *it; - if (skillSet.find(skill) == skillSet.end()) - mMiscSkills.push_back(skill); - } - - updateSkillArea(); -} - -void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); - separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - - mSkillWidgets.push_back(separator); - - coord1.top += separator->getHeight(); - coord2.top += separator->getHeight(); -} - -void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); - groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - groupWidget->setCaption(label); - mSkillWidgets.push_back(groupWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} - -MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; - MyGUI::TextBox* skillValueWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - - skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); - skillValueWidget->setCaption(value); - skillValueWidget->_setWidgetState(state); - skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - mSkillWidgets.push_back(skillValueWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; - - return skillValueWidget; -} - -void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} - -void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - { - addSeparator(coord1, coord2); - } - - addGroup(mWindowManager.getGameSettingString(titleId, titleDefault), coord1, coord2); - - SkillList::const_iterator end = skills.end(); - for (SkillList::const_iterator it = skills.begin(); it != end; ++it) - { - int skillId = *it; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; - assert(skillId >= 0 && skillId < ESM::Skill::Length); - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - - std::string state = "normal"; - if (modified > base) - 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); - - for (int i=0; i<2; ++i) + Widgets::MWAttributePtr attribute; + for (int idx = 0; idx < ESM::Attribute::Length; ++idx) { - ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId); + getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); + mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); + attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); + attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue(0, 0)); } - mSkillWidgetMap[skillId] = widget; - } -} + // Setup skills + getWidget(mSkillView, "SkillView"); + mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); -void ReviewDialog::updateSkillArea() -{ - for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + for (int i = 0; i < ESM::Skill::Length; ++i) + { + mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); + mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); + } + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); + } + + void ReviewDialog::open() { - MyGUI::Gui::getInstance().destroyWidget(*it); + WindowModal::open(); + updateSkillArea(); } - mSkillWidgets.clear(); - const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); - MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + void ReviewDialog::setPlayerName(const std::string &name) + { + mNameWidget->setCaption(name); + } - if (!mMajorSkills.empty()) - addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + void ReviewDialog::setRace(const std::string &raceId) + { + mRaceId = raceId; - if (!mMinorSkills.empty()) - addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + const ESM::Race *race = + MWBase::Environment::get().getWorld()->getStore().get().search(mRaceId); + if (race) + { + ToolTips::createRaceToolTip(mRaceWidget, race); + mRaceWidget->setCaption(race->mName); + } + } - if (!mMiscSkills.empty()) - addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + void ReviewDialog::setClass(const ESM::Class& class_) + { + mKlass = class_; + mClassWidget->setCaption(mKlass.mName); + ToolTips::createClassToolTip(mClassWidget, mKlass); + } - mClientHeight = coord1.top; + void ReviewDialog::setBirthSign(const std::string& signId) + { + mBirthSignId = signId; + + const ESM::BirthSign *sign = + MWBase::Environment::get().getWorld()->getStore().get().search(mBirthSignId); + if (sign) + { + mBirthSignWidget->setCaption(sign->mName); + ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); + } + } + + void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) + { + mHealth->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + } + + void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) + { + mMagicka->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + } + + void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) + { + mFatigue->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + } + + void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) + { + std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); + if (attr == mAttributeWidgets.end()) + return; + + attr->second->setAttributeValue(value); + } + + void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) + { + mSkillValues[skillId] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; + if (widget) + { + float modified = value.getModified(), base = value.getBase(); + std::string text = boost::lexical_cast(std::floor(modified)); + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + + widget->setCaption(text); + widget->_setWidgetState(state); + } + + } + + void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) + { + mMajorSkills = major; + mMinorSkills = minor; + + // Update misc skills with the remaining skills not in major or minor + std::set skillSet; + std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); + std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); + boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); + mMiscSkills.clear(); + for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) + { + int skill = *it; + if (skillSet.find(skill) == skillSet.end()) + mMiscSkills.push_back(skill); + } + + updateSkillArea(); + } + + void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); + separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + + mSkillWidgets.push_back(separator); + + coord1.top += separator->getHeight(); + coord2.top += separator->getHeight(); + } + + void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); + groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + groupWidget->setCaption(label); + mSkillWidgets.push_back(groupWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + } + + MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; + MyGUI::TextBox* skillValueWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + + skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); + skillValueWidget->setCaption(value); + skillValueWidget->_setWidgetState(state); + skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillValueWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + + return skillValueWidget; + } + + void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + } + + void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + { + addSeparator(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) + { + int skillId = *it; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + assert(skillId >= 0 && skillId < ESM::Skill::Length); + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + float base = stat.getBase(); + float modified = stat.getModified(); + + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + 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) + { + ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId); + } + + mSkillWidgetMap[skillId] = widget; + } + } + + void ReviewDialog::updateSkillArea() + { + for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillWidgets.clear(); + + const int valueSize = 40; + MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); + MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + + if (!mMajorSkills.empty()) + addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + + if (!mMinorSkills.empty()) + addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + + if (!mMiscSkills.empty()) + addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + + mClientHeight = coord1.top; + + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); + } + + // widget controls + + void ReviewDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); + } + + void ReviewDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + void ReviewDialog::onNameClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(NAME_DIALOG); + } + + void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(RACE_DIALOG); + } + + void ReviewDialog::onClassClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(CLASS_DIALOG); + } + + void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(BIRTHSIGN_DIALOG); + } + + void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mSkillView->getViewOffset().top + _rel*0.3 > 0) + mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); + } - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} - -// widget controls - -void ReviewDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); -} - -void ReviewDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -void ReviewDialog::onNameClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(NAME_DIALOG); -} - -void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(RACE_DIALOG); -} - -void ReviewDialog::onClassClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(CLASS_DIALOG); -} - -void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(BIRTHSIGN_DIALOG); -} - -void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mSkillView->getViewOffset().top + _rel*0.3 > 0) - mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); } 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..eb46b9c1e6 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -10,71 +10,88 @@ #include "formatting.hpp" -using namespace MWGui; - -ScrollWindow::ScrollWindow (MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_scroll.layout", parWindowManager) - , mTakeButtonShow(true) - , mTakeButtonAllowed(true) +namespace { - getWidget(mTextView, "TextView"); + void adjustButton (MWGui::ImageButton* button) + { + MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); + button->setSize(button->getRequestedSize()); - getWidget(mCloseButton, "CloseButton"); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); - - getWidget(mTakeButton, "TakeButton"); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); - - center(); + if (button->getAlign().isRight()) + button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); + } } -void ScrollWindow::open (MWWorld::Ptr scroll) +namespace MWGui { - // no 3d sounds because the object could be in a container. - MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); - mScroll = scroll; + ScrollWindow::ScrollWindow () + : WindowBase("openmw_scroll.layout") + , mTakeButtonShow(true) + , mTakeButtonAllowed(true) + { + getWidget(mTextView, "TextView"); - MWWorld::LiveCellRef *ref = mScroll.get(); + getWidget(mCloseButton, "CloseButton"); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); - BookTextParser parser; - MyGUI::IntSize size = parser.parse(ref->mBase->mText, mTextView, 390); + getWidget(mTakeButton, "TakeButton"); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); - if (size.height > mTextView->getSize().height) - mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); - else - mTextView->setCanvasSize(410, mTextView->getSize().height); + adjustButton(mCloseButton); + adjustButton(mTakeButton); - mTextView->setViewOffset(MyGUI::IntPoint(0,0)); + center(); + } - setTakeButtonShow(true); -} - -void ScrollWindow::setTakeButtonShow(bool show) -{ - mTakeButtonShow = show; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} - -void ScrollWindow::setInventoryAllowed(bool allowed) -{ - mTakeButtonAllowed = allowed; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} - -void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); - - mWindowManager.removeGuiMode(GM_Scroll); -} - -void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); - - MWWorld::ActionTake take(mScroll); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - - mWindowManager.removeGuiMode(GM_Scroll); + void ScrollWindow::open (MWWorld::Ptr scroll) + { + // no 3d sounds because the object could be in a container. + MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); + + mScroll = scroll; + + MWWorld::LiveCellRef *ref = mScroll.get(); + + BookTextParser parser; + MyGUI::IntSize size = parser.parseScroll(ref->mBase->mText, mTextView, 390); + + if (size.height > mTextView->getSize().height) + mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); + else + mTextView->setCanvasSize(410, mTextView->getSize().height); + + mTextView->setViewOffset(MyGUI::IntPoint(0,0)); + + setTakeButtonShow(true); + } + + void ScrollWindow::setTakeButtonShow(bool show) + { + mTakeButtonShow = show; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + void ScrollWindow::setInventoryAllowed(bool allowed) + { + mTakeButtonAllowed = allowed; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); + } + + void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + + MWWorld::ActionTake take(mScroll); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + + 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 04856c3ed9..413171dd4d 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1,24 +1,18 @@ #include "settingswindow.hpp" #include -#include #include -#include #include #include #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwrender/renderingmanager.hpp" - #include "confirmationdialog.hpp" namespace @@ -93,8 +87,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 +230,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 +266,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 +274,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 +321,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) { @@ -362,7 +354,7 @@ namespace MWGui { std::string msg = "This resolution is not supported in Fullscreen mode. Please select a resolution from the list."; MWBase::Environment::get().getWindowManager()-> - messageBox(msg, std::vector()); + messageBox(msg); _sender->castType()->setCaption(off); } else @@ -437,8 +429,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) @@ -559,6 +551,8 @@ namespace MWGui while (mControlsBox->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0)); + MWBase::Environment::get().getWindowManager ()->removeStaticMessageBox(); + std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); const int h = 18; @@ -593,7 +587,7 @@ namespace MWGui static_cast(_sender)->setCaptionWithReplacing("#{sNone}"); - MWBase::Environment::get().getWindowManager ()->messageBox ("#{sControlsMenu3}", std::vector()); + MWBase::Environment::get().getWindowManager ()->staticMessageBox ("#{sControlsMenu3}"); MWBase::Environment::get().getWindowManager ()->disallowMouse(); MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId); @@ -610,7 +604,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/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp new file mode 100644 index 0000000000..60cc2fb0ee --- /dev/null +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -0,0 +1,170 @@ +#include "sortfilteritemmodel.hpp" + +#include "../mwworld/class.hpp" + +namespace +{ + bool compareType(const std::string& type1, const std::string& type2) + { + // this defines the sorting order of types. types that are first in the vector appear before other types. + std::vector mapping; + mapping.push_back( typeid(ESM::Weapon).name() ); + mapping.push_back( typeid(ESM::Armor).name() ); + mapping.push_back( typeid(ESM::Clothing).name() ); + mapping.push_back( typeid(ESM::Potion).name() ); + mapping.push_back( typeid(ESM::Ingredient).name() ); + mapping.push_back( typeid(ESM::Apparatus).name() ); + mapping.push_back( typeid(ESM::Book).name() ); + mapping.push_back( typeid(ESM::Light).name() ); + mapping.push_back( typeid(ESM::Miscellaneous).name() ); + mapping.push_back( typeid(ESM::Lockpick).name() ); + mapping.push_back( typeid(ESM::Repair).name() ); + mapping.push_back( typeid(ESM::Probe).name() ); + + assert( std::find(mapping.begin(), mapping.end(), type1) != mapping.end() ); + assert( std::find(mapping.begin(), mapping.end(), type2) != mapping.end() ); + + return std::find(mapping.begin(), mapping.end(), type1) < std::find(mapping.begin(), mapping.end(), type2); + } + + bool compare (const MWGui::ItemStack& left, const MWGui::ItemStack& right) + { + if (left.mType != right.mType) + return left.mType < right.mType; + + if (left.mBase.getTypeName() == right.mBase.getTypeName()) + { + int cmp = MWWorld::Class::get(left.mBase).getName(left.mBase).compare( + MWWorld::Class::get(right.mBase).getName(right.mBase)); + return cmp < 0; + } + else + return compareType(left.mBase.getTypeName(), right.mBase.getTypeName()); + } +} + +namespace MWGui +{ + + SortFilterItemModel::SortFilterItemModel(ItemModel *sourceModel) + : mCategory(Category_All) + , mShowEquipped(true) + , mFilter(0) + { + mSourceModel = sourceModel; + } + + void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count) + { + mDragItems.push_back(std::make_pair(dragItem, count)); + } + + void SortFilterItemModel::clearDragItems() + { + mDragItems.clear(); + } + + bool SortFilterItemModel::filterAccepts (const ItemStack& item) + { + MWWorld::Ptr base = item.mBase; + + if (item.mType == ItemStack::Type_Equipped && !mShowEquipped) + return false; + + int category; + if (base.getTypeName() == typeid(ESM::Armor).name() + || base.getTypeName() == typeid(ESM::Clothing).name()) + category = Category_Apparel; + else if (base.getTypeName() == typeid(ESM::Weapon).name()) + category = Category_Weapon; + else if (base.getTypeName() == typeid(ESM::Ingredient).name() + || base.getTypeName() == typeid(ESM::Potion).name()) + category = Category_Magic; + else if (base.getTypeName() == typeid(ESM::Miscellaneous).name() + || base.getTypeName() == typeid(ESM::Ingredient).name() + || base.getTypeName() == typeid(ESM::Repair).name() + || base.getTypeName() == typeid(ESM::Lockpick).name() + || base.getTypeName() == typeid(ESM::Light).name() + || base.getTypeName() == typeid(ESM::Apparatus).name() + || base.getTypeName() == typeid(ESM::Book).name() + || base.getTypeName() == typeid(ESM::Probe).name()) + category = Category_Misc; + + if (item.mFlags & ItemStack::Flag_Enchanted) + category |= Category_Magic; + + if (!(category & mCategory)) + return false; + + if ((mFilter & Filter_OnlyIngredients) && base.getTypeName() != typeid(ESM::Ingredient).name()) + return false; + if ((mFilter & Filter_OnlyEnchanted) && !(item.mFlags & ItemStack::Flag_Enchanted)) + return false; + if ((mFilter & Filter_OnlyChargedSoulstones) && (base.getTypeName() != typeid(ESM::Miscellaneous).name() + || base.getCellRef().mSoul == "")) + return false; + if ((mFilter & Filter_OnlyEnchantable) && (item.mFlags & ItemStack::Flag_Enchanted + || (base.getTypeName() != typeid(ESM::Armor).name() + && base.getTypeName() != typeid(ESM::Clothing).name() + && base.getTypeName() != typeid(ESM::Book).name()))) + return false; + if ((mFilter & Filter_OnlyEnchantable) && base.getTypeName() == typeid(ESM::Book).name() + && !base.get()->mBase->mData.mIsScroll) + return false; + + return true; + } + + ItemStack SortFilterItemModel::getItem (ModelIndex index) + { + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; + } + + size_t SortFilterItemModel::getItemCount() + { + return mItems.size(); + } + + void SortFilterItemModel::setCategory (int category) + { + mCategory = category; + } + + void SortFilterItemModel::setFilter (int filter) + { + mFilter = filter; + } + + void SortFilterItemModel::update() + { + mSourceModel->update(); + + size_t count = mSourceModel->getItemCount(); + + mItems.clear(); + for (size_t i=0; igetItem(i); + + for (std::vector >::iterator it = mDragItems.begin(); it != mDragItems.end(); ++it) + { + if (item.mBase == it->first) + { + if (item.mCount < it->second) + throw std::runtime_error("Dragging more than present in the model"); + item.mCount -= it->second; + } + } + + if (item.mCount > 0 && filterAccepts(item)) + mItems.push_back(item); + } + + std::sort(mItems.begin(), mItems.end(), compare); + } + +} diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp new file mode 100644 index 0000000000..c7feaa3b92 --- /dev/null +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -0,0 +1,53 @@ +#ifndef MWGUI_SORT_FILTER_ITEM_MODEL_H +#define MWGUI_SORT_FILTER_ITEM_MODEL_H + +#include "itemmodel.hpp" + +namespace MWGui +{ + + class SortFilterItemModel : public ProxyItemModel + { + public: + SortFilterItemModel (ItemModel* sourceModel); + + virtual void update(); + + bool filterAccepts (const ItemStack& item); + + virtual ItemStack getItem (ModelIndex index); + virtual size_t getItemCount(); + + /// Dragged items are not displayed. + void addDragItem (const MWWorld::Ptr& dragItem, size_t count); + void clearDragItems(); + + void setCategory (int category); + void setFilter (int filter); + void setShowEquipped (bool show) { mShowEquipped = show; } + + static const int Category_Weapon = (1<<1); + static const int Category_Apparel = (1<<2); + static const int Category_Misc = (1<<3); + static const int Category_Magic = (1<<4); + static const int Category_All = 255; + + static const int Filter_OnlyIngredients = (1<<0); + static const int Filter_OnlyEnchanted = (1<<1); + static const int Filter_OnlyEnchantable = (1<<2); + static const int Filter_OnlyChargedSoulstones = (1<<3); + + + private: + std::vector mItems; + + std::vector > mDragItems; + + int mCategory; + int mFilter; + bool mShowEquipped; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/soulgemdialog.cpp b/apps/openmw/mwgui/soulgemdialog.cpp new file mode 100644 index 0000000000..b95eec0b67 --- /dev/null +++ b/apps/openmw/mwgui/soulgemdialog.cpp @@ -0,0 +1,33 @@ +#include "soulgemdialog.hpp" + +#include "../mwbase/environment.hpp" + +#include "messagebox.hpp" + +namespace MWGui +{ + + void SoulgemDialog::show(const MWWorld::Ptr &soulgem) + { + mSoulgem = soulgem; + std::vector buttons; + buttons.push_back("#{sRechargeEnchantment}"); + buttons.push_back("#{sMake Enchantment}"); + mManager->createInteractiveMessageBox("#{sDoYouWantTo}", buttons); + mManager->eventButtonPressed += MyGUI::newDelegate(this, &SoulgemDialog::onButtonPressed); + } + + void SoulgemDialog::onButtonPressed(int button) + { + if (button == 0) + { + /// \todo show recharge enchanted item dialog here + } + else + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); + MWBase::Environment::get().getWindowManager()->startSelfEnchanting(mSoulgem); + } + } + +} diff --git a/apps/openmw/mwgui/soulgemdialog.hpp b/apps/openmw/mwgui/soulgemdialog.hpp new file mode 100644 index 0000000000..9aea1f3393 --- /dev/null +++ b/apps/openmw/mwgui/soulgemdialog.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_MWGUI_SOULGEMDIALOG_H +#define OPENMW_MWGUI_SOULGEMDIALOG_H + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + + class MessageBoxManager; + + class SoulgemDialog + { + public: + SoulgemDialog (MessageBoxManager* manager) + : mManager(manager) {} + + void show (const MWWorld::Ptr& soulgem); + + void onButtonPressed(int button); + + private: + MessageBoxManager* mManager; + MWWorld::Ptr mSoulgem; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 40fcf2988a..a7fcfdd021 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -1,7 +1,5 @@ #include "spellbuyingwindow.hpp" -#include - #include #include "../mwbase/environment.hpp" @@ -11,9 +9,8 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" -#include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "inventorywindow.hpp" @@ -23,8 +20,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) { @@ -32,20 +29,9 @@ namespace MWGui getWidget(mCancelButton, "CancelButton"); getWidget(mPlayerGold, "PlayerGold"); - getWidget(mSelect, "Select"); - getWidget(mSpells, "Spells"); getWidget(mSpellsView, "SpellsView"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked); - - mSpells->setCoord(450/2-mSpells->getTextSize().width/2, - mSpells->getTop(), - mSpells->getTextSize().width, - mSpells->getHeight()); - mSelect->setCoord(8, - mSelect->getTop(), - mSelect->getTextSize().width, - mSelect->getHeight()); } void SpellBuyingWindow::addSpell(const std::string& spellId) @@ -59,7 +45,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, @@ -131,13 +117,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); @@ -146,12 +132,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, @@ -161,8 +147,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 c4988fff35..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,15 +21,13 @@ namespace MWGui class SpellBuyingWindow : public ReferenceInterface, public WindowBase { public: - SpellBuyingWindow(MWBase::WindowManager& parWindowManager); + SpellBuyingWindow(); void startSpellBuying(const MWWorld::Ptr& actor); protected: MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; - MyGUI::TextBox* mSpells; - MyGUI::TextBox* mSelect; MyGUI::ScrollView* mSpellsView; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 8a079e40ce..45cf1b0aaa 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -4,22 +4,14 @@ #include "../mwbase/windowmanager.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" - #include "tooltips.hpp" -#include "widgets.hpp" #include "class.hpp" #include "inventorywindow.hpp" #include "tradewindow.hpp" @@ -40,8 +32,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"); @@ -72,6 +64,7 @@ namespace MWGui mMagnitudeMaxSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); + constantEffect=false; } void EditEffectDialog::open() @@ -164,7 +157,7 @@ namespace MWGui mMagnitudeBox->setVisible (true); curY += mMagnitudeBox->getSize().height; } - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)&&constantEffect==false) { mDurationBox->setPosition(mDurationBox->getPosition().left, curY); mDurationBox->setVisible (true); @@ -273,9 +266,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"); @@ -302,38 +295,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}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); return; } if (mNameEdit->getCaption () == "") { - mWindowManager.messageBox ("#{sNotifyMessage10}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage10}"); return; } if (mMagickaCost->getCaption() == "0") { - mWindowManager.messageBox ("#{sEnchantmentMenu8}", std::vector()); + 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}", std::vector()); + 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); @@ -346,7 +339,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() @@ -356,8 +349,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 () @@ -411,8 +404,8 @@ namespace MWGui // ------------------------------------------------------------------------------------------------ - EffectEditorBase::EffectEditorBase(MWBase::WindowManager& parWindowManager) - : mAddEffectDialog(parWindowManager) + EffectEditorBase::EffectEditorBase() + : mAddEffectDialog() , mSelectAttributeDialog(NULL) , mSelectSkillDialog(NULL) { @@ -516,7 +509,7 @@ namespace MWGui { if (mEffects.size() >= 8) { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}"); return; } @@ -527,7 +520,7 @@ namespace MWGui { if (it->mEffectID == effectId) { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}"); return; } } @@ -540,7 +533,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); @@ -548,7 +541,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); @@ -600,7 +593,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 393d9c3ec2..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(); @@ -24,7 +24,7 @@ namespace MWGui void newEffect (const ESM::MagicEffect* effect); void editEffect (ESM::ENAMstruct effect); - + bool constantEffect; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Effect; EventHandle_Effect eventEffectAdded; @@ -69,7 +69,6 @@ namespace MWGui void onMagnitudeMaxChanged (MyGUI::ScrollBar* sender, size_t pos); void onDurationChanged (MyGUI::ScrollBar* sender, size_t pos); void onAreaChanged (MyGUI::ScrollBar* sender, size_t pos); - void setMagicEffect(const ESM::MagicEffect* effect); void updateBoxes(); @@ -84,7 +83,7 @@ namespace MWGui class EffectEditorBase { public: - EffectEditorBase(MWBase::WindowManager& parWindowManager); + EffectEditorBase(); protected: @@ -124,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/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 16e02ebba1..e762cc6105 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -1,9 +1,5 @@ #include "spellicons.hpp" -#include -#include -#include - #include #include "../mwbase/world.hpp" @@ -14,7 +10,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwmechanics/activespells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "tooltips.hpp" @@ -139,12 +134,13 @@ namespace MWGui } } - parent->setVisible(effects.size() != 0); - int w=2; + if (adjustSize) { int s = effects.size() * 16+4; + if (!effects.size()) + s = 0; int diff = parent->getWidth() - s; parent->setSize(s, parent->getHeight()); parent->setPosition(parent->getLeft()+diff, parent->getTop()); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 50691d5540..d5e3abc110 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -1,22 +1,14 @@ #include "spellwindow.hpp" -#include #include #include -#include "../mwworld/esmstore.hpp" - -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" #include "spellicons.hpp" @@ -47,8 +39,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 +63,7 @@ namespace MWGui void SpellWindow::onPinToggled() { - mWindowManager.setSpellVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned); } void SpellWindow::open() @@ -140,7 +132,7 @@ namespace MWGui { store.setSelectedEnchantItem(store.end()); spells.setSelectedSpell(""); - mWindowManager.unsetSelectedSpell(); + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); selectedItem = MWWorld::Ptr(); } } @@ -377,12 +369,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()->updateItemView(); } store.setSelectedEnchantItem(it); spells.setSelectedSpell(""); - mWindowManager.setSelectedEnchantItem(item, 100); /// \todo track charge % + MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); updateSpells(); } @@ -404,14 +396,14 @@ namespace MWGui if (spell->mData.mFlags & ESM::Spell::F_Always || spell->mData.mType == ESM::Spell::ST_Power) { - mWindowManager.messageBox("#{sDeleteSpellError}", std::vector()); + 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 +415,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 +446,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/stats_window.cpp deleted file mode 100644 index 0fa4127b55..0000000000 --- a/apps/openmw/mwgui/stats_window.cpp +++ /dev/null @@ -1,580 +0,0 @@ -#include "stats_window.hpp" - -#include -#include -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/windowmanager.hpp" - -#include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" - -#include "../mwmechanics/npcstats.hpp" - -#include "tooltips.hpp" - - -using namespace MWGui; -const int StatsWindow::sLineHeight = 18; - -StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) - : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) - , mSkillView(NULL) - , mClientHeight(0) - , mMajorSkills() - , mMinorSkills() - , mMiscSkills() - , mSkillValues() - , mSkillWidgetMap() - , mFactionWidgetMap() - , mFactions() - , mBirthSignId() - , mReputation(0) - , mBounty(0) - , mSkillWidgets() - , mChanged(true) -{ - setCoord(0,0,498, 342); - - const char *names[][2] = - { - { "Attrib1", "sAttributeStrength" }, - { "Attrib2", "sAttributeIntelligence" }, - { "Attrib3", "sAttributeWillpower" }, - { "Attrib4", "sAttributeAgility" }, - { "Attrib5", "sAttributeSpeed" }, - { "Attrib6", "sAttributeEndurance" }, - { "Attrib7", "sAttributePersonality" }, - { "Attrib8", "sAttributeLuck" }, - { 0, 0 } - }; - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for (int i=0; names[i][0]; ++i) - { - setText (names[i][0], store.get().find (names[i][1])->getString()); - } - - getWidget(mSkillView, "SkillView"); - getWidget(mLeftPane, "LeftPane"); - getWidget(mRightPane, "RightPane"); - - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); - mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)NULL)); - } - - MyGUI::WindowPtr t = static_cast(mMainWidget); - t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); -} - -void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mSkillView->getViewOffset().top + _rel*0.3 > 0) - mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); -} - -void StatsWindow::onWindowResize(MyGUI::Window* window) -{ - mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); - mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} - -void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) -{ - MyGUI::ProgressPtr pt; - getWidget(pt, name); - pt->setProgressRange(max); - pt->setProgressPosition(val); - - std::stringstream out; - out << val << "/" << max; - setText(tname, out.str().c_str()); -} - -void StatsWindow::setPlayerName(const std::string& playerName) -{ - static_cast(mMainWidget)->setCaption(playerName); - adjustWindowCaption(); -} - -void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; - - for (int i=0; ids[i]; ++i) - if (ids[i]==id) - { - std::ostringstream valueString; - valueString << value.getModified(); - setText (id, valueString.str()); - - MyGUI::TextBox* box; - getWidget(box, id); - - if (value.getModified()>value.getBase()) - box->_setWidgetState("increased"); - else if (value.getModified()_setWidgetState("decreased"); - else - box->_setWidgetState("normal"); - - break; - } -} - -void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - static const char *ids[] = - { - "HBar", "MBar", "FBar", - 0 - }; - - for (int i=0; ids[i]; ++i) - { - if (ids[i]==id) - { - std::string id (ids[i]); - setBar (id, id + "T", static_cast(value.getCurrent()), static_cast(value.getModified())); - - // health, magicka, fatigue tooltip - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - if (i==0) - { - getWidget(w, "Health"); - w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); - } - else if (i==1) - { - getWidget(w, "Magicka"); - w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); - } - else if (i==2) - { - getWidget(w, "Fatigue"); - w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); - } - } - } -} - -void StatsWindow::setValue (const std::string& id, const std::string& value) -{ - if (id=="name") - setPlayerName (value); - else if (id=="race") - setText ("RaceText", value); - else if (id=="class") - setText ("ClassText", value); -} - -void StatsWindow::setValue (const std::string& id, int value) -{ - if (id=="level") - { - std::ostringstream text; - text << value; - setText("LevelText", text.str()); - } -} - -void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) -{ - mSkillValues[parSkill] = value; - MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; - if (widget) - { - float modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(std::floor(modified)); - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - - widget->setCaption(text); - widget->_setWidgetState(state); - } -} - -void StatsWindow::configureSkills (const std::vector& major, const std::vector& minor) -{ - mMajorSkills = major; - mMinorSkills = minor; - - // Update misc skills with the remaining skills not in major or minor - std::set skillSet; - std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); - std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); - boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); - mMiscSkills.clear(); - for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) - { - int skill = *it; - if (skillSet.find(skill) == skillSet.end()) - mMiscSkills.push_back(skill); - } - - updateSkillArea(); -} - -void StatsWindow::onFrame () -{ - if (!mMainWidget->getVisible()) - return; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); - - // level progress - MyGUI::Widget* levelWidget; - for (int i=0; i<2; ++i) - { - getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); - levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); - levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/10"); - } - - setFactions(PCstats.getFactionRanks()); - setExpelled(PCstats.getExpelled ()); - - const std::string &signId = - MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); - - setBirthSign(signId); - setReputation (PCstats.getReputation ()); - setBounty (PCstats.getBounty ()); - - if (mChanged) - updateSkillArea(); -} - -void StatsWindow::setFactions (const FactionList& factions) -{ - if (mFactions != factions) - { - mFactions = factions; - mChanged = true; - } -} - -void StatsWindow::setExpelled (const std::set& expelled) -{ - if (mExpelled != expelled) - { - mExpelled = expelled; - mChanged = true; - } -} - -void StatsWindow::setBirthSign (const std::string& signId) -{ - if (signId != mBirthSignId) - { - mBirthSignId = signId; - mChanged = true; - } -} - -void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", - MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - mSkillWidgets.push_back(separator); - - coord1.top += separator->getHeight(); - coord2.top += separator->getHeight(); -} - -void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", - MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - groupWidget->setCaption(label); - groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - mSkillWidgets.push_back(groupWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} - -MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox *skillNameWidget, *skillValueWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); - skillValueWidget->setCaption(value); - skillValueWidget->_setWidgetState(state); - skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - mSkillWidgets.push_back(skillValueWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; - - return skillValueWidget; -} - -MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; - - return skillNameWidget; -} - -void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - { - addSeparator(coord1, coord2); - } - - addGroup(mWindowManager.getGameSettingString(titleId, titleDefault), coord1, coord2); - - SkillList::const_iterator end = skills.end(); - for (SkillList::const_iterator it = skills.begin(); it != end; ++it) - { - int skillId = *it; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; - assert(skillId >= 0 && skillId < ESM::Skill::Length); - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - int progressPercent = (modified - float(static_cast(modified))) * 100; - - const MWWorld::ESMStore &esmStore = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Skill* skill = esmStore.get().find(skillId); - assert(skill); - - std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; - - const ESM::Attribute* attr = - esmStore.get().find(skill->mData.mAttribute); - assert(attr); - - std::string state = "normal"; - if (modified > base) - 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); - - for (int i=0; i<2; ++i) - { - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->mDescription); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); - } - - mSkillWidgetMap[skillId] = widget; - } -} - -void StatsWindow::updateSkillArea() -{ - mChanged = false; - - for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSkillWidgets.clear(); - - mSkillView->setViewOffset (MyGUI::IntPoint(0,0)); - mClientHeight = 0; - - const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); - MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); - - if (!mMajorSkills.empty()) - addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); - - if (!mMinorSkills.empty()) - addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); - - if (!mMiscSkills.empty()) - addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - - MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::ESMStore &store = world->getStore(); - const ESM::NPC *player = - world->getPlayer().getPlayer().get()->mBase; - - // race tooltip - const ESM::Race* playerRace = store.get().find(player->mRace); - - MyGUI::Widget* raceWidget; - getWidget(raceWidget, "RaceText"); - ToolTips::createRaceToolTip(raceWidget, playerRace); - getWidget(raceWidget, "Race_str"); - ToolTips::createRaceToolTip(raceWidget, playerRace); - - // class tooltip - MyGUI::Widget* classWidget; - - const ESM::Class *playerClass = - store.get().find(player->mClass); - - getWidget(classWidget, "ClassText"); - ToolTips::createClassToolTip(classWidget, *playerClass); - getWidget(classWidget, "Class_str"); - ToolTips::createClassToolTip(classWidget, *playerClass); - - if (!mFactions.empty()) - { - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); - std::set& expelled = PCstats.getExpelled (); - - addGroup(mWindowManager.getGameSettingString("sFaction", "Faction"), coord1, coord2); - FactionList::const_iterator end = mFactions.end(); - for (FactionList::const_iterator it = mFactions.begin(); it != end; ++it) - { - const ESM::Faction *faction = - store.get().find(it->first); - MyGUI::Widget* w = addItem(faction->mName, coord1, coord2); - - std::string text; - - text += std::string("#DDC79E") + faction->mName; - - if (expelled.find(it->first) != expelled.end()) - text += "\n#{sExpelled}"; - else - { - text += std::string("\n#BF9959") + faction->mRanks[it->second]; - - if (it->second < 9) - { - // player doesn't have max rank yet - 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); - assert(attr1 && attr2); - - text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) - + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); - - text += "\n\n#DDC79E#{sFavoriteSkills}"; - text += "\n#BF9959"; - for (int i=0; i<6; ++i) - { - text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkillID[i]]+"}"; - if (i<5) - text += ", "; - } - - text += "\n"; - - if (rankData.mSkill1 > 0) - text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); - if (rankData.mSkill2 > 0) - text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); - } - } - - w->setUserString("ToolTipType", "Layout"); - w->setUserString("ToolTipLayout", "TextToolTip"); - w->setUserString("Caption_Text", text); - } - } - - if (!mBirthSignId.empty()) - { - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - addGroup(mWindowManager.getGameSettingString("sBirthSign", "Sign"), coord1, coord2); - const ESM::BirthSign *sign = - store.get().find(mBirthSignId); - MyGUI::Widget* w = addItem(sign->mName, coord1, coord2); - - ToolTips::createBirthsignToolTip(w, mBirthSignId); - } - - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - addValueItem(mWindowManager.getGameSettingString("sReputation", "Reputation"), - boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); - - for (int i=0; i<2; ++i) - { - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); - } - - addValueItem(mWindowManager.getGameSettingString("sBounty", "Bounty"), - boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); - - for (int i=0; i<2; ++i) - { - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); - } - - mClientHeight = coord1.top; - - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} - -void StatsWindow::onPinToggled() -{ - mWindowManager.setHMSVisibility(!mPinned); -} diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp new file mode 100644 index 0000000000..1134767f14 --- /dev/null +++ b/apps/openmw/mwgui/statswindow.cpp @@ -0,0 +1,577 @@ +#include "statswindow.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "tooltips.hpp" + +namespace MWGui +{ + + const int StatsWindow::sLineHeight = 18; + + StatsWindow::StatsWindow () + : WindowPinnableBase("openmw_stats_window.layout") + , mSkillView(NULL) + , mClientHeight(0) + , mMajorSkills() + , mMinorSkills() + , mMiscSkills() + , mSkillValues() + , mSkillWidgetMap() + , mFactionWidgetMap() + , mFactions() + , mBirthSignId() + , mReputation(0) + , mBounty(0) + , mSkillWidgets() + , mChanged(true) + { + setCoord(0,0,498, 342); + + const char *names[][2] = + { + { "Attrib1", "sAttributeStrength" }, + { "Attrib2", "sAttributeIntelligence" }, + { "Attrib3", "sAttributeWillpower" }, + { "Attrib4", "sAttributeAgility" }, + { "Attrib5", "sAttributeSpeed" }, + { "Attrib6", "sAttributeEndurance" }, + { "Attrib7", "sAttributePersonality" }, + { "Attrib8", "sAttributeLuck" }, + { 0, 0 } + }; + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + for (int i=0; names[i][0]; ++i) + { + setText (names[i][0], store.get().find (names[i][1])->getString()); + } + + getWidget(mSkillView, "SkillView"); + getWidget(mLeftPane, "LeftPane"); + getWidget(mRightPane, "RightPane"); + + for (int i = 0; i < ESM::Skill::Length; ++i) + { + mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); + mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)NULL)); + } + + MyGUI::WindowPtr t = static_cast(mMainWidget); + t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); + } + + void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mSkillView->getViewOffset().top + _rel*0.3 > 0) + mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); + } + + void StatsWindow::onWindowResize(MyGUI::Window* window) + { + mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); + mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); + } + + void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) + { + MyGUI::ProgressPtr pt; + getWidget(pt, name); + pt->setProgressRange(max); + pt->setProgressPosition(val); + + std::stringstream out; + out << val << "/" << max; + setText(tname, out.str().c_str()); + } + + void StatsWindow::setPlayerName(const std::string& playerName) + { + static_cast(mMainWidget)->setCaption(playerName); + adjustWindowCaption(); + } + + void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) + { + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8", + 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) + { + std::ostringstream valueString; + valueString << value.getModified(); + setText (id, valueString.str()); + + MyGUI::TextBox* box; + getWidget(box, id); + + if (value.getModified()>value.getBase()) + box->_setWidgetState("increased"); + else if (value.getModified()_setWidgetState("decreased"); + else + box->_setWidgetState("normal"); + + break; + } + } + + void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + static const char *ids[] = + { + "HBar", "MBar", "FBar", + 0 + }; + + for (int i=0; ids[i]; ++i) + { + if (ids[i]==id) + { + std::string id (ids[i]); + setBar (id, id + "T", static_cast(value.getCurrent()), static_cast(value.getModified())); + + // health, magicka, fatigue tooltip + MyGUI::Widget* w; + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + if (i==0) + { + getWidget(w, "Health"); + w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + } + else if (i==1) + { + getWidget(w, "Magicka"); + w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + } + else if (i==2) + { + getWidget(w, "Fatigue"); + w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + } + } + } + } + + void StatsWindow::setValue (const std::string& id, const std::string& value) + { + if (id=="name") + setPlayerName (value); + else if (id=="race") + setText ("RaceText", value); + else if (id=="class") + setText ("ClassText", value); + } + + void StatsWindow::setValue (const std::string& id, int value) + { + if (id=="level") + { + std::ostringstream text; + text << value; + setText("LevelText", text.str()); + } + } + + void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + { + mSkillValues[parSkill] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; + if (widget) + { + float modified = value.getModified(), base = value.getBase(); + std::string text = boost::lexical_cast(std::floor(modified)); + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + + widget->setCaption(text); + widget->_setWidgetState(state); + } + } + + void StatsWindow::configureSkills (const std::vector& major, const std::vector& minor) + { + mMajorSkills = major; + mMinorSkills = minor; + + // Update misc skills with the remaining skills not in major or minor + std::set skillSet; + std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); + std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); + boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); + mMiscSkills.clear(); + for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) + { + int skill = *it; + if (skillSet.find(skill) == skillSet.end()) + mMiscSkills.push_back(skill); + } + + updateSkillArea(); + } + + void StatsWindow::onFrame () + { + if (!mMainWidget->getVisible()) + return; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); + + // level progress + MyGUI::Widget* levelWidget; + for (int i=0; i<2; ++i) + { + getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); + levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); + levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/10"); + } + + setFactions(PCstats.getFactionRanks()); + setExpelled(PCstats.getExpelled ()); + + const std::string &signId = + MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); + + setBirthSign(signId); + setReputation (PCstats.getReputation ()); + setBounty (PCstats.getBounty ()); + + if (mChanged) + updateSkillArea(); + } + + void StatsWindow::setFactions (const FactionList& factions) + { + if (mFactions != factions) + { + mFactions = factions; + mChanged = true; + } + } + + void StatsWindow::setExpelled (const std::set& expelled) + { + if (mExpelled != expelled) + { + mExpelled = expelled; + mChanged = true; + } + } + + void StatsWindow::setBirthSign (const std::string& signId) + { + if (signId != mBirthSignId) + { + mBirthSignId = signId; + mChanged = true; + } + } + + void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", + MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + mSkillWidgets.push_back(separator); + + coord1.top += separator->getHeight(); + coord2.top += separator->getHeight(); + } + + void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", + MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + groupWidget->setCaption(label); + groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + mSkillWidgets.push_back(groupWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + } + + MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox *skillNameWidget, *skillValueWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); + skillValueWidget->setCaption(value); + skillValueWidget->_setWidgetState(state); + skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillValueWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + + return skillValueWidget; + } + + MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + + return skillNameWidget; + } + + void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + { + addSeparator(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) + { + int skillId = *it; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + assert(skillId >= 0 && skillId < ESM::Skill::Length); + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + float base = stat.getBase(); + float modified = stat.getModified(); + int progressPercent = (modified - float(static_cast(modified))) * 100; + + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Skill* skill = esmStore.get().find(skillId); + assert(skill); + + std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; + + const ESM::Attribute* attr = + esmStore.get().find(skill->mData.mAttribute); + assert(attr); + + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + 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) + { + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->mDescription); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); + } + + mSkillWidgetMap[skillId] = widget; + } + } + + void StatsWindow::updateSkillArea() + { + mChanged = false; + + for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillWidgets.clear(); + + mSkillView->setViewOffset (MyGUI::IntPoint(0,0)); + mClientHeight = 0; + + const int valueSize = 40; + MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); + MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + + if (!mMajorSkills.empty()) + addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + + if (!mMinorSkills.empty()) + addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + + if (!mMiscSkills.empty()) + addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::ESMStore &store = world->getStore(); + const ESM::NPC *player = + world->getPlayer().getPlayer().get()->mBase; + + // race tooltip + const ESM::Race* playerRace = store.get().find(player->mRace); + + MyGUI::Widget* raceWidget; + getWidget(raceWidget, "RaceText"); + ToolTips::createRaceToolTip(raceWidget, playerRace); + getWidget(raceWidget, "Race_str"); + ToolTips::createRaceToolTip(raceWidget, playerRace); + + // class tooltip + MyGUI::Widget* classWidget; + + const ESM::Class *playerClass = + store.get().find(player->mClass); + + getWidget(classWidget, "ClassText"); + ToolTips::createClassToolTip(classWidget, *playerClass); + getWidget(classWidget, "Class_str"); + ToolTips::createClassToolTip(classWidget, *playerClass); + + if (!mFactions.empty()) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(coord1, coord2); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); + std::set& expelled = PCstats.getExpelled (); + + 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) + { + const ESM::Faction *faction = + store.get().find(it->first); + MyGUI::Widget* w = addItem(faction->mName, coord1, coord2); + + std::string text; + + text += std::string("#DDC79E") + faction->mName; + + if (expelled.find(it->first) != expelled.end()) + text += "\n#{sExpelled}"; + else + { + text += std::string("\n#BF9959") + faction->mRanks[it->second]; + + if (it->second < 9) + { + // player doesn't have max rank yet + 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.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) + + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); + + text += "\n\n#DDC79E#{sFavoriteSkills}"; + text += "\n#BF9959"; + for (int i=0; i<6; ++i) + { + text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+"}"; + if (i<5) + text += ", "; + } + + text += "\n"; + + if (rankData.mSkill1 > 0) + text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); + if (rankData.mSkill2 > 0) + text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); + } + } + + w->setUserString("ToolTipType", "Layout"); + w->setUserString("ToolTipLayout", "TextToolTip"); + w->setUserString("Caption_Text", text); + } + } + + if (!mBirthSignId.empty()) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(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); + + ToolTips::createBirthsignToolTip(w, mBirthSignId); + } + + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(coord1, coord2); + + addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sReputation", "Reputation"), + boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); + + for (int i=0; i<2; ++i) + { + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); + } + + addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBounty", "Bounty"), + boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); + + for (int i=0; i<2; ++i) + { + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); + } + + mClientHeight = coord1.top; + + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); + } + + void StatsWindow::onPinToggled() + { + 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/text_input.cpp deleted file mode 100644 index 9265cadf94..0000000000 --- a/apps/openmw/mwgui/text_input.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "text_input.hpp" - -#include "../mwbase/windowmanager.hpp" - -using namespace MWGui; - -TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_text_input.layout", parWindowManager) -{ - // Centre dialog - center(); - - getWidget(mTextEdit, "TextEdit"); - mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); - - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} - -void TextInputDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} - -void TextInputDialog::setTextLabel(const std::string &label) -{ - setText("LabelT", label); -} - -void TextInputDialog::open() -{ - WindowModal::open(); - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} - -// widget controls - -void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if (mTextEdit->getCaption() == "") - { - mWindowManager.messageBox ("#{sNotifyMessage37}", std::vector()); - MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); - } - else - eventDone(this); -} - -void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) -{ - if (mTextEdit->getCaption() == "") - { - mWindowManager.messageBox ("#{sNotifyMessage37}", std::vector()); - MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); - } - else - eventDone(this); -} diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp new file mode 100644 index 0000000000..d4f8a25336 --- /dev/null +++ b/apps/openmw/mwgui/textinput.cpp @@ -0,0 +1,73 @@ +#include "textinput.hpp" + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" + +namespace MWGui +{ + + TextInputDialog::TextInputDialog() + : WindowModal("openmw_text_input.layout") + { + // Centre dialog + center(); + + getWidget(mTextEdit, "TextEdit"); + mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + } + + void TextInputDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void TextInputDialog::setTextLabel(const std::string &label) + { + setText("LabelT", label); + } + + void TextInputDialog::open() + { + WindowModal::open(); + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + } + + // widget controls + + void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) + { + if (mTextEdit->getCaption() == "") + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); + MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); + } + + void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) + { + if (mTextEdit->getCaption() == "") + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); + MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); + } + +} 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 8dcf398ca5..e73ed6b5ee 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -2,791 +2,784 @@ #include -#include - -#include - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" -#include "map_window.hpp" -#include "widgets.hpp" +#include "mapwindow.hpp" #include "inventorywindow.hpp" -using namespace MWGui; -using namespace MyGUI; +#include "itemmodel.hpp" -ToolTips::ToolTips(MWBase::WindowManager* windowManager) : - Layout("openmw_tooltips.layout") - , mGameMode(true) - , mWindowManager(windowManager) - , mFullHelp(false) - , mEnabled(true) - , mFocusToolTipX(0.0) - , mFocusToolTipY(0.0) - , mDelay(0.0) - , mRemainingDelay(0.0) - , mLastMouseX(0) - , mLastMouseY(0) - , mHorizontalScrollIndex(0) -{ - getWidget(mDynamicToolTipBox, "DynamicToolTipBox"); - - mDynamicToolTipBox->setVisible(false); - - // turn off mouse focus so that getMouseFocusWidget returns the correct widget, - // even if the mouse is over the tooltip - mDynamicToolTipBox->setNeedMouseFocus(false); - mMainWidget->setNeedMouseFocus(false); - - mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); - mRemainingDelay = mDelay; -} - -void ToolTips::setEnabled(bool enabled) -{ - mEnabled = enabled; -} - -void ToolTips::onFrame(float frameDuration) +namespace MWGui { - while (mDynamicToolTipBox->getChildCount()) + ToolTips::ToolTips() : + Layout("openmw_tooltips.layout") + , mGameMode(true) + , mFullHelp(false) + , mEnabled(true) + , mFocusToolTipX(0.0) + , mFocusToolTipY(0.0) + , mDelay(0.0) + , mRemainingDelay(0.0) + , mLastMouseX(0) + , mLastMouseY(0) + , mHorizontalScrollIndex(0) { - MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); - } + getWidget(mDynamicToolTipBox, "DynamicToolTipBox"); - // start by hiding everything - for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) - { - mMainWidget->getChildAt(i)->setVisible(false); - } + mDynamicToolTipBox->setVisible(false); - const IntSize &viewSize = RenderManager::getInstance().getViewSize(); + // turn off mouse focus so that getMouseFocusWidget returns the correct widget, + // even if the mouse is over the tooltip + mDynamicToolTipBox->setNeedMouseFocus(false); + mMainWidget->setNeedMouseFocus(false); - if (!mEnabled) - { - return; - } + mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); + mRemainingDelay = mDelay; - if (!mGameMode) - { - const MyGUI::IntPoint& mousePos = InputManager::getInstance().getMousePosition(); - - if (mWindowManager->getWorldMouseOver() && ((mWindowManager->getMode() == GM_Console) - || (mWindowManager->getMode() == GM_Container) - || (mWindowManager->getMode() == GM_Inventory))) + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) { - mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); + mMainWidget->getChildAt(i)->setVisible(false); + } + } - if (mFocusObject.isEmpty ()) - return; + void ToolTips::setEnabled(bool enabled) + { + mEnabled = enabled; + } - MyGUI::IntSize tooltipSize = getToolTipViaPtr(true); + void ToolTips::onFrame(float frameDuration) + { - IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); - - // make the tooltip stay completely in the viewport - if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) - { - tooltipPosition.left = viewSize.width - tooltipSize.width; - } - if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) - { - tooltipPosition.top = viewSize.height - tooltipSize.height; - } - - setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); + while (mDynamicToolTipBox->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); } - else + // start by hiding everything + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) { - const MyGUI::IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); + mMainWidget->getChildAt(i)->setVisible(false); + } - if (mousePos == lastPressed) // mouseclick makes tooltip disappear - return; + const MyGUI::IntSize &viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) + if (!mEnabled) + { + return; + } + + if (!mGameMode) + { + const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition(); + + 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))) { - mRemainingDelay -= frameDuration; - } - else - { - mHorizontalScrollIndex = 0; - mRemainingDelay = mDelay; - } - mLastMouseX = mousePos.left; - mLastMouseY = mousePos.top; + mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); - - if (mRemainingDelay > 0) - return; - - Widget* focus = InputManager::getInstance().getMouseFocusWidget(); - if (focus == 0) - return; - - IntSize tooltipSize; - - // try to go 1 level up until there is a widget that has tooltip - // this is necessary because some skin elements are actually separate widgets - int i=0; - while (!focus->isUserString("ToolTipType")) - { - focus = focus->getParent(); - if (!focus) + if (mFocusObject.isEmpty ()) return; - ++i; - } - std::string type = focus->getUserString("ToolTipType"); - std::string text = focus->getUserString("ToolTipText"); + const MWWorld::Class& objectclass = MWWorld::Class::get (mFocusObject); - if (type == "") - { - 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") - { - LocalMapBase::MarkerPosition pos = *focus->getUserData(); - - if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) - return; - } - - if (type == "ItemPtr") - { - mFocusObject = *focus->getUserData(); - tooltipSize = getToolTipViaPtr(false); - } - else if (type == "ToolTipInfo") - { - tooltipSize = createToolTip(*focus->getUserData()); - } - else if (type == "AvatarItemSelection") - { - MyGUI::IntCoord avatarPos = mWindowManager->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); - - mFocusObject = item; - if (!mFocusObject.isEmpty ()) - tooltipSize = getToolTipViaPtr(false); - } - else if (type == "Spell") - { - ToolTipInfo info; - - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find(focus->getUserString("Spell")); - info.caption = spell->mName; - Widgets::SpellEffectList effects; - std::vector::const_iterator end = spell->mEffects.mList.end(); - for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) + MyGUI::IntSize tooltipSize; + if ((!objectclass.hasToolTip(mFocusObject))&&(MWBase::Environment::get().getWindowManager()->getMode() == GM_Console)) { - Widgets::SpellEffectParams params; - params.mEffectID = it->mEffectID; - params.mSkill = it->mSkill; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); - params.mNoTarget = false; - effects.push_back(params); - } - info.effects = effects; - tooltipSize = createToolTip(info); - } - else if (type == "Layout") - { - // tooltip defined in the layout - MyGUI::Widget* tooltip; - getWidget(tooltip, focus->getUserString("ToolTipLayout")); - - tooltip->setVisible(true); - if (!tooltip->isUserString("DontResize")) - { - tooltip->setCoord(0, 0, 450, 300); // this is the maximum width of the tooltip before it starts word-wrapping - - tooltipSize = MyGUI::IntSize(0, tooltip->getSize().height); + setCoord(0, 0, 300, 300); + mDynamicToolTipBox->setVisible(true); + ToolTipInfo info; + info.caption=mFocusObject.getCellRef().mRefID; + info.icon=""; + tooltipSize = createToolTip(info); } else + tooltipSize = getToolTipViaPtr(true); + + MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition() + MyGUI::IntPoint(0, 24); + + // make the tooltip stay completely in the viewport + if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) + { + tooltipPosition.left = viewSize.width - tooltipSize.width; + } + if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) + { + tooltipPosition.top = viewSize.height - tooltipSize.height; + } + + setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); + } + + else + { + const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); + + if (mousePos == lastPressed) // mouseclick makes tooltip disappear + return; + + if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) + { + mRemainingDelay -= frameDuration; + } + else + { + mHorizontalScrollIndex = 0; + mRemainingDelay = mDelay; + } + mLastMouseX = mousePos.left; + mLastMouseY = mousePos.top; + + + if (mRemainingDelay > 0) + return; + + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); + if (focus == 0) + return; + + MyGUI::IntSize tooltipSize; + + // try to go 1 level up until there is a widget that has tooltip + // this is necessary because some skin elements are actually separate widgets + int i=0; + while (!focus->isUserString("ToolTipType")) + { + focus = focus->getParent(); + if (!focus) + return; + ++i; + } + + std::string type = focus->getUserString("ToolTipType"); + std::string text = focus->getUserString("ToolTipText"); + + if (type == "") + { + 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") + { + LocalMapBase::MarkerPosition pos = *focus->getUserData(); + + if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) + return; + } + + if (type == "ItemPtr") + { + mFocusObject = *focus->getUserData(); + tooltipSize = getToolTipViaPtr(false); + } + else if (type == "ItemModelIndex") + { + std::pair pair = *focus->getUserData >(); + mFocusObject = pair.second->getItem(pair.first).mBase; + // HACK: To get the correct count for multiple item stack sources + int oldCount = mFocusObject.getRefData().getCount(); + mFocusObject.getRefData().setCount(pair.second->getItem(pair.first).mCount); + tooltipSize = getToolTipViaPtr(false); + mFocusObject.getRefData().setCount(oldCount); + } + else if (type == "ToolTipInfo") + { + tooltipSize = createToolTip(*focus->getUserData()); + } + else if (type == "AvatarItemSelection") + { + 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 = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarSelectedItem (realX, realY); + + mFocusObject = item; + if (!mFocusObject.isEmpty ()) + tooltipSize = getToolTipViaPtr(false); + } + else if (type == "Spell") + { + ToolTipInfo info; + + const ESM::Spell *spell = + MWBase::Environment::get().getWorld()->getStore().get().find(focus->getUserString("Spell")); + info.caption = spell->mName; + Widgets::SpellEffectList effects; + std::vector::const_iterator end = spell->mEffects.mList.end(); + for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) + { + Widgets::SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); + params.mNoTarget = false; + effects.push_back(params); + } + info.effects = effects; + tooltipSize = createToolTip(info); + } + else if (type == "Layout") + { + // tooltip defined in the layout + MyGUI::Widget* tooltip; + getWidget(tooltip, focus->getUserString("ToolTipLayout")); + + tooltip->setVisible(true); + + std::map userStrings = focus->getUserStrings(); + for (std::map::iterator it = userStrings.begin(); + it != userStrings.end(); ++it) + { + if (it->first == "ToolTipType" + || it->first == "ToolTipLayout" + || it->first == "IsMarker") + continue; + + + size_t underscorePos = it->first.find("_"); + std::string propertyKey = it->first.substr(0, underscorePos); + std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); + + MyGUI::Widget* w; + getWidget(w, widgetName); + w->setProperty(propertyKey, it->second); + } + tooltipSize = tooltip->getSize(); - std::map userStrings = focus->getUserStrings(); - for (std::map::iterator it = userStrings.begin(); - it != userStrings.end(); ++it) + tooltip->setCoord(0, 0, tooltipSize.width, tooltipSize.height); + } + else + throw std::runtime_error ("unknown tooltip type"); + + MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition() + MyGUI::IntPoint(0, 24); + + // make the tooltip stay completely in the viewport + if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) { - if (it->first == "ToolTipType" - || it->first == "ToolTipLayout" - || it->first == "IsMarker") - continue; - - - size_t underscorePos = it->first.find("_"); - std::string propertyKey = it->first.substr(0, underscorePos); - std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); - - MyGUI::Widget* w; - getWidget(w, widgetName); - w->setProperty(propertyKey, it->second); + tooltipPosition.left = viewSize.width - tooltipSize.width; + } + if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) + { + tooltipPosition.top = viewSize.height - tooltipSize.height; } - for (unsigned int i=0; igetChildCount(); ++i) - { - MyGUI::Widget* w = tooltip->getChildAt(i); - - if (w->isUserString("AutoResizeHorizontal")) - { - MyGUI::TextBox* text = w->castType(); - tooltipSize.width = std::max(tooltipSize.width, w->getLeft() + text->getTextSize().width + 8); - } - else if (!tooltip->isUserString("DontResize")) - tooltipSize.width = std::max(tooltipSize.width, w->getLeft() + w->getWidth() + 8); - - if (w->isUserString("AutoResizeVertical")) - { - MyGUI::TextBox* text = w->castType(); - int height = text->getTextSize().height; - if (height > w->getHeight()) - { - tooltipSize += MyGUI::IntSize(0, height - w->getHeight()); - } - if (height < w->getHeight()) - { - tooltipSize -= MyGUI::IntSize(0, w->getHeight() - height); - } - } - } - tooltip->setCoord(0, 0, tooltipSize.width, tooltipSize.height); + setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); } - else - throw std::runtime_error ("unknown tooltip type"); - - IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); - - // make the tooltip stay completely in the viewport - if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) - { - tooltipPosition.left = viewSize.width - tooltipSize.width; - } - if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) - { - tooltipPosition.top = viewSize.height - tooltipSize.height; - } - - setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); } - } - else - { - if (!mFocusObject.isEmpty()) + else { - IntSize tooltipSize = getToolTipViaPtr(); + if (!mFocusObject.isEmpty()) + { + MyGUI::IntSize tooltipSize = getToolTipViaPtr(); - setCoord(viewSize.width/2 - tooltipSize.width/2, - std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), - tooltipSize.width, - tooltipSize.height); + setCoord(viewSize.width/2 - tooltipSize.width/2, + std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), + tooltipSize.width, + tooltipSize.height); - mDynamicToolTipBox->setVisible(true); + mDynamicToolTipBox->setVisible(true); + } } } -} -void ToolTips::enterGameMode() -{ - mGameMode = true; -} - -void ToolTips::enterGuiMode() -{ - mGameMode = false; -} - -void ToolTips::setFocusObject(const MWWorld::Ptr& focus) -{ - mFocusObject = focus; -} - -IntSize ToolTips::getToolTipViaPtr (bool image) -{ - // this the maximum width of the tooltip before it starts word-wrapping - setCoord(0, 0, 300, 300); - - IntSize tooltipSize; - - const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); - if (!object.hasToolTip(mFocusObject)) + void ToolTips::enterGameMode() { - mDynamicToolTipBox->setVisible(false); + mGameMode = true; } - else + + void ToolTips::enterGuiMode() + { + mGameMode = false; + } + + void ToolTips::setFocusObject(const MWWorld::Ptr& focus) + { + mFocusObject = focus; + } + + MyGUI::IntSize ToolTips::getToolTipViaPtr (bool image) + { + // this the maximum width of the tooltip before it starts word-wrapping + setCoord(0, 0, 300, 300); + + MyGUI::IntSize tooltipSize; + + const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); + if (!object.hasToolTip(mFocusObject)) + { + mDynamicToolTipBox->setVisible(false); + } + else + { + mDynamicToolTipBox->setVisible(true); + + ToolTipInfo info = object.getToolTipInfo(mFocusObject); + if (!image) + info.icon = ""; + tooltipSize = createToolTip(info); + } + + return tooltipSize; + } + + void ToolTips::findImageExtension(std::string& image) + { + int len = image.size(); + if (len < 4) return; + + if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image)) + { + // Change texture extension to .dds + image[len-3] = 'd'; + image[len-2] = 'd'; + image[len-1] = 's'; + } + } + + MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) { mDynamicToolTipBox->setVisible(true); - ToolTipInfo info = object.getToolTipInfo(mFocusObject); - if (!image) - info.icon = ""; - tooltipSize = createToolTip(info); - } + std::string caption = info.caption; + std::string image = info.icon; + int imageSize = (image != "") ? info.imageSize : 0; + std::string text = info.text; - return tooltipSize; -} + // remove the first newline (easier this way) + if (text.size() > 0 && text[0] == '\n') + text.erase(0, 1); -void ToolTips::findImageExtension(std::string& image) -{ - int len = image.size(); - if (len < 4) return; + if(caption.size() > 0 && isalnum(caption[0])) + caption[0] = toupper(caption[0]); - if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image)) - { - // Change texture extension to .dds - image[len-3] = 'd'; - image[len-2] = 'd'; - image[len-1] = 's'; - } -} - -IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) -{ - mDynamicToolTipBox->setVisible(true); - - std::string caption = info.caption; - std::string image = info.icon; - int imageSize = (image != "") ? info.imageSize : 0; - std::string text = info.text; - - // remove the first newline (easier this way) - if (text.size() > 0 && text[0] == '\n') - text.erase(0, 1); - - if(caption.size() > 0 && isalnum(caption[0])) - caption[0] = toupper(caption[0]); - - const ESM::Enchantment* enchant = 0; - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - if (info.enchant != "") - { - enchant = store.get().find(info.enchant); - if (enchant->mData.mType == ESM::Enchantment::CastOnce) - text += "\n#{sItemCastOnce}"; - else if (enchant->mData.mType == ESM::Enchantment::WhenStrikes) - text += "\n#{sItemCastWhenStrikes}"; - else if (enchant->mData.mType == ESM::Enchantment::WhenUsed) - text += "\n#{sItemCastWhenUsed}"; - else if (enchant->mData.mType == ESM::Enchantment::ConstantEffect) - text += "\n#{sItemCastConstant}"; - } - - // this the maximum width of the tooltip before it starts word-wrapping - setCoord(0, 0, 300, 300); - - const IntPoint padding(8, 8); - - const int maximumWidth = 500; - - const int imageCaptionHPadding = (caption != "" ? 8 : 0); - const int imageCaptionVPadding = (caption != "" ? 4 : 0); - - std::string realImage = "icons\\" + image; - findImageExtension(realImage); - - EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", IntCoord(0, 0, 300, 300), Align::Left | Align::Top, "ToolTipCaption"); - captionWidget->setProperty("Static", "true"); - captionWidget->setCaptionWithReplacing(caption); - IntSize captionSize = captionWidget->getTextSize(); - - int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); - - EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), Align::Stretch, "ToolTipText"); - textWidget->setProperty("Static", "true"); - textWidget->setProperty("MultiLine", "true"); - textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false"); - textWidget->setCaptionWithReplacing(text); - textWidget->setTextAlign(Align::HCenter | Align::Top); - IntSize textSize = textWidget->getTextSize(); - - captionSize += IntSize(imageSize, 0); // adjust for image - IntSize totalSize = IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), - ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); - - if (!info.effects.empty()) - { - Widget* effectArea = mDynamicToolTipBox->createWidget("", - IntCoord(0, totalSize.height, 300, 300-totalSize.height), - Align::Stretch, "ToolTipEffectArea"); - - IntCoord coord(0, 6, totalSize.width, 24); - - /** - * \todo - * the various potion effects should appear in the tooltip depending if the player - * has enough skill in alchemy to know about the effects of this potion. - */ - - Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget - ("MW_StatName", coord, Align::Default, "ToolTipEffectsWidget"); - effectsWidget->setWindowManager(mWindowManager); - effectsWidget->setEffectList(info.effects); - - std::vector effectItems; - effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0); - totalSize.height += coord.top-6; - totalSize.width = std::max(totalSize.width, coord.width); - } - - if (info.enchant != "") - { - assert(enchant); - Widget* enchantArea = mDynamicToolTipBox->createWidget("", - IntCoord(0, totalSize.height, 300, 300-totalSize.height), - Align::Stretch, "ToolTipEnchantArea"); - - IntCoord coord(0, 6, totalSize.width, 24); - - Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget - ("MW_StatName", coord, Align::Default, "ToolTipEnchantWidget"); - enchantWidget->setWindowManager(mWindowManager); - enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); - - std::vector enchantEffectItems; - int flag = (enchant->mData.mType == ESM::Enchantment::ConstantEffect) ? Widgets::MWEffectList::EF_Constant : 0; - enchantWidget->createEffectWidgets(enchantEffectItems, enchantArea, coord, true, flag); - totalSize.height += coord.top-6; - totalSize.width = std::max(totalSize.width, coord.width); - - if (enchant->mData.mType == ESM::Enchantment::WhenStrikes - || enchant->mData.mType == ESM::Enchantment::WhenUsed) + const ESM::Enchantment* enchant = 0; + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + if (info.enchant != "") { - /// \todo store the current enchantment charge somewhere - int charge = enchant->mData.mCharge; - - const int chargeWidth = 204; - - TextBox* chargeText = enchantArea->createWidget("SandText", IntCoord(0, 0, 10, 18), Align::Default, "ToolTipEnchantChargeText"); - chargeText->setCaptionWithReplacing("#{sCharges}"); - - const int chargeTextWidth = chargeText->getTextSize().width + 5; - - const int chargeAndTextWidth = chargeWidth + chargeTextWidth; - - totalSize.width = std::max(totalSize.width, chargeAndTextWidth); - - chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); - - IntCoord chargeCoord; - if (totalSize.width < chargeWidth) - { - totalSize.width = chargeWidth; - chargeCoord = IntCoord(0, coord.top+6, chargeWidth, 18); - } - else - { - chargeCoord = IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); - } - Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget - ("MW_ChargeBar", chargeCoord, Align::Default, "ToolTipEnchantCharge"); - chargeWidget->setValue(charge, charge); - totalSize.height += 24; + enchant = store.get().find(info.enchant); + if (enchant->mData.mType == ESM::Enchantment::CastOnce) + text += "\n#{sItemCastOnce}"; + else if (enchant->mData.mType == ESM::Enchantment::WhenStrikes) + text += "\n#{sItemCastWhenStrikes}"; + else if (enchant->mData.mType == ESM::Enchantment::WhenUsed) + text += "\n#{sItemCastWhenUsed}"; + else if (enchant->mData.mType == ESM::Enchantment::ConstantEffect) + text += "\n#{sItemCastConstant}"; } - } - captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize, - (captionHeight-captionSize.height)/2, - captionSize.width-imageSize, - captionSize.height); - - //if its too long we do hscroll with the caption - if (captionSize.width > maximumWidth){ - mHorizontalScrollIndex = mHorizontalScrollIndex + 2; - if (mHorizontalScrollIndex > captionSize.width){ - mHorizontalScrollIndex = -totalSize.width; - } - int horizontal_scroll = mHorizontalScrollIndex; - if (horizontal_scroll < 40){ - horizontal_scroll = 40; - }else{ - horizontal_scroll = 80 - mHorizontalScrollIndex; - } - captionWidget->setPosition (IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); - } else { - captionWidget->setPosition (captionWidget->getPosition() + padding); - } + // this the maximum width of the tooltip before it starts word-wrapping + setCoord(0, 0, 300, 300); - textWidget->setPosition (textWidget->getPosition() + IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter + const MyGUI::IntPoint padding(8, 8); - if (image != "") - { - ImageBox* imageWidget = mDynamicToolTipBox->createWidget("ImageBox", - IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize), - Align::Left | Align::Top, "ToolTipImage"); - imageWidget->setImageTexture(realImage); - imageWidget->setPosition (imageWidget->getPosition() + padding); - } + const int maximumWidth = 500; - totalSize += IntSize(padding.left*2, padding.top*2); + const int imageCaptionHPadding = (caption != "" ? 8 : 0); + const int imageCaptionVPadding = (caption != "" ? 4 : 0); - return totalSize; -} + std::string realImage = "icons\\" + image; + findImageExtension(realImage); -std::string ToolTips::toString(const float value) -{ - std::ostringstream stream; - stream << std::setprecision(3) << value; - return stream.str(); -} + MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); + captionWidget->setProperty("Static", "true"); + captionWidget->setCaptionWithReplacing(caption); + MyGUI::IntSize captionSize = captionWidget->getTextSize(); -std::string ToolTips::toString(const int value) -{ - std::ostringstream stream; - stream << value; - return stream.str(); -} + int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); -std::string ToolTips::getValueString(const int value, const std::string& prefix) -{ - if (value == 0) - return ""; - else - return "\n" + prefix + ": " + toString(value); -} + MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); + textWidget->setProperty("Static", "true"); + textWidget->setProperty("MultiLine", "true"); + textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false"); + textWidget->setCaptionWithReplacing(text); + textWidget->setTextAlign(MyGUI::Align::HCenter | MyGUI::Align::Top); + MyGUI::IntSize textSize = textWidget->getTextSize(); -std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) -{ - if (text == "") - return ""; - else - return "\n" + prefix + ": " + text; -} + captionSize += MyGUI::IntSize(imageSize, 0); // adjust for image + MyGUI::IntSize totalSize = MyGUI::IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), + ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); -std::string ToolTips::getCountString(const int value) -{ - if (value == 1) - return ""; - else - return " (" + boost::lexical_cast(value) + ")"; -} - -void ToolTips::toggleFullHelp() -{ - mFullHelp = !mFullHelp; -} - -bool ToolTips::getFullHelp() const -{ - return mFullHelp; -} - -void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) -{ - mFocusToolTipX = (min_x + max_x) / 2; - mFocusToolTipY = min_y; -} - -void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) -{ - if (skillId == -1) - return; - - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const ESM::Skill* skill = store.get().find(skillId); - assert(skill); - - const ESM::Attribute* attr = - store.get().find(skill->mData.mAttribute); - assert(attr); - std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip"); - widget->setUserString("Caption_SkillNoProgressName", "#{"+skillNameId+"}"); - widget->setUserString("Caption_SkillNoProgressDescription", skill->mDescription); - widget->setUserString("Caption_SkillNoProgressAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); - widget->setUserString("ImageTexture_SkillNoProgressImage", icon); -} - -void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) -{ - if (attributeId == -1) - return; - - std::string icon = ESM::Attribute::sAttributeIcons[attributeId]; - std::string name = ESM::Attribute::sGmstAttributeIds[attributeId]; - std::string desc = ESM::Attribute::sGmstAttributeDescIds[attributeId]; - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "AttributeToolTip"); - widget->setUserString("Caption_AttributeName", "#{"+name+"}"); - widget->setUserString("Caption_AttributeDescription", "#{"+desc+"}"); - widget->setUserString("ImageTexture_AttributeImage", icon); -} - -void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) -{ - widget->setUserString("Caption_CenteredCaption", name); - std::string specText; - // get all skills of this specialisation - const MWWorld::Store &skills = - MWBase::Environment::get().getWorld()->getStore().get(); - - MWWorld::Store::iterator it = skills.begin(); - for (; it != skills.end(); ++it) - { - if (it->mData.mSpecialization == specId) - specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->mIndex] + "}"; - } - widget->setUserString("Caption_CenteredCaptionText", specText); - widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); - widget->setUserString("ToolTipType", "Layout"); -} - -void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId) -{ - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::BirthSign *sign = store.get().find(birthsignId); - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "BirthSignToolTip"); - std::string image = sign->mTexture; - image.replace(image.size()-3, 3, "dds"); - widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image); - std::string text; - - text += sign->mName; - text += "\n#BF9959" + sign->mDescription; - - std::vector abilities, powers, spells; - - std::vector::const_iterator it = sign->mPowers.mList.begin(); - std::vector::const_iterator end = sign->mPowers.mList.end(); - for (; it != end; ++it) - { - const std::string &spellId = *it; - const ESM::Spell *spell = store.get().search(spellId); - if (!spell) - continue; // Skip spells which cannot be found - ESM::Spell::SpellType type = static_cast(spell->mData.mType); - if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) - continue; // We only want spell, ability and powers. - - if (type == ESM::Spell::ST_Ability) - abilities.push_back(spellId); - else if (type == ESM::Spell::ST_Power) - powers.push_back(spellId); - else if (type == ESM::Spell::ST_Spell) - spells.push_back(spellId); - } - - struct { - const std::vector &spells; - std::string label; - } - categories[3] = { - {abilities, "sBirthsignmenu1"}, - {powers, "sPowers"}, - {spells, "sBirthsignmenu2"} - }; - - for (int category = 0; category < 3; ++category) - { - for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) + if (!info.effects.empty()) { - if (it == categories[category].spells.begin()) - { - text += std::string("\n#DDC79E") + std::string("#{") + categories[category].label + "}"; - } + MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget("", + MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), + MyGUI::Align::Stretch, "ToolTipEffectArea"); + MyGUI::IntCoord coord(0, 6, totalSize.width, 24); + + /** + * \todo + * the various potion effects should appear in the tooltip depending if the player + * has enough skill in alchemy to know about the effects of this potion. + */ + + Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget + ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEffectsWidget"); + effectsWidget->setEffectList(info.effects); + + std::vector effectItems; + effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0); + totalSize.height += coord.top-6; + totalSize.width = std::max(totalSize.width, coord.width); + } + + if (info.enchant != "") + { + assert(enchant); + MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget("", + MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), + MyGUI::Align::Stretch, "ToolTipEnchantArea"); + + MyGUI::IntCoord coord(0, 6, totalSize.width, 24); + + Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget + ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEnchantWidget"); + enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); + + std::vector enchantEffectItems; + int flag = (enchant->mData.mType == ESM::Enchantment::ConstantEffect) ? Widgets::MWEffectList::EF_Constant : 0; + enchantWidget->createEffectWidgets(enchantEffectItems, enchantArea, coord, true, flag); + totalSize.height += coord.top-6; + totalSize.width = std::max(totalSize.width, coord.width); + + if (enchant->mData.mType == ESM::Enchantment::WhenStrikes + || enchant->mData.mType == ESM::Enchantment::WhenUsed) + { + int maxCharge = enchant->mData.mCharge; + int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge; + + const int chargeWidth = 204; + + MyGUI::TextBox* chargeText = enchantArea->createWidget("SandText", MyGUI::IntCoord(0, 0, 10, 18), MyGUI::Align::Default, "ToolTipEnchantChargeText"); + chargeText->setCaptionWithReplacing("#{sCharges}"); + + const int chargeTextWidth = chargeText->getTextSize().width + 5; + + const int chargeAndTextWidth = chargeWidth + chargeTextWidth; + + totalSize.width = std::max(totalSize.width, chargeAndTextWidth); + + chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); + + MyGUI::IntCoord chargeCoord; + if (totalSize.width < chargeWidth) + { + totalSize.width = chargeWidth; + chargeCoord = MyGUI::IntCoord(0, coord.top+6, chargeWidth, 18); + } + else + { + chargeCoord = MyGUI::IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); + } + Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget + ("MW_ChargeBar", chargeCoord, MyGUI::Align::Default, "ToolTipEnchantCharge"); + chargeWidget->setValue(charge, charge); + totalSize.height += 24; + } + } + + captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize, + (captionHeight-captionSize.height)/2, + captionSize.width-imageSize, + captionSize.height); + + //if its too long we do hscroll with the caption + if (captionSize.width > maximumWidth) + { + mHorizontalScrollIndex = mHorizontalScrollIndex + 2; + if (mHorizontalScrollIndex > captionSize.width){ + mHorizontalScrollIndex = -totalSize.width; + } + int horizontal_scroll = mHorizontalScrollIndex; + if (horizontal_scroll < 40){ + horizontal_scroll = 40; + }else{ + horizontal_scroll = 80 - mHorizontalScrollIndex; + } + captionWidget->setPosition (MyGUI::IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); + } else { + captionWidget->setPosition (captionWidget->getPosition() + padding); + } + + textWidget->setPosition (textWidget->getPosition() + MyGUI::IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter + + if (image != "") + { + MyGUI::ImageBox* imageWidget = mDynamicToolTipBox->createWidget("ImageBox", + MyGUI::IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize), + MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipImage"); + imageWidget->setImageTexture(realImage); + imageWidget->setPosition (imageWidget->getPosition() + padding); + } + + totalSize += MyGUI::IntSize(padding.left*2, padding.top*2); + + return totalSize; + } + + std::string ToolTips::toString(const float value) + { + std::ostringstream stream; + stream << std::setprecision(3) << value; + return stream.str(); + } + + std::string ToolTips::toString(const int value) + { + std::ostringstream stream; + stream << value; + return stream.str(); + } + + std::string ToolTips::getValueString(const int value, const std::string& prefix) + { + if (value == 0) + return ""; + else + return "\n" + prefix + ": " + toString(value); + } + + std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) + { + if (text == "") + return ""; + else + return "\n" + prefix + ": " + text; + } + + std::string ToolTips::getCountString(const int value) + { + if (value == 1) + return ""; + else + return " (" + boost::lexical_cast(value) + ")"; + } + + void ToolTips::toggleFullHelp() + { + mFullHelp = !mFullHelp; + } + + bool ToolTips::getFullHelp() const + { + return mFullHelp; + } + + void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) + { + mFocusToolTipX = (min_x + max_x) / 2; + mFocusToolTipY = min_y; + } + + void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) + { + if (skillId == -1) + return; + + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const ESM::Skill* skill = store.get().find(skillId); + assert(skill); + + const ESM::Attribute* attr = + store.get().find(skill->mData.mAttribute); + assert(attr); + std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip"); + widget->setUserString("Caption_SkillNoProgressName", "#{"+skillNameId+"}"); + widget->setUserString("Caption_SkillNoProgressDescription", skill->mDescription); + widget->setUserString("Caption_SkillNoProgressAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); + widget->setUserString("ImageTexture_SkillNoProgressImage", icon); + } + + void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) + { + if (attributeId == -1) + return; + + std::string icon = ESM::Attribute::sAttributeIcons[attributeId]; + std::string name = ESM::Attribute::sGmstAttributeIds[attributeId]; + std::string desc = ESM::Attribute::sGmstAttributeDescIds[attributeId]; + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "AttributeToolTip"); + widget->setUserString("Caption_AttributeName", "#{"+name+"}"); + widget->setUserString("Caption_AttributeDescription", "#{"+desc+"}"); + widget->setUserString("ImageTexture_AttributeImage", icon); + } + + void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) + { + widget->setUserString("Caption_CenteredCaption", name); + std::string specText; + // get all skills of this specialisation + const MWWorld::Store &skills = + MWBase::Environment::get().getWorld()->getStore().get(); + + MWWorld::Store::iterator it = skills.begin(); + for (; it != skills.end(); ++it) + { + if (it->mData.mSpecialization == specId) + specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->mIndex] + "}"; + } + widget->setUserString("Caption_CenteredCaptionText", specText); + widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); + widget->setUserString("ToolTipType", "Layout"); + } + + void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::BirthSign *sign = store.get().find(birthsignId); + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "BirthSignToolTip"); + std::string image = sign->mTexture; + image.replace(image.size()-3, 3, "dds"); + widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image); + std::string text; + + text += sign->mName; + text += "\n#BF9959" + sign->mDescription; + + std::vector abilities, powers, spells; + + std::vector::const_iterator it = sign->mPowers.mList.begin(); + std::vector::const_iterator end = sign->mPowers.mList.end(); + for (; it != end; ++it) + { const std::string &spellId = *it; + const ESM::Spell *spell = store.get().search(spellId); + if (!spell) + continue; // Skip spells which cannot be found + ESM::Spell::SpellType type = static_cast(spell->mData.mType); + if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) + continue; // We only want spell, ability and powers. - const ESM::Spell *spell = store.get().find(spellId); - text += "\n#BF9959" + spell->mName; + if (type == ESM::Spell::ST_Ability) + abilities.push_back(spellId); + else if (type == ESM::Spell::ST_Power) + powers.push_back(spellId); + else if (type == ESM::Spell::ST_Spell) + spells.push_back(spellId); } + + struct { + const std::vector &spells; + std::string label; + } + categories[3] = { + {abilities, "sBirthsignmenu1"}, + {powers, "sPowers"}, + {spells, "sBirthsignmenu2"} + }; + + for (int category = 0; category < 3; ++category) + { + for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) + { + if (it == categories[category].spells.begin()) + { + text += std::string("\n#DDC79E") + std::string("#{") + categories[category].label + "}"; + } + + const std::string &spellId = *it; + + const ESM::Spell *spell = store.get().find(spellId); + text += "\n#BF9959" + spell->mName; + } + } + + widget->setUserString("Caption_BirthSignText", text); + } + + void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace) + { + widget->setUserString("Caption_CenteredCaption", playerRace->mName); + widget->setUserString("Caption_CenteredCaptionText", playerRace->mDescription); + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); + } + + void ToolTips::createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass) + { + if (playerClass.mName == "") + return; + + int spec = playerClass.mData.mSpecialization; + std::string specStr; + if (spec == 0) + specStr = "#{sSpecializationCombat}"; + else if (spec == 1) + specStr = "#{sSpecializationMagic}"; + else if (spec == 2) + specStr = "#{sSpecializationStealth}"; + + widget->setUserString("Caption_ClassName", playerClass.mName); + widget->setUserString("Caption_ClassDescription", playerClass.mDescription); + widget->setUserString("Caption_ClassSpecialisation", "#{sSpecialization}: " + specStr); + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "ClassToolTip"); + } + + void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) + { + const ESM::MagicEffect* effect = + MWBase::Environment::get().getWorld ()->getStore ().get().find(id); + const std::string &name = ESM::MagicEffect::effectIdToString (id); + + std::string icon = effect->mIcon; + + int slashPos = icon.find("\\"); + icon.insert(slashPos+1, "b_"); + + icon[icon.size()-3] = 'd'; + icon[icon.size()-2] = 'd'; + icon[icon.size()-1] = 's'; + + icon = "icons\\" + icon; + + std::vector schools; + schools.push_back ("#{sSchoolAlteration}"); + schools.push_back ("#{sSchoolConjuration}"); + schools.push_back ("#{sSchoolDestruction}"); + schools.push_back ("#{sSchoolIllusion}"); + schools.push_back ("#{sSchoolMysticism}"); + schools.push_back ("#{sSchoolRestoration}"); + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); + widget->setUserString("Caption_MagicEffectName", "#{" + name + "}"); + widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); + widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + schools[effect->mData.mSchool]); + widget->setUserString("ImageTexture_MagicEffectImage", icon); + } + + void ToolTips::setDelay(float delay) + { + mDelay = delay; + mRemainingDelay = mDelay; } - widget->setUserString("Caption_BirthSignText", text); -} - -void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace) -{ - widget->setUserString("Caption_CenteredCaption", playerRace->mName); - widget->setUserString("Caption_CenteredCaptionText", playerRace->mDescription); - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); -} - -void ToolTips::createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass) -{ - if (playerClass.mName == "") - return; - - int spec = playerClass.mData.mSpecialization; - std::string specStr; - if (spec == 0) - specStr = "#{sSpecializationCombat}"; - else if (spec == 1) - specStr = "#{sSpecializationMagic}"; - else if (spec == 2) - specStr = "#{sSpecializationStealth}"; - - widget->setUserString("Caption_ClassName", playerClass.mName); - widget->setUserString("Caption_ClassDescription", playerClass.mDescription); - widget->setUserString("Caption_ClassSpecialisation", "#{sSpecialization}: " + specStr); - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "ClassToolTip"); -} - -void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) -{ - const ESM::MagicEffect* effect = - MWBase::Environment::get().getWorld ()->getStore ().get().find(id); - const std::string &name = ESM::MagicEffect::effectIdToString (id); - - std::string icon = effect->mIcon; - - int slashPos = icon.find("\\"); - icon.insert(slashPos+1, "b_"); - - icon[icon.size()-3] = 'd'; - icon[icon.size()-2] = 'd'; - icon[icon.size()-1] = 's'; - - icon = "icons\\" + icon; - - std::vector schools; - schools.push_back ("#{sSchoolAlteration}"); - schools.push_back ("#{sSchoolConjuration}"); - schools.push_back ("#{sSchoolDestruction}"); - schools.push_back ("#{sSchoolIllusion}"); - schools.push_back ("#{sSchoolMysticism}"); - schools.push_back ("#{sSchoolRestoration}"); - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); - widget->setUserString("Caption_MagicEffectName", "#{" + name + "}"); - widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); - widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + schools[effect->mData.mSchool]); - widget->setUserString("ImageTexture_MagicEffectImage", icon); -} - -void ToolTips::setDelay(float delay) -{ - mDelay = delay; - mRemainingDelay = mDelay; } 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/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp new file mode 100644 index 0000000000..b4fa4e16fd --- /dev/null +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -0,0 +1,198 @@ +#include "tradeitemmodel.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/inventorystore.hpp" + +namespace MWGui +{ + + TradeItemModel::TradeItemModel(ItemModel *sourceModel, const MWWorld::Ptr& merchant) + : mMerchant(merchant) + { + mSourceModel = sourceModel; + } + + ItemStack TradeItemModel::getItem (ModelIndex index) + { + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; + } + + size_t TradeItemModel::getItemCount() + { + return mItems.size(); + } + + void TradeItemModel::borrowImpl(const ItemStack &item, std::vector &out) + { + std::vector::iterator it = out.begin(); + bool found = false; + for (; it != out.end(); ++it) + { + if (it->stacks(item)) + { + it->mCount += item.mCount; + found = true; + break; + } + } + if (!found) + out.push_back(item); + } + + void TradeItemModel::unborrowImpl(const ItemStack &item, size_t count, std::vector &out) + { + std::vector::iterator it = out.begin(); + bool found = false; + for (; it != out.end(); ++it) + { + if (it->stacks(item)) + { + if (it->mCount < count) + throw std::runtime_error("Not enough borrowed items to return"); + it->mCount -= count; + if (it->mCount == 0) + out.erase(it); + found = true; + break; + } + } + if (!found) + throw std::runtime_error("Can't find borrowed item to return"); + } + + void TradeItemModel::borrowItemFromUs (ModelIndex itemIndex, size_t count) + { + ItemStack item = getItem(itemIndex); + item.mCount = count; + borrowImpl(item, mBorrowedFromUs); + } + + void TradeItemModel::borrowItemToUs (ModelIndex itemIndex, ItemModel* source, size_t count) + { + ItemStack item = source->getItem(itemIndex); + item.mCount = count; + borrowImpl(item, mBorrowedToUs); + } + + void TradeItemModel::returnItemBorrowedToUs (ModelIndex itemIndex, size_t count) + { + ItemStack item = getItem(itemIndex); + unborrowImpl(item, count, mBorrowedToUs); + } + + void TradeItemModel::returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count) + { + ItemStack item = source->getItem(itemIndex); + unborrowImpl(item, count, mBorrowedFromUs); + } + + void TradeItemModel::abort() + { + mBorrowedFromUs.clear(); + mBorrowedToUs.clear(); + } + + std::vector TradeItemModel::getItemsBorrowedToUs() + { + return mBorrowedToUs; + } + + void TradeItemModel::transferItems() + { + std::vector::iterator it = mBorrowedToUs.begin(); + for (; it != mBorrowedToUs.end(); ++it) + { + // get index in the source model + ItemModel* sourceModel = it->mCreator; + size_t i=0; + for (; igetItemCount(); ++i) + { + if (it->stacks(sourceModel->getItem(i))) + break; + } + if (i == sourceModel->getItemCount()) + throw std::runtime_error("The borrowed item disappeared"); + + // reset owner before copying + const ItemStack& item = sourceModel->getItem(i); + std::string owner = item.mBase.getCellRef().mOwner; + if (mMerchant.isEmpty()) // only for items bought by player + item.mBase.getCellRef().mOwner = ""; + // copy the borrowed items to our model + copyItem(item, it->mCount); + item.mBase.getCellRef().mOwner = owner; + // then remove them from the source model + sourceModel->removeItem(item, it->mCount); + } + mBorrowedToUs.clear(); + mBorrowedFromUs.clear(); + } + + void TradeItemModel::update() + { + mSourceModel->update(); + + int services = 0; + if (!mMerchant.isEmpty()) + services = MWWorld::Class::get(mMerchant).getServices(mMerchant); + + mItems.clear(); + // add regular items + for (size_t i=0; igetItemCount(); ++i) + { + ItemStack item = mSourceModel->getItem(i); + MWWorld::Ptr base = item.mBase; + if (!mMerchant.isEmpty() && Misc::StringUtils::ciEqual(base.getCellRef().mRefID, "gold_001")) + continue; + if (!mMerchant.isEmpty() && !MWWorld::Class::get(base).canSell(base, services)) + continue; + + // don't show equipped items + if (mMerchant.getTypeName() == typeid(ESM::NPC).name()) + { + bool isEquipped = false; + MWWorld::InventoryStore& store = MWWorld::Class::get(mMerchant).getInventoryStore(mMerchant); + for (int slot=0; slot::iterator it = mBorrowedFromUs.begin(); + for (; it != mBorrowedFromUs.end(); ++it) + { + if (it->stacks(item)) + { + if (item.mCount < it->mCount) + throw std::runtime_error("Lent more items than present"); + item.mCount -= it->mCount; + } + } + + if (item.mCount > 0) + mItems.push_back(item); + } + + // add items borrowed to us + std::vector::iterator it = mBorrowedToUs.begin(); + for (; it != mBorrowedToUs.end(); ++it) + { + ItemStack item = *it; + item.mType = ItemStack::Type_Barter; + mItems.push_back(item); + } + } + +} diff --git a/apps/openmw/mwgui/tradeitemmodel.hpp b/apps/openmw/mwgui/tradeitemmodel.hpp new file mode 100644 index 0000000000..5cfaaafc76 --- /dev/null +++ b/apps/openmw/mwgui/tradeitemmodel.hpp @@ -0,0 +1,53 @@ +#ifndef MWGUI_TRADE_ITEM_MODEL_H +#define MWGUI_TRADE_ITEM_MODEL_H + +#include "itemmodel.hpp" + +namespace MWGui +{ + + class ItemModel; + + /// @brief An item model that allows 'borrowing' items from another item model. Used for previewing barter offers. + /// Also filters items that the merchant does not sell. + class TradeItemModel : public ProxyItemModel + { + public: + TradeItemModel (ItemModel* sourceModel, const MWWorld::Ptr& merchant); + + virtual ItemStack getItem (ModelIndex index); + virtual size_t getItemCount(); + + virtual void update(); + + void borrowItemFromUs (ModelIndex itemIndex, size_t count); + + void borrowItemToUs (ModelIndex itemIndex, ItemModel* source, size_t count); + ///< @note itemIndex points to an item in \a source + + void returnItemBorrowedToUs (ModelIndex itemIndex, size_t count); + + void returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count); + + /// Permanently transfers items that were borrowed to us from another model to this model + void transferItems (); + /// Aborts trade + void abort(); + + std::vector getItemsBorrowedToUs(); + + private: + void borrowImpl(const ItemStack& item, std::vector& out); + void unborrowImpl(const ItemStack& item, size_t count, std::vector& out); + + std::vector mItems; + + std::vector mBorrowedToUs; + std::vector mBorrowedFromUs; + + MWWorld::Ptr mMerchant; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0fd24601a8..6a46478afe 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -9,8 +9,9 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -18,28 +19,24 @@ #include "../mwworld/player.hpp" #include "inventorywindow.hpp" +#include "itemview.hpp" +#include "sortfilteritemmodel.hpp" +#include "containeritemmodel.hpp" +#include "tradeitemmodel.hpp" +#include "countdialog.hpp" 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) - , ContainerBase(NULL) // no drag&drop + TradeWindow::TradeWindow() + : WindowBase("openmw_trade_window.layout") , mCurrentBalance(0) , mBalanceButtonsState(BBS_None) , mBalanceChangePause(0.0) + , mItemToSell(-1) { - // items the NPC is wearing should not be for trade - mDisplayEquippedItems = false; - - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); - getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); getWidget(mFilterApparel, "ApparelButton"); @@ -57,6 +54,9 @@ namespace MWGui getWidget(mTotalBalanceLabel, "TotalBalanceLabel"); getWidget(mBottomPane, "BottomPane"); + getWidget(mItemView, "ItemView"); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &TradeWindow::onItemSelected); + mFilterAll->setStateSelected(true); mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); @@ -74,40 +74,40 @@ namespace MWGui mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased); setCoord(400, 0, 400, 300); - - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &TradeWindow::onWindowResize); } - void TradeWindow::startTrade(MWWorld::Ptr actor) + void TradeWindow::startTrade(const MWWorld::Ptr& actor) { + mPtr = actor; setTitle(MWWorld::Class::get(actor).getName(actor)); mCurrentBalance = 0; mCurrentMerchantOffer = 0; - mWindowManager.getInventoryWindow()->startTrade(); + std::vector itemSources; + MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); + // Important: actor goes last, so that items purchased by the merchant go into his inventory + itemSources.push_back(actor); - mBoughtItems.clear(); - - ContainerBase::openContainer(actor); + mTradeModel = new TradeItemModel(new ContainerItemModel(itemSources), mPtr); + mSortModel = new SortFilterItemModel(mTradeModel); + mItemView->setModel (mSortModel); updateLabels(); - - drawItems(); } void TradeWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) - setFilter(ContainerBase::Filter_All); + mSortModel->setCategory(SortFilterItemModel::Category_All); else if (_sender == mFilterWeapon) - setFilter(ContainerBase::Filter_Weapon); + mSortModel->setCategory(SortFilterItemModel::Category_Weapon); else if (_sender == mFilterApparel) - setFilter(ContainerBase::Filter_Apparel); + mSortModel->setCategory(SortFilterItemModel::Category_Apparel); else if (_sender == mFilterMagic) - setFilter(ContainerBase::Filter_Magic); + mSortModel->setCategory(SortFilterItemModel::Category_Magic); else if (_sender == mFilterMisc) - setFilter(ContainerBase::Filter_Misc); + mSortModel->setCategory(SortFilterItemModel::Category_Misc); mFilterAll->setStateSelected(false); mFilterWeapon->setStateSelected(false); @@ -116,26 +116,96 @@ namespace MWGui mFilterMisc->setStateSelected(false); static_cast(_sender)->setStateSelected(true); + + mItemView->update(); } - void TradeWindow::onWindowResize(MyGUI::Window* _sender) + int TradeWindow::getMerchantServices() { - drawItems(); + return MWWorld::Class::get(mPtr).getServices(mPtr); + } + + void TradeWindow::onItemSelected (int index) + { + const ItemStack& item = mSortModel->getItem(index); + + MWWorld::Ptr object = item.mBase; + int count = item.mCount; + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); + if (MyGUI::InputManager::getInstance().isControlPressed()) + count = 1; + + if (count > 1 && !shift) + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + std::string message = "#{sQuanityMenuMessage02}"; + dialog->open(MWWorld::Class::get(object).getName(object), message, count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &TradeWindow::sellItem); + mItemToSell = mSortModel->mapToSource(index); + } + else + { + mItemToSell = mSortModel->mapToSource(index); + sellItem (NULL, count); + } + } + + void TradeWindow::sellItem(MyGUI::Widget* sender, int count) + { + const ItemStack& item = mTradeModel->getItem(mItemToSell); + std::string sound = MWWorld::Class::get(item.mBase).getDownSoundId(item.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + + if (item.mType == ItemStack::Type_Barter) + { + // this was an item borrowed to us by the player + mTradeModel->returnItemBorrowedToUs(mItemToSell, count); + playerTradeModel->returnItemBorrowedFromUs(mItemToSell, mTradeModel, count); + buyFromNpc(item.mBase, count, true); + } + else + { + // borrow item to player + playerTradeModel->borrowItemToUs(mItemToSell, mTradeModel, count); + mTradeModel->borrowItemFromUs(mItemToSell, count); + buyFromNpc(item.mBase, count, false); + } + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + mItemView->update(); + } + + void TradeWindow::borrowItem (int index, size_t count) + { + TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + mTradeModel->borrowItemToUs(index, playerTradeModel, count); + mItemView->update(); + sellToNpc(playerTradeModel->getItem(index).mBase, count, false); + } + + void TradeWindow::returnItem (int index, size_t count) + { + TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + const ItemStack& item = playerTradeModel->getItem(index); + mTradeModel->returnItemBorrowedFromUs(index, playerTradeModel, count); + mItemView->update(); + sellToNpc(item.mBase, count, true); } void TradeWindow::addOrRemoveGold(int amount) { bool goldFound = false; MWWorld::Ptr gold; - MWWorld::ContainerStore& playerStore = mWindowManager.getInventoryWindow()->getContainerStore(); - - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& playerStore = MWWorld::Class::get(player).getContainerStore(player); for (MWWorld::ContainerStoreIterator it = playerStore.begin(); it != playerStore.end(); ++it) { - if (MWWorld::Class::get(*it).getName(*it) == gmst.find("sGold")->getString()) + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) { goldFound = true; gold = *it; @@ -171,26 +241,28 @@ namespace MWGui void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender) { + TradeItemModel* playerItemModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); // were there any items traded at all? - MWWorld::ContainerStore& playerBought = mWindowManager.getInventoryWindow()->getBoughtItems(); - MWWorld::ContainerStore& merchantBought = getBoughtItems(); - if (playerBought.begin() == playerBought.end() && merchantBought.begin() == merchantBought.end()) + std::vector playerBought = playerItemModel->getItemsBorrowedToUs(); + std::vector merchantBought = mTradeModel->getItemsBorrowedToUs(); + if (!playerBought.size() && !merchantBought.size()) { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog11}", std::vector()); + messageBox("#{sBarterDialog11}"); return; } // 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()-> - messageBox("#{sBarterDialog1}", std::vector()); + messageBox("#{sBarterDialog1}"); return; } @@ -199,7 +271,7 @@ namespace MWGui { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog2}", std::vector()); + messageBox("#{sBarterDialog2}"); return; } @@ -209,15 +281,17 @@ namespace MWGui if (mPtr.getTypeName() != typeid(ESM::NPC).name()) { MWBase::Environment::get().getWindowManager()-> - messageBox("#{sNotifyMessage9}", std::vector()); + messageBox("#{sNotifyMessage9}"); return; } int a = abs(mCurrentMerchantOffer); int b = abs(mCurrentBalance); int d = 0; - if (mCurrentMerchantOffer<0) d = int(100 * (a - b) / a); - else d = int(100 * (b - a) / a); + if (mCurrentBalance<0) + d = int(100 * (a - b) / a); + else + d = int(100 * (b - a) / a); float clampedDisposition = std::max(0,std::min(int(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()),100)); @@ -238,14 +312,16 @@ namespace MWGui float pcTerm = (clampedDisposition - 50 + a1 + b1 + c1) * playerStats.getFatigueTerm(); float npcTerm = (d1 + e1 + f1) * sellerStats.getFatigueTerm(); float x = gmst.find("fBargainOfferMulti")->getFloat() * d + gmst.find("fBargainOfferBase")->getFloat(); - if (mCurrentMerchantOffer<0) x += abs(int(pcTerm - npcTerm)); - else x += abs(int(npcTerm - pcTerm)); + if (mCurrentBalance<0) + x += abs(int(pcTerm - npcTerm)); + else + x += abs(int(npcTerm - pcTerm)); int roll = std::rand()%100 + 1; if(roll > x) //trade refused { MWBase::Environment::get().getWindowManager()-> - messageBox("#{sNotifyMessage9}", std::vector()); + messageBox("#{sNotifyMessage9}"); int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt(); MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterFailDisposition); @@ -254,14 +330,14 @@ namespace MWGui //skill use! MWWorld::Class::get(playerPtr).skillUsageSucceeded(playerPtr, ESM::Skill::Mercantile, 0); - } + } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterSuccessDisposition); - // success! make the item transfer. - transferBoughtItems(); - mWindowManager.getInventoryWindow()->transferBoughtItems(); + // make the item transfer + mTradeModel->transferItems(); + playerItemModel->transferItems(); // add or remove gold from the player. if (mCurrentBalance != 0) @@ -270,17 +346,14 @@ 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()); - // now gimme back my stuff! - mWindowManager.getInventoryWindow()->returnBoughtItems(MWWorld::Class::get(mPtr).getContainerStore(mPtr)); - - mWindowManager.removeGuiMode(GM_Barter); + mTradeModel->abort(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->abort(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onMaxSaleButtonClicked(MyGUI::Widget* _sender) @@ -324,7 +397,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) { @@ -340,66 +413,7 @@ namespace MWGui mMerchantGold->setCaptionWithReplacing("#{sSellerGold} " + boost::lexical_cast(getMerchantGold())); } - bool TradeWindow::npcAcceptsItem(MWWorld::Ptr item) - { - int services = 0; - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mHasAI) - services = ref->mBase->mAiData.mServices; - } - else if (mPtr.getTypeName() == typeid(ESM::Creature).name()) - { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mHasAI) - 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::Tool).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; - } - - std::vector TradeWindow::itemsToIgnore() - { - std::vector items; - MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - for (MWWorld::ContainerStoreIterator it = invStore.begin(); - it != invStore.end(); ++it) - { - if (!npcAcceptsItem(*it)) - items.push_back(*it); - } - - return items; - } - - void TradeWindow::sellToNpc(MWWorld::Ptr item, int count, bool boughtItem) + void TradeWindow::sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem) { int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count, boughtItem); @@ -409,7 +423,7 @@ namespace MWGui updateLabels(); } - void TradeWindow::buyFromNpc(MWWorld::Ptr item, int count, bool soldItem) + void TradeWindow::buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem) { int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count, !soldItem); @@ -422,8 +436,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..4e905915a0 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 { @@ -20,23 +17,32 @@ namespace MWGui namespace MWGui { - class TradeWindow : public ContainerBase, public WindowBase + class ItemView; + class SortFilterItemModel; + class TradeItemModel; + + class TradeWindow : public WindowBase, public ReferenceInterface { public: - TradeWindow(MWBase::WindowManager& parWindowManager); + TradeWindow(); - void startTrade(MWWorld::Ptr actor); - - void sellToNpc(MWWorld::Ptr item, int count, bool boughtItem); ///< only used for adjusting the gold balance - void buyFromNpc(MWWorld::Ptr item, int count, bool soldItem); ///< only used for adjusting the gold balance - - bool npcAcceptsItem(MWWorld::Ptr item); + void startTrade(const MWWorld::Ptr& actor); void addOrRemoveGold(int gold); void onFrame(float frameDuration); - protected: + void borrowItem (int index, size_t count); + void returnItem (int index, size_t count); + + int getMerchantServices(); + + + private: + ItemView* mItemView; + SortFilterItemModel* mSortModel; + TradeItemModel* mTradeModel; + static const float sBalanceChangeInitialPause; // in seconds static const float sBalanceChangeInterval; // in seconds @@ -59,6 +65,8 @@ namespace MWGui MyGUI::TextBox* mPlayerGold; MyGUI::TextBox* mMerchantGold; + int mItemToSell; + int mCurrentBalance; int mCurrentMerchantOffer; @@ -70,7 +78,12 @@ namespace MWGui /// pause before next balance change will trigger while user holds +/- button pressed float mBalanceChangePause; - void onWindowResize(MyGUI::Window* _sender); + void sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem); ///< only used for adjusting the gold balance + void buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem); ///< only used for adjusting the gold balance + + void onItemSelected (int index); + void sellItem (MyGUI::Widget* sender, int count); + void onFilterChanged(MyGUI::Widget* _sender); void onOfferButtonClicked(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender); @@ -82,16 +95,10 @@ namespace MWGui void onIncreaseButtonTriggered(); void onDecreaseButtonTriggered(); - virtual bool isTrading() { return true; } - virtual bool isTradeWindow() { return true; } - - virtual std::vector itemsToIgnore(); - void updateLabels(); virtual void onReferenceUnavailable(); - private: int getMerchantGold(); }; } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index ba39ee601c..7ddac38f54 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -10,6 +10,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" @@ -20,8 +21,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 +41,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 +83,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 +103,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 +124,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()()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}"); return; } @@ -141,11 +142,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..83e158e4a7 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -1,22 +1,16 @@ #include "travelwindow.hpp" -#include - #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" - -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" +#include "../mwworld/class.hpp" #include "inventorywindow.hpp" #include "tradewindow.hpp" @@ -25,8 +19,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 +65,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 +123,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(); @@ -141,35 +135,38 @@ namespace MWGui int x,y; bool interior = _sender->getUserString("interior") == "y"; MWBase::Environment::get().getWorld()->positionToIndex(pos.pos[0],pos.pos[1],x,y); - MWWorld::CellStore* cell; - if(interior) cell = MWBase::Environment::get().getWorld()->getInterior(cellname); + if(interior) + MWBase::Environment::get().getWorld()->changeToInteriorCell(cellname, pos); else { - cell = MWBase::Environment::get().getWorld()->getExterior(x,y); - ESM::Position PlayerPos = player.getRefData().getPosition(); - float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); - int time = int(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); - for(int i = 0;i < time;i++) + ESM::Position playerPos = player.getRefData().getPosition(); + float d = Ogre::Vector3(pos.pos[0], pos.pos[1], 0).distance( + Ogre::Vector3(playerPos.pos[0], playerPos.pos[1], 0)); + int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); + for(int i = 0;i < hours;i++) { MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats (); } - MWBase::Environment::get().getWorld()->advanceTime(time); + MWBase::Environment::get().getWorld()->advanceTime(hours); + + MWBase::Environment::get().getWorld()->changeToExteriorCell(pos); } - MWBase::Environment::get().getWorld()->moveObject(player,*cell,pos.pos[0],pos.pos[1],pos.pos[2]); - 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 +175,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 794a9ab55f..ad2b4710cc 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -1,7 +1,5 @@ #include "waitdialog.hpp" -#include - #include #include @@ -11,9 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/timestamp.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -25,8 +21,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 +42,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 +71,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 +79,8 @@ namespace MWGui if (canRest == 2) { // resting underwater or mid-air not allowed - mWindowManager.messageBox ("#{sNotifyMessage1}", std::vector()); - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } setCanRest(canRest == 0); @@ -132,7 +128,7 @@ namespace MWGui case 11: month = "#{sMonthEveningstar}"; break; - default: + default: break; } int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour (); @@ -142,7 +138,7 @@ namespace MWGui std::string dateTimeText = boost::lexical_cast(MWBase::Environment::get().getWorld ()->getDay ()) + " " - + month + " (#{sDay} " + boost::lexical_cast(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay ()+1) + + month + " (#{sDay} " + boost::lexical_cast(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) + ") " + boost::lexical_cast(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mDateTimeText->setCaptionWithReplacing (dateTimeText); @@ -156,13 +152,13 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats stats = MWWorld::Class::get(player).getCreatureStats(player); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - + float hourlyHealthDelta = stats.getAttribute(ESM::Attribute::Endurance).getModified() * 0.1; - - bool stunted = (stats.getMagicEffects().get(MWMechanics::EffectKey(136)).mMagnitude > 0); + + bool stunted = (stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0); float fRestMagicMult = store.get().find("fRestMagicMult")->getFloat(); float hourlyMagickaDelta = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); - + // this massive duplication is why it has to be put into helper functions instead float fFatigueReturnBase = store.get().find("fFatigueReturnBase")->getFloat(); float fFatigueReturnMult = store.get().find("fFatigueReturnMult")->getFloat(); @@ -174,7 +170,7 @@ namespace MWGui normalizedEncumbrance = 1; float hourlyFatigueDelta = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); hourlyFatigueDelta *= 3600 * fEndFatigueMult * stats.getAttribute(ESM::Attribute::Endurance).getModified(); - + float healthHours = hourlyHealthDelta >= 0.0 ? (stats.getHealth().getBase() - stats.getHealth().getCurrent()) / hourlyHealthDelta : 1.0f; @@ -185,9 +181,9 @@ namespace MWGui float fatigueHours = hourlyFatigueDelta >= 0.0 ? (stats.getFatigue().getBase() - stats.getFatigue().getCurrent()) / hourlyFatigueDelta : 1.0f; - + int autoHours = int(std::ceil( std::max(std::max(healthHours, magickaHours), std::max(fatigueHours, 1.0f)) )); // this should use a variadic max if possible - + startWaiting(autoHours); } @@ -201,18 +197,18 @@ namespace MWGui MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.2); setVisible(false); mProgressBar.setVisible (true); - + mWaiting = true; mCurHour = 0; mHours = hoursToWait; - + mRemainingTime = 0.05; mProgressBar.setProgress (0, mHours); } void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) { - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position) @@ -263,8 +259,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 +269,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 506b906982..1662c0597d 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -9,873 +9,889 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/esmstore.hpp" - #undef min #undef max -using namespace MWGui; -using namespace MWGui::Widgets; - -/* Helper functions */ - -/* - * Fixes the filename of a texture path to use the correct .dds extension. - * This is needed on some ESM entries which point to a .tga file instead. - */ -void MWGui::Widgets::fixTexturePath(std::string &path) +namespace MWGui { - int offset = path.rfind("."); - if (offset < 0) - return; - path.replace(offset, path.length() - offset, ".dds"); -} - -/* MWSkill */ - -MWSkill::MWSkill() - : mManager(NULL) - , mSkillId(ESM::Skill::Length) - , mSkillNameWidget(NULL) - , mSkillValueWidget(NULL) -{ -} - -void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) -{ - mSkillId = skill; - updateWidgets(); -} - -void MWSkill::setSkillNumber(int skill) -{ - if (skill < 0) - setSkillId(ESM::Skill::Length); - else if (skill < ESM::Skill::Length) - setSkillId(static_cast(skill)); - else - throw new std::runtime_error("Skill number out of range"); -} - -void MWSkill::setSkillValue(const SkillValue& value) -{ - mValue = value; - updateWidgets(); -} - -void MWSkill::updateWidgets() -{ - if (mSkillNameWidget && mManager) + namespace Widgets { - if (mSkillId == ESM::Skill::Length) + + /* Helper functions */ + + /* + * Fixes the filename of a texture path to use the correct .dds extension. + * This is needed on some ESM entries which point to a .tga file instead. + */ + void fixTexturePath(std::string &path) { - static_cast(mSkillNameWidget)->setCaption(""); - } - else - { - const std::string &name = mManager->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); - static_cast(mSkillNameWidget)->setCaption(name); - } - } - if (mSkillValueWidget) - { - SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); - static_cast(mSkillValueWidget)->setCaption(boost::lexical_cast(modified)); - if (modified > base) - mSkillValueWidget->_setWidgetState("increased"); - else if (modified < base) - mSkillValueWidget->_setWidgetState("decreased"); - else - mSkillValueWidget->_setWidgetState("normal"); - } -} - -void MWSkill::onClicked(MyGUI::Widget* _sender) -{ - eventClicked(this); -} - -MWSkill::~MWSkill() -{ -} - -void MWSkill::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mSkillNameWidget, "StatName"); - assignWidget(mSkillValueWidget, "StatValue"); - - MyGUI::Button* button; - assignWidget(button, "StatNameButton"); - if (button) - { - mSkillNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); - } - - button = 0; - assignWidget(button, "StatValueButton"); - if (button) - { - mSkillNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); - } -} - -/* MWAttribute */ - -MWAttribute::MWAttribute() - : mManager(NULL) - , mId(-1) - , mAttributeNameWidget(NULL) - , mAttributeValueWidget(NULL) -{ -} - -void MWAttribute::setAttributeId(int attributeId) -{ - mId = attributeId; - updateWidgets(); -} - -void MWAttribute::setAttributeValue(const AttributeValue& value) -{ - mValue = value; - updateWidgets(); -} - -void MWAttribute::onClicked(MyGUI::Widget* _sender) -{ - eventClicked(this); -} - -void MWAttribute::updateWidgets() -{ - if (mAttributeNameWidget && mManager) - { - if (mId < 0 || mId >= 8) - { - static_cast(mAttributeNameWidget)->setCaption(""); - } - else - { - static const char *attributes[8] = { - "sAttributeStrength", - "sAttributeIntelligence", - "sAttributeWillpower", - "sAttributeAgility", - "sAttributeSpeed", - "sAttributeEndurance", - "sAttributePersonality", - "sAttributeLuck" - }; - const std::string &name = mManager->getGameSettingString(attributes[mId], ""); - static_cast(mAttributeNameWidget)->setCaption(name); - } - } - if (mAttributeValueWidget) - { - AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); - static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); - if (modified > base) - mAttributeValueWidget->_setWidgetState("increased"); - else if (modified < base) - mAttributeValueWidget->_setWidgetState("decreased"); - else - mAttributeValueWidget->_setWidgetState("normal"); - } -} - -MWAttribute::~MWAttribute() -{ -} - -void MWAttribute::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mAttributeNameWidget, "StatName"); - assignWidget(mAttributeValueWidget, "StatValue"); - - MyGUI::Button* button; - assignWidget(button, "StatNameButton"); - if (button) - { - mAttributeNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); - } - - button = 0; - assignWidget(button, "StatValueButton"); - if (button) - { - mAttributeValueWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); - } -} - -/* MWSpell */ - -MWSpell::MWSpell() - : mWindowManager(NULL) - , mSpellNameWidget(NULL) -{ -} - -void MWSpell::setSpellId(const std::string &spellId) -{ - mId = spellId; - updateWidgets(); -} - -void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags) -{ - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Spell *spell = store.get().search(mId); - MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); - - MWSpellEffectPtr effect = NULL; - std::vector::const_iterator end = spell->mEffects.mList.end(); - 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; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mIsConstant = (flags & MWEffectList::EF_Constant); - params.mNoTarget = (flags & MWEffectList::EF_NoTarget); - effect->setSpellEffect(params); - effects.push_back(effect); - coord.top += effect->getHeight(); - coord.width = std::max(coord.width, effect->getRequestedWidth()); - } -} - -void MWSpell::updateWidgets() -{ - if (mSpellNameWidget && mWindowManager) - { - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Spell *spell = store.get().search(mId); - if (spell) - static_cast(mSpellNameWidget)->setCaption(spell->mName); - else - static_cast(mSpellNameWidget)->setCaption(""); - } -} - -void MWSpell::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mSpellNameWidget, "StatName"); -} - -MWSpell::~MWSpell() -{ -} - -/* MWEffectList */ - -MWEffectList::MWEffectList() - : mWindowManager(NULL) - , mEffectList(0) -{ -} - -void MWEffectList::setEffectList(const SpellEffectList& list) -{ - mEffectList = list; - updateWidgets(); -} - -void MWEffectList::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags) -{ - // We don't know the width of all the elements beforehand, so we do it in - // 2 steps: first, create all widgets and check their width.... - MWSpellEffectPtr effect = NULL; - int maxwidth = coord.width; - - for (SpellEffectList::iterator it=mEffectList.begin(); - 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); - effects.push_back(effect); - if (effect->getRequestedWidth() > maxwidth) - maxwidth = effect->getRequestedWidth(); - - coord.top += effect->getHeight(); - } - - // ... then adjust the size for all widgets - for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) - { - effect = static_cast(*it); - bool needcenter = center && (maxwidth > effect->getRequestedWidth()); - int diff = maxwidth - effect->getRequestedWidth(); - if (needcenter) - { - effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); - } - else - { - effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); - } - } - - // inform the parent about width - coord.width = maxwidth; -} - -void MWEffectList::updateWidgets() -{ -} - -void MWEffectList::initialiseOverride() -{ - Base::initialiseOverride(); -} - -MWEffectList::~MWEffectList() -{ -} - -SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) -{ - SpellEffectList result; - std::vector::const_iterator end = effects->mList.end(); - for (std::vector::const_iterator it = effects->mList.begin(); it != end; ++it) - { - SpellEffectParams params; - params.mEffectID = it->mEffectID; - params.mSkill = it->mSkill; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mArea = it->mArea; - result.push_back(params); - } - return result; -} - -/* MWSpellEffect */ - -MWSpellEffect::MWSpellEffect() - : mWindowManager(NULL) - , mImageWidget(NULL) - , mTextWidget(NULL) - , mRequestedWidth(0) -{ -} - -void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) -{ - mEffectParams = params; - updateWidgets(); -} - -void MWSpellEffect::updateWidgets() -{ - if (!mEffectParams.mKnown) - { - mTextWidget->setCaption ("?"); - mRequestedWidth = mTextWidget->getTextSize().width + 24; - mImageWidget->setImageTexture (""); - return; - } - - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::MagicEffect *magicEffect = - store.get().search(mEffectParams.mEffectID); - - 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 effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); - std::string spellLine = mWindowManager->getGameSettingString(effectIDStr, ""); - - if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) - { - spellLine += " " + mWindowManager->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); - } - if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) - { - spellLine += " " + mWindowManager->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); - } - - if ((mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) - { - if (mEffectParams.mMagnMin == mEffectParams.mMagnMax) - spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + " " + ((mEffectParams.mMagnMin == 1) ? pt : pts); - else - { - spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + to + boost::lexical_cast(mEffectParams.mMagnMax) + " " + pts; - } - } - - // constant effects have no duration and no target - if (!mEffectParams.mIsConstant) - { - if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) - { - spellLine += " " + mWindowManager->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); + int offset = path.rfind("."); + if (offset < 0) + return; + path.replace(offset, path.length() - offset, ".dds"); } - if (mEffectParams.mArea > 0) + /* MWSkill */ + + MWSkill::MWSkill() + : mSkillId(ESM::Skill::Length) + , mSkillNameWidget(NULL) + , mSkillValueWidget(NULL) { - spellLine += " #{sin} " + boost::lexical_cast(mEffectParams.mArea) + " #{sfootarea}"; } - // potions have no target - if (!mEffectParams.mNoTarget) + void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) { - std::string on = mWindowManager->getGameSettingString("sonword", ""); - if (mEffectParams.mRange == ESM::RT_Self) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeSelf", ""); - else if (mEffectParams.mRange == ESM::RT_Touch) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeTouch", ""); - else if (mEffectParams.mRange == ESM::RT_Target) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeTarget", ""); + mSkillId = skill; + updateWidgets(); } - } - static_cast(mTextWidget)->setCaptionWithReplacing(spellLine); - mRequestedWidth = mTextWidget->getTextSize().width + 24; - - std::string path = std::string("icons\\") + magicEffect->mIcon; - fixTexturePath(path); - mImageWidget->setImageTexture(path); -} - -MWSpellEffect::~MWSpellEffect() -{ -} - -void MWSpellEffect::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mTextWidget, "Text"); - assignWidget(mImageWidget, "Image"); -} - -/* MWDynamicStat */ - -MWDynamicStat::MWDynamicStat() -: mValue(0) -, mMax(1) -, mTextWidget(NULL) -, mBarWidget(NULL) -, mBarTextWidget(NULL) -{ -} - -void MWDynamicStat::setValue(int cur, int max) -{ - mValue = cur; - mMax = max; - - if (mBarWidget) - { - mBarWidget->setProgressRange(mMax); - mBarWidget->setProgressPosition(mValue); - } - - - if (mBarTextWidget) - { - if (mValue >= 0 && mMax > 0) + void MWSkill::setSkillNumber(int skill) { - std::stringstream out; - out << mValue << "/" << mMax; - static_cast(mBarTextWidget)->setCaption(out.str().c_str()); + if (skill < 0) + setSkillId(ESM::Skill::Length); + else if (skill < ESM::Skill::Length) + setSkillId(static_cast(skill)); + else + throw new std::runtime_error("Skill number out of range"); } - else - static_cast(mBarTextWidget)->setCaption(""); - } -} -void MWDynamicStat::setTitle(const std::string& text) -{ - if (mTextWidget) - static_cast(mTextWidget)->setCaption(text); -} -MWDynamicStat::~MWDynamicStat() -{ -} - -void MWDynamicStat::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mTextWidget, "Text"); - assignWidget(mBarWidget, "Bar"); - assignWidget(mBarTextWidget, "BarText"); -} - - - - -// --------------------------------------------------------------------------------------------------------------------- - -void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) -{ - if (w->getParent () != 0) - { - Box* b = dynamic_cast(w->getParent()); - if (b) - b->notifyChildrenSizeChanged (); - else + void MWSkill::setSkillValue(const SkillValue& value) { - if (mExpandDirection == MyGUI::Align::Left) + mValue = value; + updateWidgets(); + } + + void MWSkill::updateWidgets() + { + if (mSkillNameWidget) { - int hdiff = getRequestedSize ().width - w->getSize().width; - w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); + if (mSkillId == ESM::Skill::Length) + { + static_cast(mSkillNameWidget)->setCaption(""); + } + else + { + const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); + static_cast(mSkillNameWidget)->setCaption(name); + } + } + if (mSkillValueWidget) + { + SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); + static_cast(mSkillValueWidget)->setCaption(boost::lexical_cast(modified)); + if (modified > base) + mSkillValueWidget->_setWidgetState("increased"); + else if (modified < base) + mSkillValueWidget->_setWidgetState("decreased"); + else + mSkillValueWidget->_setWidgetState("normal"); } - w->setSize(getRequestedSize ()); } - } -} - -MyGUI::IntSize AutoSizedTextBox::getRequestedSize() -{ - return getTextSize(); -} - -void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) -{ - TextBox::setCaption(_value); - - notifySizeChange (this); -} - -void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "ExpandDirection") - { - mExpandDirection = MyGUI::Align::parse (_value); - } - else - { - TextBox::setPropertyOverride (_key, _value); - } -} - - -MyGUI::IntSize AutoSizedButton::getRequestedSize() -{ - MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); - size.height = std::max(24, size.height); - return size; -} - -void AutoSizedButton::setCaption(const MyGUI::UString& _value) -{ - Button::setCaption(_value); - - notifySizeChange (this); -} - -void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "ExpandDirection") - { - mExpandDirection = MyGUI::Align::parse (_value); - } - else - { - Button::setPropertyOverride (_key, _value); - } -} - -Box::Box() - : mSpacing(4) - , mPadding(0) - , mAutoResize(false) -{ - -} - -void Box::notifyChildrenSizeChanged () -{ - align(); -} - -void Box::_setPropertyImpl(const std::string& _key, const std::string& _value) -{ - if (_key == "Spacing") - mSpacing = MyGUI::utility::parseValue(_value); - else if (_key == "Padding") - mPadding = MyGUI::utility::parseValue(_value); - else if (_key == "AutoResize") - mAutoResize = MyGUI::utility::parseValue(_value); -} - -void HBox::align () -{ - unsigned int count = getChildCount (); - size_t h_stretched_count = 0; - int total_width = 0; - int total_height = 0; - std::vector< std::pair > sizes; - - for (unsigned int i = 0; i < count; ++i) - { - MyGUI::Widget* w = getChildAt(i); - bool hstretch = w->getUserString ("HStretch") == "true"; - h_stretched_count += hstretch; - AutoSizedWidget* aw = dynamic_cast(w); - if (aw) + void MWSkill::onClicked(MyGUI::Widget* _sender) { - sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); - total_width += aw->getRequestedSize ().width; - total_height = std::max(total_height, aw->getRequestedSize ().height); + eventClicked(this); } - else + + MWSkill::~MWSkill() { - sizes.push_back (std::make_pair(w->getSize(), hstretch)); - total_width += w->getSize().width; } - if (i != count-1) - total_width += mSpacing; - } - - if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) - { - setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); - return; - } - - - int curX = 0; - for (unsigned int i = 0; i < count; ++i) - { - if (i == 0) - curX += mPadding; - - MyGUI::Widget* w = getChildAt(i); - - bool vstretch = w->getUserString ("VStretch") == "true"; - int height = vstretch ? total_height : sizes[i].first.height; - - MyGUI::IntCoord widgetCoord; - widgetCoord.left = curX; - widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; - int width = sizes[i].second ? sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count - : sizes[i].first.width; - widgetCoord.width = width; - widgetCoord.height = height; - w->setCoord(widgetCoord); - curX += width; - - if (i != count-1) - curX += mSpacing; - } -} - -void HBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - Box::_setPropertyImpl (_key, _value); -} - -void HBox::setSize (const MyGUI::IntSize& _value) -{ - MyGUI::Widget::setSize (_value); - align(); -} - -void HBox::setCoord (const MyGUI::IntCoord& _value) -{ - MyGUI::Widget::setCoord (_value); - align(); -} - -void HBox::onWidgetCreated(MyGUI::Widget* _widget) -{ - align(); -} - -void HBox::onWidgetDestroy(MyGUI::Widget* _widget) -{ - align(); -} - -MyGUI::IntSize HBox::getRequestedSize () -{ - MyGUI::IntSize size(0,0); - for (unsigned int i = 0; i < getChildCount (); ++i) - { - AutoSizedWidget* w = dynamic_cast(getChildAt(i)); - if (w) + void MWSkill::initialiseOverride() { - MyGUI::IntSize requested = w->getRequestedSize (); - size.height = std::max(size.height, requested.height); - size.width = size.width + requested.width; - if (i != getChildCount()-1) - size.width += mSpacing; + Base::initialiseOverride(); + + assignWidget(mSkillNameWidget, "StatName"); + assignWidget(mSkillValueWidget, "StatValue"); + + MyGUI::Button* button; + assignWidget(button, "StatNameButton"); + if (button) + { + mSkillNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); + } + + button = 0; + assignWidget(button, "StatValueButton"); + if (button) + { + mSkillNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); + } } - else + + /* MWAttribute */ + + MWAttribute::MWAttribute() + : mId(-1) + , mAttributeNameWidget(NULL) + , mAttributeValueWidget(NULL) { - MyGUI::IntSize requested = getChildAt(i)->getSize (); - size.height = std::max(size.height, requested.height); - - if (getChildAt(i)->getUserString("HStretch") != "true") - size.width = size.width + requested.width; - - if (i != getChildCount()-1) - size.width += mSpacing; } - size.height += mPadding*2; - size.width += mPadding*2; - } - return size; -} - - - -void VBox::align () -{ - unsigned int count = getChildCount (); - size_t v_stretched_count = 0; - int total_height = 0; - int total_width = 0; - std::vector< std::pair > sizes; - for (unsigned int i = 0; i < count; ++i) - { - MyGUI::Widget* w = getChildAt(i); - bool vstretch = w->getUserString ("VStretch") == "true"; - v_stretched_count += vstretch; - AutoSizedWidget* aw = dynamic_cast(w); - if (aw) + void MWAttribute::setAttributeId(int attributeId) { - sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); - total_height += aw->getRequestedSize ().height; - total_width = std::max(total_width, aw->getRequestedSize ().width); + mId = attributeId; + updateWidgets(); } - else + + void MWAttribute::setAttributeValue(const AttributeValue& value) { - sizes.push_back (std::make_pair(w->getSize(), vstretch)); - total_height += w->getSize().height; + mValue = value; + updateWidgets(); } - if (i != count-1) - total_height += mSpacing; - } + void MWAttribute::onClicked(MyGUI::Widget* _sender) + { + eventClicked(this); + } - if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) - { - setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); - return; - } + void MWAttribute::updateWidgets() + { + if (mAttributeNameWidget) + { + if (mId < 0 || mId >= 8) + { + static_cast(mAttributeNameWidget)->setCaption(""); + } + else + { + static const char *attributes[8] = { + "sAttributeStrength", + "sAttributeIntelligence", + "sAttributeWillpower", + "sAttributeAgility", + "sAttributeSpeed", + "sAttributeEndurance", + "sAttributePersonality", + "sAttributeLuck" + }; + const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(attributes[mId], ""); + static_cast(mAttributeNameWidget)->setCaption(name); + } + } + if (mAttributeValueWidget) + { + AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); + static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); + if (modified > base) + mAttributeValueWidget->_setWidgetState("increased"); + else if (modified < base) + mAttributeValueWidget->_setWidgetState("decreased"); + else + mAttributeValueWidget->_setWidgetState("normal"); + } + } + + MWAttribute::~MWAttribute() + { + } + + void MWAttribute::initialiseOverride() + { + Base::initialiseOverride(); + + assignWidget(mAttributeNameWidget, "StatName"); + assignWidget(mAttributeValueWidget, "StatValue"); + + MyGUI::Button* button; + assignWidget(button, "StatNameButton"); + if (button) + { + mAttributeNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); + } + + button = 0; + assignWidget(button, "StatValueButton"); + if (button) + { + mAttributeValueWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); + } + } + + /* MWSpell */ + + MWSpell::MWSpell() + : mSpellNameWidget(NULL) + { + } + + void MWSpell::setSpellId(const std::string &spellId) + { + mId = spellId; + updateWidgets(); + } + + void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Spell *spell = store.get().search(mId); + MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); + + MWSpellEffectPtr effect = NULL; + std::vector::const_iterator end = spell->mEffects.mList.end(); + for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) + { + effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); + SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mIsConstant = (flags & MWEffectList::EF_Constant); + params.mNoTarget = (flags & MWEffectList::EF_NoTarget); + effect->setSpellEffect(params); + effects.push_back(effect); + coord.top += effect->getHeight(); + coord.width = std::max(coord.width, effect->getRequestedWidth()); + } + } + + void MWSpell::updateWidgets() + { + if (mSpellNameWidget && MWBase::Environment::get().getWindowManager()) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Spell *spell = store.get().search(mId); + if (spell) + static_cast(mSpellNameWidget)->setCaption(spell->mName); + else + static_cast(mSpellNameWidget)->setCaption(""); + } + } + + void MWSpell::initialiseOverride() + { + Base::initialiseOverride(); + + assignWidget(mSpellNameWidget, "StatName"); + } + + MWSpell::~MWSpell() + { + } + + /* MWEffectList */ + + MWEffectList::MWEffectList() + : mEffectList(0) + { + } + + void MWEffectList::setEffectList(const SpellEffectList& list) + { + mEffectList = list; + updateWidgets(); + } + + void MWEffectList::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags) + { + // We don't know the width of all the elements beforehand, so we do it in + // 2 steps: first, create all widgets and check their width.... + MWSpellEffectPtr effect = NULL; + int maxwidth = coord.width; + + for (SpellEffectList::iterator it=mEffectList.begin(); + it != mEffectList.end(); ++it) + { + effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); + it->mIsConstant = (flags & EF_Constant) || it->mIsConstant; + it->mNoTarget = (flags & EF_NoTarget) || it->mNoTarget; + effect->setSpellEffect(*it); + effects.push_back(effect); + if (effect->getRequestedWidth() > maxwidth) + maxwidth = effect->getRequestedWidth(); + + coord.top += effect->getHeight(); + } + + // ... then adjust the size for all widgets + for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) + { + effect = static_cast(*it); + bool needcenter = center && (maxwidth > effect->getRequestedWidth()); + int diff = maxwidth - effect->getRequestedWidth(); + if (needcenter) + { + effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); + } + else + { + effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); + } + } + + // inform the parent about width + coord.width = maxwidth; + } + + void MWEffectList::updateWidgets() + { + } + + void MWEffectList::initialiseOverride() + { + Base::initialiseOverride(); + } + + MWEffectList::~MWEffectList() + { + } + + SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) + { + SpellEffectList result; + std::vector::const_iterator end = effects->mList.end(); + for (std::vector::const_iterator it = effects->mList.begin(); it != end; ++it) + { + SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mArea = it->mArea; + result.push_back(params); + } + return result; + } + + /* MWSpellEffect */ + + MWSpellEffect::MWSpellEffect() + : mImageWidget(NULL) + , mTextWidget(NULL) + , mRequestedWidth(0) + { + } + + void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) + { + mEffectParams = params; + updateWidgets(); + } + + void MWSpellEffect::updateWidgets() + { + if (!mEffectParams.mKnown) + { + mTextWidget->setCaption ("?"); + mRequestedWidth = mTextWidget->getTextSize().width + 24; + mImageWidget->setImageTexture (""); + return; + } + + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::MagicEffect *magicEffect = + store.get().search(mEffectParams.mEffectID); + + assert(magicEffect); + + 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 = MWBase::Environment::get().getWindowManager()->getGameSettingString(effectIDStr, ""); + + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); + } + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); + } + + if ((mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + { + if (mEffectParams.mMagnMin == mEffectParams.mMagnMax) + spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + " " + ((mEffectParams.mMagnMin == 1) ? pt : pts); + else + { + spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + to + boost::lexical_cast(mEffectParams.mMagnMax) + " " + pts; + } + } + + // constant effects have no duration and no target + if (!mEffectParams.mIsConstant) + { + if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); + } + + if (mEffectParams.mArea > 0) + { + spellLine += " #{sin} " + boost::lexical_cast(mEffectParams.mArea) + " #{sfootarea}"; + } + + // potions have no target + if (!mEffectParams.mNoTarget) + { + std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sonword", ""); + if (mEffectParams.mRange == ESM::RT_Self) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeSelf", ""); + else if (mEffectParams.mRange == ESM::RT_Touch) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTouch", ""); + else if (mEffectParams.mRange == ESM::RT_Target) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTarget", ""); + } + } + + static_cast(mTextWidget)->setCaptionWithReplacing(spellLine); + mRequestedWidth = mTextWidget->getTextSize().width + 24; + + std::string path = std::string("icons\\") + magicEffect->mIcon; + fixTexturePath(path); + mImageWidget->setImageTexture(path); + } + + MWSpellEffect::~MWSpellEffect() + { + } + + void MWSpellEffect::initialiseOverride() + { + Base::initialiseOverride(); + + assignWidget(mTextWidget, "Text"); + assignWidget(mImageWidget, "Image"); + } + + /* MWDynamicStat */ + + MWDynamicStat::MWDynamicStat() + : mValue(0) + , mMax(1) + , mTextWidget(NULL) + , mBarWidget(NULL) + , mBarTextWidget(NULL) + { + } + + void MWDynamicStat::setValue(int cur, int max) + { + mValue = cur; + mMax = max; + + if (mBarWidget) + { + mBarWidget->setProgressRange(mMax); + mBarWidget->setProgressPosition(mValue); + } - int curY = 0; - for (unsigned int i = 0; i < count; ++i) - { - if (i==0) - curY += mPadding; + if (mBarTextWidget) + { + if (mValue >= 0 && mMax > 0) + { + std::stringstream out; + out << mValue << "/" << mMax; + static_cast(mBarTextWidget)->setCaption(out.str().c_str()); + } + else + static_cast(mBarTextWidget)->setCaption(""); + } + } + void MWDynamicStat::setTitle(const std::string& text) + { + if (mTextWidget) + static_cast(mTextWidget)->setCaption(text); + } - MyGUI::Widget* w = getChildAt(i); + MWDynamicStat::~MWDynamicStat() + { + } - bool hstretch = w->getUserString ("HStretch") == "true"; - int width = hstretch ? total_width : sizes[i].first.width; + void MWDynamicStat::initialiseOverride() + { + Base::initialiseOverride(); - MyGUI::IntCoord widgetCoord; - widgetCoord.top = curY; - widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; - int height = sizes[i].second ? sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count - : sizes[i].first.height; - widgetCoord.height = height; - widgetCoord.width = width; - w->setCoord(widgetCoord); - curY += height; + assignWidget(mTextWidget, "Text"); + assignWidget(mBarWidget, "Bar"); + assignWidget(mBarTextWidget, "BarText"); + } - if (i != count-1) - curY += mSpacing; + + + + // --------------------------------------------------------------------------------------------------------------------- + + void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) + { + if (w->getParent () != 0) + { + Box* b = dynamic_cast(w->getParent()); + if (b) + b->notifyChildrenSizeChanged (); + else + { + if (mExpandDirection == MyGUI::Align::Left) + { + int hdiff = getRequestedSize ().width - w->getSize().width; + w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); + } + w->setSize(getRequestedSize ()); + } + } + } + + + MyGUI::IntSize AutoSizedTextBox::getRequestedSize() + { + return getTextSize(); + } + + void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) + { + TextBox::setCaption(_value); + + notifySizeChange (this); + } + + void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + TextBox::setPropertyOverride (_key, _value); + } + } + + MyGUI::IntSize AutoSizedEditBox::getRequestedSize() + { + if (getAlign().isHStretch()) + throw std::runtime_error("AutoSizedEditBox can't have HStretch align (" + getName() + ")"); + return MyGUI::IntSize(getSize().width, getTextSize().height); + } + + void AutoSizedEditBox::setCaption(const MyGUI::UString& _value) + { + EditBox::setCaption(_value); + + notifySizeChange (this); + } + + void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + EditBox::setPropertyOverride (_key, _value); + } + } + + + MyGUI::IntSize AutoSizedButton::getRequestedSize() + { + MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); + size.height = std::max(24, size.height); + return size; + } + + void AutoSizedButton::setCaption(const MyGUI::UString& _value) + { + Button::setCaption(_value); + + notifySizeChange (this); + } + + void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + Button::setPropertyOverride (_key, _value); + } + } + + Box::Box() + : mSpacing(4) + , mPadding(0) + , mAutoResize(false) + { + + } + + void Box::notifyChildrenSizeChanged () + { + align(); + } + + void Box::_setPropertyImpl(const std::string& _key, const std::string& _value) + { + if (_key == "Spacing") + mSpacing = MyGUI::utility::parseValue(_value); + else if (_key == "Padding") + mPadding = MyGUI::utility::parseValue(_value); + else if (_key == "AutoResize") + mAutoResize = MyGUI::utility::parseValue(_value); + } + + void HBox::align () + { + unsigned int count = getChildCount (); + size_t h_stretched_count = 0; + int total_width = 0; + int total_height = 0; + std::vector< std::pair > sizes; + + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + bool hstretch = w->getUserString ("HStretch") == "true"; + h_stretched_count += hstretch; + AutoSizedWidget* aw = dynamic_cast(w); + if (aw) + { + sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); + total_width += aw->getRequestedSize ().width; + total_height = std::max(total_height, aw->getRequestedSize ().height); + } + else + { + sizes.push_back (std::make_pair(w->getSize(), hstretch)); + total_width += w->getSize().width; + if (!(w->getUserString("VStretch") == "true")) + total_height = std::max(total_height, w->getSize().height); + } + + if (i != count-1) + total_width += mSpacing; + } + + if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) + { + setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); + return; + } + + + int curX = 0; + for (unsigned int i = 0; i < count; ++i) + { + if (i == 0) + curX += mPadding; + + MyGUI::Widget* w = getChildAt(i); + + bool vstretch = w->getUserString ("VStretch") == "true"; + int height = vstretch ? total_height : sizes[i].first.height; + + MyGUI::IntCoord widgetCoord; + widgetCoord.left = curX; + widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; + int width = sizes[i].second ? sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count + : sizes[i].first.width; + widgetCoord.width = width; + widgetCoord.height = height; + w->setCoord(widgetCoord); + curX += width; + + if (i != count-1) + curX += mSpacing; + } + } + + void HBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + Box::_setPropertyImpl (_key, _value); + } + + void HBox::setSize (const MyGUI::IntSize& _value) + { + MyGUI::Widget::setSize (_value); + align(); + } + + void HBox::setCoord (const MyGUI::IntCoord& _value) + { + MyGUI::Widget::setCoord (_value); + align(); + } + + void HBox::onWidgetCreated(MyGUI::Widget* _widget) + { + align(); + } + + MyGUI::IntSize HBox::getRequestedSize () + { + MyGUI::IntSize size(0,0); + for (unsigned int i = 0; i < getChildCount (); ++i) + { + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); + if (w) + { + MyGUI::IntSize requested = w->getRequestedSize (); + size.height = std::max(size.height, requested.height); + size.width = size.width + requested.width; + if (i != getChildCount()-1) + size.width += mSpacing; + } + else + { + MyGUI::IntSize requested = getChildAt(i)->getSize (); + size.height = std::max(size.height, requested.height); + + if (getChildAt(i)->getUserString("HStretch") != "true") + size.width = size.width + requested.width; + + if (i != getChildCount()-1) + size.width += mSpacing; + } + size.height += mPadding*2; + size.width += mPadding*2; + } + return size; + } + + + + + void VBox::align () + { + unsigned int count = getChildCount (); + size_t v_stretched_count = 0; + int total_height = 0; + int total_width = 0; + std::vector< std::pair > sizes; + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + bool vstretch = w->getUserString ("VStretch") == "true"; + v_stretched_count += vstretch; + AutoSizedWidget* aw = dynamic_cast(w); + if (aw) + { + sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); + total_height += aw->getRequestedSize ().height; + total_width = std::max(total_width, aw->getRequestedSize ().width); + } + else + { + sizes.push_back (std::make_pair(w->getSize(), vstretch)); + total_height += w->getSize().height; + + if (!(w->getUserString("HStretch") == "true")) + total_width = std::max(total_width, w->getSize().width); + } + + if (i != count-1) + total_height += mSpacing; + } + + if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) + { + setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); + return; + } + + + int curY = 0; + for (unsigned int i = 0; i < count; ++i) + { + if (i==0) + curY += mPadding; + + MyGUI::Widget* w = getChildAt(i); + + bool hstretch = w->getUserString ("HStretch") == "true"; + int width = hstretch ? total_width : sizes[i].first.width; + + MyGUI::IntCoord widgetCoord; + widgetCoord.top = curY; + widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; + int height = sizes[i].second ? sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count + : sizes[i].first.height; + widgetCoord.height = height; + widgetCoord.width = width; + w->setCoord(widgetCoord); + curY += height; + + if (i != count-1) + curY += mSpacing; + } + } + + void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + Box::_setPropertyImpl (_key, _value); + } + + void VBox::setSize (const MyGUI::IntSize& _value) + { + MyGUI::Widget::setSize (_value); + align(); + } + + void VBox::setCoord (const MyGUI::IntCoord& _value) + { + MyGUI::Widget::setCoord (_value); + align(); + } + + MyGUI::IntSize VBox::getRequestedSize () + { + MyGUI::IntSize size(0,0); + for (unsigned int i = 0; i < getChildCount (); ++i) + { + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); + if (w) + { + MyGUI::IntSize requested = w->getRequestedSize (); + size.width = std::max(size.width, requested.width); + size.height = size.height + requested.height; + if (i != getChildCount()-1) + size.height += mSpacing; + } + else + { + MyGUI::IntSize requested = getChildAt(i)->getSize (); + size.width = std::max(size.width, requested.width); + + if (getChildAt(i)->getUserString("VStretch") != "true") + size.height = size.height + requested.height; + + if (i != getChildCount()-1) + size.height += mSpacing; + } + size.height += mPadding*2; + size.width += mPadding*2; + } + return size; + } + + void VBox::onWidgetCreated(MyGUI::Widget* _widget) + { + align(); + } } } - -void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - Box::_setPropertyImpl (_key, _value); -} - -void VBox::setSize (const MyGUI::IntSize& _value) -{ - MyGUI::Widget::setSize (_value); - align(); -} - -void VBox::setCoord (const MyGUI::IntCoord& _value) -{ - MyGUI::Widget::setCoord (_value); - align(); -} - -MyGUI::IntSize VBox::getRequestedSize () -{ - MyGUI::IntSize size(0,0); - for (unsigned int i = 0; i < getChildCount (); ++i) - { - AutoSizedWidget* w = dynamic_cast(getChildAt(i)); - if (w) - { - MyGUI::IntSize requested = w->getRequestedSize (); - size.width = std::max(size.width, requested.width); - size.height = size.height + requested.height; - if (i != getChildCount()-1) - size.height += mSpacing; - } - else - { - MyGUI::IntSize requested = getChildAt(i)->getSize (); - size.width = std::max(size.width, requested.width); - - if (getChildAt(i)->getUserString("VStretch") != "true") - size.height = size.height + requested.height; - - if (i != getChildCount()-1) - size.height += mSpacing; - } - size.height += mPadding*2; - size.width += mPadding*2; - } - return size; -} - -void VBox::onWidgetCreated(MyGUI::Widget* _widget) -{ - align(); -} - -void VBox::onWidgetDestroy(MyGUI::Widget* _widget) -{ - align(); -} diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 597bcbe324..1567946913 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -4,9 +4,8 @@ #include "../mwworld/esmstore.hpp" #include "../mwmechanics/stat.hpp" -#include -#include #include +#include namespace MyGUI { @@ -93,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; } @@ -121,7 +118,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager *mManager; ESM::Skill::SkillEnum mSkillId; SkillValue mValue; MyGUI::Widget* mSkillNameWidget; @@ -137,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; } @@ -164,7 +158,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager *mManager; int mId; AttributeValue mValue; MyGUI::Widget* mAttributeNameWidget; @@ -184,7 +177,6 @@ namespace MWGui typedef MWMechanics::Stat SpellValue; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellId(const std::string &id); /** @@ -206,7 +198,6 @@ namespace MWGui private: void updateWidgets(); - MWBase::WindowManager* mWindowManager; std::string mId; MyGUI::TextBox* mSpellNameWidget; }; @@ -226,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); @@ -248,7 +238,6 @@ namespace MWGui private: void updateWidgets(); - MWBase::WindowManager* mWindowManager; SpellEffectList mEffectList; }; typedef MWEffectList* MWEffectListPtr; @@ -261,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; } @@ -275,7 +263,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager* mWindowManager; SpellEffectParams mEffectParams; MyGUI::ImageBox* mImageWidget; MyGUI::TextBox* mTextWidget; @@ -340,6 +327,18 @@ namespace MWGui virtual void setPropertyOverride(const std::string& _key, const std::string& _value); }; + class AutoSizedEditBox : public AutoSizedWidget, public MyGUI::EditBox + { + MYGUI_RTTI_DERIVED( AutoSizedEditBox ) + + public: + virtual MyGUI::IntSize getRequestedSize(); + virtual void setCaption(const MyGUI::UString& _value); + + protected: + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + }; + class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button { MYGUI_RTTI_DERIVED( AutoSizedButton ) @@ -390,7 +389,6 @@ namespace MWGui virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void onWidgetCreated(MyGUI::Widget* _widget); - virtual void onWidgetDestroy(MyGUI::Widget* _widget); }; class VBox : public Box, public MyGUI::Widget @@ -408,7 +406,6 @@ namespace MWGui virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void onWidgetCreated(MyGUI::Widget* _widget); - virtual void onWidgetDestroy(MyGUI::Widget* _widget); }; } } diff --git a/apps/openmw/mwgui/window_pinnable_base.cpp b/apps/openmw/mwgui/window_pinnable_base.cpp deleted file mode 100644 index 651b3a1e98..0000000000 --- a/apps/openmw/mwgui/window_pinnable_base.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "window_pinnable_base.hpp" - -#include "../mwbase/windowmanager.hpp" - -#include "exposedwindow.hpp" - -using namespace MWGui; - -WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) - : WindowBase(parLayout, parWindowManager), mPinned(false), mVisible(false) -{ - ExposedWindow* window = static_cast(mMainWidget); - mPinButton = window->getSkinWidget ("Button"); - - mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked); -} - -void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender) -{ - mPinned = !mPinned; - - if (mPinned) - mPinButton->changeWidgetSkin ("PinDown"); - else - mPinButton->changeWidgetSkin ("PinUp"); - - onPinToggled(); -} diff --git a/apps/openmw/mwgui/window_base.cpp b/apps/openmw/mwgui/windowbase.cpp similarity index 77% rename from apps/openmw/mwgui/window_base.cpp rename to apps/openmw/mwgui/windowbase.cpp index 38bee9ea30..cc74579abf 100644 --- a/apps/openmw/mwgui/window_base.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -1,14 +1,11 @@ -#include "window_base.hpp" - -#include +#include "windowbase.hpp" #include "../mwbase/windowmanager.hpp" using namespace MWGui; -WindowBase::WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) +WindowBase::WindowBase(const std::string& parLayout) : Layout(parLayout) - , mWindowManager(parWindowManager) { } @@ -39,8 +36,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 0e4c3a6082..48c5aba6ba 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1,39 +1,22 @@ #include "windowmanagerimp.hpp" -#include -#include - -#include - #include #include -#include -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/inputmanager.hpp" -#include "../mwworld/ptr.hpp" -#include "../mwworld/cellstore.hpp" +#include "../mwworld/class.hpp" #include "console.hpp" #include "journalwindow.hpp" +#include "journalviewmodel.hpp" #include "charactercreation.hpp" -#include "text_input.hpp" -#include "review.hpp" #include "dialogue.hpp" -#include "dialogue_history.hpp" -#include "map_window.hpp" -#include "stats_window.hpp" +#include "statswindow.hpp" #include "messagebox.hpp" -#include "container.hpp" -#include "inventorywindow.hpp" #include "tooltips.hpp" #include "scrollwindow.hpp" #include "bookwindow.hpp" -#include "list.hpp" #include "hud.hpp" #include "mainmenu.hpp" #include "countdialog.hpp" @@ -48,1088 +31,1190 @@ #include "loadingscreen.hpp" #include "levelupdialog.hpp" #include "waitdialog.hpp" -#include "spellcreationdialog.hpp" #include "enchantingdialog.hpp" #include "trainingwindow.hpp" -#include "imagebutton.hpp" #include "exposedwindow.hpp" #include "cursor.hpp" -#include "spellicons.hpp" +#include "merchantrepair.hpp" +#include "repair.hpp" +#include "soulgemdialog.hpp" +#include "companionwindow.hpp" +#include "inventorywindow.hpp" +#include "bookpage.hpp" +#include "itemview.hpp" -using namespace MWGui; - -WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *ogre, - const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage) - : mGuiManager(NULL) - , mRendering(ogre) - , mHud(NULL) - , mMap(NULL) - , mMenu(NULL) - , mStatsWindow(NULL) - , mToolTips(NULL) - , mMessageBoxManager(NULL) - , mConsole(NULL) - , mJournal(NULL) - , mDialogueWindow(NULL) - , mBookWindow(NULL) - , mScrollWindow(NULL) - , mCountDialog(NULL) - , mTradeWindow(NULL) - , mSpellBuyingWindow(NULL) - , mTravelWindow(NULL) - , mSettingsWindow(NULL) - , mConfirmationDialog(NULL) - , mAlchemyWindow(NULL) - , mSpellWindow(NULL) - , mLoadingScreen(NULL) - , mCharGen(NULL) - , mLevelupDialog(NULL) - , mWaitDialog(NULL) - , mSpellCreationDialog(NULL) - , mEnchantingDialog(NULL) - , mTrainingWindow(NULL) - , mPlayerName() - , mPlayerRaceId() - , mPlayerAttributes() - , mPlayerMajorSkills() - , mPlayerMinorSkills() - , mPlayerSkillValues() - , mPlayerHealth() - , mPlayerMagicka() - , mPlayerFatigue() - , mGui(NULL) - , mGarbageDialogs() - , mShown(GW_ALL) - , mAllowed(newGame ? GW_None : GW_ALL) - , mRestAllowed(newGame ? false : true) - , mShowFPSLevel(fpsLevel) - , mFPS(0.0f) - , mTriangleCount(0) - , mBatchCount(0) - , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) - , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) - , mHudEnabled(true) - , mTranslationDataStorage (translationDataStorage) +namespace MWGui { - // Set up the GUI system - mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); - mGui = mGuiManager->getGui(); - //Register own widgets with MyGUI - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - - MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); - MyGUI::ResourceManager::getInstance().load("core.xml"); - - MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); - - // Get size info from the Gui object - assert(mGui); - int w = MyGUI::RenderManager::getInstance().getViewSize().width; - int h = MyGUI::RenderManager::getInstance().getViewSize().height; - - MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget"); - dragAndDropWidget->setVisible(false); - - mDragAndDrop = new DragAndDrop(); - mDragAndDrop->mIsOnDragAndDrop = false; - mDragAndDrop->mDraggedWidget = 0; - mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - - mMenu = new MainMenu(w,h); - mMap = new MapWindow(*this, cacheDir); - mStatsWindow = new StatsWindow(*this); - 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); - 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); - - mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow (), *this); - mLoadingScreen->onResChange (w,h); - - mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); - - mCursor = new Cursor(); - - mHud->setVisible(mHudEnabled); - - mCharGen = new CharacterCreation(this); - - // Setup player stats - for (int i = 0; i < ESM::Attribute::Length; ++i) + WindowManager::WindowManager( + const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre, + const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, + Translation::Storage& translationDataStorage) + : mGuiManager(NULL) + , mRendering(ogre) + , mHud(NULL) + , mMap(NULL) + , mMenu(NULL) + , mStatsWindow(NULL) + , mToolTips(NULL) + , mMessageBoxManager(NULL) + , mConsole(NULL) + , mJournal(NULL) + , mDialogueWindow(NULL) + , mBookWindow(NULL) + , mScrollWindow(NULL) + , mCountDialog(NULL) + , mTradeWindow(NULL) + , mSpellBuyingWindow(NULL) + , mTravelWindow(NULL) + , mSettingsWindow(NULL) + , mConfirmationDialog(NULL) + , mAlchemyWindow(NULL) + , mSpellWindow(NULL) + , mLoadingScreen(NULL) + , mCharGen(NULL) + , mLevelupDialog(NULL) + , mWaitDialog(NULL) + , mSpellCreationDialog(NULL) + , mEnchantingDialog(NULL) + , mTrainingWindow(NULL) + , mMerchantRepair(NULL) + , mRepair(NULL) + , mSoulgemDialog(NULL) + , mCompanionWindow(NULL) + , mPlayerName() + , mPlayerRaceId() + , mPlayerAttributes() + , mPlayerMajorSkills() + , mPlayerMinorSkills() + , mPlayerSkillValues() + , mPlayerHealth() + , mPlayerMagicka() + , mPlayerFatigue() + , mGui(NULL) + , mGarbageDialogs() + , mShown(GW_ALL) + , mAllowed(GW_ALL) + , mRestAllowed(true) + , mShowFPSLevel(fpsLevel) + , mFPS(0.0f) + , mTriangleCount(0) + , mBatchCount(0) + , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) + , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) + , mHudEnabled(true) + , mTranslationDataStorage (translationDataStorage) { - mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); - } + // Set up the GUI system + mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); + mGui = mGuiManager->getGui(); - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); - } + //Register own widgets with MyGUI + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + BookPage::registerMyGUIComponents (); + ItemView::registerComponents(); - unsetSelectedSpell(); - unsetSelectedWeapon(); + MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); + MyGUI::ResourceManager::getInstance().load("core.xml"); - if (newGame) - disallowAll (); + MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); - // Set up visibility - updateVisible(); -} + // Get size info from the Gui object + assert(mGui); + int w = MyGUI::RenderManager::getInstance().getViewSize().width; + int h = MyGUI::RenderManager::getInstance().getViewSize().height; -WindowManager::~WindowManager() -{ - delete mConsole; - delete mMessageBoxManager; - delete mHud; - delete mMap; - delete mMenu; - delete mStatsWindow; - delete mJournal; - delete mDialogueWindow; - delete mContainerWindow; - delete mInventoryWindow; - delete mToolTips; - delete mCharGen; - delete mDragAndDrop; - delete mBookWindow; - delete mScrollWindow; - delete mTradeWindow; - delete mSpellBuyingWindow; - delete mTravelWindow; - delete mSettingsWindow; - delete mConfirmationDialog; - delete mAlchemyWindow; - delete mSpellWindow; - delete mLoadingScreen; - delete mLevelupDialog; - delete mWaitDialog; - delete mSpellCreationDialog; - delete mEnchantingDialog; - delete mTrainingWindow; - delete mCountDialog; - delete mQuickKeysMenu; - delete mCursor; + MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget"); + dragAndDropWidget->setVisible(false); - cleanupGarbage(); + mDragAndDrop = new DragAndDrop(); + mDragAndDrop->mIsOnDragAndDrop = false; + mDragAndDrop->mDraggedWidget = 0; + mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - delete mGuiManager; -} + mMenu = new MainMenu(w,h); + mMap = new MapWindow(cacheDir); + mStatsWindow = new StatsWindow(); + mConsole = new Console(w,h, consoleOnlyScripts); + mJournal = JournalWindow::create(JournalViewModel::create ()); + 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(); + 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(mDragAndDrop, mMessageBoxManager); -void WindowManager::cleanupGarbage() -{ - // Delete any dialogs which are no longer in use - if (!mGarbageDialogs.empty()) - { - for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) + mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ()); + mLoadingScreen->onResChange (w,h); + + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + + mCursor = new Cursor(); + + mHud->setVisible(mHudEnabled); + + mCharGen = new CharacterCreation(); + + // Setup player stats + for (int i = 0; i < ESM::Attribute::Length; ++i) { - delete *it; + mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); } - mGarbageDialogs.clear(); - } -} -void WindowManager::update() -{ - cleanupGarbage(); - - mHud->setFPS(mFPS); - mHud->setTriangleCount(mTriangleCount); - mHud->setBatchCount(mBatchCount); - - mHud->update(); - - mCursor->update(); -} - -void WindowManager::updateVisible() -{ - // Start out by hiding everything except the HUD - mMap->setVisible(false); - mMenu->setVisible(false); - mStatsWindow->setVisible(false); - mConsole->disable(); - mJournal->setVisible(false); - mDialogueWindow->setVisible(false); - mContainerWindow->setVisible(false); - mInventoryWindow->setVisible(false); - mScrollWindow->setVisible(false); - mBookWindow->setVisible(false); - mTradeWindow->setVisible(false); - mSpellBuyingWindow->setVisible(false); - mTravelWindow->setVisible(false); - mSettingsWindow->setVisible(false); - mAlchemyWindow->setVisible(false); - mSpellWindow->setVisible(false); - mQuickKeysMenu->setVisible(false); - mLevelupDialog->setVisible(false); - mWaitDialog->setVisible(false); - mSpellCreationDialog->setVisible(false); - mEnchantingDialog->setVisible(false); - mTrainingWindow->setVisible(false); - - mHud->setVisible(mHudEnabled); - - // Mouse is visible whenever we're not in game mode - mCursor->setVisible(isGuiMode()); - - bool gameMode = !isGuiMode(); - - mInputBlocker->setVisible (gameMode); - - if (gameMode) - mToolTips->enterGameMode(); - else - mToolTips->enterGuiMode(); - - if (gameMode) - MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); - - setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); - setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); - setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); - setHMSVisibility((mAllowed & GW_Stats) && !mStatsWindow->pinned()); - - // If in game mode, show only the pinned windows - if (gameMode) - { - mMap->setVisible(mMap->pinned()); - mStatsWindow->setVisible(mStatsWindow->pinned()); - mInventoryWindow->setVisible(mInventoryWindow->pinned()); - mSpellWindow->setVisible(mSpellWindow->pinned()); - - return; - } - - GuiMode mode = mGuiModes.back(); - - switch(mode) { - case GM_QuickKeysMenu: - mQuickKeysMenu->setVisible (true); - break; - case GM_MainMenu: - mMenu->setVisible(true); - break; - case GM_Settings: - mSettingsWindow->setVisible(true); - break; - case GM_Console: - // Show the pinned windows - mMap->setVisible(mMap->pinned()); - mStatsWindow->setVisible(mStatsWindow->pinned()); - mInventoryWindow->setVisible(mInventoryWindow->pinned()); - mSpellWindow->setVisible(mSpellWindow->pinned()); - - mConsole->enable(); - break; - case GM_Scroll: - mScrollWindow->setVisible(true); - break; - case GM_Book: - mBookWindow->setVisible(true); - break; - case GM_Alchemy: - mAlchemyWindow->setVisible(true); - break; - case GM_Rest: - mWaitDialog->setVisible(true); - break; - case GM_RestBed: - mWaitDialog->setVisible(true); - mWaitDialog->bedActivated(); - break; - case GM_Levelup: - mLevelupDialog->setVisible(true); - break; - case GM_Name: - case GM_Race: - case GM_Class: - case GM_ClassPick: - case GM_ClassCreate: - case GM_Birth: - case GM_ClassGenerate: - case GM_Review: - mCharGen->spawnDialog(mode); - break; - case GM_Inventory: + for (int i = 0; i < ESM::Skill::Length; ++i) { - // First, compute the effective set of windows to show. - // This is controlled both by what windows the - // user has opened/closed (the 'shown' variable) and by what - // windows we are allowed to show (the 'allowed' var.) - int eff = mShown & mAllowed; - - // Show the windows we want - mMap ->setVisible(eff & GW_Map); - mStatsWindow ->setVisible(eff & GW_Stats); - mInventoryWindow->setVisible(eff & GW_Inventory); - mSpellWindow ->setVisible(eff & GW_Magic); - break; + mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); } - case GM_Container: - mContainerWindow->setVisible(true); - mInventoryWindow->setVisible(true); - break; - case GM_Dialogue: - mDialogueWindow->setVisible(true); - break; - case GM_Barter: - mInventoryWindow->setVisible(true); - mTradeWindow->setVisible(true); - break; - case GM_SpellBuying: - mSpellBuyingWindow->setVisible(true); - break; - case GM_Travel: - mTravelWindow->setVisible(true); - break; - case GM_SpellCreation: - mSpellCreationDialog->setVisible(true); - break; - case GM_Enchanting: - mEnchantingDialog->setVisible(true); - break; - case GM_Training: - mTrainingWindow->setVisible(true); - break; - case GM_InterMessageBox: - break; - case GM_Journal: - mJournal->setVisible(true); - break; - case GM_LoadingWallpaper: - mHud->setVisible(false); - mCursor->setVisible(false); - break; - case GM_Loading: - // Show the pinned windows - mMap->setVisible(mMap->pinned()); - mStatsWindow->setVisible(mStatsWindow->pinned()); - mInventoryWindow->setVisible(mInventoryWindow->pinned()); - mSpellWindow->setVisible(mSpellWindow->pinned()); - mCursor->setVisible(false); - break; - case GM_Video: - mCursor->setVisible(false); - mHud->setVisible(false); - break; - default: - // Unsupported mode, switch back to game - break; + unsetSelectedSpell(); + unsetSelectedWeapon(); + + // Set up visibility + updateVisible(); } -} -void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - mStatsWindow->setValue (id, value); - mCharGen->setValue(id, value); - - static const char *ids[] = + void WindowManager::setNewGame(bool newgame) { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8" - }; - static ESM::Attribute::AttributeID attributes[] = - { - ESM::Attribute::Strength, - ESM::Attribute::Intelligence, - ESM::Attribute::Willpower, - ESM::Attribute::Agility, - ESM::Attribute::Speed, - ESM::Attribute::Endurance, - ESM::Attribute::Personality, - ESM::Attribute::Luck - }; - for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) - { - if (id != ids[i]) - continue; - mPlayerAttributes[attributes[i]] = value; - break; - } -} - - -void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) -{ - /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we - /// allow custom skills. - mStatsWindow->setValue(static_cast (parSkill), value); - mCharGen->setValue(static_cast (parSkill), value); - mPlayerSkillValues[parSkill] = value; -} - -void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - mStatsWindow->setValue (id, value); - mHud->setValue (id, value); - mCharGen->setValue(id, value); - if (id == "HBar") - { - mPlayerHealth = value; - mCharGen->setPlayerHealth (value); - } - else if (id == "MBar") - { - mPlayerMagicka = value; - mCharGen->setPlayerMagicka (value); - } - else if (id == "FBar") - { - mPlayerFatigue = value; - mCharGen->setPlayerFatigue (value); - } -} - -#if 0 -MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) -{ - if(id == "HBar") - return layerHealth; - else if (id == "MBar") - return mPlayerMagicka; - else if (id == "FBar") - return mPlayerFatigue; -} -#endif - -void WindowManager::setValue (const std::string& id, const std::string& value) -{ - mStatsWindow->setValue (id, value); - if (id=="name") - mPlayerName = value; - else if (id=="race") - mPlayerRaceId = value; -} - -void WindowManager::setValue (const std::string& id, int value) -{ - mStatsWindow->setValue (id, value); -} - -void WindowManager::setPlayerClass (const ESM::Class &class_) -{ - mStatsWindow->setValue("class", class_.mName); -} - -void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) -{ - mStatsWindow->configureSkills (major, minor); - mCharGen->configureSkills(major, minor); - mPlayerMajorSkills = major; - mPlayerMinorSkills = minor; -} - -void WindowManager::setReputation (int reputation) -{ - mStatsWindow->setReputation (reputation); -} - -void WindowManager::setBounty (int bounty) -{ - mStatsWindow->setBounty (bounty); -} - -void WindowManager::updateSkillArea() -{ - mStatsWindow->updateSkillArea(); -} - -void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) -{ - if (!dialog) - return; - dialog->setVisible(false); - mGarbageDialogs.push_back(dialog); -} - -void WindowManager::messageBox (const std::string& message, const std::vector& buttons) -{ - if(buttons.empty()){ - /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ - if(!mGuiModes.empty() && mGuiModes.back() == GM_Dialogue) - mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); + if (newgame) + { + disallowAll(); + delete mCharGen; + mCharGen = new CharacterCreation(); + mGuiModes.clear(); + } else - mMessageBoxManager->createMessageBox(message); + allow(GW_ALL); + + mRestAllowed = !newgame; } - else + WindowManager::~WindowManager() { - mMessageBoxManager->createInteractiveMessageBox(message, buttons); - pushGuiMode(GM_InterMessageBox); + delete mConsole; + delete mMessageBoxManager; + delete mHud; + delete mMap; + delete mMenu; + delete mStatsWindow; + delete mJournal; + delete mDialogueWindow; + delete mContainerWindow; + delete mInventoryWindow; + delete mToolTips; + delete mCharGen; + delete mDragAndDrop; + delete mBookWindow; + delete mScrollWindow; + delete mTradeWindow; + delete mSpellBuyingWindow; + delete mTravelWindow; + delete mSettingsWindow; + delete mConfirmationDialog; + delete mAlchemyWindow; + delete mSpellWindow; + delete mLoadingScreen; + delete mLevelupDialog; + delete mWaitDialog; + delete mSpellCreationDialog; + delete mEnchantingDialog; + delete mTrainingWindow; + delete mCountDialog; + delete mQuickKeysMenu; + delete mMerchantRepair; + delete mRepair; + delete mSoulgemDialog; + delete mCursor; + + cleanupGarbage(); + + delete mGuiManager; } -} -void WindowManager::enterPressed () -{ - mMessageBoxManager->enterPressed(); -} - -int WindowManager::readPressedButton () -{ - return mMessageBoxManager->readPressedButton(); -} - -std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_) -{ - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().search(id); - - if (setting && setting->mValue.getType()==ESM::VT_String) - return setting->mValue.getString(); - - return default_; -} - -void WindowManager::onDialogueWindowBye() -{ - if (mDialogueWindow) + void WindowManager::cleanupGarbage() { - //FIXME set some state and stuff? - //removeDialog(dialogueWindow); + // Delete any dialogs which are no longer in use + if (!mGarbageDialogs.empty()) + { + for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) + { + delete *it; + } + mGarbageDialogs.clear(); + } + } + + void WindowManager::update() + { + cleanupGarbage(); + + mHud->setFPS(mFPS); + mHud->setTriangleCount(mTriangleCount); + mHud->setBatchCount(mBatchCount); + + mHud->update(); + + mCursor->update(); + } + + void WindowManager::updateVisible() + { + // Start out by hiding everything except the HUD + mMap->setVisible(false); + mMenu->setVisible(false); + mStatsWindow->setVisible(false); + mConsole->disable(); + mJournal->setVisible(false); mDialogueWindow->setVisible(false); - } - removeGuiMode(GM_Dialogue); -} + mContainerWindow->setVisible(false); + mInventoryWindow->setVisible(false); + mScrollWindow->setVisible(false); + mBookWindow->setVisible(false); + mTradeWindow->setVisible(false); + mSpellBuyingWindow->setVisible(false); + mTravelWindow->setVisible(false); + mSettingsWindow->setVisible(false); + mAlchemyWindow->setVisible(false); + mSpellWindow->setVisible(false); + mQuickKeysMenu->setVisible(false); + mLevelupDialog->setVisible(false); + mWaitDialog->setVisible(false); + mSpellCreationDialog->setVisible(false); + mEnchantingDialog->setVisible(false); + mTrainingWindow->setVisible(false); + mMerchantRepair->setVisible(false); + mRepair->setVisible(false); + mCompanionWindow->setVisible(false); + mInventoryWindow->setTrading(false); -void WindowManager::onFrame (float frameDuration) -{ - mMessageBoxManager->onFrame(frameDuration); - mToolTips->onFrame(frameDuration); + mHud->setVisible(mHudEnabled); - if (mDragAndDrop->mIsOnDragAndDrop) - { - assert(mDragAndDrop->mDraggedWidget); - mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); - } + bool gameMode = !isGuiMode(); - mDialogueWindow->onFrame(); + mInputBlocker->setVisible (gameMode); - mInventoryWindow->onFrame(); - - mStatsWindow->onFrame(); - - mWaitDialog->onFrame(frameDuration); - - mHud->onFrame(frameDuration); - - mTrainingWindow->onFrame (frameDuration); - mTradeWindow->onFrame(frameDuration); - - mTrainingWindow->checkReferenceAvailable(); - mDialogueWindow->checkReferenceAvailable(); - mTradeWindow->checkReferenceAvailable(); - mSpellBuyingWindow->checkReferenceAvailable(); - mSpellCreationDialog->checkReferenceAvailable(); - mEnchantingDialog->checkReferenceAvailable(); - mContainerWindow->checkReferenceAvailable(); - mConsole->checkReferenceAvailable(); -} - -void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) -{ - if (cell->mCell->isExterior()) - { - std::string name; - if (cell->mCell->mName != "") - { - name = cell->mCell->mName; - mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); - } + if (gameMode) + mToolTips->enterGameMode(); else + mToolTips->enterGuiMode(); + + if (gameMode) + MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); + + setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); + setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); + setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); + setHMSVisibility((mAllowed & GW_Stats) && !mStatsWindow->pinned()); + + // If in game mode, show only the pinned windows + if (gameMode) { - const ESM::Region* region = - MWBase::Environment::get().getWorld()->getStore().get().search(cell->mCell->mRegion); - if (region) - name = region->mName; - else - name = getGameSettingString("sDefaultCellname", "Wilderness"); + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + return; } - mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY()); + GuiMode mode = mGuiModes.back(); - mMap->setCellName( name ); - mHud->setCellName( name ); + switch(mode) { + case GM_QuickKeysMenu: + mQuickKeysMenu->setVisible (true); + break; + case GM_MainMenu: + mMenu->setVisible(true); + break; + case GM_Settings: + mSettingsWindow->setVisible(true); + break; + case GM_Console: + // Show the pinned windows + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); - mMap->setCellPrefix("Cell"); - mHud->setCellPrefix("Cell"); - mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); - mHud->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); + mConsole->enable(); + break; + case GM_Scroll: + mScrollWindow->setVisible(true); + break; + case GM_Book: + mBookWindow->setVisible(true); + break; + case GM_Alchemy: + mAlchemyWindow->setVisible(true); + break; + case GM_Rest: + mWaitDialog->setVisible(true); + break; + case GM_RestBed: + mWaitDialog->setVisible(true); + mWaitDialog->bedActivated(); + break; + case GM_Levelup: + mLevelupDialog->setVisible(true); + break; + case GM_Name: + case GM_Race: + case GM_Class: + case GM_ClassPick: + case GM_ClassCreate: + case GM_Birth: + case GM_ClassGenerate: + case GM_Review: + mCharGen->spawnDialog(mode); + break; + case GM_Inventory: + { + // First, compute the effective set of windows to show. + // This is controlled both by what windows the + // user has opened/closed (the 'shown' variable) and by what + // windows we are allowed to show (the 'allowed' var.) + int eff = mShown & mAllowed; + + // Show the windows we want + mMap ->setVisible(eff & GW_Map); + mStatsWindow ->setVisible(eff & GW_Stats); + mInventoryWindow->setVisible(eff & GW_Inventory); + mSpellWindow ->setVisible(eff & GW_Magic); + break; + } + case GM_Container: + mContainerWindow->setVisible(true); + mInventoryWindow->setVisible(true); + break; + case GM_Companion: + mCompanionWindow->setVisible(true); + mInventoryWindow->setVisible(true); + break; + case GM_Dialogue: + mDialogueWindow->setVisible(true); + break; + case GM_Barter: + mInventoryWindow->setVisible(true); + mInventoryWindow->setTrading(true); + mTradeWindow->setVisible(true); + break; + case GM_SpellBuying: + mSpellBuyingWindow->setVisible(true); + break; + case GM_Travel: + mTravelWindow->setVisible(true); + break; + case GM_SpellCreation: + mSpellCreationDialog->setVisible(true); + break; + case GM_Enchanting: + mEnchantingDialog->setVisible(true); + break; + case GM_Training: + mTrainingWindow->setVisible(true); + break; + case GM_MerchantRepair: + mMerchantRepair->setVisible(true); + break; + case GM_Repair: + mRepair->setVisible(true); + break; + case GM_Journal: + mJournal->setVisible(true); + break; + case GM_LoadingWallpaper: + mHud->setVisible(false); + mCursor->setVisible(false); + break; + case GM_Loading: + // Show the pinned windows + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + mCursor->setVisible(false); + break; + case GM_Video: + mCursor->setVisible(false); + mHud->setVisible(false); + break; + default: + // Unsupported mode, switch back to game + break; + } } - else + + void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) { - mMap->setCellName( cell->mCell->mName ); - mHud->setCellName( cell->mCell->mName ); - mMap->setCellPrefix( cell->mCell->mName ); - mHud->setCellPrefix( cell->mCell->mName ); + mStatsWindow->setValue (id, value); + mCharGen->setValue(id, value); + + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8" + }; + static ESM::Attribute::AttributeID attributes[] = + { + ESM::Attribute::Strength, + ESM::Attribute::Intelligence, + ESM::Attribute::Willpower, + ESM::Attribute::Agility, + ESM::Attribute::Speed, + ESM::Attribute::Endurance, + ESM::Attribute::Personality, + ESM::Attribute::Luck + }; + for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) + { + if (id != ids[i]) + continue; + mPlayerAttributes[attributes[i]] = value; + break; + } } -} -void WindowManager::setInteriorMapTexture(const int x, const int y) -{ - mMap->setActiveCell(x,y, true); - mHud->setActiveCell(x,y, true); -} - -void WindowManager::setPlayerPos(const float x, const float y) -{ - mMap->setPlayerPos(x,y); - mHud->setPlayerPos(x,y); -} - -void WindowManager::setPlayerDir(const float x, const float y) -{ - mMap->setPlayerDir(x,y); - mHud->setPlayerDir(x,y); -} - -void WindowManager::setHMSVisibility(bool visible) -{ - mHud->setHmsVisible (visible); -} - -void WindowManager::setMinimapVisibility(bool visible) -{ - mHud->setMinimapVisible (visible); -} - -void WindowManager::toggleFogOfWar() -{ - mMap->toggleFogOfWar(); - mHud->toggleFogOfWar(); -} - -void WindowManager::setFocusObject(const MWWorld::Ptr& focus) -{ - mToolTips->setFocusObject(focus); -} - -void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) -{ - mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); -} - -void WindowManager::toggleFullHelp() -{ - mToolTips->toggleFullHelp(); -} - -bool WindowManager::getFullHelp() const -{ - return mToolTips->getFullHelp(); -} - -void WindowManager::setWeaponVisibility(bool visible) -{ - mHud->setWeapVisible (visible); -} - -void WindowManager::setSpellVisibility(bool visible) -{ - mHud->setSpellVisible (visible); - mHud->setEffectVisible (visible); -} - -void WindowManager::setMouseVisible(bool visible) -{ - mCursor->setVisible(visible); -} - -void WindowManager::setDragDrop(bool dragDrop) -{ - mToolTips->setEnabled(!dragDrop); - MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); -} - -void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) -{ - std::string tag(_tag); - - std::string tokenToFind = "sCell="; - size_t tokenLength = tokenToFind.length(); - - if (tag.substr(0, tokenLength) == tokenToFind) + void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) { - _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); + /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we + /// allow custom skills. + mStatsWindow->setValue(static_cast (parSkill), value); + mCharGen->setValue(static_cast (parSkill), value); + mPlayerSkillValues[parSkill] = value; } - else + + void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + mStatsWindow->setValue (id, value); + mHud->setValue (id, value); + mCharGen->setValue(id, value); + if (id == "HBar") + { + mPlayerHealth = value; + mCharGen->setPlayerHealth (value); + } + else if (id == "MBar") + { + mPlayerMagicka = value; + mCharGen->setPlayerMagicka (value); + } + else if (id == "FBar") + { + mPlayerFatigue = value; + mCharGen->setPlayerFatigue (value); + } + } + + #if 0 + MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) + { + if(id == "HBar") + return layerHealth; + else if (id == "MBar") + return mPlayerMagicka; + else if (id == "FBar") + return mPlayerFatigue; + } + #endif + + void WindowManager::setValue (const std::string& id, const std::string& value) + { + mStatsWindow->setValue (id, value); + if (id=="name") + mPlayerName = value; + else if (id=="race") + mPlayerRaceId = value; + } + + void WindowManager::setValue (const std::string& id, int value) + { + mStatsWindow->setValue (id, value); + } + + void WindowManager::setPlayerClass (const ESM::Class &class_) + { + mStatsWindow->setValue("class", class_.mName); + } + + void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) + { + mStatsWindow->configureSkills (major, minor); + mCharGen->configureSkills(major, minor); + mPlayerMajorSkills = major; + mPlayerMinorSkills = minor; + } + + void WindowManager::setReputation (int reputation) + { + mStatsWindow->setReputation (reputation); + } + + void WindowManager::setBounty (int bounty) + { + mStatsWindow->setBounty (bounty); + } + + void WindowManager::updateSkillArea() + { + mStatsWindow->updateSkillArea(); + } + + void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) + { + if (!dialog) + return; + dialog->setVisible(false); + mGarbageDialogs.push_back(dialog); + } + + void WindowManager::messageBox (const std::string& message, const std::vector& buttons, bool showInDialogueModeOnly) + { + if (buttons.empty()) { + /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ + if (getMode() == GM_Dialogue) { + mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); + } else { + if (showInDialogueModeOnly) { + if (getMode() == GM_Dialogue) + mMessageBoxManager->createMessageBox(message); + } else { + mMessageBoxManager->createMessageBox(message); + } + } + } else { + mMessageBoxManager->createInteractiveMessageBox(message, buttons); + MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); + } + } + + void WindowManager::staticMessageBox(const std::string& message) + { + mMessageBoxManager->createMessageBox(message, true); + } + + void WindowManager::removeStaticMessageBox() + { + mMessageBoxManager->removeStaticMessageBox(); + } + + void WindowManager::enterPressed () + { + mMessageBoxManager->enterPressed(); + } + + int WindowManager::readPressedButton () + { + return mMessageBoxManager->readPressedButton(); + } + + std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_) { const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().find(tag); + MWBase::Environment::get().getWorld()->getStore().get().search(id); if (setting && setting->mValue.getType()==ESM::VT_String) - _result = setting->mValue.getString(); - else - _result = tag; + return setting->mValue.getString(); + + return default_; } -} -void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) -{ - mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); - mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); - - bool changeRes = false; - bool windowRecreated = false; - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); - it != changed.end(); ++it) + void WindowManager::onFrame (float frameDuration) { - if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y")) + mMessageBoxManager->onFrame(frameDuration); + + mToolTips->onFrame(frameDuration); + + if (mDragAndDrop->mIsOnDragAndDrop) { - changeRes = true; + assert(mDragAndDrop->mDraggedWidget); + mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); } - else if (it->first == "Video" && it->second == "vsync") - windowRecreated = true; - else if (it->first == "HUD" && it->second == "crosshair") - mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); - else if (it->first == "GUI" && it->second == "subtitles") - mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); + + mDialogueWindow->onFrame(); + + mInventoryWindow->onFrame(); + + mStatsWindow->onFrame(); + + mWaitDialog->onFrame(frameDuration); + + mHud->onFrame(frameDuration); + + mTrainingWindow->onFrame (frameDuration); + mTradeWindow->onFrame(frameDuration); + + mTrainingWindow->checkReferenceAvailable(); + mDialogueWindow->checkReferenceAvailable(); + mTradeWindow->checkReferenceAvailable(); + mSpellBuyingWindow->checkReferenceAvailable(); + mSpellCreationDialog->checkReferenceAvailable(); + mEnchantingDialog->checkReferenceAvailable(); + mContainerWindow->checkReferenceAvailable(); + mCompanionWindow->checkReferenceAvailable(); + mConsole->checkReferenceAvailable(); + mCompanionWindow->onFrame(); } - if (changeRes) + void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) { - int x = Settings::Manager::getInt("resolution x", "Video"); - int y = Settings::Manager::getInt("resolution y", "Video"); - mHud->onResChange(x, y); - mConsole->onResChange(x, y); - mMenu->onResChange(x, y); - mSettingsWindow->center(); - mAlchemyWindow->center(); - mScrollWindow->center(); - mBookWindow->center(); - mQuickKeysMenu->center(); - mSpellBuyingWindow->center(); - mLoadingScreen->onResChange (x,y); - mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); - mInputBlocker->setSize(MyGUI::IntSize(x,y)); - } - if (windowRecreated) - { - mGuiManager->updateWindow (mRendering->getWindow ()); - mLoadingScreen->updateWindow (mRendering->getWindow ()); - } -} + if (cell->mCell->isExterior()) + { + std::string name; + if (cell->mCell->mName != "") + { + name = cell->mCell->mName; + mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); + } + else + { + const ESM::Region* region = + MWBase::Environment::get().getWorld()->getStore().get().search(cell->mCell->mRegion); + if (region) + name = region->mName; + else + name = getGameSettingString("sDefaultCellname", "Wilderness"); + } -void WindowManager::pushGuiMode(GuiMode mode) -{ - if (mode==GM_Inventory && mAllowed==GW_None) - return; + mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY()); + mMap->setCellName( name ); + mHud->setCellName( name ); - // If this mode already exists somewhere in the stack, just bring it to the front. - if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) - { - mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); - } - - mGuiModes.push_back(mode); - - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); - - updateVisible(); -} - -void WindowManager::popGuiMode() -{ - if (!mGuiModes.empty()) - mGuiModes.pop_back(); - - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); - - updateVisible(); -} - -void WindowManager::removeGuiMode(GuiMode mode) -{ - std::vector::iterator it = mGuiModes.begin(); - while (it != mGuiModes.end()) - { - if (*it == mode) - it = mGuiModes.erase(it); + mMap->setCellPrefix("Cell"); + mHud->setCellPrefix("Cell"); + mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); + mHud->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); + } else - ++it; + { + mMap->setCellName( cell->mCell->mName ); + mHud->setCellName( cell->mCell->mName ); + mMap->setCellPrefix( cell->mCell->mName ); + mHud->setCellPrefix( cell->mCell->mName ); + } + } - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); - - updateVisible(); -} - -void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) -{ - mHud->setSelectedSpell(spellId, successChancePercent); - - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - - mSpellWindow->setTitle(spell->mName); -} - -void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) -{ - mHud->setSelectedEnchantItem(item, chargePercent); - mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); -} - -void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) -{ - mHud->setSelectedWeapon(item, durabilityPercent); - mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); -} - -void WindowManager::unsetSelectedSpell() -{ - mHud->unsetSelectedSpell(); - mSpellWindow->setTitle("#{sNone}"); -} - -void WindowManager::unsetSelectedWeapon() -{ - mHud->unsetSelectedWeapon(); - mInventoryWindow->setTitle("#{sSkillHandtohand}"); -} - -void WindowManager::getMousePosition(int &x, int &y) -{ - const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); - x = pos.left; - y = pos.top; -} - -void WindowManager::getMousePosition(float &x, float &y) -{ - const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); - x = pos.left; - y = pos.top; - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - x /= viewSize.width; - y /= viewSize.height; -} - -bool WindowManager::getWorldMouseOver() -{ - return mHud->getWorldMouseOver(); -} - -void WindowManager::executeInConsole (const std::string& path) -{ - mConsole->executeFile (path); -} - -void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) -{ - mFPS = fps; - mTriangleCount = triangleCount; - mBatchCount = batchCount; -} - -MyGUI::Gui* WindowManager::getGui() const { return mGui; } - -MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } -MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } -MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } -MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } -MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } -MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } -MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } -MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } -MWGui::SpellBuyingWindow* WindowManager::getSpellBuyingWindow() { return mSpellBuyingWindow; } -MWGui::TravelWindow* WindowManager::getTravelWindow() { return mTravelWindow; } -MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } -MWGui::Console* WindowManager::getConsole() { return mConsole; } - -bool WindowManager::isAllowed (GuiWindow wnd) const -{ - return mAllowed & wnd; -} - -void WindowManager::allow (GuiWindow wnd) -{ - mAllowed = (GuiWindow)(mAllowed | wnd); - - if (wnd & GW_Inventory) + void WindowManager::setInteriorMapTexture(const int x, const int y) { - mBookWindow->setInventoryAllowed (true); - mScrollWindow->setInventoryAllowed (true); + mMap->setActiveCell(x,y, true); + mHud->setActiveCell(x,y, true); + } + + void WindowManager::setPlayerPos(const float x, const float y) + { + mMap->setPlayerPos(x,y); + mHud->setPlayerPos(x,y); + } + + void WindowManager::setPlayerDir(const float x, const float y) + { + mMap->setPlayerDir(x,y); + mHud->setPlayerDir(x,y); + } + + void WindowManager::setHMSVisibility(bool visible) + { + mHud->setHmsVisible (visible); + } + + void WindowManager::setMinimapVisibility(bool visible) + { + mHud->setMinimapVisible (visible); + } + + void WindowManager::toggleFogOfWar() + { + mMap->toggleFogOfWar(); + mHud->toggleFogOfWar(); + } + + void WindowManager::setFocusObject(const MWWorld::Ptr& focus) + { + mToolTips->setFocusObject(focus); + } + + void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) + { + mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); + } + + void WindowManager::toggleFullHelp() + { + mToolTips->toggleFullHelp(); + } + + bool WindowManager::getFullHelp() const + { + return mToolTips->getFullHelp(); + } + + void WindowManager::setWeaponVisibility(bool visible) + { + mHud->setWeapVisible (visible); + } + + void WindowManager::setSpellVisibility(bool visible) + { + mHud->setSpellVisible (visible); + mHud->setEffectVisible (visible); + } + + void WindowManager::setMouseVisible(bool visible) + { + mCursor->setVisible(visible); + } + + void WindowManager::setDragDrop(bool dragDrop) + { + mToolTips->setEnabled(!dragDrop); + MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); + } + + void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) + { + std::string tag(_tag); + + std::string tokenToFind = "sCell="; + size_t tokenLength = tokenToFind.length(); + + if (tag.substr(0, tokenLength) == tokenToFind) + { + _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); + } + else + { + const ESM::GameSetting *setting = + MWBase::Environment::get().getWorld()->getStore().get().find(tag); + + if (setting && setting->mValue.getType()==ESM::VT_String) + _result = setting->mValue.getString(); + else + _result = tag; + } + } + + void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); + mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); + + bool changeRes = false; + bool windowRecreated = false; + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); + it != changed.end(); ++it) + { + if (it->first == "Video" && ( + it->second == "resolution x" + || it->second == "resolution y")) + { + changeRes = true; + } + else if (it->first == "Video" && it->second == "vsync") + windowRecreated = true; + else if (it->first == "HUD" && it->second == "crosshair") + mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); + else if (it->first == "GUI" && it->second == "subtitles") + mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); + } + + if (changeRes) + { + int x = Settings::Manager::getInt("resolution x", "Video"); + int y = Settings::Manager::getInt("resolution y", "Video"); + mHud->onResChange(x, y); + mConsole->onResChange(x, y); + mMenu->onResChange(x, y); + mSettingsWindow->center(); + mAlchemyWindow->center(); + mScrollWindow->center(); + mBookWindow->center(); + mQuickKeysMenu->center(); + mSpellBuyingWindow->center(); + mLoadingScreen->onResChange (x,y); + mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); + mInputBlocker->setSize(MyGUI::IntSize(x,y)); + } + if (windowRecreated) + { + mGuiManager->updateWindow (mRendering->getWindow ()); + mLoadingScreen->updateWindow (mRendering->getWindow ()); + } + } + + void WindowManager::pushGuiMode(GuiMode mode) + { + if (mode==GM_Inventory && mAllowed==GW_None) + return; + + + // If this mode already exists somewhere in the stack, just bring it to the front. + if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) + { + mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); + } + + mGuiModes.push_back(mode); + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); + } + + void WindowManager::popGuiMode() + { + if (!mGuiModes.empty()) + mGuiModes.pop_back(); + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); + } + + void WindowManager::removeGuiMode(GuiMode mode) + { + std::vector::iterator it = mGuiModes.begin(); + while (it != mGuiModes.end()) + { + if (*it == mode) + it = mGuiModes.erase(it); + else + ++it; + } + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); + } + + void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) + { + mHud->setSelectedSpell(spellId, successChancePercent); + + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + + mSpellWindow->setTitle(spell->mName); + } + + 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 == -1) ? 100 + : (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 = (item.getCellRef().mCharge == -1) ? 100 + : (item.getCellRef().mCharge / static_cast(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100); + mHud->setSelectedWeapon(item, durabilityPercent); + mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); + } + + void WindowManager::unsetSelectedSpell() + { + mHud->unsetSelectedSpell(); + mSpellWindow->setTitle("#{sNone}"); + } + + void WindowManager::unsetSelectedWeapon() + { + mHud->unsetSelectedWeapon(); + mInventoryWindow->setTitle("#{sSkillHandtohand}"); + } + + void WindowManager::getMousePosition(int &x, int &y) + { + const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); + x = pos.left; + y = pos.top; + } + + void WindowManager::getMousePosition(float &x, float &y) + { + const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); + x = pos.left; + y = pos.top; + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + x /= viewSize.width; + y /= viewSize.height; + } + + bool WindowManager::getWorldMouseOver() + { + return mHud->getWorldMouseOver(); + } + + void WindowManager::executeInConsole (const std::string& path) + { + mConsole->executeFile (path); + } + + void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) + { + mFPS = fps; + mTriangleCount = triangleCount; + mBatchCount = batchCount; + } + + MyGUI::Gui* WindowManager::getGui() const { return mGui; } + + MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } + MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } + MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } + MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } + MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } + MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } + MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } + MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } + MWGui::SpellBuyingWindow* WindowManager::getSpellBuyingWindow() { return mSpellBuyingWindow; } + MWGui::TravelWindow* WindowManager::getTravelWindow() { return mTravelWindow; } + MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } + MWGui::Console* WindowManager::getConsole() { return mConsole; } + + bool WindowManager::isAllowed (GuiWindow wnd) const + { + return mAllowed & wnd; + } + + void WindowManager::allow (GuiWindow wnd) + { + mAllowed = (GuiWindow)(mAllowed | wnd); + + if (wnd & GW_Inventory) + { + mBookWindow->setInventoryAllowed (true); + mScrollWindow->setInventoryAllowed (true); + } + + updateVisible(); + } + + void WindowManager::disallowAll() + { + mAllowed = GW_None; + + mBookWindow->setInventoryAllowed (false); + mScrollWindow->setInventoryAllowed (false); + + updateVisible(); + } + + void WindowManager::toggleVisible (GuiWindow wnd) + { + mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); + updateVisible(); + } + + bool WindowManager::isGuiMode() const + { + return !mGuiModes.empty() || mMessageBoxManager->isInteractiveMessageBox(); + } + + bool WindowManager::isConsoleMode() const + { + if (!mGuiModes.empty() && mGuiModes.back()==GM_Console) + return true; + return false; + } + + MWGui::GuiMode WindowManager::getMode() const + { + if (mGuiModes.empty()) + return GM_None; + return mGuiModes.back(); + } + + std::map > WindowManager::getPlayerSkillValues() + { + return mPlayerSkillValues; + } + + std::map > WindowManager::getPlayerAttributeValues() + { + return mPlayerAttributes; + } + + WindowManager::SkillList WindowManager::getPlayerMinorSkills() + { + return mPlayerMinorSkills; + } + + WindowManager::SkillList WindowManager::getPlayerMajorSkills() + { + return mPlayerMajorSkills; + } + + void WindowManager::disallowMouse() + { + mInputBlocker->setVisible (true); + } + + void WindowManager::allowMouse() + { + mInputBlocker->setVisible (!isGuiMode ()); + } + + void WindowManager::notifyInputActionBound () + { + mSettingsWindow->updateControlsBox (); + allowMouse(); + } + + void WindowManager::showCrosshair (bool show) + { + mHud->setCrosshairVisible (show && mCrosshairEnabled); + } + + void WindowManager::activateQuickKey (int index) + { + mQuickKeysMenu->activateQuickKey(index); + } + + bool WindowManager::getSubtitlesEnabled () + { + return mSubtitlesEnabled; + } + + void WindowManager::toggleHud () + { + mHudEnabled = !mHudEnabled; + mHud->setVisible (mHudEnabled); + } + + void WindowManager::setLoadingProgress (const std::string& stage, int depth, int current, int total) + { + mLoadingScreen->setLoadingProgress (stage, depth, current, total); + } + + void WindowManager::loadingDone () + { + mLoadingScreen->loadingDone (); + } + bool WindowManager::getRestEnabled() + { + //Enable rest dialogue if character creation finished + if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalVariable ("chargenstate").mFloat==-1) + mRestAllowed=true; + return mRestAllowed; + } + + bool WindowManager::getPlayerSleeping () + { + return mWaitDialog->getSleeping(); + } + + void WindowManager::wakeUpPlayer() + { + mWaitDialog->wakeUp(); + } + + void WindowManager::addVisitedLocation(const std::string& name, int x, int y) + { + mMap->addVisitedLocation (name, x, y); + } + + void WindowManager::startSpellMaking(MWWorld::Ptr actor) + { + mSpellCreationDialog->startSpellMaking (actor); + } + + void WindowManager::startEnchanting (MWWorld::Ptr actor) + { + mEnchantingDialog->startEnchanting (actor); + } + + void WindowManager::startSelfEnchanting(MWWorld::Ptr soulgem) + { + mEnchantingDialog->startSelfEnchanting(soulgem); + } + + void WindowManager::startTraining(MWWorld::Ptr actor) + { + mTrainingWindow->startTraining(actor); + } + + void WindowManager::startRepair(MWWorld::Ptr actor) + { + mMerchantRepair->startRepair(actor); + } + + void WindowManager::startRepairItem(MWWorld::Ptr item) + { + mRepair->startRepairItem(item); + } + + const Translation::Storage& WindowManager::getTranslationDataStorage() const + { + return mTranslationDataStorage; + } + + void WindowManager::showCompanionWindow(MWWorld::Ptr actor) + { + mCompanionWindow->open(actor); + } + + void WindowManager::changePointer(const std::string &name) + { + mCursor->onCursorChange(name); + } + + void WindowManager::showSoulgemDialog(MWWorld::Ptr item) + { + mSoulgemDialog->show(item); + } + + void WindowManager::frameStarted (float dt) + { + mInventoryWindow->doRenderUpdate (); + } + + void WindowManager::updatePlayer() + { + mInventoryWindow->updatePlayer(); } - updateVisible(); -} - -void WindowManager::disallowAll() -{ - mAllowed = GW_None; - - mBookWindow->setInventoryAllowed (false); - mScrollWindow->setInventoryAllowed (false); - - updateVisible(); -} - -void WindowManager::toggleVisible (GuiWindow wnd) -{ - mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); - updateVisible(); -} - -bool WindowManager::isGuiMode() const -{ - return !mGuiModes.empty(); -} - -MWGui::GuiMode WindowManager::getMode() const -{ - if (mGuiModes.empty()) - throw std::runtime_error ("getMode() called, but there is no active mode"); - - return mGuiModes.back(); -} - -std::map > WindowManager::getPlayerSkillValues() -{ - return mPlayerSkillValues; -} - -std::map > WindowManager::getPlayerAttributeValues() -{ - return mPlayerAttributes; -} - -WindowManager::SkillList WindowManager::getPlayerMinorSkills() -{ - return mPlayerMinorSkills; -} - -WindowManager::SkillList WindowManager::getPlayerMajorSkills() -{ - return mPlayerMajorSkills; -} - -void WindowManager::disallowMouse() -{ - mInputBlocker->setVisible (true); -} - -void WindowManager::allowMouse() -{ - mInputBlocker->setVisible (!isGuiMode ()); -} - -void WindowManager::notifyInputActionBound () -{ - mSettingsWindow->updateControlsBox (); - allowMouse(); -} - -void WindowManager::showCrosshair (bool show) -{ - mHud->setCrosshairVisible (show && mCrosshairEnabled); -} - -void WindowManager::activateQuickKey (int index) -{ - mQuickKeysMenu->activateQuickKey(index); -} - -bool WindowManager::getSubtitlesEnabled () -{ - return mSubtitlesEnabled; -} - -void WindowManager::toggleHud () -{ - mHudEnabled = !mHudEnabled; - mHud->setVisible (mHudEnabled); -} - -void WindowManager::setLoadingProgress (const std::string& stage, int depth, int current, int total) -{ - mLoadingScreen->setLoadingProgress (stage, depth, current, total); -} - -void WindowManager::loadingDone () -{ - mLoadingScreen->loadingDone (); -} - -bool WindowManager::getPlayerSleeping () -{ - return mWaitDialog->getSleeping(); -} - -void WindowManager::wakeUpPlayer() -{ - mWaitDialog->wakeUp(); -} - -void WindowManager::addVisitedLocation(const std::string& name, int x, int y) -{ - mMap->addVisitedLocation (name, x, y); -} - -void WindowManager::startSpellMaking(MWWorld::Ptr actor) -{ - mSpellCreationDialog->startSpellMaking (actor); -} - -void WindowManager::startEnchanting (MWWorld::Ptr actor) -{ - mEnchantingDialog->startEnchanting (actor); -} - -void WindowManager::startTraining(MWWorld::Ptr actor) -{ - mTrainingWindow->startTraining(actor); -} - -const Translation::Storage& WindowManager::getTranslationDataStorage() const -{ - return mTranslationDataStorage; -} - -void WindowManager::changePointer(const std::string &name) -{ - mCursor->onCursorChange(name); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 122b10cc39..71bd2c9a75 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 @@ -74,6 +69,10 @@ namespace MWGui class TrainingWindow; class Cursor; class SpellIcons; + class MerchantRepair; + class Repair; + class SoulgemDialog; + class CompanionWindow; class WindowManager : public MWBase::WindowManager { @@ -81,7 +80,7 @@ namespace MWGui typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage); @@ -94,6 +93,8 @@ namespace MWGui */ virtual void update(); + virtual void setNewGame(bool newgame); + virtual void pushGuiMode(GuiMode mode); virtual void popGuiMode(); virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack @@ -102,6 +103,8 @@ namespace MWGui virtual bool isGuiMode() const; + virtual bool isConsoleMode() const; + virtual void toggleVisible(GuiWindow wnd); // Disallow all inventory mode windows @@ -173,8 +176,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(); @@ -190,7 +193,9 @@ namespace MWGui virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector()); + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false); + virtual void staticMessageBox(const std::string& message); + virtual void removeStaticMessageBox(); virtual void enterPressed (); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) @@ -219,19 +224,33 @@ namespace MWGui virtual void loadingDone(); virtual void enableRest() { mRestAllowed = true; } - virtual bool getRestEnabled() { return mRestAllowed; } + virtual bool getRestEnabled(); + + virtual bool getJournalAllowed() { return (mAllowed & GW_Magic); } virtual bool getPlayerSleeping(); virtual void wakeUpPlayer(); + virtual void updatePlayer(); + + virtual void showCompanionWindow(MWWorld::Ptr actor); virtual void startSpellMaking(MWWorld::Ptr actor); virtual void startEnchanting(MWWorld::Ptr actor); + virtual void startSelfEnchanting(MWWorld::Ptr soulgem); virtual void startTraining(MWWorld::Ptr actor); + 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); virtual const Translation::Storage& getTranslationDataStorage() const; + void onSoulgemDialogButtonPressed (int button); + private: OEngine::GUI::MyGUIManager *mGuiManager; OEngine::Render::OgreRenderer *mRendering; @@ -264,6 +283,11 @@ namespace MWGui SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; + MerchantRepair* mMerchantRepair; + SoulgemDialog* mSoulgemDialog; + Repair* mRepair; + CompanionWindow* mCompanionWindow; + Translation::Storage& mTranslationDataStorage; Cursor* mCursor; @@ -309,8 +333,6 @@ namespace MWGui unsigned int mTriangleCount; unsigned int mBatchCount; - void onDialogueWindowBye(); - /** * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp new file mode 100644 index 0000000000..e5a94fc721 --- /dev/null +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -0,0 +1,27 @@ +#include "windowpinnablebase.hpp" + +#include "exposedwindow.hpp" + +namespace MWGui +{ + WindowPinnableBase::WindowPinnableBase(const std::string& parLayout) + : WindowBase(parLayout), mPinned(false), mVisible(false) + { + ExposedWindow* window = static_cast(mMainWidget); + mPinButton = window->getSkinWidget ("Button"); + + mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked); + } + + void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender) + { + mPinned = !mPinned; + + if (mPinned) + mPinButton->changeWidgetSkin ("PinDown"); + else + mPinButton->changeWidgetSkin ("PinUp"); + + onPinToggled(); + } +} 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 f18c02a0e8..00c520de9e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -29,7 +29,7 @@ namespace MWInput { InputManager::InputManager(OEngine::Render::OgreRenderer &ogre, - MWWorld::Player &player, + MWWorld::Player& player, MWBase::WindowManager &windows, bool debug, OMW::Engine& engine, @@ -55,6 +55,7 @@ namespace MWInput , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) , mOverencumberedMessageDelay(0.f) + , mAlwaysRunActive(false) { Ogre::RenderWindow* window = mOgre.getWindow (); size_t windowHnd; @@ -180,12 +181,11 @@ namespace MWInput break; case A_Activate: resetIdleTime(); - activate(); - if( MWBase::Environment::get().getWindowManager()->isGuiMode() - && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_InterMessageBox ) { + if( MWBase::Environment::get().getWindowManager()->isGuiMode()) { // Pressing the activation key when a messagebox is prompting for "ok" will activate the ok button MWBase::Environment::get().getWindowManager()->enterPressed(); } + activate(); break; case A_Journal: toggleJournal (); @@ -193,7 +193,7 @@ namespace MWInput case A_AutoMove: toggleAutoMove (); break; - case A_ToggleWalk: + case A_AlwaysRun: toggleWalking (); break; case A_ToggleWeapon: @@ -241,6 +241,10 @@ namespace MWInput case A_ToggleHUD: mWindows.toggleHud(); break; + case A_Use: + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + mPlayer.use(); + break; } } } @@ -287,8 +291,6 @@ namespace MWInput triedToMove = true; mPlayer.setLeftRight (1); } - else - mPlayer.setLeftRight (0); if (actionIsActive(A_MoveForward)) { @@ -302,8 +304,12 @@ namespace MWInput mPlayer.setAutoMove (false); mPlayer.setForwardBackward (-1); } - else - mPlayer.setForwardBackward (0); + + else if(mPlayer.getAutoMove()) + { + triedToMove = true; + mPlayer.setForwardBackward (1); + } mPlayer.setSneak(actionIsActive(A_Sneak)); @@ -312,13 +318,11 @@ namespace MWInput mPlayer.setUpDown (1); triedToMove = true; } - else - mPlayer.setUpDown (0); - if(actionIsActive(A_Run)) - mPlayer.setRunState(true); + if (mAlwaysRunActive) + mPlayer.setRunState(!actionIsActive(A_Run)); else - mPlayer.setRunState(false); + mPlayer.setRunState(actionIsActive(A_Run)); // if player tried to start moving, but can't (due to being overencumbered), display a notification. if (triedToMove) @@ -327,6 +331,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}"); @@ -377,27 +382,12 @@ namespace MWInput void InputManager::changeInputMode(bool guiMode) { - // Are we in GUI mode now? - if(guiMode) - { - // Disable mouse look - mMouseLookEnabled = false; - - mWindows.showCrosshair (false); - - // Enable GUI events - mGuiCursorEnabled = true; - } - else - { - // Enable mouse look - mMouseLookEnabled = true; - - mWindows.showCrosshair (false); - - // Disable GUI events - mGuiCursorEnabled = false; - } + MWBase::Environment::get().getWindowManager()->setMouseVisible(guiMode); + mGuiCursorEnabled = guiMode; + mMouseLookEnabled = !guiMode; + if (guiMode) + mWindows.showCrosshair(false); + // if not in gui mode, the camera decides whether to show crosshair or not. } void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) @@ -461,8 +451,7 @@ namespace MWInput bool InputManager::keyPressed( const OIS::KeyEvent &arg ) { if(arg.key == OIS::KC_RETURN - && MWBase::Environment::get().getWindowManager()->isGuiMode() - && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_InterMessageBox ) + && MWBase::Environment::get().getWindowManager()->isGuiMode()) { // Pressing enter when a messagebox is prompting for "ok" will activate the ok button MWBase::Environment::get().getWindowManager()->enterPressed(); @@ -543,11 +532,22 @@ namespace MWInput { resetIdleTime(); - float x = arg.state.X.rel * mCameraSensitivity * 0.2; - float y = arg.state.Y.rel * mCameraSensitivity * 0.2 * (mInvertY ? -1 : 1) * mUIYMultiplier; + float x = arg.state.X.rel * (1.0f/256.0f) * mCameraSensitivity; + float y = arg.state.Y.rel * (1.0f/256.0f) * mCameraSensitivity * mCameraYMultiplier * (mInvertY ? -1 : 1); + float scale = MWBase::Environment::get().getFrameDuration(); + if(scale <= 0.0f) scale = 1.0f; - MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); + 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); @@ -575,15 +575,9 @@ namespace MWInput MWMechanics::DrawState_ state = mPlayer.getDrawState(); if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) - { mPlayer.setDrawState(MWMechanics::DrawState_Spell); - std::cout << "Player has now readied his hands for spellcasting!\n" << std::endl; - } else - { mPlayer.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; - } } void InputManager::toggleWeapon() @@ -592,15 +586,9 @@ namespace MWInput MWMechanics::DrawState_ state = mPlayer.getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - { mPlayer.setDrawState(MWMechanics::DrawState_Weapon); - std::cout << "Player is now drawing his weapon.\n" << std::endl; - } else - { mPlayer.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; - } } void InputManager::rest() @@ -622,6 +610,9 @@ namespace MWInput void InputManager::toggleInventory() { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + bool gameMode = !mWindows.isGuiMode(); // Toggle between game mode and inventory mode @@ -659,19 +650,29 @@ namespace MWInput void InputManager::toggleJournal() { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + // Toggle between game mode and journal mode 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. } void InputManager::quickKey (int index) { - mWindows.activateQuickKey (index); + if (!mWindows.isGuiMode()) + mWindows.activateQuickKey (index); } void InputManager::showQuickKeysMenu() @@ -699,7 +700,7 @@ namespace MWInput void InputManager::toggleWalking() { if (mWindows.isGuiMode()) return; - mPlayer.toggleRunning(); + mAlwaysRunActive = !mAlwaysRunActive; } // Exit program now button (which is disabled in GUI mode) @@ -711,19 +712,17 @@ namespace MWInput void InputManager::resetIdleTime() { - if (mTimeIdle < 0) { - MWBase::Environment::get().getWorld()->toggleVanityMode(false, false); - } + if (mTimeIdle < 0) + MWBase::Environment::get().getWorld()->toggleVanityMode(false); mTimeIdle = 0.f; } void InputManager::updateIdleTime(float dt) { - if (mTimeIdle >= 0.f) { + if (mTimeIdle >= 0.f) mTimeIdle += dt; - } if (mTimeIdle > 30.f) { - MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); + MWBase::Environment::get().getWorld()->toggleVanityMode(true); mTimeIdle = -1.f; } } @@ -768,6 +767,7 @@ namespace MWInput defaultKeyBindings[A_QuickKey10] = OIS::KC_0; defaultKeyBindings[A_Screenshot] = OIS::KC_SYSRQ; defaultKeyBindings[A_ToggleHUD] = OIS::KC_F12; + defaultKeyBindings[A_AlwaysRun] = OIS::KC_Y; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; @@ -807,6 +807,7 @@ namespace MWInput { std::map descriptions; + descriptions[A_Use] = "sUse"; descriptions[A_Activate] = "sActivate"; descriptions[A_MoveBackward] = "sBack"; descriptions[A_MoveForward] = "sForward"; @@ -834,6 +835,7 @@ namespace MWInput descriptions[A_QuickKey8] = "sQuick8Cmd"; descriptions[A_QuickKey9] = "sQuick9Cmd"; descriptions[A_QuickKey10] = "sQuick10Cmd"; + descriptions[A_AlwaysRun] = "sAlways_Run"; if (descriptions[action] == "") return ""; // not configurable @@ -865,8 +867,10 @@ namespace MWInput ret.push_back(A_MoveRight); ret.push_back(A_TogglePOV); ret.push_back(A_Run); + ret.push_back(A_AlwaysRun); ret.push_back(A_Sneak); ret.push_back(A_Activate); + ret.push_back(A_Use); ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleSpell); ret.push_back(A_AutoMove); @@ -907,6 +911,10 @@ namespace MWInput void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction) { + //Disallow binding escape key, and unassigned keys + if(key==OIS::KC_ESCAPE || key==OIS::KC_UNASSIGNED) + return + clearAllBindings(control); ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 8bb20b7bed..8aa0d32ade 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -119,7 +119,7 @@ namespace MWInput private: OEngine::Render::OgreRenderer &mOgre; - MWWorld::Player &mPlayer; + MWWorld::Player& mPlayer; MWBase::WindowManager &mWindows; OMW::Engine& mEngine; @@ -152,6 +152,7 @@ namespace MWInput int mMouseWheel; bool mDebug; bool mUserFileExists; + bool mAlwaysRunActive; std::map mControlSwitch; @@ -217,7 +218,7 @@ namespace MWInput A_CycleWeaponLeft,//Cycling through weapons A_CycleWeaponRight, A_ToggleSneak, //Toggles Sneak - A_ToggleWalk, //Toggle Walking/Running + A_AlwaysRun, //Toggle Walking/Running A_Sneak, A_QuickSave, diff --git a/apps/openmw/mwmechanics/activators.cpp b/apps/openmw/mwmechanics/activators.cpp deleted file mode 100644 index b67fcb2164..0000000000 --- a/apps/openmw/mwmechanics/activators.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "activators.hpp" - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -namespace MWMechanics -{ - -Activators::Activators() -{ -} - -void Activators::addActivator(const MWWorld::Ptr& ptr) -{ - MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); - if(anim != NULL) - mActivators.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true))); -} - -void Activators::removeActivator (const MWWorld::Ptr& ptr) -{ - PtrControllerMap::iterator iter = mActivators.find(ptr); - if(iter != mActivators.end()) - mActivators.erase(iter); -} - -void Activators::updateActivator(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) -{ - PtrControllerMap::iterator iter = mActivators.find(old); - if(iter != mActivators.end()) - { - CharacterController ctrl = iter->second; - mActivators.erase(iter); - - ctrl.updatePtr(ptr); - mActivators.insert(std::make_pair(ptr, ctrl)); - } -} - -void Activators::dropActivators (const MWWorld::Ptr::CellStore *cellStore) -{ - PtrControllerMap::iterator iter = mActivators.begin(); - while(iter != mActivators.end()) - { - if(iter->first.getCell()==cellStore) - mActivators.erase(iter++); - else - ++iter; - } -} - -void Activators::update(float duration, bool paused) -{ - if(!paused) - { - for(PtrControllerMap::iterator iter(mActivators.begin());iter != mActivators.end();++iter) - iter->second.update(duration); - } -} - -void Activators::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) -{ - PtrControllerMap::iterator iter = mActivators.find(ptr); - if(iter != mActivators.end()) - iter->second.playGroup(groupName, mode, number); -} -void Activators::skipAnimation(const MWWorld::Ptr& ptr) -{ - PtrControllerMap::iterator iter = mActivators.find(ptr); - if(iter != mActivators.end()) - iter->second.skipAnim(); -} - -} diff --git a/apps/openmw/mwmechanics/activators.hpp b/apps/openmw/mwmechanics/activators.hpp deleted file mode 100644 index 137674a57a..0000000000 --- a/apps/openmw/mwmechanics/activators.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef GAME_MWMECHANICS_ACTIVATORS_H -#define GAME_MWMECHANICS_ACTOVATRS_H - -#include -#include - -#include "character.hpp" - -namespace MWWorld -{ - class Ptr; - class CellStore; -} - -namespace MWMechanics -{ - class Activators - { - typedef std::map PtrControllerMap; - PtrControllerMap mActivators; - - public: - Activators(); - - void addActivator (const MWWorld::Ptr& ptr); - ///< Register an animated activator - - void removeActivator (const MWWorld::Ptr& ptr); - ///< Deregister an activator - - void updateActivator(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); - ///< Updates an activator with a new Ptr - - void dropActivators (const MWWorld::CellStore *cellStore); - ///< Deregister all activators in the given cell. - - void update (float duration, bool paused); - ///< Update activator animations - - void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); - void skipAnimation(const MWWorld::Ptr& ptr); - }; -} - -#endif diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9632bdf769..4db574cb8d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -17,6 +17,7 @@ #include "../mwbase/windowmanager.hpp" #include "creaturestats.hpp" +#include "movement.hpp" namespace MWMechanics { @@ -35,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) @@ -71,7 +71,7 @@ namespace MWMechanics int endurance = creatureStats.getAttribute(5).getBase(); double magickaFactor = - creatureStats.getMagicEffects().get (EffectKey (84)).mMagnitude * 0.1 + 0.5; + creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 0.5; DynamicStat health = creatureStats.getHealth(); health.setBase (static_cast (0.5 * (strength + endurance)) + creatureStats.getLevelHealthBonus ()); @@ -80,7 +80,7 @@ namespace MWMechanics DynamicStat magicka = creatureStats.getMagicka(); magicka.setBase (static_cast (intelligence + magickaFactor * intelligence)); creatureStats.setMagicka (magicka); - + DynamicStat fatigue = creatureStats.getFatigue(); fatigue.setBase (strength+willpower+agility+endurance); creatureStats.setFatigue (fatigue); @@ -92,11 +92,10 @@ namespace MWMechanics if (duration == 3600) { - // stunted magicka - bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(136)).mMagnitude > 0; + bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0; int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - + DynamicStat health = stats.getHealth(); health.setCurrent (health.getCurrent() + 0.1 * endurance); stats.setHealth (health); @@ -115,15 +114,15 @@ namespace MWMechanics float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; - + DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); stats.setFatigue (fatigue); - + if (!stunted) { float fRestMagicMult = store.get().find("fRestMagicMult")->getFloat (); - + DynamicStat magicka = stats.getMagicka(); magicka.setCurrent (magicka.getCurrent() + fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified()); @@ -140,36 +139,39 @@ namespace MWMechanics for (int i=0; i<8; ++i) { int modifier = - creatureStats.getMagicEffects().get (EffectKey (79, i)).mMagnitude; + creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyAttribute, i)).mMagnitude; - modifier -= creatureStats.getMagicEffects().get (EffectKey (17, i)).mMagnitude; + modifier -= creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::DrainAttribute, i)).mMagnitude; creatureStats.getAttribute(i).setModifier (modifier); } // dynamic stats MagicEffects effects = creatureStats.getMagicEffects(); - + for (int i=0; i<3; ++i) { DynamicStat stat = creatureStats.getDynamic (i); - + stat.setModifier ( effects.get (EffectKey(80+i)).mMagnitude - effects.get (EffectKey(18+i)).mMagnitude); - + creatureStats.setDynamic (i, stat); - } + } } Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) { + // erase previous death events since we are currently only tracking them while in an active cell + MWWorld::Class::get (ptr).getCreatureStats (ptr).clearHasDied(); + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead()) - mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true))); + mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle))); else - mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1, false))); + mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1))); } void Actors::removeActor (const MWWorld::Ptr& ptr) @@ -208,7 +210,7 @@ namespace MWMechanics { mDuration += duration; - if (mDuration>=0.25) + //if (mDuration>=0.25) { float totalDuration = mDuration; mDuration = 0; @@ -218,7 +220,7 @@ namespace MWMechanics if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) { if(iter->second.getState() >= CharState_Death1) - iter->second.setState(CharState_Idle, true); + iter->second.setState(CharState_Idle); updateActor(iter->first, totalDuration); if(iter->first.getTypeName() == typeid(ESM::NPC).name()) @@ -248,13 +250,13 @@ namespace MWMechanics if(iter->second.getState() >= CharState_Death1) continue; - iter->second.setState(CharState_Death1, false); + iter->second.setState(CharState_Death1); ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; if(MWWorld::Class::get(iter->first).isEssential(iter->first)) MWBase::Environment::get().getWindowManager()->messageBox( - "#{sKilledEssential}", std::vector()); + "#{sKilledEssential}"); } } @@ -264,7 +266,8 @@ namespace MWMechanics for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { - Ogre::Vector3 movement = iter->second.update(duration); + Movement movement; + iter->second.update(duration, movement); mMovement.push_back(std::make_pair(iter->first, movement)); } MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration); @@ -278,7 +281,7 @@ namespace MWMechanics for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) calculateRestoration(iter->first, 3600); } - + int Actors::countDeaths (const std::string& id) const { std::map::const_iterator iter = mDeathCount.find(id); @@ -287,6 +290,13 @@ namespace MWMechanics return 0; } + void Actors::forceStateUpdate(const MWWorld::Ptr & ptr) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + iter->second.forceStateUpdate(); + } + void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { PtrControllerMap::iterator iter = mActors.find(ptr); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index fc4af8dd63..c01d630930 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -7,6 +7,7 @@ #include #include "character.hpp" +#include "movement.hpp" #include "../mwbase/world.hpp" namespace Ogre @@ -77,6 +78,8 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. + void forceStateUpdate(const MWWorld::Ptr &ptr); + void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); }; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index ebbea55b0e..7c28dd216c 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,30 +1,182 @@ #include "aiescort.hpp" -#include - -MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) -: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) -{ -} + +#include "character.hpp" +#include "movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/timestamp.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include +#include +#include "boost/tuple/tuple.hpp" + +namespace +{ + float sgn(float a) + { + if(a>0) return 1.; + else return -1.; + } +} + +/* + TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0. + TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z. + TODO: Take account for actors being in different cells. +*/ + +MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) +: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) +{ + mMaxDist = 470; + + // The CS Help File states that if a duration is givin, the AI package will run for that long + // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. + if(mX != 0 || mY != 0 || mZ != 0) + mDuration = 0; + + else + { + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); + } +} MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z) : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) { + mMaxDist = 470; + + // The CS Help File states that if a duration is givin, the AI package will run for that long + // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. + if(mX != 0 || mY != 0 || mZ != 0) + mDuration = 0; + + else + { + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); + } +} + + +MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const +{ + return new AiEscort(*this); +} + +bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) +{ + // If AiEscort has ran for as long or longer then the duration specified + // and the duration is not infinite, the package is complete. + if(mDuration != 0) + { + MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); + unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); + std::cout << "AiEscort: " << currentSecond << " time: " << currentSecond - mStartingSecond << std::endl; + if(currentSecond - mStartingSecond >= mDuration) + { + return true; + } + } + + ESM::Position pos = actor.getRefData().getPosition(); + bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + + if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) + { + int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); + // Check if actor is near the border of an inactive cell. If so, disable AiEscort. + // FIXME: This *should* pause the AiEscort package instead of terminating it. + if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) + { + int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); + // Check if actor is near the border of an inactive cell. If so, disable AiEscort. + // FIXME: This *should* pause the AiEscort package instead of terminating it. + if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + + + if(!mPathFinder.isPathConstructed() || cellChange) + { + cellX = actor.getCell()->mCell->mData.mX; + cellY = actor.getCell()->mCell->mData.mY; + float xCell = 0; + float yCell = 0; + if (actor.getCell()->mCell->isExterior()) + { + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + } + + ESM::Pathgrid::Point dest; + dest.mX = mX; + dest.mY = mY; + dest.mZ = mZ; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + } + + if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + + const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); + const float* const leaderPos = actor.getRefData().getPosition().pos; + const float* const followerPos = follower.getRefData().getPosition().pos; + double differenceBetween[3]; + + for (short i = 0; i < 3; ++i) + differenceBetween[i] = (leaderPos[i] - followerPos[i]); + + float distanceBetweenResult = + (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); + + if(distanceBetweenResult <= mMaxDist * mMaxDist) + { + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + mMaxDist = 470; + } + else + { + // Stop moving if the player is to far away + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + mMaxDist = 330; + } + + return false; +} + +int MWMechanics::AiEscort::getTypeId() const +{ + return 2; } - -MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const -{ - return new AiEscort(*this); -} - -bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) -{ - std::cout << "AiEscort completed. \n"; - return true; -} - -int MWMechanics::AiEscort::getTypeId() const -{ - return 2; -} - diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index d89a9586ce..667b88e31a 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -2,13 +2,36 @@ #define GAME_MWMECHANICS_AIESCORT_H #include "aipackage.hpp" +#include "pathfinding.hpp" #include +/* From CS: +Escort + +Makes the actor escort another actor to a location or for a specified period of time. During this time the actor will also protect the actor it is escorting. + +If you are not doing this package with the player as the target, you’ll want to also put a follow package on the target Actor, since escorting an actor makes the escorter wait for the other actor. If the Target does not know they are supposed to follow, the escorter will most likely just stand there. + +Target: The ActorID to Escort. Remember that since all ActorIDs share the same AI packages, putting this on an Actor with multiple references will cause ALL references of that actor to attempt to escort the same actor. Thus, this type of AI should only be placed on specific or unique sets of Actors. + +Duration: The duration the actor should escort for. Trumped by providing a location. + +Escort to: Check this to use location data for the escort. + +Cell: The Cell to escort to. + +XYZ: Like Travel, specify the XYZ location to escort to. + +View Location: A red X will appear in the render window that you can move around with the standard render window object controls. Place the X on the escort destination. + + +*/ + namespace MWMechanics -{ - class AiEscort : public AiPackage - { - public: +{ + class AiEscort : public AiPackage + { + public: AiEscort(const std::string &actorId,int duration, float x, float y, float z); ///< \implement AiEscort AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); @@ -27,8 +50,13 @@ namespace MWMechanics float mX; float mY; float mZ; - int mDuration; - - }; -} -#endif + float mMaxDist; + unsigned int mStartingSecond; + unsigned int mDuration; + + PathFinder mPathFinder; + int cellX; + int cellY; + }; +} +#endif diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 897dd17480..b221856674 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,25 +1,110 @@ #include "aitravel.hpp" #include -MWMechanics::AiTravel::AiTravel(float x, float y, float z) -: mX(x),mY(y),mZ(z) +#include "character.hpp" + +#include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "movement.hpp" +#include "../mwworld/player.hpp" + +#include +#include +#include "boost/tuple/tuple.hpp" + +namespace { + float sgn(float a) + { + if(a>0) return 1.; + else return -1.; + } } -MWMechanics::AiTravel * MWMechanics::AiTravel::clone() const +namespace MWMechanics { - return new AiTravel(*this); + + AiTravel::AiTravel(float x, float y, float z) + : mX(x),mY(y),mZ(z),mPathFinder() + { + } + + AiTravel * AiTravel::clone() const + { + return new AiTravel(*this); + } + + bool AiTravel::execute (const MWWorld::Ptr& actor) + { + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + ESM::Position pos = actor.getRefData().getPosition(); + bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) + { + int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); + //check if actor is near the border of an inactive cell. If so, disable aitravel. + if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) + { + int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); + //check if actor is near the border of an inactive cell. If so, disable aitravel. + if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + + if(!mPathFinder.isPathConstructed() ||cellChange) + { + cellX = actor.getCell()->mCell->mData.mX; + cellY = actor.getCell()->mCell->mData.mY; + float xCell = 0; + float yCell = 0; + if (actor.getCell()->mCell->isExterior()) + { + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + } + + ESM::Pathgrid::Point dest; + dest.mX = mX; + dest.mY = mY; + dest.mZ = mZ; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + } + if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + return false; + } + + int AiTravel::getTypeId() const + { + return 1; + } + } - -bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) -{ - std::cout << "AiTravel completed.\n"; - return true; -} - -int MWMechanics::AiTravel::getTypeId() const -{ - return 1; -} - - diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 1c6abbf279..52b41850f1 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -1,27 +1,35 @@ #ifndef GAME_MWMECHANICS_AITRAVEL_H #define GAME_MWMECHANICS_AITRAVEL_H - + #include "aipackage.hpp" - -namespace MWMechanics -{ - class AiTravel : public AiPackage - { - public: - AiTravel(float x, float y, float z); - virtual AiTravel *clone() const; - - virtual bool execute (const MWWorld::Ptr& actor); - ///< \return Package completed? - - virtual int getTypeId() const; - - private: - float mX; - float mY; - float mZ; - - }; +#include "pathfinding.hpp" + +namespace MWMechanics +{ + class AiTravel : public AiPackage + { + public: + AiTravel(float x, float y, float z); + virtual AiTravel *clone() const; + + virtual bool execute (const MWWorld::Ptr& actor); + ///< \return Package completed? + + virtual int getTypeId() const; + + private: + float mX; + float mY; + float mZ; + + int cellX; + int cellY; + + //bool isPathConstructed; + //std::list mPath; + PathFinder mPathFinder; + + }; } #endif diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index e9db6d212a..a730475482 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,8 +1,8 @@ #include "aiwander.hpp" #include -MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle): - mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle) +MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) { } @@ -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/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index a71858febc..e06e714bc4 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -6,24 +6,24 @@ namespace MWMechanics { - class AiWander : public AiPackage { - public: + public: - AiWander(int distance, int duration, int timeOfDay, const std::vector& idle); - virtual AiPackage *clone() const; - virtual bool execute (const MWWorld::Ptr& actor); - ///< \return Package completed? - virtual int getTypeId() const; - ///< 0: Wander + AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat); + virtual AiPackage *clone() const; + virtual bool execute (const MWWorld::Ptr& actor); + ///< \return Package completed? + virtual int getTypeId() const; + ///< 0: Wander - private: - int mDistance; - int mDuration; - int mTimeOfDay; - std::vector mIdle; + private: + int mDistance; + int mDuration; + int mTimeOfDay; + std::vector mIdle; + bool mRepeat; }; - } +} #endif diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 62958db8d4..31ba833925 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -21,115 +21,186 @@ #include +#include "movement.hpp" +#include "npcstats.hpp" +#include "creaturestats.hpp" + #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" namespace MWMechanics { -static const struct { +static const struct StateInfo { CharacterState state; const char groupname[32]; + Priority priority; + bool loops; } sStateList[] = { - { CharState_Idle, "idle" }, - { CharState_Idle2, "idle2" }, - { CharState_Idle3, "idle3" }, - { CharState_Idle4, "idle4" }, - { CharState_Idle5, "idle5" }, - { CharState_Idle6, "idle6" }, - { CharState_Idle7, "idle7" }, - { CharState_Idle8, "idle8" }, - { CharState_Idle9, "idle9" }, - { CharState_IdleSwim, "idleswim" }, - { CharState_IdleSneak, "idlesneak" }, + { CharState_Idle, "idle", Priority_Default, true }, + { CharState_Idle2, "idle2", Priority_Default, true }, + { CharState_Idle3, "idle3", Priority_Default, true }, + { CharState_Idle4, "idle4", Priority_Default, true }, + { CharState_Idle5, "idle5", Priority_Default, true }, + { CharState_Idle6, "idle6", Priority_Default, true }, + { CharState_Idle7, "idle7", Priority_Default, true }, + { CharState_Idle8, "idle8", Priority_Default, true }, + { CharState_Idle9, "idle9", Priority_Default, true }, + { CharState_IdleSwim, "idleswim", Priority_Default, true }, + { CharState_IdleSneak, "idlesneak", Priority_Default, true }, - { CharState_WalkForward, "walkforward" }, - { CharState_WalkBack, "walkback" }, - { CharState_WalkLeft, "walkleft" }, - { CharState_WalkRight, "walkright" }, + { CharState_WalkForward, "walkforward", Priority_Default, true }, + { CharState_WalkBack, "walkback", Priority_Default, true }, + { CharState_WalkLeft, "walkleft", Priority_Default, true }, + { CharState_WalkRight, "walkright", Priority_Default, true }, - { CharState_SwimWalkForward, "swimwalkforward" }, - { CharState_SwimWalkBack, "swimwalkback" }, - { CharState_SwimWalkLeft, "swimwalkleft" }, - { CharState_SwimWalkRight, "swimwalkright" }, + { CharState_SwimWalkForward, "swimwalkforward", Priority_Default, true }, + { CharState_SwimWalkBack, "swimwalkback", Priority_Default, true }, + { CharState_SwimWalkLeft, "swimwalkleft", Priority_Default, true }, + { CharState_SwimWalkRight, "swimwalkright", Priority_Default, true }, - { CharState_RunForward, "runforward" }, - { CharState_RunBack, "runback" }, - { CharState_RunLeft, "runleft" }, - { CharState_RunRight, "runright" }, + { CharState_RunForward, "runforward", Priority_Default, true }, + { CharState_RunBack, "runback", Priority_Default, true }, + { CharState_RunLeft, "runleft", Priority_Default, true }, + { CharState_RunRight, "runright", Priority_Default, true }, - { CharState_SwimRunForward, "swimrunforward" }, - { CharState_SwimRunBack, "swimrunback" }, - { CharState_SwimRunLeft, "swimrunleft" }, - { CharState_SwimRunRight, "swimrunright" }, + { CharState_SwimRunForward, "swimrunforward", Priority_Default, true }, + { CharState_SwimRunBack, "swimrunback", Priority_Default, true }, + { CharState_SwimRunLeft, "swimrunleft", Priority_Default, true }, + { CharState_SwimRunRight, "swimrunright", Priority_Default, true }, - { CharState_SneakForward, "sneakforward" }, - { CharState_SneakBack, "sneakback" }, - { CharState_SneakLeft, "sneakleft" }, - { CharState_SneakRight, "sneakright" }, + { CharState_SneakForward, "sneakforward", Priority_Default, true }, + { CharState_SneakBack, "sneakback", Priority_Default, true }, + { CharState_SneakLeft, "sneakleft", Priority_Default, true }, + { CharState_SneakRight, "sneakright", Priority_Default, true }, - { CharState_Jump, "jump" }, + { CharState_TurnLeft, "turnleft", Priority_Default, true }, + { CharState_TurnRight, "turnright", Priority_Default, true }, - { CharState_Death1, "death1" }, - { CharState_Death2, "death2" }, - { CharState_Death3, "death3" }, - { CharState_Death4, "death4" }, - { CharState_Death5, "death5" }, + { CharState_Jump, "jump", Priority_Default, true }, + + { CharState_Death1, "death1", Priority_Death, false }, + { CharState_Death2, "death2", Priority_Death, false }, + { CharState_Death3, "death3", Priority_Death, false }, + { CharState_Death4, "death4", Priority_Death, false }, + { CharState_Death5, "death5", Priority_Death, false }, }; -static const size_t sStateListSize = sizeof(sStateList)/sizeof(sStateList[0]); +static const StateInfo *sStateListEnd = &sStateList[sizeof(sStateList)/sizeof(sStateList[0])]; -static void getStateInfo(CharacterState state, std::string *group) +class FindCharState { + CharacterState state; + +public: + FindCharState(CharacterState _state) : state(_state) { } + + bool operator()(const StateInfo &info) const + { return info.state == state; } +}; + + +static const struct WeaponInfo { + WeaponType type; + const char idlegroup[16]; + const char movementgroup[16]; + const char actiongroup[16]; +} sWeaponTypeList[] = { + { WeapType_HandToHand, "hh", "hh", "handtohand" }, + { WeapType_OneHand, "1h", "1h", "weapononehand" }, + { WeapType_TwoHand, "2c", "2c", "weapontwohand" }, + { WeapType_TwoWide, "2w", "2w", "weapontwowide" }, + { WeapType_BowAndArrow, "1h", "1h", "bowandarrow" }, + { WeapType_Crossbow, "crossbow", "1h", "crossbow" }, + { WeapType_ThowWeapon, "1h", "1h", "throwweapon" }, + { WeapType_PickProbe, "1h", "1h", "pickprobe" }, + { WeapType_Spell, "spell", "", "spellcast" }, +}; +static const WeaponInfo *sWeaponTypeListEnd = &sWeaponTypeList[sizeof(sWeaponTypeList)/sizeof(sWeaponTypeList[0])]; + +class FindWeaponType { + WeaponType type; + +public: + FindWeaponType(WeaponType _type) : type(_type) { } + + bool operator()(const WeaponInfo &weap) const + { return weap.type == type; } +}; + + +void CharacterController::getCurrentGroup(std::string &group, Priority &priority, bool &loops) const { - for(size_t i = 0;i < sStateListSize;i++) + std::string name; + const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mCharState)); + if(state == sStateListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mCharState)); + + name = state->groupname; + priority = state->priority; + loops = state->loops; + + if(!(mCharState >= CharState_Death1) && mWeaponType != WeapType_None) { - if(sStateList[i].state == state) + const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); + if(weap != sWeaponTypeListEnd) { - *group = sStateList[i].groupname; - return; + if(mCharState == CharState_Idle) + (group=name) += weap->idlegroup; + else + (group=name) += weap->movementgroup; } } - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(state)); + + if(group.empty() || !mAnimation->hasAnimation(group)) + group = (mAnimation->hasAnimation(name) ? name : std::string()); } -CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) - : mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false) +void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group) +{ + const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); + if(info != sWeaponTypeListEnd) + group = info->actiongroup; +} + + +CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state) + : mPtr(ptr) + , mAnimation(anim) + , mCharState(state) + , mWeaponType(WeapType_None) + , mSkipAnim(false) + , mSecondsOfRunning(0) + , mSecondsOfSwimming(0) { if(!mAnimation) return; - mAnimation->setController(this); - - getStateInfo(mState, &mCurrentGroup); - if(ptr.getTypeName() == typeid(ESM::Activator).name()) - { - /* Don't accumulate with activators (they don't get moved). */ - mAnimation->setAccumulation(Ogre::Vector3::ZERO); - } - else + if(MWWorld::Class::get(mPtr).isActor()) { /* Accumulate along X/Y only for now, until we can figure out how we should * handle knockout and death which moves the character down. */ mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); } - if(mAnimation->hasAnimation(mCurrentGroup)) - mAnimation->play(mCurrentGroup, "stop", "stop", loop); -} + else + { + /* Don't accumulate with non-actors. */ + mAnimation->setAccumulation(Ogre::Vector3(0.0f)); + } -CharacterController::CharacterController(const CharacterController &rhs) - : mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue) - , mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState) - , mSkipAnim(rhs.mSkipAnim) -{ - if(!mAnimation) - return; - /* We've been copied. Update the animation with the new controller. */ - mAnimation->setController(this); + std::string group; + Priority prio; + bool loops; + getCurrentGroup(group, prio, loops); + mAnimation->play(group, prio, MWRender::Animation::Group_All, false, + "start", "stop", 1.0f, loops ? (~(size_t)0) : 0); } CharacterController::~CharacterController() @@ -143,63 +214,78 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) } -void CharacterController::markerEvent(float time, const std::string &evt) +void CharacterController::update(float duration, Movement &movement) { - if(evt == "stop") + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + float speed = 0.0f; + + if(!cls.isActor()) { - if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) + if(mAnimQueue.size() > 1) { - mAnimQueue.pop_front(); - mAnimation->play(mCurrentGroup, "loop start", "stop", false); - } - else if(mAnimQueue.size() > 0) - { - mAnimQueue.pop_front(); - if(mAnimQueue.size() > 0) + if(mAnimation->isPlaying(mAnimQueue.front().first) == false) { - mCurrentGroup = mAnimQueue.front(); - mAnimation->play(mCurrentGroup, "start", "stop", false); + mAnimation->disable(mAnimQueue.front().first); + mAnimQueue.pop_front(); + + mAnimation->play(mAnimQueue.front().first, Priority_Default, + MWRender::Animation::Group_All, false, + "start", "stop", 0.0f, mAnimQueue.front().second); } } - return; } - - std::cerr<< "Unhandled animation event: "<= CharState_Death1)) + else if(!cls.getCreatureStats(mPtr).isDead()) { - const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Class &cls = MWWorld::Class::get(mPtr); - const Ogre::Vector3 &vec = cls.getMovementVector(mPtr); + MWBase::World *world = MWBase::Environment::get().getWorld(); bool onground = world->isOnGround(mPtr); bool inwater = world->isSwimming(mPtr); bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run); bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); + Ogre::Vector3 vec = cls.getMovementVector(mPtr); + Ogre::Vector3 rot = cls.getRotationVector(mPtr); speed = cls.getSpeed(mPtr); + // advance athletics + if (vec.squaredLength() > 0 && mPtr.getRefData().getHandle() == "player") + { + if (inwater) + { + mSecondsOfSwimming += duration; + while(mSecondsOfSwimming > 1) + { + cls.skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 1); + mSecondsOfSwimming -= 1; + } + } + else if (isrunning) + { + mSecondsOfRunning += duration; + while(mSecondsOfRunning > 1) + { + cls.skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 0); + mSecondsOfRunning -= 1; + } + } + } + /* FIXME: The state should be set to Jump, and X/Y movement should be disallowed except * for the initial thrust (which would be carried by "physics" until landing). */ - if(onground && vec.z > 0.0f) + if(!onground) + vec.z = 0.0f; + else if(vec.z > 0.0f) { - float x = cls.getJump(mPtr); + float z = cls.getJump(mPtr); if(vec.x == 0 && vec.y == 0) - movement.z += x*duration; + vec.z *= z; else { /* FIXME: this would be more correct if we were going into a jumping state, * rather than normal walking/idle states. */ //Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy(); - //movement += Ogre::Vector3(lat.x, lat.y, 1.0f) * x * 0.707f * duration; - movement.z += x * 0.707f * duration; + //vec *= Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f; + vec.z *= z * 0.707f; } //decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult; @@ -209,39 +295,187 @@ Ogre::Vector3 CharacterController::update(float duration) { if(vec.x > 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) - : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true); - + : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight))); else if(vec.x < 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) - : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true); + : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft))); - // Apply any forward/backward movement manually - movement.y += vec.y * (speed*duration); + vec.x *= speed; + vec.y *= speed; } else if(vec.y != 0.0f && speed > 0.0f) { if(vec.y > 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) - : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true); - + : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward))); else if(vec.y < 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) - : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true); - // Apply any sideways movement manually - movement.x += vec.x * (speed*duration); + : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); + + vec.x *= speed; + vec.y *= speed; + } + else if(rot.z != 0.0f && !inwater && !sneak) + { + if(rot.z > 0.0f) + setState(CharState_TurnRight); + else if(rot.z < 0.0f) + setState(CharState_TurnLeft); + } + else if(mAnimQueue.size() > 0) + { + if(mAnimQueue.size() > 1) + { + if(mAnimation->isPlaying(mAnimQueue.front().first) == false) + { + mAnimation->disable(mAnimQueue.front().first); + mAnimQueue.pop_front(); + + mAnimation->play(mAnimQueue.front().first, Priority_Default, + MWRender::Animation::Group_All, false, + "start", "stop", 0.0f, mAnimQueue.front().second); + } + } + } + else + setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle))); + + vec *= duration; + movement.mPosition[0] += vec.x; + movement.mPosition[1] += vec.y; + movement.mPosition[2] += vec.z; + rot *= duration; + movement.mRotation[0] += rot.x; + movement.mRotation[1] += rot.y; + movement.mRotation[2] += rot.z; + + if(mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + NpcStats &stats = cls.getNpcStats(mPtr); + WeaponType weaptype = WeapType_None; + MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weapon = inv.end(); + + if(stats.getDrawState() == DrawState_Spell) + weaptype = WeapType_Spell; + else if(stats.getDrawState() == MWMechanics::DrawState_Weapon) + { + weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end()) + weaptype = WeapType_HandToHand; + else + { + const std::string &type = weapon->getTypeName(); + if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) + weaptype = WeapType_PickProbe; + else if(type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef *ref = weapon->get(); + ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; + switch(type) + { + case ESM::Weapon::ShortBladeOneHand: + case ESM::Weapon::LongBladeOneHand: + case ESM::Weapon::BluntOneHand: + case ESM::Weapon::AxeOneHand: + case ESM::Weapon::Arrow: + case ESM::Weapon::Bolt: + weaptype = WeapType_OneHand; + break; + case ESM::Weapon::LongBladeTwoHand: + case ESM::Weapon::BluntTwoClose: + case ESM::Weapon::AxeTwoHand: + weaptype = WeapType_TwoHand; + break; + case ESM::Weapon::BluntTwoWide: + case ESM::Weapon::SpearTwoWide: + weaptype = WeapType_TwoWide; + break; + case ESM::Weapon::MarksmanBow: + weaptype = WeapType_BowAndArrow; + break; + case ESM::Weapon::MarksmanCrossbow: + weaptype = WeapType_Crossbow; + break; + case ESM::Weapon::MarksmanThrown: + weaptype = WeapType_ThowWeapon; + break; + } + } + } + } + else + weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if(weaptype != mWeaponType) + { + std::string weapgroup; + if(weaptype == WeapType_None) + { + getWeaponGroup(mWeaponType, weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "unequip start", "unequip stop", 0.0f, 0); + } + else + { + getWeaponGroup(weaptype, weapgroup); + mAnimation->showWeapons(false); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "equip start", "equip stop", 0.0f, 0); + } + + mWeaponType = weaptype; + forceStateUpdate(); + + if(weapon != inv.end()) + { + std::string soundid = (mWeaponType == WeapType_None) ? + MWWorld::Class::get(*weapon).getDownSoundId(*weapon) : + MWWorld::Class::get(*weapon).getUpSoundId(*weapon); + if(!soundid.empty()) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f); + } + } + } + + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + { + if(!mAnimation->isPlaying("torch")) + mAnimation->play("torch", Priority_Torch, + MWRender::Animation::Group_LeftArm, false, + "start", "stop", 0.0f, (~(size_t)0)); + } + else if(mAnimation->isPlaying("torch")) + mAnimation->disable("torch"); } - else if(mAnimQueue.size() == 0) - setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); } if(mAnimation && !mSkipAnim) { mAnimation->setSpeed(speed); - movement += mAnimation->runAnimation(duration); + + Ogre::Vector3 moved = mAnimation->runAnimation(duration); + // Ensure we're moving in generally the right direction + if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) || + (movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f)) + moved.x = movement.mPosition[0]; + if((movement.mPosition[1] < 0.0f && movement.mPosition[1] < moved.y*2.0f) || + (movement.mPosition[1] > 0.0f && movement.mPosition[1] > moved.y*2.0f)) + moved.y = movement.mPosition[1]; + if((movement.mPosition[2] < 0.0f && movement.mPosition[2] < moved.z*2.0f) || + (movement.mPosition[2] > 0.0f && movement.mPosition[2] > moved.z*2.0f)) + moved.z = movement.mPosition[2]; + + movement.mPosition[0] = moved.x; + movement.mPosition[1] = moved.y; + movement.mPosition[2] = moved.z; } mSkipAnim = false; - - return movement; } @@ -254,18 +488,18 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int count = std::max(count, 1); if(mode != 0 || mAnimQueue.size() == 0) { - mAnimQueue.clear(); - while(count-- > 0) - mAnimQueue.push_back(groupname); - mCurrentGroup = groupname; - mState = CharState_SpecialIdle; - mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false); + clearAnimQueue(); + mAnimQueue.push_back(std::make_pair(groupname, count-1)); + + mCharState = CharState_SpecialIdle; + mAnimation->play(groupname, Priority_Default, + MWRender::Animation::Group_All, false, + ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) { mAnimQueue.resize(1); - while(count-- > 0) - mAnimQueue.push_back(groupname); + mAnimQueue.push_back(std::make_pair(groupname, count-1)); } } } @@ -276,27 +510,35 @@ void CharacterController::skipAnim() } -void CharacterController::setState(CharacterState state, bool loop) +void CharacterController::clearAnimQueue() { - if(mState == state) - { - if(mAnimation) - mAnimation->setLooping(loop); - return; - } - mState = state; + if(mAnimQueue.size() > 0) + mAnimation->disable(mAnimQueue.front().first); + mAnimQueue.clear(); +} + +void CharacterController::setState(CharacterState state) +{ + if(mCharState == state) + return; + mCharState = state; + + forceStateUpdate(); +} + +void CharacterController::forceStateUpdate() +{ if(!mAnimation) return; - mAnimQueue.clear(); + clearAnimQueue(); - std::string anim; - getStateInfo(mState, &anim); - if(mAnimation->hasAnimation(anim)) - { - mCurrentGroup = anim; - mAnimation->play(mCurrentGroup, "start", "stop", loop); - } + std::string group; + Priority prio; + bool loops; + getCurrentGroup(group, prio, loops); + mAnimation->play(group, prio, MWRender::Animation::Group_All, false, + "start", "stop", 0.0f, loops ? (~(size_t)0) : 0); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 46f0690e77..7ffefab470 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -13,6 +13,18 @@ namespace MWRender namespace MWMechanics { +class Movement; + +enum Priority { + Priority_Default, + Priority_Weapon, + Priority_Torch, + + Priority_Death, + + Num_Priorities +}; + enum CharacterState { CharState_SpecialIdle, CharState_Idle, @@ -52,6 +64,9 @@ enum CharacterState { CharState_SneakLeft, CharState_SneakRight, + CharState_TurnLeft, + CharState_TurnRight, + CharState_Jump, /* Death states must be last! */ @@ -62,39 +77,60 @@ enum CharacterState { CharState_Death5 }; +enum WeaponType { + WeapType_None, + + WeapType_HandToHand, + WeapType_OneHand, + WeapType_TwoHand, + WeapType_TwoWide, + WeapType_BowAndArrow, + WeapType_Crossbow, + WeapType_ThowWeapon, + WeapType_PickProbe, + + WeapType_Spell +}; + class CharacterController { MWWorld::Ptr mPtr; MWRender::Animation *mAnimation; - typedef std::deque AnimationQueue; + typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; - std::string mCurrentGroup; - CharacterState mState; + CharacterState mCharState; + WeaponType mWeaponType; bool mSkipAnim; -protected: - /* Called by the animation whenever a new text key is reached. */ - void markerEvent(float time, const std::string &evt); + // counted for skill increase + float mSecondsOfSwimming; + float mSecondsOfRunning; - friend class MWRender::Animation; + // Gets an animation group name from the current character state, and whether it should loop. + void getCurrentGroup(std::string &group, Priority &prio, bool &loops) const; + + static void getWeaponGroup(WeaponType weaptype, std::string &group); + + void clearAnimQueue(); public: - CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); - CharacterController(const CharacterController &rhs); + CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state); virtual ~CharacterController(); void updatePtr(const MWWorld::Ptr &ptr); - Ogre::Vector3 update(float duration); + void update(float duration, Movement &movement); void playGroup(const std::string &groupname, int mode, int count); void skipAnim(); - void setState(CharacterState state, bool loop); + void setState(CharacterState state); CharacterState getState() const - { return mState; } + { return mCharState; } + + void forceStateUpdate(); }; } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 4be5d55b22..19d2080211 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -10,7 +10,8 @@ namespace MWMechanics { CreatureStats::CreatureStats() - : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), + : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), + mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false) { for (int i=0; i<4; ++i) @@ -167,7 +168,12 @@ namespace MWMechanics mDynamic[index] = value; if (index==0 && mDynamic[index].getCurrent()<1) + { + if (!mDead) + mDied = true; + mDead = true; + } } void CreatureStats::setLevel(int level) @@ -196,6 +202,16 @@ namespace MWMechanics return mDead; } + bool CreatureStats::hasDied() const + { + return mDied; + } + + void CreatureStats::clearHasDied() + { + mDied = false; + } + void CreatureStats::resurrect() { if (mDead) diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 3375c1af83..63de13d0d4 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -28,6 +28,7 @@ namespace MWMechanics AiSequence mAiSequence; float mLevelHealthBonus; bool mDead; + bool mDied; int mFriendlyHits; bool mTalkedTo; bool mAlarmed; @@ -100,6 +101,10 @@ namespace MWMechanics bool isDead() const; + bool hasDied() const; + + void clearHasDied(); + void resurrect(); bool hasCommonDisease() const; diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp new file mode 100644 index 0000000000..ded75f03a9 --- /dev/null +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -0,0 +1,265 @@ +#include "enchanting.hpp" +#include "../mwworld/player.hpp" +#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(): + mEnchantType(0) + {} + + void Enchanting::setOldItem(MWWorld::Ptr oldItem) + { + mOldItemPtr=oldItem; + if(!itemEmpty()) + { + mObjectType = mOldItemPtr.getTypeName(); + mOldItemId = mOldItemPtr.getCellRef().mRefID; + mOldItemCount = mOldItemPtr.getRefData().getCount(); + } + else + { + mObjectType=""; + mOldItemId=""; + } + } + + void Enchanting::setNewItemName(const std::string& s) + { + mNewItemName=s; + } + + void Enchanting::setEffect(ESM::EffectList effectList) + { + mEffectList=effectList; + } + + int Enchanting::getEnchantType() const + { + return mEnchantType; + } + + void Enchanting::setSoulGem(MWWorld::Ptr soulGem) + { + mSoulGemPtr=soulGem; + } + + bool Enchanting::create() + { + 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); + } + + if(mEnchantType==3) + { + enchantment.mData.mCharge=0; + } + enchantment.mData.mType = mEnchantType; + enchantment.mData.mCost = getEnchantCost(); + enchantment.mEffects = mEffectList; + + const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); + + 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() + { + mEnchantType++; + if (itemEmpty()) + { + mEnchantType = 0; + return; + } + 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()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); + + float cost1 = ((it->mMagnMin + it->mMagnMax)*it->mDuration*effect->mData.mBaseCost*0.025); + + float cost2 = (std::max(1, it->mArea)*0.125*effect->mData.mBaseCost); + + if(mEnchantType==3) + { + int constDurationMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentConstantDurationMult")->getFloat(); + cost1 *= constDurationMultipler; + cost2 *= 2; + } + if(effect->mData.mFlags & ESM::MagicEffect::CastTarget) + cost1 *= 1.5; + + float fullcost = cost1+cost2; + fullcost*= i; + i--; + + cost+=fullcost; + } + return cost; + } + + 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()) + return 0; + if(mSoulGemPtr.getCellRef().mSoul=="") + return 0; + const ESM::Creature* soul = store.get().find(mSoulGemPtr.getCellRef().mSoul); + return soul->mData.mSoul; + } + + float Enchanting::getMaxEnchantValue() const + { + if (itemEmpty()) + return 0; + return MWWorld::Class::get(mOldItemPtr).getEnchantmentPoints(mOldItemPtr); + } + bool Enchanting::soulEmpty() const + { + if (mSoulGemPtr.isEmpty()) + return true; + return false; + } + + 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 new file mode 100644 index 0000000000..2831f9ddb6 --- /dev/null +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -0,0 +1,48 @@ +#ifndef GAME_MWMECHANICS_ENCHANTING_H +#define GAME_MWMECHANICS_ENCHANTING_H +#include +#include "../mwworld/ptr.hpp" +#include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +namespace MWMechanics +{ + class Enchanting + { + MWWorld::Ptr mOldItemPtr; + MWWorld::Ptr mSoulGemPtr; + MWWorld::Ptr mEnchanter; + + int mEnchantType; + + bool mSelfEnchanting; + + ESM::EffectList mEffectList; + + std::string mNewItemName; + std::string mObjectType; + std::string mOldItemId; + int mOldItemCount; + + public: + Enchanting(); + void setEnchanter(MWWorld::Ptr enchanter); + void setSelfEnchanting(bool selfEnchanting); + void setOldItem(MWWorld::Ptr oldItem); + void setNewItemName(const std::string& s); + void setEffect(ESM::EffectList effectList); + void setSoulGem(MWWorld::Ptr soulGem); + bool create(); //Return true if created, false if failed. + void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object) + int getEnchantType() const; + float getEnchantCost() const; + int getEnchantPrice() const; + float 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 c9e4c77eaf..b83cfb365a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -2,6 +2,7 @@ #include "mechanicsmanagerimp.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -52,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) @@ -160,12 +149,18 @@ 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()); creatureStats.setDynamic (i, stat); } + + // auto-equip again. we need this for when the race is changed to a beast race + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); + for (int i=0; igetFloat(); - if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); + else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; @@ -653,19 +648,25 @@ namespace MWMechanics } } + void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr) + { + if(MWWorld::Class::get(ptr).isActor()) + mActors.forceStateUpdate(ptr); + } + void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { - if(ptr.getTypeName() == typeid(ESM::Activator).name()) - mActivators.playAnimationGroup(ptr, groupName, mode, number); - else + if(MWWorld::Class::get(ptr).isActor()) mActors.playAnimationGroup(ptr, groupName, mode, number); + else + mObjects.playAnimationGroup(ptr, groupName, mode, number); } void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr) { - if(ptr.getTypeName() == typeid(ESM::Activator).name()) - mActivators.skipAnimation(ptr); - else + if(MWWorld::Class::get(ptr).isActor()) mActors.skipAnimation(ptr); + else + mObjects.skipAnimation(ptr); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 5ad8705719..f3a38bf368 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -7,7 +7,7 @@ #include "creaturestats.hpp" #include "npcstats.hpp" -#include "activators.hpp" +#include "objects.hpp" #include "actors.hpp" namespace Ogre @@ -31,7 +31,7 @@ namespace MWMechanics bool mClassSelected; bool mRaceSelected; - Activators mActivators; + Objects mObjects; Actors mActors; void buildPlayer(); @@ -96,6 +96,8 @@ namespace MWMechanics void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC + virtual void forceStateUpdate(const MWWorld::Ptr &ptr); + virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void skipAnimation(const MWWorld::Ptr& ptr); }; diff --git a/apps/openmw/mwmechanics/movement.hpp b/apps/openmw/mwmechanics/movement.hpp index 11eb83151e..6c9a4b7589 100644 --- a/apps/openmw/mwmechanics/movement.hpp +++ b/apps/openmw/mwmechanics/movement.hpp @@ -6,11 +6,14 @@ namespace MWMechanics /// Desired movement for an actor struct Movement { - signed char mLeftRight; // 1: wants to move left, -1: wants to move right - signed char mForwardBackward; // 1:wants to move forward, -1: wants to move backward - signed char mUpDown; + float mPosition[3]; + float mRotation[3]; - Movement() : mLeftRight (0), mForwardBackward (0), mUpDown(0) {} + Movement() + { + mPosition[0] = mPosition[1] = mPosition[2] = 0.0f; + mRotation[0] = mRotation[1] = mRotation[2] = 0.0f; + } }; } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 26c4c8e9ac..def91a6c54 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -21,8 +21,16 @@ #include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() -: mMovementFlags (0), mDrawState (DrawState_Nothing), mBounty (0) -, mLevelProgress(0), mDisposition(0), mVampire (0), mReputation(0), mWerewolf (false), mWerewolfKills (0) +: mMovementFlags (0) +, mDrawState (DrawState_Nothing) +, mBounty (0) +, mLevelProgress(0) +, mDisposition(0) +, mVampire (0) +, mReputation(0) +, mWerewolf (false) +, mWerewolfKills (0) +, mProfit(0) { mSkillIncreases.resize (ESM::Attribute::Length); for (int i=0; igetGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); - MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox(message.str()); if (mLevelProgress >= 10) { // levelup is possible now - MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}"); } getSkill (skillIndex).setBase (base); @@ -326,7 +333,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()); @@ -354,3 +361,13 @@ int MWMechanics::NpcStats::getWerewolfKills() const { return mWerewolfKills; } + +int MWMechanics::NpcStats::getProfit() const +{ + return mProfit; +} + +void MWMechanics::NpcStats::modifyProfit(int diff) +{ + mProfit += diff; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index af32bd294a..b0d8db056e 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -53,6 +53,7 @@ namespace MWMechanics int mReputation; bool mWerewolf; int mWerewolfKills; + int mProfit; int mLevelProgress; // 0-10 @@ -64,6 +65,10 @@ namespace MWMechanics NpcStats(); + /// for mercenary companions. starts out as 0, and changes when items are added or removed through the UI. + int getProfit() const; + void modifyProfit(int diff); + DrawState_ getDrawState() const; void setDrawState (DrawState_ state); diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp new file mode 100644 index 0000000000..24d8a8bf7a --- /dev/null +++ b/apps/openmw/mwmechanics/objects.cpp @@ -0,0 +1,81 @@ +#include "objects.hpp" + +#include + +#include "movement.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +namespace MWMechanics +{ + +Objects::Objects() +{ +} + +void Objects::addObject(const MWWorld::Ptr& ptr) +{ + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); + if(anim != NULL) + mObjects.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle))); +} + +void Objects::removeObject(const MWWorld::Ptr& ptr) +{ + PtrControllerMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) + mObjects.erase(iter); +} + +void Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) +{ + PtrControllerMap::iterator iter = mObjects.find(old); + if(iter != mObjects.end()) + { + CharacterController ctrl = iter->second; + mObjects.erase(iter); + + ctrl.updatePtr(ptr); + mObjects.insert(std::make_pair(ptr, ctrl)); + } +} + +void Objects::dropObjects (const MWWorld::Ptr::CellStore *cellStore) +{ + PtrControllerMap::iterator iter = mObjects.begin(); + while(iter != mObjects.end()) + { + if(iter->first.getCell()==cellStore) + mObjects.erase(iter++); + else + ++iter; + } +} + +void Objects::update(float duration, bool paused) +{ + if(!paused) + { + for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter) + { + Movement movement; + iter->second.update(duration, movement); + } + } +} + +void Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) +{ + PtrControllerMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) + iter->second.playGroup(groupName, mode, number); +} +void Objects::skipAnimation(const MWWorld::Ptr& ptr) +{ + PtrControllerMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) + iter->second.skipAnim(); +} + +} diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp new file mode 100644 index 0000000000..7b1185a29a --- /dev/null +++ b/apps/openmw/mwmechanics/objects.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_MWMECHANICS_ACTIVATORS_H +#define GAME_MWMECHANICS_ACTIVATORS_H + +#include +#include + +#include "character.hpp" + +namespace MWWorld +{ + class Ptr; + class CellStore; +} + +namespace MWMechanics +{ + class Objects + { + typedef std::map PtrControllerMap; + PtrControllerMap mObjects; + + public: + Objects(); + + void addObject (const MWWorld::Ptr& ptr); + ///< Register an animated object + + void removeObject (const MWWorld::Ptr& ptr); + ///< Deregister an object + + void updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); + ///< Updates an object with a new Ptr + + void dropObjects(const MWWorld::CellStore *cellStore); + ///< Deregister all objects in the given cell. + + void update(float duration, bool paused); + ///< Update object animations + + void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + void skipAnimation(const MWWorld::Ptr& ptr); + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp new file mode 100644 index 0000000000..62c825be75 --- /dev/null +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -0,0 +1,233 @@ +#include "pathfinding.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include +#include +#include "boost/tuple/tuple.hpp" +#include "OgreMath.h" + +namespace +{ + //helpers functions + float distanceZCorrected(ESM::Pathgrid::Point point,float x,float y,float z) + { + return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+0.1*(point.mZ - z)*(point.mZ - z)); + } + + float distance(ESM::Pathgrid::Point point,float x,float y,float z) + { + return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+(point.mZ - z)*(point.mZ - z)); + } + + float distance(ESM::Pathgrid::Point a,ESM::Pathgrid::Point b) + { + return sqrt(float(a.mX - b.mX)*(a.mX - b.mX)+(a.mY - b.mY)*(a.mY - b.mY)+(a.mZ - b.mZ)*(a.mZ - b.mZ)); + } + + static float sgn(float a) + { + if(a>0) return 1.; + else return -1.; + } + + int getClosestPoint(const ESM::Pathgrid* grid,float x,float y,float z) + { + if(!grid) return -1; + if(grid->mPoints.empty()) return -1; + + float m = distance(grid->mPoints[0],x,y,z); + int i0 = 0; + + for(unsigned int i=1; imPoints.size();++i) + { + if(distance(grid->mPoints[i],x,y,z)mPoints[i],x,y,z); + i0 = i; + } + } + return i0; + } + + typedef boost::adjacency_list,boost::property > PathGridGraph; + typedef boost::property_map::type WeightMap; + typedef PathGridGraph::vertex_descriptor PointID; + typedef PathGridGraph::edge_descriptor PointConnectionID; + + struct found_path {}; + + /*class goalVisited : public boost::default_astar_visitor + { + public: + goalVisited(PointID goal) : mGoal(goal) {} + + void examine_vertex(PointID u, const PathGridGraph g) + { + if(u == mGoal) + throw found_path(); + } + private: + PointID mGoal; + }; + + class DistanceHeuristic : public boost::atasr_heuristic + { + public: + DistanceHeuristic(const PathGridGraph & l, PointID goal) + : mGraph(l), mGoal(goal) {} + + float operator()(PointID u) + { + const ESM::Pathgrid::Point & U = mGraph[u]; + const ESM::Pathgrid::Point & V = mGraph[mGoal]; + float dx = U.mX - V.mX; + float dy = U.mY - V.mY; + float dz = U.mZ - V.mZ; + return sqrt(dx * dx + dy * dy + dz * dz); + } + private: + const PathGridGraph & mGraph; + PointID mGoal; + };*/ + + class goalVisited : public boost::default_dijkstra_visitor + { + public: + goalVisited(PointID goal) : mGoal(goal) {} + + void examine_vertex(PointID u, const PathGridGraph g) + { + if(u == mGoal) + throw found_path(); + } + private: + PointID mGoal; + }; + + + PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0) + { + PathGridGraph graph; + + for(unsigned int i = 0;imPoints.size();++i) + { + PointID pID = boost::add_vertex(graph); + graph[pID].mX = pathgrid->mPoints[i].mX + xCell; + graph[pID].mY = pathgrid->mPoints[i].mY + yCell; + graph[pID].mZ = pathgrid->mPoints[i].mZ; + } + + for(unsigned int i = 0;imEdges.size();++i) + { + PointID u = pathgrid->mEdges[i].mV0; + PointID v = pathgrid->mEdges[i].mV1; + + PointConnectionID edge; + bool done; + boost::tie(edge,done) = boost::add_edge(u,v,graph); + WeightMap weightmap = boost::get(boost::edge_weight, graph); + weightmap[edge] = distance(graph[u],graph[v]); + + } + + return graph; + } + + std::list findPath(PointID start,PointID end,PathGridGraph graph){ + std::vector p(boost::num_vertices(graph)); + std::vector d(boost::num_vertices(graph)); + std::list shortest_path; + + try { + boost::dijkstra_shortest_paths + ( + graph, + start, + boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph)) + ); + + } catch(found_path fg) { + for(PointID v = end;; v = p[v]) { + shortest_path.push_front(graph[v]); + if(p[v] == v) + break; + } + } + return shortest_path; + } + + //end of helpers functions + +} + +namespace MWMechanics +{ + PathFinder::PathFinder() + { + mIsPathConstructed = false; + } + + void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, + const ESM::Pathgrid* pathGrid,float xCell,float yCell) + { + //first check if there is an obstacle + if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX,startPoint.mY,startPoint.mZ, + endPoint.mX,endPoint.mY,endPoint.mZ) ) + { + int start = getClosestPoint(pathGrid,startPoint.mX - xCell,startPoint.mY - yCell,startPoint.mZ); + int end = getClosestPoint(pathGrid,endPoint.mX - xCell,endPoint.mY - yCell,endPoint.mZ); + + if(start != -1 && end != -1) + { + PathGridGraph graph = buildGraph(pathGrid,xCell,yCell); + mPath = findPath(start,end,graph); + } + } + mPath.push_back(endPoint); + mIsPathConstructed = true; + } + + float PathFinder::getZAngleToNext(float x,float y,float z) + { + if(mPath.empty()) + { + return 0;/// shouldn't happen! + } + ESM::Pathgrid::Point nextPoint = *mPath.begin(); + float dX = nextPoint.mX - x; + float dY = nextPoint.mY - y; + float h = sqrt(dX*dX+dY*dY); + return Ogre::Radian(acos(dY/h)*sgn(asin(dX/h))).valueDegrees(); + } + + bool PathFinder::checkIfNextPointReached(float x,float y,float z) + { + if(mPath.empty()) + { + return true; + } + ESM::Pathgrid::Point nextPoint = *mPath.begin(); + if(distanceZCorrected(nextPoint,x,y,z) < 20) + { + mPath.pop_front(); + if(mPath.empty()) + { + return true; + } + nextPoint = *mPath.begin(); + } + return false; + } + + std::list PathFinder::getPath() + { + return mPath; + } + bool PathFinder::isPathConstructed() + { + return mIsPathConstructed; + } +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp new file mode 100644 index 0000000000..b1bbab37ab --- /dev/null +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -0,0 +1,29 @@ +#ifndef GAME_MWMECHANICS_PATHFINDING_H +#define GAME_MWMECHANICS_PATHFINDING_H + +#include +#include + +namespace MWMechanics +{ + class PathFinder + { + public: + PathFinder(); + + void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, + const ESM::Pathgrid* pathGrid,float xCell = 0,float yCell = 0); + + bool checkIfNextPointReached(float x,float y,float z);//returns true if the last point of the path has been reached. + float getZAngleToNext(float x,float y,float z); + + std::list getPath(); + bool isPathConstructed(); + + private: + std::list mPath; + bool mIsPathConstructed; + }; +} + +#endif \ No newline at end of file diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp new file mode 100644 index 0000000000..8ed4498859 --- /dev/null +++ b/apps/openmw/mwmechanics/repair.cpp @@ -0,0 +1,110 @@ +#include "repair.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" + +namespace MWMechanics +{ + +void Repair::repair(const MWWorld::Ptr &itemToRepair) +{ + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::LiveCellRef *ref = + mTool.get(); + + // reduce number of uses left + int uses = (mTool.getCellRef().mCharge != -1) ? mTool.getCellRef().mCharge : ref->mBase->mData.mUses; + mTool.getCellRef().mCharge = uses-1; + + // unstack tool if required + if (mTool.getRefData().getCount() > 1 && uses == ref->mBase->mData.mUses) + { + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + MWWorld::ContainerStoreIterator it = store.add(mTool); + it->getRefData().setCount(mTool.getRefData().getCount()-1); + it->getCellRef().mCharge = -1; + + mTool.getRefData().setCount(1); + } + + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player); + + float fatigueTerm = stats.getFatigueTerm(); + int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); + int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + int armorerSkill = npcStats.getSkill(ESM::Skill::Armorer).getModified(); + + float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() + .find("fRepairAmountMult")->getFloat(); + + float toolQuality = ref->mBase->mData.mQuality; + + float x = (0.1 * pcStrength + 0.1 * pcLuck + armorerSkill) * fatigueTerm; + + int roll = static_cast (std::rand()) / RAND_MAX * 100; + if (roll <= x) + { + int y = fRepairAmountMult * toolQuality * roll; + y = std::max(1, y); + + // repair by 'y' points + itemToRepair.getCellRef().mCharge += y; + itemToRepair.getCellRef().mCharge = std::min(itemToRepair.getCellRef().mCharge, + MWWorld::Class::get(itemToRepair).getItemMaxHealth(itemToRepair)); + + // set the OnPCRepair variable on the item's script + std::string script = MWWorld::Class::get(itemToRepair).getScript(itemToRepair); + if(script != "") + itemToRepair.getRefData().getLocals().setVarByInt(script, "onpcrepair", 1); + + // increase skill + MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Armorer, 0); + + MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); + MWBase::Environment::get().getWindowManager()->messageBox("#{sRepairSuccess}"); + } + else + { + MWBase::Environment::get().getSoundManager()->playSound("Repair Fail",1,1); + MWBase::Environment::get().getWindowManager()->messageBox("#{sRepairFailed}"); + } + + // tool used up? + if (mTool.getCellRef().mCharge == 0) + { + mTool.getRefData().setCount(0); + + std::string message = MWBase::Environment::get().getWorld()->getStore().get() + .find("sNotifyMessage51")->getString(); + + MWBase::Environment::get().getWindowManager()->messageBox((boost::format(message) % MWWorld::Class::get(mTool).getName(mTool)).str()); + + // try to find a new tool of the same ID + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + for (MWWorld::ContainerStoreIterator iter (store.begin()); + iter!=store.end(); ++iter) + { + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, mTool.getCellRef().mRefID)) + { + mTool = *iter; + break; + } + } + } +} + +} diff --git a/apps/openmw/mwmechanics/repair.hpp b/apps/openmw/mwmechanics/repair.hpp new file mode 100644 index 0000000000..6f9a866af1 --- /dev/null +++ b/apps/openmw/mwmechanics/repair.hpp @@ -0,0 +1,23 @@ +#ifndef OPENMW_MWMECHANICS_REPAIR_H +#define OPENMW_MWMECHANICS_REPAIR_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + class Repair + { + public: + void setTool (const MWWorld::Ptr& tool) { mTool = tool; } + MWWorld::Ptr getTool() { return mTool; } + + void repair (const MWWorld::Ptr& itemToRepair); + + private: + MWWorld::Ptr mTool; + }; + +} + +#endif diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp new file mode 100644 index 0000000000..d19da6e2af --- /dev/null +++ b/apps/openmw/mwmechanics/security.cpp @@ -0,0 +1,109 @@ +#include "security.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "npcstats.hpp" +#include "creaturestats.hpp" + +namespace MWMechanics +{ + + Security::Security(const MWWorld::Ptr &actor) + : mActor(actor) + { + CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats(actor); + mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified(); + mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); + mSecuritySkill = npcStats.getSkill(ESM::Skill::Security).getModified(); + mFatigueTerm = creatureStats.getFatigueTerm(); + } + + void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick, + std::string& resultMessage, std::string& resultSound) + { + if (lock.getCellRef().mLockLevel <= 0) + return; + + int lockStrength = lock.getCellRef().mLockLevel; + + float pickQuality = lockpick.get()->mBase->mData.mQuality; + + float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get().find("fPickLockMult")->getFloat(); + + float x = 0.2 * mAgility + 0.1 * mLuck + mSecuritySkill; + x *= pickQuality * mFatigueTerm; + x += fPickLockMult * lockStrength; + + resultSound = "Open Lock Fail"; + if (x <= 0) + resultMessage = "#{sLockImpossible}"; + else + { + int roll = static_cast (std::rand()) / RAND_MAX * 100; + if (roll <= x) + { + MWWorld::Class::get(lock).unlock(lock); + resultMessage = "#{sLockSuccess}"; + resultSound = "Open Lock"; + MWWorld::Class::get(mActor).skillUsageSucceeded(mActor, ESM::Skill::Security, 1); + } + else + resultMessage = "#{sLockFail}"; + } + + if (lockpick.getCellRef().mCharge == -1) + lockpick.getCellRef().mCharge = lockpick.get()->mBase->mData.mUses; + --lockpick.getCellRef().mCharge; + if (!lockpick.getCellRef().mCharge) + lockpick.getRefData().setCount(0); + } + + void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe, + std::string& resultMessage, std::string& resultSound) + { + if (trap.getCellRef().mTrap == "") + return; + + float probeQuality = probe.get()->mBase->mData.mQuality; + + const ESM::Spell* trapSpell = MWBase::Environment::get().getWorld()->getStore().get().find(trap.getCellRef().mTrap); + float trapSpellPoints = trapSpell->mData.mCost; + + float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fTrapCostMult")->getFloat(); + + float x = 0.2 * mAgility + 0.1 * mLuck + mSecuritySkill; + x += fTrapCostMult * trapSpellPoints; + x *= probeQuality * mFatigueTerm; + + resultSound = "Disarm Trap Fail"; + if (x <= 0) + resultMessage = "#{sTrapImpossible}"; + else + { + int roll = static_cast (std::rand()) / RAND_MAX * 100; + if (roll <= x) + { + trap.getCellRef().mTrap = ""; + + resultSound = "Disarm Trap"; + resultMessage = "#{sTrapSuccess}"; + MWWorld::Class::get(mActor).skillUsageSucceeded(mActor, ESM::Skill::Security, 0); + } + else + resultMessage = "#{sTrapFail}"; + } + + if (probe.getCellRef().mCharge == -1) + probe.getCellRef().mCharge = probe.get()->mBase->mData.mUses; + --probe.getCellRef().mCharge; + if (!probe.getCellRef().mCharge) + probe.getRefData().setCount(0); + } + +} diff --git a/apps/openmw/mwmechanics/security.hpp b/apps/openmw/mwmechanics/security.hpp new file mode 100644 index 0000000000..f3efb04ed8 --- /dev/null +++ b/apps/openmw/mwmechanics/security.hpp @@ -0,0 +1,27 @@ +#ifndef MWMECHANICS_SECURITY_H +#define MWMECHANICS_SECURITY_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + /// @brief implementation of Security skill + class Security + { + public: + Security (const MWWorld::Ptr& actor); + + void pickLock (const MWWorld::Ptr& lock, const MWWorld::Ptr& lockpick, + std::string& resultMessage, std::string& resultSound); + void probeTrap (const MWWorld::Ptr& trap, const MWWorld::Ptr& probe, + std::string& resultMessage, std::string& resultSound); + + private: + float mAgility, mLuck, mSecuritySkill, mFatigueTerm; + MWWorld::Ptr mActor; + }; + +} + +#endif diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index e2da7cdc86..e10dcdc93d 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -80,7 +80,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mFlags & ESM::Spell::ST_Disease) + if (spell->mData.mType == ESM::Spell::ST_Disease) return true; } @@ -94,7 +94,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mFlags & ESM::Spell::ST_Blight) + if (spell->mData.mType == ESM::Spell::ST_Blight) return true; } diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 961c070038..f3d0ec3ff4 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -1,9 +1,5 @@ #include "activatoranimation.hpp" -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" @@ -23,22 +19,12 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) assert (ref->mBase != NULL); if(!ref->mBase->mModel.empty()) { - std::string mesh = "meshes\\" + ref->mBase->mModel; + const std::string name = "meshes\\"+ref->mBase->mModel; - createEntityList(mPtr.getRefData().getBaseNode(), mesh); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - { - Ogre::Entity *ent = mEntityList.mEntities[i]; + setObjectRoot(mPtr.getRefData().getBaseNode(), name, false); + setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha); - for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = ent->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - - ent->setVisibilityFlags(RV_Misc); - } - setAnimationSource(mesh); + addAnimSource(name); } } diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 644d3613bd..566b6fa81e 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -94,6 +94,9 @@ void Actors::insertActivator (const MWWorld::Ptr& ptr) bool Actors::deleteObject (const MWWorld::Ptr& ptr) { + if (mAllActors.find(ptr) == mAllActors.end()) + return false; + mRendering->removeWaterRippleEmitter (ptr); delete mAllActors[ptr]; @@ -139,6 +142,7 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store) Ogre::SceneNode *base = celliter->second; base->removeAndDestroyAllChildren(); mRend.getScene()->destroySceneNode(base); + mCellSceneNodes.erase(celliter); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index cc926e685d..853ffc3752 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,100 +18,84 @@ namespace MWRender { +Ogre::Real Animation::AnimationValue::getValue() const +{ + AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName); + if(iter != mAnimation->mStates.end()) + return iter->second.mTime; + return 0.0f; +} + +void Animation::AnimationValue::setValue(Ogre::Real) +{ +} + + +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.mParticles.clear(); + objects.mEntities.clear(); + objects.mSkelBase = NULL; +} + Animation::Animation(const MWWorld::Ptr &ptr) : mPtr(ptr) - , mController(NULL) , mInsert(NULL) + , mSkelBase(NULL) , mAccumRoot(NULL) , mNonAccumRoot(NULL) - , mAccumulate(Ogre::Vector3::ZERO) - , mLastPosition(0.0f) - , mCurrentKeys(NULL) - , mCurrentAnim(NULL) - , mCurrentTime(0.0f) - , mStopTime(0.0f) - , mPlaying(false) - , mLooping(false) + , mNonAccumCtrl(NULL) + , mAccumulate(0.0f) , mAnimVelocity(0.0f) , mAnimSpeedMult(1.0f) { + for(size_t i = 0;i < sNumGroups;i++) + mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); } Animation::~Animation() { if(mInsert) { + mAnimSources.clear(); + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - sceneMgr->destroyEntity(mEntityList.mEntities[i]); - } - mEntityList.mEntities.clear(); - mEntityList.mSkelBase = NULL; -} - - -void Animation::setAnimationSources(const std::vector &names) -{ - if(!mEntityList.mSkelBase) - return; - - mCurrentAnim = NULL; - mCurrentKeys = NULL; - mAnimVelocity = 0.0f; - mAccumRoot = NULL; - mNonAccumRoot = NULL; - mSkeletonSources.clear(); - - std::vector::const_iterator nameiter; - for(nameiter = names.begin();nameiter != names.end();nameiter++) - { - Ogre::SkeletonPtr skel = NifOgre::Loader::getSkeleton(*nameiter); - if(skel.isNull()) - { - std::cerr<< "Failed to get skeleton source "<<*nameiter <touch(); - - Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); - while(boneiter.hasMoreElements()) - { - Ogre::Bone *bone = boneiter.getNext(); - Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings(); - const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID); - if(data.isEmpty() || !Ogre::any_cast(data)) - continue; - - if(!mNonAccumRoot) - { - mAccumRoot = mInsert; - mNonAccumRoot = mEntityList.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()); - if(!groupdata.isEmpty()) - mTextKeys[anim->getName()] = Ogre::any_cast(groupdata); - } - - break; - } + destroyObjectList(sceneMgr, mObjectRoot); } } -void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model) + +void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly) { + OgreAssert(!mInsert, "Object already has a root!"); mInsert = node->createChildSceneNode(); - assert(mInsert); - mEntityList = NifOgre::Loader::createEntities(mInsert, model); - if(mEntityList.mSkelBase) + std::string mdlname = Misc::StringUtils::lowerCase(model); + std::string::size_type p = mdlname.rfind('\\'); + if(p == std::string::npos) + p = mdlname.rfind('/'); + if(p != std::string::npos) + mdlname.insert(mdlname.begin()+p+1, 'x'); + else + mdlname.insert(mdlname.begin(), 'x'); + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(mdlname)) { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + mdlname = model; + Misc::StringUtils::toLower(mdlname); + } + + mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : + NifOgre::Loader::createObjectBase(mInsert, mdlname)); + if(mObjectRoot.mSkelBase) + { + mSkelBase = mObjectRoot.mSkelBase; + + Ogre::AnimationStateSet *aset = mObjectRoot.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -119,33 +105,173 @@ 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(); + // transformations manually + Ogre::SkeletonInstance *skelinst = mObjectRoot.mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); } + for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) + { + if(mObjectRoot.mControllers[i].getSource().isNull()) + mObjectRoot.mControllers[i].setSource(mAnimationValuePtr[0]); + } +} + +void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) +{ + for(size_t i = 0;i < objlist.mEntities.size();i++) + { + Ogre::Entity *ent = objlist.mEntities[i]; + if(visflags != 0) + ent->setVisibilityFlags(visflags); + + for(unsigned int j = 0;j < ent->getNumSubEntities();++j) + { + Ogre::SubEntity* subEnt = ent->getSubEntity(j); + subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue); + } + } + for(size_t i = 0;i < objlist.mParticles.size();i++) + { + Ogre::ParticleSystem *part = objlist.mParticles[i]; + if(visflags != 0) + part->setVisibilityFlags(visflags); + // TODO: Check particle material for actual transparency + part->setRenderQueueGroup(transqueue); + } +} + + +size_t Animation::detectAnimGroup(const Ogre::Node *node) +{ + static const char sGroupRoots[sNumGroups][32] = { + "", /* Lower body / character root */ + "Bip01 Spine1", /* Torso */ + "Bip01 L Clavicle", /* Left arm */ + "Bip01 R Clavicle", /* Right arm */ + }; + + while(node) + { + const Ogre::String &name = node->getName(); + for(size_t i = 1;i < sNumGroups;i++) + { + if(name == sGroupRoots[i]) + return i; + } + + node = node->getParent(); + } + + return 0; +} + + +void Animation::addAnimSource(const std::string &model) +{ + OgreAssert(mInsert, "Object is missing a root!"); + if(!mSkelBase) + return; + + std::string kfname = Misc::StringUtils::lowerCase(model); + std::string::size_type p = kfname.rfind('\\'); + if(p == std::string::npos) + p = kfname.rfind('/'); + if(p != std::string::npos) + kfname.insert(kfname.begin()+p+1, 'x'); + else + kfname.insert(kfname.begin(), 'x'); + + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + kfname.replace(kfname.size()-4, 4, ".kf"); + + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(kfname)) + return; + + std::vector > ctrls; + Ogre::SharedPtr animsrc(OGRE_NEW AnimSource); + NifOgre::Loader::createKfControllers(mSkelBase, kfname, animsrc->mTextKeys, ctrls); + if(animsrc->mTextKeys.size() == 0 || ctrls.size() == 0) + return; + + mAnimSources.push_back(animsrc); + + std::vector > *grpctrls = animsrc->mControllers; + for(size_t i = 0;i < ctrls.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + + size_t grp = detectAnimGroup(dstval->getNode()); + + if(!mAccumRoot && grp == 0) + { + mAccumRoot = mInsert; + mNonAccumRoot = dstval->getNode(); + } + + ctrls[i].setSource(mAnimationValuePtr[grp]); + grpctrls[grp].push_back(ctrls[i]); + } +} + +void Animation::clearAnimSources() +{ + mStates.clear(); + + for(size_t i = 0;i < sNumGroups;i++) + mAnimationValuePtr[i]->setAnimName(std::string()); + + mNonAccumCtrl = NULL; + mAnimVelocity = 0.0f; + + mAccumRoot = NULL; + mNonAccumRoot = NULL; + + mAnimSources.clear(); +} + + +Ogre::Node *Animation::getNode(const std::string &name) +{ + if(mSkelBase) + { + Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton(); + if(skel->hasBone(name)) + return skel->getBone(name); + } + return NULL; +} + + +NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname) +{ + NifOgre::TextKeyMap::const_iterator iter(keys.begin()); + for(;iter != keys.end();iter++) + { + if(iter->second.compare(0, groupname.size(), groupname) == 0 && + iter->second.compare(groupname.size(), 2, ": ") == 0) + break; + } + return iter; } bool Animation::hasAnimation(const std::string &anim) { - for(std::vector::const_iterator iter(mSkeletonSources.begin());iter != mSkeletonSources.end();iter++) + AnimSourceList::const_iterator iter(mAnimSources.begin()); + for(;iter != mAnimSources.end();iter++) { - if((*iter)->hasAnimation(anim)) + const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; + if(findGroupStart(keys, anim) != keys.end()) return true; } + return false; } -void Animation::setController(MWMechanics::CharacterController *controller) -{ - mController = controller; -} - - void Animation::setAccumulation(const Ogre::Vector3 &accum) { mAccumulate = accum; @@ -154,14 +280,10 @@ void Animation::setAccumulation(const Ogre::Vector3 &accum) void Animation::setSpeed(float speed) { mAnimSpeedMult = 1.0f; - if(mAnimVelocity > 1.0f && speed > 0.0f) + if(speed > 0.0f && mAnimVelocity > 1.0f) mAnimSpeedMult = speed / mAnimVelocity; } -void Animation::setLooping(bool loop) -{ - mLooping = loop; -} void Animation::updatePtr(const MWWorld::Ptr &ptr) { @@ -169,69 +291,36 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr) } -void Animation::calcAnimVelocity() +float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname) { - const Ogre::NodeAnimationTrack *track = 0; - - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(!track && trackiter.hasMoreElements()) + const std::string start = groupname+": start"; + const std::string loopstart = groupname+": loop start"; + const std::string loopstop = groupname+": loop stop"; + const std::string stop = groupname+": stop"; + float starttime = std::numeric_limits::max(); + float stoptime = 0.0f; + NifOgre::TextKeyMap::const_iterator keyiter(keys.begin()); + while(keyiter != keys.end()) { - const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) - track = cur; - } - - if(track && track->getNumKeyFrames() > 1) - { - float loopstarttime = 0.0f; - float loopstoptime = mCurrentAnim->getLength(); - NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin(); - while(keyiter != mCurrentKeys->end()) + if(keyiter->second == start || keyiter->second == loopstart) + starttime = keyiter->first; + else if(keyiter->second == loopstop || keyiter->second == stop) { - if(keyiter->second == "loop start") - loopstarttime = keyiter->first; - else if(keyiter->second == "loop stop") - { - loopstoptime = keyiter->first; - break; - } - keyiter++; - } - - if(loopstoptime > loopstarttime) - { - Ogre::TransformKeyFrame startkf(0, loopstarttime); - Ogre::TransformKeyFrame endkf(0, loopstoptime); - - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf); - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf); - - mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) / - (loopstoptime-loopstarttime); + stoptime = keyiter->first; + break; } + keyiter++; } -} -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()) + if(stoptime > starttime) { - 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); + Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum; + Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum; + + return startpos.distance(endpos) / (stoptime - starttime); } - // 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(); + return 0.0f; } static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) @@ -272,80 +361,81 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og } -Ogre::Vector3 Animation::updatePosition(float time) +void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &position) { - 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()); + /* Get the non-accumulation root's difference from the last update, and move the position + * accordingly. + */ + Ogre::Vector3 off = mNonAccumCtrl->getTranslation(newtime)*mAccumulate; + position += off - mNonAccumCtrl->getTranslation(oldtime)*mAccumulate; - Ogre::Vector3 posdiff = Ogre::Vector3::ZERO; - if(mNonAccumRoot) - { - /* 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); - } - return posdiff; + /* Translate the accumulation root back to compensate for the move. */ + mAccumRoot->setPosition(-off); } -void Animation::reset(const std::string &start, const std::string &stop) +bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) { - mNextKey = mCurrentKeys->begin(); - - while(mNextKey != mCurrentKeys->end() && mNextKey->second != start) - mNextKey++; - if(mNextKey != mCurrentKeys->end()) - mCurrentTime = mNextKey->first; - else + std::string tag = groupname+": "+start; + NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); + while(startkey != keys.end() && startkey->second != tag) + startkey++; + if(startkey == keys.end() && start == "loop start") { - mNextKey = mCurrentKeys->begin(); - mCurrentTime = 0.0f; + tag = groupname+": start"; + startkey = keys.begin(); + while(startkey != keys.end() && startkey->second != tag) + startkey++; + } + if(startkey == keys.end()) + return false; + + tag = groupname+": "+stop; + NifOgre::TextKeyMap::const_iterator stopkey(startkey); + while(stopkey != keys.end() && stopkey->second != tag) + stopkey++; + if(stopkey == keys.end()) + return false; + + if(startkey == stopkey) + return false; + + state.mStartKey = startkey; + state.mLoopStartKey = startkey; + state.mStopKey = stopkey; + state.mNextKey = startkey; + + state.mTime = state.mStartKey->first + ((state.mStopKey->first - state.mStartKey->first) * startpoint); + + tag = groupname+": loop start"; + while(state.mNextKey->first <= state.mTime && state.mNextKey != state.mStopKey) + { + if(state.mNextKey->second == tag) + state.mLoopStartKey = state.mNextKey; + state.mNextKey++; } - if(stop.length() > 0) - { - NifOgre::TextKeyMap::const_iterator stopKey = mNextKey; - while(stopKey != mCurrentKeys->end() && stopKey->second != stop) - stopKey++; - if(stopKey != mCurrentKeys->end()) - mStopTime = stopKey->first; - else - mStopTime = mCurrentAnim->getLength(); - } + return true; +} - if(mNonAccumRoot) - { - const Ogre::NodeAnimationTrack *track = 0; - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(!track && trackiter.hasMoreElements()) - { - const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) - track = cur; - } +bool Animation::doLoop(AnimState &state) +{ + if(state.mLoopCount == 0) + return false; + state.mLoopCount--; - if(track) - { - Ogre::TransformKeyFrame kf(0, mCurrentTime); - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf); - mLastPosition = kf.getTranslate() * mAccumulate; - } - } + state.mTime = state.mLoopStartKey->first; + state.mNextKey = state.mLoopStartKey; + state.mNextKey++; + state.mPlaying = true; + + return true; } -bool Animation::handleEvent(float time, const std::string &evt) +bool Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) { - if(evt == "start" || evt == "loop start") - { - /* Do nothing */ - return true; - } + float time = key->first; + const std::string &evt = key->second; if(evt.compare(0, 7, "sound: ") == 0) { @@ -360,94 +450,290 @@ bool Animation::handleEvent(float time, const std::string &evt) return true; } - if(evt == "loop stop") + if(evt.compare(0, groupname.size(), groupname) != 0 || + evt.compare(groupname.size(), 2, ": ") != 0) { - if(mLooping) + // Not ours, skip it + return true; + } + size_t off = groupname.size()+2; + size_t len = evt.size() - off; + + if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) + { + state.mLoopStartKey = key; + return true; + } + + if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) + { + if(doLoop(state)) { - reset("loop start", ""); - if(mCurrentTime >= time) + if(state.mTime >= time) return false; } return true; } - if(evt == "stop") + + if(evt.compare(off, len, "equip attach") == 0) { - if(mLooping) - { - reset("loop start", ""); - if(mCurrentTime >= time) - return false; - return true; - } - // fall-through + showWeapons(true); + return true; } - if(mController) - mController->markerEvent(time, evt); + if(evt.compare(off, len, "unequip detach") == 0) + { + showWeapons(false); + return true; + } + + /* Nothing to do for these */ + if(evt.compare(off, len, "equip start") == 0 || evt.compare(off, len, "equip stop") == 0 || + evt.compare(off, len, "unequip start") == 0 || evt.compare(off, len, "unequip stop") == 0) + return true; + + std::cerr<< "Unhandled animation textkey: "<::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++) + if(!mSkelBase) + return; + + if(groupname.empty()) + { + resetActiveGroups(); + return; + } + + priority = std::max(0, priority); + + AnimStateMap::iterator stateiter = mStates.begin(); + while(stateiter != mStates.end()) + { + if(stateiter->second.mPriority == priority) + mStates.erase(stateiter++); + else + stateiter++; + } + + stateiter = mStates.find(groupname); + if(stateiter != mStates.end()) + { + stateiter->second.mPriority = priority; + resetActiveGroups(); + return; + } + + /* Look in reverse; last-inserted source has priority. */ + AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); + for(;iter != mAnimSources.rend();iter++) + { + AnimState state; + if(reset(state, (*iter)->mTextKeys, groupname, start, stop, startpoint)) { - if((*iter)->hasAnimation(groupname)) + state.mSource = *iter; + state.mLoopCount = loops; + state.mPlaying = true; + state.mPriority = priority; + state.mGroups = groups; + state.mAutoDisable = autodisable; + mStates[groupname] = state; + + break; + } + } + if(iter == mAnimSources.rend()) + std::cerr<< "Failed to find animation "<second.mPlaying; + return false; +} + +void Animation::resetActiveGroups() +{ + for(size_t grp = 0;grp < sNumGroups;grp++) + { + AnimStateMap::const_iterator active = mStates.end(); + + AnimStateMap::const_iterator state = mStates.begin(); + for(;state != mStates.end();state++) + { + if(!(state->second.mGroups&(1<second.mPriority < state->second.mPriority) + active = state; + } + + mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ? + std::string() : active->first); + } + + mNonAccumCtrl = NULL; + mAnimVelocity = 0.0f; + + if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f)) + return; + + AnimStateMap::const_iterator state = mStates.find(mAnimationValuePtr[0]->getAnimName()); + if(state == mStates.end()) + return; + + const Ogre::SharedPtr &animsrc = state->second.mSource; + const NifOgre::TextKeyMap &keys = animsrc->mTextKeys; + const std::vector >&ctrls = animsrc->mControllers[0]; + for(size_t i = 0;i < ctrls.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + mAnimVelocity = calcAnimVelocity(keys, dstval, mAccumulate, state->first); + mNonAccumCtrl = dstval; + break; + } + } + + // If there's no velocity, keep looking + if(!(mAnimVelocity > 1.0f)) + { + AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin(); + while(*animiter != animsrc) + ++animiter; + + while(!(mAnimVelocity > 1.0f) && ++animiter != mAnimSources.rend()) + { + const NifOgre::TextKeyMap &keys = (*animiter)->mTextKeys; + const std::vector >&ctrls = (*animiter)->mControllers[0]; + for(size_t i = 0;i < ctrls.size();i++) { - mCurrentAnim = (*iter)->getAnimation(groupname); - mCurrentKeys = &mTextKeys[groupname]; - mAnimVelocity = 0.0f; - - if(mNonAccumRoot) - calcAnimVelocity(); - - found = true; - break; + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + mAnimVelocity = calcAnimVelocity(keys, dstval, mAccumulate, state->first); + break; + } } } - if(!found) - throw std::runtime_error("Failed to find animation "+groupname); - - reset(start, stop); - setLooping(loop); - mPlaying = true; - } - catch(std::exception &e) { - std::cerr<< e.what() <end() || mNextKey->first > targetTime) + if(complete) *complete = 0.0f; + if(start) *start = ""; + if(stop) *stop = ""; + return false; + } + + if(complete) *complete = (iter->second.mTime - iter->second.mStartKey->first) / + (iter->second.mStopKey->first - iter->second.mStartKey->first); + if(start) *start = iter->second.mStartKey->second.substr(groupname.size()+2); + if(stop) *stop = iter->second.mStopKey->second.substr(groupname.size()+2); + return true; +} + + +void Animation::disable(const std::string &groupname) +{ + AnimStateMap::iterator iter = mStates.find(groupname); + if(iter != mStates.end()) + mStates.erase(iter); + resetActiveGroups(); +} + + +Ogre::Vector3 Animation::runAnimation(float duration) +{ + Ogre::Vector3 movement(0.0f); + + duration *= mAnimSpeedMult; + AnimStateMap::iterator stateiter = mStates.begin(); + while(stateiter != mStates.end()) + { + AnimState &state = stateiter->second; + float timepassed = duration; + while(state.mPlaying) { - movement += updatePosition(targetTime); - mPlaying = (mLooping || mStopTime > targetTime); - break; + float targetTime = state.mTime + timepassed; + if(state.mNextKey->first > targetTime) + { + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + updatePosition(state.mTime, targetTime, movement); + state.mTime = targetTime; + break; + } + + NifOgre::TextKeyMap::const_iterator key(state.mNextKey++); + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + updatePosition(state.mTime, key->first, movement); + state.mTime = key->first; + + state.mPlaying = (key != state.mStopKey); + timepassed = targetTime - state.mTime; + + if(!handleTextKey(state, stateiter->first, key)) + break; } - float time = mNextKey->first; - const std::string &evt = mNextKey->second; - mNextKey++; + if(!state.mPlaying && state.mAutoDisable) + { + mStates.erase(stateiter++); + resetActiveGroups(); + } + else + stateiter++; + } - movement += updatePosition(time); - mPlaying = (mLooping || mStopTime > time); + for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) + mObjectRoot.mControllers[i].update(); - timepassed = targetTime - time; + // Apply group controllers + for(size_t grp = 0;grp < sNumGroups;grp++) + { + const std::string &name = mAnimationValuePtr[grp]->getAnimName(); + if(!name.empty() && (stateiter=mStates.find(name)) != mStates.end()) + { + const Ogre::SharedPtr &src = stateiter->second.mSource; + for(size_t i = 0;i < src->mControllers[grp].size();i++) + src->mControllers[grp][i].update(); + } + } - if(!handleEvent(time, evt)) - break; + if(mSkelBase) + { + // HACK: Dirty the animation state set so that Ogre will apply the + // transformations to entities this skeleton instance is shared with. + mSkelBase->getAllAnimationStates()->_notifyDirty(); } return movement; } +void Animation::showWeapons(bool showWeapon) +{ +} + +bool Animation::isPriorityActive(int priority) const +{ + for (AnimStateMap::const_iterator it = mStates.begin(); it != mStates.end(); ++it) + if (it->second.mPriority == priority) + return true; + return false; +} + } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 7caf351694..31be0fb2a7 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,88 +1,156 @@ #ifndef _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H +#include +#include + #include #include "../mwworld/ptr.hpp" -namespace MWMechanics -{ - class CharacterController; -} namespace MWRender { class Animation { +public: + enum Group { + Group_LowerBody = 1<<0, + + Group_Torso = 1<<1, + Group_LeftArm = 1<<2, + Group_RightArm = 1<<3, + + Group_UpperBody = Group_Torso | Group_LeftArm | Group_RightArm, + + Group_All = Group_LowerBody | Group_UpperBody + }; + protected: + /* This is the number of *discrete* groups. */ + static const size_t sNumGroups = 4; + + class AnimationValue : public Ogre::ControllerValue + { + private: + Animation *mAnimation; + std::string mAnimationName; + + public: + AnimationValue(Animation *anim) + : mAnimation(anim) + { } + + void setAnimName(const std::string &name) + { mAnimationName = name; } + const std::string &getAnimName() const + { return mAnimationName; } + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value); + }; + + struct AnimSource : public Ogre::AnimationAlloc { + NifOgre::TextKeyMap mTextKeys; + std::vector > mControllers[sNumGroups]; + }; + typedef std::vector< Ogre::SharedPtr > AnimSourceList; + + struct AnimState { + Ogre::SharedPtr mSource; + NifOgre::TextKeyMap::const_iterator mStartKey; + NifOgre::TextKeyMap::const_iterator mLoopStartKey; + NifOgre::TextKeyMap::const_iterator mStopKey; + NifOgre::TextKeyMap::const_iterator mNextKey; + + float mTime; + + bool mPlaying; + size_t mLoopCount; + + int mPriority; + int mGroups; + bool mAutoDisable; + + AnimState() : mTime(0.0f), mPlaying(false), mLoopCount(0), + mPriority(0), mGroups(0), mAutoDisable(true) + { } + }; + typedef std::map AnimStateMap; + MWWorld::Ptr mPtr; - MWMechanics::CharacterController *mController; - Ogre::SceneNode* mInsert; - NifOgre::EntityList mEntityList; - std::map mTextKeys; + Ogre::SceneNode *mInsert; + Ogre::Entity *mSkelBase; + NifOgre::ObjectList mObjectRoot; + AnimSourceList mAnimSources; Ogre::Node *mAccumRoot; - Ogre::Bone *mNonAccumRoot; + Ogre::Node *mNonAccumRoot; + NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; - Ogre::Vector3 mLastPosition; - std::vector mSkeletonSources; + AnimStateMap mStates; - NifOgre::TextKeyMap *mCurrentKeys; - NifOgre::TextKeyMap::const_iterator mNextKey; - Ogre::Animation *mCurrentAnim; - float mCurrentTime; - float mStopTime; - bool mPlaying; - bool mLooping; + Ogre::SharedPtr mAnimationValuePtr[sNumGroups]; float mAnimVelocity; float mAnimSpeedMult; - void calcAnimVelocity(); + /* Sets the appropriate animations on the bone groups based on priority. + */ + void resetActiveGroups(); - /* Applies the given animation to the given skeleton instance, using the specified time. */ - void applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel); + static size_t detectAnimGroup(const Ogre::Node *node); + + static float calcAnimVelocity(const NifOgre::TextKeyMap &keys, + NifOgre::NodeTargetValue *nonaccumctrl, + const Ogre::Vector3 &accum, + const std::string &groupname); /* 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 given time, and + * returns the wanted movement vector from the previous time. */ + void updatePosition(float oldtime, float newtime, Ogre::Vector3 &position); + + static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname); /* 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 - * the marker is not found, it resets to the beginning or end respectively. + * the marker is not found, or if the markers are the same, it returns + * false. */ - void reset(const std::string &start, const std::string &stop); + bool reset(AnimState &state, const NifOgre::TextKeyMap &keys, + const std::string &groupname, const std::string &start, const std::string &stop, + float startpoint); - bool handleEvent(float time, const std::string &evt); + bool doLoop(AnimState &state); - /* Specifies a list of skeleton names to use as animation sources. */ - void setAnimationSources(const std::vector &names); + bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); - /* Specifies a single skeleton name to use as an animation source. */ - void setAnimationSource(const std::string &name) - { - std::vector names(1, name); - setAnimationSources(names); - } + void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly); + void addAnimSource(const std::string &model); - void createEntityList(Ogre::SceneNode *node, const std::string &model); + static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); + + static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); + + void clearAnimSources(); public: Animation(const MWWorld::Ptr &ptr); virtual ~Animation(); - void setController(MWMechanics::CharacterController *controller); - void updatePtr(const MWWorld::Ptr &ptr); bool hasAnimation(const std::string &anim); + bool isPriorityActive (int priority) const; + ///< Is there an animation playing with the given priority? + // Specifies the axis' to accumulate on. Non-accumulated axis will just // move visually, but not affect the actual movement. Each x/y/z value // should be on the scale of 0 to 1. @@ -90,10 +158,48 @@ public: void setSpeed(float speed); - void setLooping(bool loop); + /** Plays an animation. + * \param groupname Name of the animation group to play. + * \param priority Priority of the animation. The animation will play on + * bone groups that don't have another animation set of a + * higher priority. + * \param groups Bone groups to play the animation on. + * \param autodisable Automatically disable the animation when it stops + * playing. + * \param start Key marker from which to start. + * \param stop Key marker to stop at. + * \param startpoint How far in between the two markers to start. 0 starts + * at the start marker, 1 starts at the stop marker. + * \param loops How many times to loop the animation. This will use the + * "loop start" and "loop stop" markers if they exist, + * otherwise it will use "start" and "stop". + */ + void play(const std::string &groupname, int priority, int groups, bool autodisable, + const std::string &start, const std::string &stop, + float startpoint, size_t loops); - void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); - virtual Ogre::Vector3 runAnimation(float timepassed); + /** Returns true if the named animation group is playing. */ + bool isPlaying(const std::string &groupname) const; + + /** Gets info about the given animation group. + * \param groupname Animation group to check. + * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. + * \param start Stores the start key + * \param stop Stores the stop key + * \return True if the animation is active, false otherwise. + */ + bool getInfo(const std::string &groupname, float *complete=NULL, std::string *start=NULL, std::string *stop=NULL) const; + + /** Disables the specified animation group; + * \param groupname Animation group to disable. + */ + void disable(const std::string &groupname); + + virtual Ogre::Vector3 runAnimation(float duration); + + virtual void showWeapons(bool showWeapon); + + Ogre::Node *getNode(const std::string &name); }; } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/camera.cpp similarity index 55% rename from apps/openmw/mwrender/player.cpp rename to apps/openmw/mwrender/camera.cpp index 63396378d0..e71e694f96 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -1,7 +1,8 @@ -#include "player.hpp" +#include "camera.hpp" #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -14,10 +15,9 @@ namespace MWRender { - Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) + Camera::Camera (Ogre::Camera *camera) : mCamera(camera), - mPlayerNode(node), - mCameraNode(mPlayerNode->createChildSceneNode()), + mCameraNode(NULL), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), @@ -28,51 +28,24 @@ namespace MWRender { mVanity.enabled = false; mVanity.allowed = true; - mVanity.forced = false; - - mCameraNode->attachObject(mCamera); - mCameraNode->setPosition(0.f, 0.f, mHeight); mPreviewCam.yaw = 0.f; mPreviewCam.offset = 400.f; } - Player::~Player() + Camera::~Camera() { - delete mAnimation; - } - - bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) - { - if (mVanity.enabled) { - toggleVanityMode(false); - } - - Ogre::Vector3 trueRot = rot; - - /// \note rotate player on forced vanity - if (mVanity.forced) { - if (mFreeLook) { - float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z; - - mVanity.enabled = false; - rotateCamera(rot, adjust); - mVanity.enabled = true; - - compensateYaw(diff); - } - trueRot.z = 0.f; - } - - if (mFreeLook || mVanity.enabled || mPreviewMode) { - rotateCamera(trueRot, adjust); - } - - /// \note if vanity mode is forced by TVM then rotate player - return (!mVanity.enabled && !mPreviewMode) || mVanity.forced; } - void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) + void Camera::reset() + { + togglePreviewMode(false); + toggleVanityMode(false); + if (!mFirstPersonView) + toggleViewMode(); + } + + void Camera::rotateCamera(const Ogre::Vector3 &rot, bool adjust) { if (adjust) { setYaw(getYaw() + rot.z); @@ -81,33 +54,37 @@ namespace MWRender setYaw(rot.z); setPitch(rot.x); } - Ogre::Quaternion xr( - Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), - Ogre::Vector3::UNIT_X - ); - Ogre::Quaternion zr( - Ogre::Radian(getYaw()), - Ogre::Vector3::NEGATIVE_UNIT_Z - ); + + Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X); if (!mVanity.enabled && !mPreviewMode) { - mPlayerNode->setOrientation(zr); mCameraNode->setOrientation(xr); } else { + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); mCameraNode->setOrientation(zr * xr); } } - std::string Player::getHandle() const + const std::string &Camera::getHandle() const { - return mPlayerNode->getName(); + return mTrackingPtr.getRefData().getHandle(); } - void Player::attachTo(const MWWorld::Ptr &ptr) + void Camera::attachTo(const MWWorld::Ptr &ptr) { - ptr.getRefData().setBaseNode(mPlayerNode); + mTrackingPtr = ptr; + Ogre::SceneNode *node = mTrackingPtr.getRefData().getBaseNode()->createChildSceneNode(Ogre::Vector3(0.0f, 0.0f, mHeight)); + if(mCameraNode) + { + node->setOrientation(mCameraNode->getOrientation()); + node->setPosition(mCameraNode->getPosition()); + node->setScale(mCameraNode->getScale()); + mCameraNode->getCreator()->destroySceneNode(mCameraNode); + } + mCameraNode = node; + mCameraNode->attachObject(mCamera); } - void Player::updateListener() + void Camera::updateListener() { Ogre::Vector3 pos = mCamera->getRealPosition(); Ogre::Vector3 dir = mCamera->getRealDirection(); @@ -116,29 +93,27 @@ namespace MWRender MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); } - void Player::update(float duration) + void Camera::update(float duration) { updateListener(); // only show the crosshair in game mode and in first person mode. - MWBase::Environment::get().getWindowManager ()->showCrosshair - (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); + MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); + wm->showCrosshair(!wm->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; - } - if (mVanity.enabled) { + if(mVanity.enabled) + { Ogre::Vector3 rot(0.f, 0.f, 0.f); rot.z = Ogre::Degree(3.f * duration).valueRadians(); rotateCamera(rot, true); } } - void Player::toggleViewMode() + void Camera::toggleViewMode() { mFirstPersonView = !mFirstPersonView; + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); setLowHeight(false); @@ -148,25 +123,24 @@ namespace MWRender } } - void Player::allowVanityMode(bool allow) + void Camera::allowVanityMode(bool allow) { - if (!allow && mVanity.enabled && !mVanity.forced) { + if (!allow && mVanity.enabled) toggleVanityMode(false); - } mVanity.allowed = allow; } - bool Player::toggleVanityMode(bool enable, bool force) + bool Camera::toggleVanityMode(bool enable) { - if ((mVanity.forced && !force) || - (!mVanity.allowed && (force || enable))) - { + if(!mVanity.allowed && enable) return false; - } else if (mVanity.enabled == enable) { + + if(mVanity.enabled == enable) return true; - } mVanity.enabled = enable; - mVanity.forced = force && enable; + + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); @@ -182,18 +156,22 @@ namespace MWRender setLowHeight(!mFirstPersonView); } rot.z = getYaw(); + mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); return true; } - void Player::togglePreviewMode(bool enable) + void Camera::togglePreviewMode(bool enable) { - if (mPreviewMode == enable) { + if(mPreviewMode == enable) return; - } + mPreviewMode = enable; + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); + float offset = mCamera->getPosition().z; if (mPreviewMode) { mMainCam.offset = offset; @@ -206,19 +184,19 @@ namespace MWRender setLowHeight(!mFirstPersonView); } + mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } - float Player::getYaw() + float Camera::getYaw() { - if (mVanity.enabled || mPreviewMode) { + if(mVanity.enabled || mPreviewMode) return mPreviewCam.yaw; - } return mMainCam.yaw; } - void Player::setYaw(float angle) + void Camera::setYaw(float angle) { if (angle > Ogre::Math::PI) { angle -= Ogre::Math::TWO_PI; @@ -232,7 +210,7 @@ namespace MWRender } } - float Player::getPitch() + float Camera::getPitch() { if (mVanity.enabled || mPreviewMode) { return mPreviewCam.pitch; @@ -240,17 +218,18 @@ namespace MWRender return mMainCam.pitch; } - void Player::setPitch(float angle) + void Camera::setPitch(float angle) { - float limit = Ogre::Math::HALF_PI; - if (mVanity.forced || mPreviewMode) { - limit /= 2; - } - if (angle > limit) { - angle = limit - 0.01; - } else if (angle < -limit) { - angle = -limit + 0.01; - } + const float epsilon = 0.000001f; + float limit = Ogre::Math::HALF_PI - epsilon; + if(mPreviewMode) + limit /= 2; + + if(angle > limit) + angle = limit; + else if(angle < -limit) + angle = -limit; + if (mVanity.enabled || mPreviewMode) { mPreviewCam.pitch = angle; } else { @@ -258,11 +237,11 @@ namespace MWRender } } - void Player::setCameraDistance(float dist, bool adjust, bool override) + void Camera::setCameraDistance(float dist, bool adjust, bool override) { - if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) { + if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) return; - } + Ogre::Vector3 v(0.f, 0.f, dist); if (adjust) { v += mCamera->getPosition(); @@ -287,7 +266,7 @@ namespace MWRender } } - void Player::setCameraDistance() + void Camera::setCameraDistance() { if (mDistanceAdjusted) { if (mVanity.enabled || mPreviewMode) { @@ -299,64 +278,54 @@ namespace MWRender mDistanceAdjusted = false; } - void Player::setAnimation(NpcAnimation *anim) + void Camera::setAnimation(NpcAnimation *anim) { - delete mAnimation; + // If we're switching to a new NpcAnimation, ensure the old one is + // using a normal view mode + if(mAnimation && mAnimation != anim) + mAnimation->setViewMode(NpcAnimation::VM_Normal); mAnimation = anim; - - mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView); + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); } - void Player::setHeight(float height) + void Camera::setHeight(float height) { mHeight = height; mCameraNode->setPosition(0.f, 0.f, mHeight); } - float Player::getHeight() + float Camera::getHeight() { - return mHeight * mPlayerNode->getScale().z; + return mHeight * mTrackingPtr.getRefData().getBaseNode()->getScale().z; } - bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) + bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) { mCamera->getParentSceneNode ()->needUpdate(true); camera = mCamera->getRealPosition(); - player = mPlayerNode->getPosition(); + player = mTrackingPtr.getRefData().getBaseNode()->getPosition(); return mFirstPersonView && !mVanity.enabled && !mPreviewMode; } - Ogre::Vector3 Player::getPosition() + Ogre::Vector3 Camera::getPosition() { - return mPlayerNode->getPosition(); + return mTrackingPtr.getRefData().getBaseNode()->getPosition(); } - void Player::getSightAngles(float &pitch, float &yaw) + void Camera::getSightAngles(float &pitch, float &yaw) { pitch = mMainCam.pitch; yaw = mMainCam.yaw; } - void Player::compensateYaw(float diff) - { - mPreviewCam.yaw -= diff; - Ogre::Quaternion zr( - Ogre::Radian(mPreviewCam.yaw), - Ogre::Vector3::NEGATIVE_UNIT_Z - ); - Ogre::Quaternion xr( - Ogre::Radian(mPreviewCam.pitch), - Ogre::Vector3::UNIT_X); - mCameraNode->setOrientation(zr * xr); - } - - void Player::togglePlayerLooking(bool enable) + void Camera::togglePlayerLooking(bool enable) { mFreeLook = enable; } - void Player::setLowHeight(bool low) + void Camera::setLowHeight(bool low) { if (low) { mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); @@ -365,7 +334,7 @@ namespace MWRender } } - bool Player::isVanityOrPreviewModeEnabled() + bool Camera::isVanityOrPreviewModeEnabled() { return mPreviewMode || mVanity.enabled; } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/camera.hpp similarity index 71% rename from apps/openmw/mwrender/player.hpp rename to apps/openmw/mwrender/camera.hpp index 9de41823de..ad5e35f939 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -1,8 +1,10 @@ -#ifndef GAME_MWRENDER_PLAYER_H -#define GAME_MWRENDER_PLAYER_H +#ifndef GAME_MWRENDER_CAMERA_H +#define GAME_MWRENDER_CAMERA_H #include +#include "../mwworld/ptr.hpp" + namespace Ogre { class Vector3; @@ -10,24 +12,20 @@ namespace Ogre class SceneNode; } -namespace MWWorld -{ - class Ptr; -} - namespace MWRender { class NpcAnimation; - /// \brief Player character rendering and camera control - class Player + + /// \brief Camera control + class Camera { struct CamData { float pitch, yaw, offset; }; - Ogre::Camera *mCamera; + MWWorld::Ptr mTrackingPtr; - Ogre::SceneNode *mPlayerNode; + Ogre::Camera *mCamera; Ogre::SceneNode *mCameraNode; NpcAnimation *mAnimation; @@ -37,7 +35,7 @@ namespace MWRender bool mFreeLook; struct { - bool enabled, allowed, forced; + bool enabled, allowed; } mVanity; float mHeight, mCameraDistance; @@ -51,15 +49,14 @@ namespace MWRender void setLowHeight(bool low = true); public: + Camera(Ogre::Camera *camera); + ~Camera(); - Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); - ~Player(); + /// Reset to defaults + void reset(); - /// Set where the player is looking at. Uses Morrowind (euler) angles + /// Set where the camera is looking at. Uses Morrowind (euler) angles /// \param rot Rotation angles in radians - /// \return true if player object needs to bo rotated physically - bool rotate(const Ogre::Vector3 &rot, bool adjust); - void rotateCamera(const Ogre::Vector3 &rot, bool adjust); float getYaw(); @@ -68,22 +65,21 @@ namespace MWRender float getPitch(); void setPitch(float angle); - void compensateYaw(float diff); - - std::string getHandle() const; + const std::string &getHandle() const; /// Attach camera to object - /// \note there is no protection from attaching the same camera to - /// several different objects void attachTo(const MWWorld::Ptr &); void toggleViewMode(); - bool toggleVanityMode(bool enable, bool force = false); + bool toggleVanityMode(bool enable); void allowVanityMode(bool allow); void togglePreviewMode(bool enable); + bool isFirstPerson() const + { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } + void update(float duration); /// Set camera distance for current mode. Don't work on 1st person view. @@ -96,8 +92,6 @@ namespace MWRender void setCameraDistance(); void setAnimation(NpcAnimation *anim); - NpcAnimation *getAnimation() const - { return mAnimation; } void setHeight(float height); float getHeight(); diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c99e426624..e4bba289f2 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -12,6 +12,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" #include "renderconst.hpp" #include "npcanimation.hpp" @@ -21,13 +22,15 @@ namespace MWRender CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, Ogre::Vector3 position, Ogre::Vector3 lookAt) - : mSizeX(sizeX) - , mSizeY(sizeY) - , mName(name) + + : mSceneMgr (0) , mPosition(position) , mLookAt(lookAt) , mCharacter(character) , mAnimation(NULL) + , mName(name) + , mSizeX(sizeX) + , mSizeY(sizeY) { } @@ -59,10 +62,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); @@ -90,25 +91,30 @@ namespace MWRender CharacterPreview::~CharacterPreview () { - //Ogre::TextureManager::getSingleton().remove(mName); - mSceneMgr->destroyCamera (mName); - delete mAnimation; - Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); + if (mSceneMgr) + { + //Ogre::TextureManager::getSingleton().remove(mName); + mSceneMgr->destroyAllCameras(); + delete mAnimation; + Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); + } } void CharacterPreview::rebuild() { assert(mAnimation); delete mAnimation; + mAnimation = 0; - 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)); - mNode->setVisible (false); + float scale=1.f; + MWWorld::Class::get(mCharacter).adjustScale(mCharacter, scale); + mNode->setScale(Ogre::Vector3(scale)); - Ogre::Vector3 scale = mNode->getScale(); - mCamera->setPosition(mPosition * scale); - mCamera->lookAt(mLookAt * scale); + mCamera->setPosition(mPosition * mNode->getScale()); + mCamera->lookAt(mLookAt * mNode->getScale()); onSetup(); } @@ -129,6 +135,56 @@ namespace MWRender void InventoryPreview::update(int sizeX, int sizeY) { + MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter); + MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + std::string groupname; + if(iter == inv.end()) + groupname = "inventoryhandtohand"; + else + { + const std::string &type = iter->getTypeName(); + if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) + groupname = "inventoryweapononehand"; + else if(type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef *ref = iter->get(); + + int type = ref->mBase->mData.mType; + if(type == ESM::Weapon::ShortBladeOneHand || + type == ESM::Weapon::LongBladeOneHand || + type == ESM::Weapon::BluntOneHand || + type == ESM::Weapon::AxeOneHand) + groupname = "inventoryweapononehand"; + else if(type == ESM::Weapon::LongBladeTwoHand || + type == ESM::Weapon::BluntTwoClose || + type == ESM::Weapon::AxeTwoHand) + groupname = "inventoryweapontwohand"; + else if(type == ESM::Weapon::BluntTwoWide || + type == ESM::Weapon::SpearTwoWide) + groupname = "inventoryweapontwowide"; + else + groupname = "inventoryhandtohand"; + } + else + groupname = "inventoryhandtohand"; + } + + if(groupname != mCurrentAnimGroup) + { + mCurrentAnimGroup = groupname; + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); + } + + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + { + if(!mAnimation->getInfo("torch")) + mAnimation->play("torch", 2, MWRender::Animation::Group_LeftArm, false, + "start", "stop", 0.0f, (~(size_t)0)); + } + else if(mAnimation->getInfo("torch")) + mAnimation->disable("torch"); + mAnimation->forceUpdate(); mAnimation->runAnimation(0.0f); @@ -136,12 +192,9 @@ namespace MWRender mNode->setOrientation (Ogre::Quaternion::IDENTITY); - mNode->setVisible (true); - mRenderTarget->update(); - mSelectionBuffer->update(); - mNode->setVisible (false); + mSelectionBuffer->update(); } int InventoryPreview::getSlotSelected (int posX, int posY) @@ -151,10 +204,13 @@ namespace MWRender void InventoryPreview::onSetup () { - if (!mSelectionBuffer) - mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); + delete mSelectionBuffer; + mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); - mAnimation->play("inventoryhandtohand", "start", "stop", false); + mAnimation->showWeapons(true); + + mCurrentAnimGroup = "inventoryhandtohand"; + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); } // -------------------------------------------------------------------------------------------------- @@ -175,9 +231,7 @@ namespace MWRender updateCamera(); - mNode->setVisible (true); mRenderTarget->update(); - mNode->setVisible (false); } void RaceSelectionPreview::setPrototype(const ESM::NPC &proto) @@ -190,7 +244,7 @@ namespace MWRender void RaceSelectionPreview::onSetup () { - mAnimation->play("idle", "start", "stop", false); + mAnimation->play("idle", 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); updateCamera(); } @@ -198,7 +252,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/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index 08cbd51087..562cb3784a 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -53,6 +53,7 @@ namespace MWRender MWWorld::Ptr mCharacter; MWRender::NpcAnimation* mAnimation; + std::string mCurrentAnimGroup; std::string mName; @@ -72,8 +73,6 @@ namespace MWRender int getSlotSelected(int posX, int posY); - void setNpcAnimation (NpcAnimation* anim); - private: OEngine::Render::SelectionBuffer* mSelectionBuffer; diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 22f84ee018..7817c23c9e 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,9 +1,5 @@ #include "creatureanimation.hpp" -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" @@ -25,24 +21,12 @@ 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++) - { - Ogre::Entity *ent = mEntityList.mEntities[i]; - ent->setVisibilityFlags(RV_Actors); + setObjectRoot(mPtr.getRefData().getBaseNode(), model, false); + setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = ent->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - } - - std::vector names; if((ref->mBase->mFlags&ESM::Creature::Biped)) - names.push_back("meshes\\base_anim.nif"); - names.push_back(model); - setAnimationSources(names); + addAnimSource("meshes\\base_anim.nif"); + addAnimSource(model); } } diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 54f288bff8..b318c2d569 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -20,7 +20,6 @@ #include "../mwworld/ptr.hpp" -#include "player.hpp" #include "renderconst.hpp" using namespace Ogre; diff --git a/apps/openmw/mwrender/debugging.hpp b/apps/openmw/mwrender/debugging.hpp index 6a4eef58f4..4a574017ce 100644 --- a/apps/openmw/mwrender/debugging.hpp +++ b/apps/openmw/mwrender/debugging.hpp @@ -39,8 +39,6 @@ namespace MWWorld namespace MWRender { - class Player; - class Debugging { OEngine::Physic::PhysicEngine* mEngine; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index c86a61cfae..8043f8b122 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -197,11 +197,11 @@ void LocalMap::render(const float x, const float y, const float zlow, const float zhigh, const float xw, const float yw, const std::string& texture) { - //mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 ); - mCellCamera->setFarClipDistance(0); // infinite + mCellCamera->setFarClipDistance( (zhigh-zlow) + 2000 ); + mCellCamera->setNearClipDistance(50); mCellCamera->setOrthoWindow(xw, yw); - mCameraNode->setPosition(Vector3(x, y, zhigh+100000)); + mCameraNode->setPosition(Vector3(x, y, zhigh+1000)); // disable fog (only necessary for fixed function, the shader based // materials already do this through local_map material configuration) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 9da70beb45..b5f2ea031a 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" @@ -10,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "renderconst.hpp" @@ -28,7 +30,7 @@ const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize { ESM::PRT_LHand, "Left Hand" }, { ESM::PRT_RWrist, "Right Wrist" }, { ESM::PRT_LWrist, "Left Wrist" }, - { ESM::PRT_Shield, "Shield" }, + { ESM::PRT_Shield, "Shield Bone" }, { ESM::PRT_RForearm, "Right Forearm" }, { ESM::PRT_LForearm, "Left Forearm" }, { ESM::PRT_RUpperarm, "Right Upper Arm" }, @@ -43,18 +45,19 @@ const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize { ESM::PRT_LLeg, "Left Upper Leg" }, { ESM::PRT_RPauldron, "Right Clavicle" }, { ESM::PRT_LPauldron, "Left Clavicle" }, - { ESM::PRT_Weapon, "Weapon" }, + { ESM::PRT_Weapon, "Weapon Bone" }, { ESM::PRT_Tail, "Tail" } }; 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 +74,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor mGloveL(inv.end()), mGloveR(inv.end()), mSkirtIter(inv.end()), - mHeadOnly(headOnly) + mWeapon(inv.end()), + mShield(inv.end()), + mViewMode(viewMode), + mShowWeapons(false) { mNpc = mPtr.get()->mBase; @@ -85,11 +91,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); - float scale = race->mData.mHeight.mMale; - if(!mNpc->isMale()) - scale = race->mData.mHeight.mFemale; - node->scale(Ogre::Vector3(scale)); - mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; mHairModel = "meshes\\" + store.get().find(mNpc->mHair)->mModel; @@ -98,116 +99,96 @@ 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"); + setObjectRoot(node, smodel, true); - createEntityList(node, smodel); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) + addAnimSource(smodel); + if(mBodyPrefix.find("argonian") != std::string::npos) + addAnimSource("meshes\\argonian_swimkna.nif"); + else if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.nif"); + if(mNpc->mModel.length() > 0) + addAnimSource("meshes\\"+mNpc->mModel); + if(mViewMode == VM_FirstPerson) { - Ogre::Entity *base = mEntityList.mEntities[i]; - - base->getUserObjectBindings().setUserAny(Ogre::Any(-1)); - if (mVisibilityFlags != 0) - base->setVisibilityFlags(mVisibilityFlags); - - for(unsigned int j=0; j < base->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = base->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } + /* A bit counter-intuitive, but unlike third-person anims, it seems + * beast races get both base_anim.1st.nif and base_animkna.1st.nif. + */ + addAnimSource("meshes\\base_anim.1st.nif"); + if(isBeast) + addAnimSource("meshes\\base_animkna.1st.nif"); + if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.1st.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)); - setAnimationSources(skelnames); + forceUpdate(); +} - updateParts(true); +void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) +{ + assert(viewMode != VM_HeadOnly); + mViewMode = viewMode; + + 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"); + + clearAnimSources(); + addAnimSource(smodel); + if(mBodyPrefix.find("argonian") != std::string::npos) + addAnimSource("meshes\\argonian_swimkna.nif"); + else if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.nif"); + if(mNpc->mModel.length() > 0) + addAnimSource("meshes\\"+mNpc->mModel); + if(mViewMode == VM_FirstPerson) + { + /* A bit counter-intuitive, but unlike third-person anims, it seems + * beast races get both base_anim.1st.nif and base_animkna.1st.nif. + */ + addAnimSource("meshes\\base_anim.1st.nif"); + if(isBeast) + addAnimSource("meshes\\base_animkna.1st.nif"); + if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.1st.nif"); + } + MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); + + for(size_t i = 0;i < sPartListSize;i++) + removeIndividualPart(i); + forceUpdate(); } void NpcAnimation::updateParts(bool forceupdate) { static const struct { - int numRemoveParts; // Max: 1 - ESM::PartReferenceType removeParts[1]; - - MWWorld::ContainerStoreIterator NpcAnimation::*part; - int slot; - - int numReserveParts; // Max: 12 - ESM::PartReferenceType reserveParts[12]; + MWWorld::ContainerStoreIterator NpcAnimation::*mPart; + int mSlot; + int mBasePriority; } slotlist[] = { - { 0, { }, - &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, - 12, { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, - ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, - ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron } - }, - - { 0, { }, - &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, - 3, { ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg } - }, - - { 1, { ESM::PRT_Hair }, - &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, - 0, { } - }, + // FIXME: Priority is based on the number of reserved slots. There should be a better way. + { &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, 12 }, + { &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, 3 }, + { &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, 0 }, + { &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, 0 }, + { &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, 0 }, + { &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, 0 }, + { &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, 0 }, + { &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, 0 }, + { &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, 0 }, + { &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, 0 }, + { &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, 0 }, + { &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, 0 }, + { &NpcAnimation::mShield, MWWorld::InventoryStore::Slot_CarriedLeft, 0 }, + { &NpcAnimation::mWeapon, MWWorld::InventoryStore::Slot_CarriedRight, 0 } }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); for(size_t i = 0;!forceupdate && i < slotlistsize;i++) { - MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); - if(this->*slotlist[i].part != iter) + MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot); + if(this->*slotlist[i].mPart != iter) { forceupdate = true; break; @@ -216,117 +197,163 @@ 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) { - MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); + for(size_t i = 0;i < slotlistsize;i++) + this->*slotlist[i].mPart = inv.getSlot(slotlist[i].mSlot); + return; + } - this->*slotlist[i].part = iter; - removePartGroup(slotlist[i].slot); + for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) + { + MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot); - if(this->*slotlist[i].part == inv.end()) + this->*slotlist[i].mPart = iter; + removePartGroup(slotlist[i].mSlot); + + if(this->*slotlist[i].mPart == inv.end()) continue; - for(int rem = 0;rem < slotlist[i].numRemoveParts;rem++) - removeIndividualPart(slotlist[i].removeParts[rem]); + if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet) + removeIndividualPart(ESM::PRT_Hair); int prio = 1; - MWWorld::ContainerStoreIterator &store = this->*slotlist[i].part; + MWWorld::ContainerStoreIterator &store = this->*slotlist[i].mPart; if(store->getTypeName() == typeid(ESM::Clothing).name()) { - prio = ((slotlist[i].numReserveParts+1)<<1) + 0; + prio = ((slotlist[i].mBasePriority+1)<<1) + 0; const ESM::Clothing *clothes = store->get()->mBase; - addPartGroup(slotlist[i].slot, prio, clothes->mParts.mParts); + addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts); } else if(store->getTypeName() == typeid(ESM::Armor).name()) { - prio = ((slotlist[i].numReserveParts+1)<<1) + 1; + prio = ((slotlist[i].mBasePriority+1)<<1) + 1; const ESM::Armor *armor = store->get()->mBase; - addPartGroup(slotlist[i].slot, prio, armor->mParts.mParts); + addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts); } - for(int res = 0;res < slotlist[i].numReserveParts;res++) - reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio); + if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe) + { + ESM::PartReferenceType parts[] = { + ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, + ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, + ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron + }; + size_t parts_size = sizeof(parts)/sizeof(parts[0]); + for(size_t p = 0;p < parts_size;++p) + reserveIndividualPart(parts[p], slotlist[i].mSlot, prio); + } + else if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Skirt) + { + reserveIndividualPart(ESM::PRT_Groin, slotlist[i].mSlot, prio); + reserveIndividualPart(ESM::PRT_RLeg, slotlist[i].mSlot, prio); + reserveIndividualPart(ESM::PRT_LLeg, slotlist[i].mSlot, 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 { - ESM::PartReferenceType type; - const char name[2][12]; - } PartTypeList[] = { - { ESM::PRT_Neck, { "neck", "" } }, - { ESM::PRT_Cuirass, { "chest", "" } }, - { ESM::PRT_Groin, { "groin", "" } }, - { ESM::PRT_RHand, { "hand", "hands" } }, - { ESM::PRT_LHand, { "hand", "hands" } }, - { ESM::PRT_RWrist, { "wrist", "" } }, - { ESM::PRT_LWrist, { "wrist", "" } }, - { ESM::PRT_RForearm, { "forearm", "" } }, - { ESM::PRT_LForearm, { "forearm", "" } }, - { ESM::PRT_RUpperarm, { "upper arm", "" } }, - { ESM::PRT_LUpperarm, { "upper arm", "" } }, - { ESM::PRT_RFoot, { "foot", "feet" } }, - { ESM::PRT_LFoot, { "foot", "feet" } }, - { ESM::PRT_RAnkle, { "ankle", "" } }, - { ESM::PRT_LAnkle, { "ankle", "" } }, - { ESM::PRT_RKnee, { "knee", "" } }, - { ESM::PRT_LKnee, { "knee", "" } }, - { ESM::PRT_RLeg, { "upper leg", "" } }, - { ESM::PRT_LLeg, { "upper leg", "" } }, - { ESM::PRT_Tail, { "tail", "" } } - }; + showWeapons(mShowWeapons); - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++) + const int Flag_Female = 0x01; + const int Flag_FirstPerson = 0x02; + + int flags = 0; + if (!mNpc->isMale()) + flags |= Flag_Female; + if (mViewMode == VM_FirstPerson) + flags |= Flag_FirstPerson; + + // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination + static std::map< std::pair , std::vector > sRaceMapping; + std::string race = Misc::StringUtils::lowerCase(mNpc->mRace); + std::pair thisCombination = std::make_pair(race, flags); + if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { - if(mPartPriorities[PartTypeList[i].type] < 1) + static std::map bodypartMap; + if(bodypartMap.size() == 0) { - const ESM::BodyPart *part = NULL; - const MWWorld::Store &partStore = store.get(); - - if(!mNpc->isMale()) - { - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]); - if(part == 0) - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]); - } - if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]); - if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]); - - if(part) - addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); + bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck; + bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest; + bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin; + bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail; } + + sRaceMapping[thisCombination].resize(ESM::PRT_Count, NULL); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const MWWorld::Store &partStore = store.get(); + for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) + { + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) + continue; + + if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) + continue; + + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson != (mViewMode == VM_FirstPerson)) + continue; + for (std::map::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt ) + if (bIt->second == bodypart.mData.mPart) + sRaceMapping[thisCombination][bIt->first] = &*it; + } + } + + for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) + { + const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part]; + if (mPartPriorities[part] < 1 && bodypart) + addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); } } -NifOgre::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++) - { - parts[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); - if (mVisibilityFlags != 0) - parts[i]->setVisibilityFlags(mVisibilityFlags); + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha); - for(unsigned int j=0; j < parts[i]->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = parts[i]->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - } - if(entities.mSkelBase) + for(size_t i = 0;i < objects.mEntities.size();i++) + objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + for(size_t i = 0;i < objects.mParticles.size();i++) + objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + + if(objects.mSkelBase) { - Ogre::AnimationStateSet *aset = entities.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -334,12 +361,13 @@ 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) @@ -352,31 +380,19 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) mTimeToChange -= timepassed; Ogre::Vector3 ret = Animation::runAnimation(timepassed); - const Ogre::SkeletonInstance *skelsrc = mEntityList.mSkelBase->getSkeleton(); + + Ogre::SkeletonInstance *baseinst = 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()); + updateSkeletonInstance(baseinst, ent->getSkeleton()); ent->getAllAnimationStates()->_notifyDirty(); } + 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; @@ -386,7 +402,7 @@ void NpcAnimation::removeIndividualPart(int type) { if(type == sPartList[i].type) { - removeEntities(mEntityParts[i]); + destroyObjectList(mInsert->getCreator(), mObjectParts[i]); break; } } @@ -424,7 +440,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; } } @@ -433,29 +449,44 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts) { - for(std::size_t i = 0; i < parts.size(); i++) + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const MWWorld::Store &partStore = store.get(); + + const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : ""; + std::vector::const_iterator part(parts.begin()); + for(;part != parts.end();part++) { - const ESM::PartReference &part = parts[i]; - - const MWWorld::Store &partStore = - MWBase::Environment::get().getWorld()->getStore().get(); - const ESM::BodyPart *bodypart = 0; - if(!mNpc->isMale()) - bodypart = partStore.search(part.mFemale); - if(!bodypart) - bodypart = partStore.search(part.mMale); + if(!mNpc->isMale() && !part->mFemale.empty()) + bodypart = partStore.search(part->mFemale+ext); + if(!bodypart && !part->mMale.empty()) + bodypart = partStore.search(part->mMale+ext); if(bodypart) - addOrReplaceIndividualPart(part.mPart, group, priority, "meshes\\"+bodypart->mModel); + addOrReplaceIndividualPart(part->mPart, group, priority, "meshes\\"+bodypart->mModel); else - reserveIndividualPart(part.mPart, group, priority); + reserveIndividualPart(part->mPart, group, priority); } } -Ogre::Node* NpcAnimation::getHeadNode() +void NpcAnimation::showWeapons(bool showWeapon) { - return mEntityList.mSkelBase->getSkeleton()->getBone("Bip01 Head"); + mShowWeapons = showWeapon; + if(showWeapon && + mViewMode != VM_FirstPerson/* FIXME: Remove this once first-person bodies work */) + { + MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(mWeapon != inv.end()) // special case for weapons + { + std::string mesh = MWWorld::Class::get(*mWeapon).getModel(*mWeapon); + addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh); + } + } + else + { + removeIndividualPart(ESM::PRT_Weapon); + } } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 5da4afef8a..e72fa56ed7 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,14 @@ 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; + bool mShowWeapons; float mTimeToChange; MWWorld::ContainerStoreIterator mRobe; @@ -54,17 +61,18 @@ private: MWWorld::ContainerStoreIterator mGloveL; MWWorld::ContainerStoreIterator mGloveR; MWWorld::ContainerStoreIterator mSkirtIter; + MWWorld::ContainerStoreIterator mWeapon; + MWWorld::ContainerStoreIterator mShield; int mVisibilityFlags; 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 +82,15 @@ 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(); + virtual void showWeapons(bool showWeapon); + + void setViewMode(ViewMode viewMode); void forceUpdate() { updateParts(true); } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index cb1dfa75be..3456e1c16a 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -1,10 +1,14 @@ #include "objects.hpp" +#include + #include #include #include #include #include +#include +#include #include #include @@ -16,16 +20,31 @@ #include "renderconst.hpp" using namespace MWRender; +float Objects::lightLinearValue() +{ + return mFallback->getFallbackFloat("LightAttenuation_LinearValue"); +} +float Objects::lightLinearRadiusMult() +{ + return mFallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); +} +float Objects::lightQuadraticValue() +{ + return mFallback->getFallbackFloat("LightAttenuation_QuadraticValue"); +} +float Objects::lightQuadraticRadiusMult() +{ + return mFallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); +} -/// \todo Replace these, once fallback values from the ini file are available. -float Objects::lightLinearValue = 3; -float Objects::lightLinearRadiusMult = 1; - -float Objects::lightQuadraticValue = 16; -float Objects::lightQuadraticRadiusMult = 1; - -bool Objects::lightOutQuadInLin = true; -bool Objects::lightQuadratic = false; +bool Objects::lightOutQuadInLin() +{ + return mFallback->getFallbackBool("LightAttenuation_OutQuadInLin"); +} +bool Objects::lightQuadratic() +{ + return mFallback->getFallbackBool("LightAttenuation_UseQuadratic"); +} int Objects::uniqueID = 0; @@ -111,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(); @@ -130,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); @@ -152,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 { @@ -197,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()); @@ -211,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()); } } @@ -256,28 +284,25 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre info.time = Ogre::Math::RangeRandom(-500, +500); info.phase = Ogre::Math::RangeRandom(-500, +500); - // changed to linear to look like morrowind - bool quadratic = false; - /* - if (!lightOutQuadInLin) - quadratic = lightQuadratic; - else - { - quadratic = !info.interior; - } - */ + bool quadratic = lightOutQuadInLin() ? !info.interior : lightQuadratic(); + + // with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero, + // so we ignore lights if their attenuation falls below this factor. + const float threshold = 0.03; if (!quadratic) { - float r = radius * lightLinearRadiusMult; - float attenuation = lightLinearValue / r; - light->setAttenuation(r*10, 0, attenuation, 0); + float r = radius * lightLinearRadiusMult(); + float attenuation = lightLinearValue() / r; + float activationRange = 1 / (threshold * attenuation); + light->setAttenuation(activationRange, 0, attenuation, 0); } else { - float r = radius * lightQuadraticRadiusMult; - float attenuation = lightQuadraticValue / pow(r, 2); - light->setAttenuation(r*10, 0, 0, attenuation); + float r = radius * lightQuadraticRadiusMult(); + float attenuation = lightQuadraticValue() / std::pow(r, 2); + float activationRange = std::sqrt(1 / (threshold * attenuation)); + light->setAttenuation(activationRange, 0, 0, attenuation); } // If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 73e95a3c53..cfe5243061 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -5,6 +5,7 @@ #include #include +#include "../mwworld/fallback.hpp" namespace MWWorld { @@ -56,21 +57,21 @@ class Objects{ Ogre::SceneNode* mRootNode; bool mIsStatic; static int uniqueID; + MWWorld::Fallback* mFallback; + float lightLinearValue(); + float lightLinearRadiusMult(); - static float lightLinearValue; - static float lightLinearRadiusMult; + bool lightQuadratic(); + float lightQuadraticValue(); + float lightQuadraticRadiusMult(); - static bool lightQuadratic; - static float lightQuadraticValue; - static float lightQuadraticRadiusMult; - - static bool lightOutQuadInLin; + bool lightOutQuadInLin(); void clearSceneNode (Ogre::SceneNode *node); ///< Remove all movable objects from \a node. public: - Objects(OEngine::Render::OgreRenderer& renderer): mRenderer (renderer), mIsStatic(false) {} + Objects(OEngine::Render::OgreRenderer& renderer, MWWorld::Fallback* fallback): mRenderer (renderer), mIsStatic(false), mFallback(fallback) {} ~Objects(){} void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light=false); diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index eaa155b066..477e382154 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -1,5 +1,4 @@ #include "occlusionquery.hpp" -#include "renderconst.hpp" #include #include @@ -7,8 +6,11 @@ #include #include #include +#include #include +#include "renderconst.hpp" + using namespace MWRender; using namespace Ogre; @@ -16,7 +18,8 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mActiveQuery(0), mDoQuery(0), mSunVisibility(0), mWasVisible(false), - mBBNode(0), mActive(false) + mActive(false), + mFirstFrame(true) { mRendering = renderer; mSunNode = sunNode; @@ -40,39 +43,24 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod return; } - MaterialPtr matBase = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting"); - MaterialPtr matQueryArea = matBase->clone("QueryTotalPixels"); - matQueryArea->setDepthWriteEnabled(false); - matQueryArea->setColourWriteEnabled(false); - matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects - MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels"); - matQueryVisible->setDepthWriteEnabled(false); - matQueryVisible->setColourWriteEnabled(false); // Uncomment this to visualize the occlusion query - matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects - matQueryVisible->setCullingMode(CULL_NONE); - matQueryVisible->setManualCullingMode(MANUAL_CULL_NONE); - - if (sunNode) - mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode(); - mBBNodeReal = mRendering->getScene()->getRootSceneNode()->createChildSceneNode(); - mBBQueryTotal = mRendering->getScene()->createBillboardSet(1); + static Ogre::Mesh* plane = MeshManager::getSingleton().createPlane("occlusionbillboard", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::Plane(Ogre::Vector3(0,0,1), 0), 1, 1, 1, 1, true, 1, 1, 1, Vector3::UNIT_Y).get(); + plane->_setBounds(Ogre::AxisAlignedBox::BOX_INFINITE); + + mBBQueryTotal = mRendering->getScene()->createEntity("occlusionbillboard"); mBBQueryTotal->setCastShadows(false); - mBBQueryTotal->setDefaultDimensions(150, 150); - mBBQueryTotal->createBillboard(Vector3::ZERO); - mBBQueryTotal->setMaterialName("QueryTotalPixels"); - mBBQueryTotal->setRenderQueueGroup(RQG_OcclusionQuery+1); mBBQueryTotal->setVisibilityFlags(RV_OcclusionQuery); + mBBQueryTotal->setRenderQueueGroup(RQG_OcclusionQuery+1); + mBBQueryTotal->setMaterialName("QueryTotalPixels"); mBBNodeReal->attachObject(mBBQueryTotal); - mBBQueryVisible = mRendering->getScene()->createBillboardSet(1); + mBBQueryVisible = mRendering->getScene()->createEntity("occlusionbillboard"); mBBQueryVisible->setCastShadows(false); - mBBQueryVisible->setDefaultDimensions(150, 150); - mBBQueryVisible->createBillboard(Vector3::ZERO); - mBBQueryVisible->setMaterialName("QueryVisiblePixels"); - mBBQueryVisible->setRenderQueueGroup(RQG_OcclusionQuery+1); mBBQueryVisible->setVisibilityFlags(RV_OcclusionQuery); + mBBQueryVisible->setRenderQueueGroup(RQG_OcclusionQuery+1); + mBBQueryVisible->setMaterialName("QueryVisiblePixels"); mBBNodeReal->attachObject(mBBQueryVisible); mRendering->getScene()->addRenderObjectListener(this); @@ -116,12 +104,12 @@ void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass // Open a new occlusion query if (mDoQuery == true) { - if (rend == mBBQueryTotal) + if (rend == mBBQueryTotal->getSubEntity(0)) { mActiveQuery = mSunTotalAreaQuery; mWasVisible = true; } - else if (rend == mBBQueryVisible) + else if (rend == mBBQueryVisible->getSubEntity(0)) { mActiveQuery = mSunVisibleAreaQuery; } @@ -160,6 +148,12 @@ void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocati void OcclusionQuery::update(float duration) { + if (mFirstFrame) + { + // GLHardwareOcclusionQuery::isStillOutstanding doesn't seem to like getting called when nothing has been rendered yet + mFirstFrame = false; + return; + } if (!mSupported) return; mWasVisible = false; @@ -170,12 +164,11 @@ void OcclusionQuery::update(float duration) if (dist==0) dist = 10000000; dist -= 1000; // bias dist /= 1000.f; - if (mBBNode) + if (mSunNode) { - mBBNode->setPosition(mSunNode->getPosition() * dist); - mBBNode->setScale(dist, dist, dist); - mBBNodeReal->setPosition(mBBNode->_getDerivedPosition()); - mBBNodeReal->setScale(mBBNode->getScale()); + mBBNodeReal->setPosition(mSunNode->getPosition() * dist); + mBBNodeReal->setOrientation(Ogre::Vector3::UNIT_Z.getRotationTo(-mBBNodeReal->getPosition().normalisedCopy())); + mBBNodeReal->setScale(150.f*dist, 150.f*dist, 150.f*dist); } // Stop occlusion queries until we get their information @@ -209,6 +202,4 @@ void OcclusionQuery::update(float duration) void OcclusionQuery::setSunNode(Ogre::SceneNode* node) { mSunNode = node; - if (!mBBNode) - mBBNode = node->getParentSceneNode()->createChildSceneNode(); } diff --git a/apps/openmw/mwrender/occlusionquery.hpp b/apps/openmw/mwrender/occlusionquery.hpp index 145d773553..983361c187 100644 --- a/apps/openmw/mwrender/occlusionquery.hpp +++ b/apps/openmw/mwrender/occlusionquery.hpp @@ -49,17 +49,17 @@ namespace MWRender Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery; Ogre::HardwareOcclusionQuery* mActiveQuery; - Ogre::BillboardSet* mBBQueryVisible; - Ogre::BillboardSet* mBBQueryTotal; + Ogre::Entity* mBBQueryVisible; + Ogre::Entity* mBBQueryTotal; Ogre::SceneNode* mSunNode; - Ogre::SceneNode* mBBNode; Ogre::SceneNode* mBBNodeReal; float mSunVisibility; bool mWasVisible; bool mActive; + bool mFirstFrame; bool mSupported; bool mDoQuery; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e6216b5372..9061f84027 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,8 @@ #include "../mwbase/inputmanager.hpp" // FIXME #include "../mwbase/windowmanager.hpp" // FIXME +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" @@ -47,11 +50,14 @@ using namespace Ogre; namespace MWRender { -RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, - const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine) +RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, + const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine, + MWWorld::Fallback* fallback) : mRendering(_rend) - , mObjects(mRendering) + , mFallback(fallback) + , mObjects(mRendering, mFallback) , mActors(mRendering, this) + , mPlayerAnimation(NULL) , mAmbientMode(0) , mSunEnabled(0) , mPhysicsEngine(engine) @@ -115,13 +121,11 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); - //ResourceGroupManager::getSingleton ().declareResource ("GlobalMap.png", "Texture", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + Ogre::TextureManager::getSingleton().setMemoryBudget(126*1024*1024); + Ogre::MeshManager::getSingleton().setMemoryBudget(64*1024*1024); ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); - // causes light flicker in opengl when moving.. - //mRendering.getScene()->setCameraRelativeRendering(true); - // disable unsupported effects if (!Settings::Manager::getBool("shaders", "Objects")) Settings::Manager::setBool("enabled", "Shadows", false); @@ -145,14 +149,13 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const applyCompositors(); - SceneNode *rt = mRendering.getScene()->getRootSceneNode(); - mRootNode = rt; + mRootNode = mRendering.getScene()->getRootSceneNode(); + mRootNode->createChildSceneNode("player"); mObjects.setRootNode(mRootNode); mActors.setRootNode(mRootNode); - Ogre::SceneNode *playerNode = mRootNode->createChildSceneNode ("player"); - mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); + mCamera = new MWRender::Camera(mRendering.getCamera()); mShadows = new Shadows(&mRendering); @@ -180,7 +183,8 @@ RenderingManager::~RenderingManager () mRendering.getWindow()->removeListener(this); mRendering.removeWindowEventListener(this); - delete mPlayer; + delete mPlayerAnimation; + delete mCamera; delete mSkyManager; delete mDebugging; delete mShadows; @@ -232,9 +236,10 @@ void RenderingManager::toggleWater() void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) { mObjects.buildStaticGeometry (*store); + sh::Factory::getInstance().unloadUnreferencedMaterials(); mDebugging->cellAdded(store); if (store->mCell->isExterior()) - mTerrainManager->cellAdded(store); + mTerrainManager->cellAdded(store); waterAdded(store); } @@ -261,45 +266,20 @@ void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3 ptr.getRefData().getBaseNode()->setScale(scale); } -bool RenderingManager::rotateObject( const MWWorld::Ptr &ptr, Ogre::Vector3 &rot, bool adjust) +void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) { - bool isActive = ptr.getRefData().getBaseNode() != 0; - bool isPlayer = isActive && ptr.getRefData().getHandle() == "player"; - bool force = true; - - if (isPlayer) - force = mPlayer->rotate(rot, adjust); - - MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); + Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - if (!isPlayer && isActive) - { - Ogre::Quaternion xr(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); + if(ptr.getRefData().getHandle() == mCamera->getHandle() && + !mCamera->isVanityOrPreviewModeEnabled()) + mCamera->rotateCamera(rot, false); - Ogre::Quaternion xref(Ogre::Radian(-ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yref(Ogre::Radian(-ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zref(Ogre::Radian(-ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); + if(!MWWorld::Class::get(ptr).isActor()) + newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) * + Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo; - Ogre::Quaternion newo = adjust ? (xr * yr * zr) * (xref*yref*zref) : xr * yr * zr; - - Ogre::Matrix3 mat; - newo.ToRotationMatrix(mat); - Ogre::Radian ax,ay,az; - mat.ToEulerAnglesXYZ(ax,ay,az); - rot.x = -ax.valueRadians(); - rot.y = -ay.valueRadians(); - rot.z = -az.valueRadians(); - - ptr.getRefData().getBaseNode()->setOrientation(newo); - } - else if(isPlayer) - { - rot.x = -mPlayer->getPitch(); - rot.z = mPlayer->getYaw(); - } - return force; + ptr.getRefData().getBaseNode()->setOrientation(newo); } void @@ -322,28 +302,28 @@ void RenderingManager::update (float duration, bool paused) { MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + + int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + mRendering.getFader()->setFactor(1.f-(blind / 100.f)); + setAmbientMode(); + // player position - MWWorld::RefData &data = - MWBase::Environment::get() - .getWorld() - ->getPlayer() - .getPlayer() - .getRefData(); + MWWorld::RefData &data = player.getRefData(); float *_playerPos = data.getPosition().pos; Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]); Ogre::Vector3 orig, dest; - mPlayer->setCameraDistance(); - if (!mPlayer->getPosition(orig, dest)) { - orig.z += mPlayer->getHeight() * mRootNode->getScale().z; + mCamera->setCameraDistance(); + if(!mCamera->getPosition(orig, dest)) + { + orig.z += mCamera->getHeight() * mRootNode->getScale().z; btVector3 btOrig(orig.x, orig.y, orig.z); btVector3 btDest(dest.x, dest.y, dest.z); - std::pair test = - mPhysicsEngine->rayTest(btOrig, btDest); - if (!test.first.empty()) { - mPlayer->setCameraDistance(test.second * orig.distance(dest), false, false); - } + std::pair test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5, btOrig, btDest); + if (test.first) + mCamera->setCameraDistance(test.second * orig.distance(dest), false, false); } mOcclusionQuery->update(duration); @@ -356,14 +336,12 @@ void RenderingManager::update (float duration, bool paused) Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); - applyFog(world->isUnderwater (world->getPlayer().getPlayer().getCell(), cam)); + applyFog(world->isUnderwater(player.getCell(), cam)); if(paused) - { return; - } - mPlayer->update(duration); + mCamera->update(duration); mActors.update (duration); mObjects.update (duration); @@ -374,18 +352,11 @@ void RenderingManager::update (float duration, bool paused) mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); Ogre::SceneNode *node = data.getBaseNode(); - //Ogre::Quaternion orient = - //node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); - Ogre::Quaternion orient = -node->_getDerivedOrientation(); + Ogre::Quaternion orient = node->_getDerivedOrientation(); mLocalMap->updatePlayer(playerPos, orient); - mWater->updateUnderwater( - world->isUnderwater( - world->getPlayer().getPlayer().getCell(), - cam) - ); + mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam)); mWater->update(duration, playerPos); } @@ -536,25 +507,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); @@ -601,8 +575,15 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour) void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) { - mRendering.getScene()->setAmbientLight(colour); - mTerrainManager->setAmbient(colour); + mAmbientColor = colour; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::NightEye)).mMagnitude; + Ogre::ColourValue final = colour; + final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f)); + + mRendering.getScene()->setAmbientLight(final); + mTerrainManager->setAmbient(final); } void RenderingManager::sunEnable(bool real) @@ -869,28 +850,50 @@ void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned i } } -void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) +void RenderingManager::setupPlayer(const MWWorld::Ptr &ptr) { - mPlayer->attachTo(ptr); + ptr.getRefData().setBaseNode(mRendering.getScene()->getSceneNode("player")); + mCamera->attachTo(ptr); } void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) { - MWRender::NpcAnimation *anim = - new MWRender::NpcAnimation( - ptr, ptr.getRefData ().getBaseNode (), - MWWorld::Class::get(ptr).getInventoryStore(ptr), RV_Actors - ); - mPlayer->setAnimation(anim); - mWater->removeEmitter (ptr); - mWater->addEmitter (ptr); + if(!mPlayerAnimation) + { + mPlayerAnimation = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), + MWWorld::Class::get(ptr).getInventoryStore(ptr), + RV_Actors); + } + else + { + // Reconstruct the NpcAnimation in-place + mPlayerAnimation->~NpcAnimation(); + new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(), + MWWorld::Class::get(ptr).getInventoryStore(ptr), + RV_Actors); + } + mCamera->setAnimation(mPlayerAnimation); + mWater->removeEmitter(ptr); + mWater->addEmitter(ptr); + // apply race height + MWBase::Environment::get().getWorld()->scaleObject(ptr, 1.f); } -void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) +void RenderingManager::getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) { - eyepos = mPlayer->getPosition(); - eyepos.z += mPlayer->getHeight(); - mPlayer->getSightAngles(pitch, yaw); + eyepos = mCamera->getPosition(); + eyepos.z += mCamera->getHeight(); + mCamera->getSightAngles(pitch, yaw); +} + +bool RenderingManager::vanityRotateCamera(const float *rot) +{ + if(!mCamera->isVanityOrPreviewModeEnabled()) + return false; + + Ogre::Vector3 vRot(rot); + mCamera->rotateCamera(vRot, true); + return true; } void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) @@ -912,7 +915,7 @@ Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) { Animation *anim = mActors.getAnimation(ptr); if(!anim && ptr.getRefData().getHandle() == "player") - anim = mPlayer->getAnimation(); + anim = mPlayerAnimation; return anim; } @@ -947,4 +950,9 @@ void RenderingManager::frameStarted(float dt) mWater->frameStarted(dt); } +void RenderingManager::resetCamera() +{ + mCamera->reset(); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 1777a72c33..b492a0db90 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -17,7 +17,7 @@ #include "objects.hpp" #include "actors.hpp" -#include "player.hpp" +#include "camera.hpp" #include "occlusionquery.hpp" namespace Ogre @@ -50,47 +50,46 @@ namespace MWRender class VideoPlayer; class Animation; -class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener { - - private: - - +class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener +{ +private: virtual MWRender::Objects& getObjects(); virtual MWRender::Actors& getActors(); - public: +public: RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, - const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine); + const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine, + MWWorld::Fallback* fallback); virtual ~RenderingManager(); - void togglePOV() { - mPlayer->toggleViewMode(); + void togglePOV() + { mCamera->toggleViewMode(); } + + void togglePreviewMode(bool enable) + { mCamera->togglePreviewMode(enable); } + + bool toggleVanityMode(bool enable) + { return mCamera->toggleVanityMode(enable); } + + void allowVanityMode(bool allow) + { mCamera->allowVanityMode(allow); } + + void togglePlayerLooking(bool enable) + { mCamera->togglePlayerLooking(enable); } + + void changeVanityModeScale(float factor) + { + if(mCamera->isVanityOrPreviewModeEnabled()) + mCamera->setCameraDistance(-factor/120.f*10, true, true); } - void togglePreviewMode(bool enable) { - mPlayer->togglePreviewMode(enable); - } + void resetCamera(); - bool toggleVanityMode(bool enable, bool force) { - return mPlayer->toggleVanityMode(enable, force); - } + bool vanityRotateCamera(const float *rot); - void allowVanityMode(bool allow) { - mPlayer->allowVanityMode(allow); - } + void getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); - void togglePlayerLooking(bool enable) { - mPlayer->togglePlayerLooking(enable); - } - - void changeVanityModeScale(float factor) { - if (mPlayer->isVanityOrPreviewModeEnabled()) - mPlayer->setCameraDistance(-factor/120.f*10, true, true); - } - - void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); - - void attachCameraTo(const MWWorld::Ptr &ptr); + void setupPlayer(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); @@ -119,11 +118,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); - /// Rotates object accordingly to its type - /// \param rot euler angles in radians - /// \param adjust indicates should rotation be set or adjusted - /// \return true if object needs to be rotated physically - bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false); + /// Updates an object's rotation + void rotateObject (const MWWorld::Ptr& ptr); void setWaterHeight(const float height); void toggleWater(); @@ -158,6 +154,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 (); @@ -203,12 +201,11 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void stopVideo(); void frameStarted(float dt); - protected: - virtual void windowResized(Ogre::RenderWindow* rw); +protected: + virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); - private: - +private: sh::Factory* mFactory; void setAmbientMode(); @@ -220,6 +217,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList bool mSunEnabled; + MWWorld::Fallback* mFallback; + SkyManager* mSkyManager; OcclusionQuery* mOcclusionQuery; @@ -235,6 +234,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Objects mObjects; MWRender::Actors mActors; + MWRender::NpcAnimation *mPlayerAnimation; + // 0 normal, 1 more bright, 2 max int mAmbientMode; @@ -249,7 +250,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList OEngine::Physic::PhysicEngine* mPhysicsEngine; - MWRender::Player *mPlayer; + MWRender::Camera *mCamera; MWRender::Debugging *mDebugging; 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 604ad50d22..03e14dc07d 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -21,6 +23,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/fallback.hpp" + #include "renderconst.hpp" #include "renderingmanager.hpp" @@ -39,25 +43,25 @@ BillboardObject::BillboardObject( const String& textureName, static unsigned int bodyCount=0; - /// \todo These billboards are not 100% correct, might want to revisit them later - mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1); - mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize); - mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON); - mBBSet->setCommonDirection( -position.normalisedCopy() ); - mBBSet->setVisibilityFlags(RV_Sky); + mMaterial = sh::Factory::getInstance().createMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount), material); + mMaterial->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); + + static Ogre::Mesh* plane = MeshManager::getSingleton().createPlane("billboard", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::Plane(Ogre::Vector3(0,0,1), 0), 1, 1, 1, 1, true, 1, 1, 1, Vector3::UNIT_Y).get(); + plane->_setBounds(Ogre::AxisAlignedBox::BOX_INFINITE); + mEntity = sceneMgr->createEntity("billboard"); + mEntity->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); + mEntity->setVisibilityFlags(RV_Sky); + mEntity->setCastShadows(false); + mNode = rootNode->createChildSceneNode(); mNode->setPosition(finalPosition); - mNode->attachObject(mBBSet); - mBBSet->createBillboard(0,0,0); - mBBSet->setCastShadows(false); - - mMaterial = sh::Factory::getInstance().createMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount), material); - mMaterial->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); + mNode->attachObject(mEntity); + mNode->setScale(Ogre::Vector3(450.f*initialSize)); + mNode->setOrientation(Ogre::Vector3::UNIT_Z.getRotationTo(-position.normalisedCopy())); sh::Factory::getInstance().getMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount))->setListener(this); - mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); - bodyCount++; } @@ -77,12 +81,12 @@ void BillboardObject::createdConfiguration (sh::MaterialInstance* m, const std:: void BillboardObject::setVisible(const bool visible) { - mBBSet->setVisible(visible); + mEntity->setVisible(visible); } void BillboardObject::setSize(const float size) { - mNode->setScale(size, size, size); + mNode->setScale(450.f*size, 450.f*size, 450.f*size); } void BillboardObject::setVisibility(const float visibility) @@ -101,21 +105,18 @@ void BillboardObject::setPosition(const Vector3& pPosition) { Vector3 normalised = pPosition.normalisedCopy(); Vector3 finalPosition = normalised * 1000.f; - - mBBSet->setCommonDirection( -normalised ); - + mNode->setOrientation(Ogre::Vector3::UNIT_Z.getRotationTo(-normalised)); mNode->setPosition(finalPosition); } Vector3 BillboardObject::getPosition() const { - Vector3 p = mNode->_getDerivedPosition() - mNode->getParentSceneNode()->_getDerivedPosition(); - return p; + return mNode->getPosition(); } void BillboardObject::setVisibilityFlags(int flags) { - mBBSet->setVisibilityFlags(flags); + mEntity->setVisibilityFlags(flags); } void BillboardObject::setColour(const ColourValue& pColour) @@ -132,7 +133,7 @@ void BillboardObject::setColour(const ColourValue& pColour) void BillboardObject::setRenderQueue(unsigned int id) { - mBBSet->setRenderQueueGroup(id); + mEntity->setRenderQueueGroup(id); } SceneNode* BillboardObject::getNode() @@ -203,7 +204,7 @@ unsigned int Moon::getPhaseInt() const return 0; } -SkyManager::SkyManager (SceneNode* root, Camera* pCamera) +SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) : mHour(0.0f) , mDay(0) , mMonth(0) @@ -216,7 +217,6 @@ SkyManager::SkyManager (SceneNode* root, Camera* pCamera) , mSceneMgr(NULL) , mAtmosphereDay(NULL) , mAtmosphereNight(NULL) - , mCloudFragmentShader() , mClouds() , mNextClouds() , mCloudBlendFactor(0.0f) @@ -266,11 +266,12 @@ void SkyManager::create() mLightning->setVisible (false); mLightning->setDiffuseColour (ColourValue(3,3,3)); - mSecunda = new Moon("secunda_texture", 0.5, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); + const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); + mSecunda = new Moon("secunda_texture", fallback->getFallbackFloat("Moons_Secunda_Size")/100, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); mSecunda->setType(Moon::Type_Secunda); mSecunda->setRenderQueue(RQG_SkiesEarly+4); - mMasser = new Moon("masser_texture", 0.75, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); + mMasser = new Moon("masser_texture", fallback->getFallbackFloat("Moons_Masser_Size")/100, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); mMasser->setRenderQueue(RQG_SkiesEarly+3); mMasser->setType(Moon::Type_Masser); @@ -280,15 +281,14 @@ void SkyManager::create() mSunGlare->setRenderQueue(RQG_SkiesLate); mSunGlare->setVisibilityFlags(RV_NoReflection); - Ogre::AxisAlignedBox aabInf; - aabInf.setInfinite (); + Ogre::AxisAlignedBox aabInf = Ogre::AxisAlignedBox::BOX_INFINITE; // 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); @@ -311,10 +311,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); @@ -329,10 +329,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++) @@ -368,9 +368,7 @@ int SkyManager::getSecundaPhase() const void SkyManager::update(float duration) { if (!mEnabled) return; - - mCamera->getParentSceneNode ()->needUpdate (); - mRootNode->setPosition(mCamera->getDerivedPosition()); + const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed; @@ -381,7 +379,7 @@ void SkyManager::update(float duration) mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); mSecunda->setPhase ( static_cast( (int) ((mDay % 32)/4.f)) ); - mSecunda->setColour ( mMoonRed ? ColourValue(1.0, 0.0784, 0.0784) : ColourValue(1,1,1,1)); + mSecunda->setColour ( mMoonRed ? fallback->getFallbackColour("Moons_Script_Color") : ColourValue(1,1,1,1)); mMasser->setColour (ColourValue(1,1,1,1)); if (mSunEnabled) @@ -404,8 +402,7 @@ void SkyManager::update(float duration) Vector3 cam = mCamera->getRealDirection(); const Degree angle = sun.angleBetween( cam ); float val = 1- (angle.valueDegrees() / 180.f); - val = (val*val*val*val)*2; - + val = (val*val*val*val)*6; mSunGlare->setSize(val * mGlareFade); } @@ -534,7 +531,7 @@ void SkyManager::setGlare(const float glare) Vector3 SkyManager::getRealSunPos() { if (!mCreated) return Vector3(0,0,0); - return mSun->getNode()->_getDerivedPosition(); + return mSun->getNode()->getPosition() + mCamera->getRealPosition(); } void SkyManager::sunEnable() @@ -641,25 +638,9 @@ Ogre::SceneNode* SkyManager::getSunNode() return mSun->getNode(); } -void SkyManager::setSkyPosition(const Ogre::Vector3& position) -{ - mRootNode->setPosition(position); -} - -void SkyManager::resetSkyPosition() -{ - mCamera->getParentSceneNode ()->needUpdate (); - mRootNode->setPosition(mCamera->getDerivedPosition()); -} - -void SkyManager::scaleSky(float scale) -{ - mRootNode->setScale(scale, scale, scale); -} - void SkyManager::setGlareEnabled (bool enabled) { - if (!mCreated) + if (!mCreated || !mEnabled) return; mSunGlare->setVisible (mSunEnabled && enabled); } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 66557a6c12..3df8846cd4 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_SKY_H -#define _GAME_RENDER_SKY_H +#ifndef GAME_RENDER_SKY_H +#define GAME_RENDER_SKY_H #include @@ -61,7 +61,7 @@ namespace MWRender Ogre::ColourValue mColour; Ogre::SceneNode* mNode; sh::MaterialInstance* mMaterial; - Ogre::BillboardSet* mBBSet; + Ogre::Entity* mEntity; }; @@ -117,9 +117,6 @@ namespace MWRender void update(float duration); - void create(); - ///< no need to call this, automatically done on first enable() - void enable(); void disable(); @@ -173,11 +170,10 @@ namespace MWRender void setGlareEnabled(bool enabled); Ogre::Vector3 getRealSunPos(); - void setSkyPosition(const Ogre::Vector3& position); - void resetSkyPosition(); - void scaleSky(float scale); - private: + void create(); + ///< no need to call this, automatically done on first enable() + bool mCreated; bool mMoonRed; @@ -200,8 +196,6 @@ namespace MWRender Ogre::SceneNode* mAtmosphereDay; Ogre::SceneNode* mAtmosphereNight; - Ogre::HighLevelGpuProgramPtr mCloudFragmentShader; - // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; @@ -227,4 +221,4 @@ namespace MWRender }; } -#endif // _GAME_RENDER_SKY_H +#endif // GAME_RENDER_SKY_H 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..892dab7cfc 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -49,14 +49,16 @@ namespace MWRender TerrainMaterial::Profile::Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc) : Ogre::TerrainMaterialGenerator::Profile(parent, name, desc) , mGlobalColourMap(false) + , mMaterial(0) { } TerrainMaterial::Profile::~Profile() { + if (mMaterial) + sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName()); } - Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain) { const Ogre::String& matName = terrain->getMaterialName(); @@ -68,59 +70,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 +198,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 +212,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/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 2cbc85cd38..87ae8175de 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -788,8 +788,8 @@ void VideoState::decode_thread_loop(VideoState *self) // main decode loop while(!self->quit) { - if((self->audio_st >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || - (self->video_st >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) + if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE)) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index c8b9db7f10..772eaf623e 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -136,9 +136,6 @@ void PlaneReflection::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) mCamera->setFOVy(mParentCamera->getFOVy()); mRenderActive = true; - Vector3 pos = mParentCamera->getRealPosition(); - pos.y = (mWaterPlane).d*2 - pos.y; - mSky->setSkyPosition(pos); mCamera->enableReflection(mWaterPlane); // for depth calculation, we want the original viewproj matrix _without_ the custom near clip plane. @@ -153,7 +150,6 @@ void PlaneReflection::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) void PlaneReflection::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) { - mSky->resetSkyPosition(); mCamera->disableReflection(); mCamera->disableCustomNearClipPlane(); mRenderActive = false; diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 6c03236370..a21d03ad6a 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -41,7 +41,9 @@ namespace MWRender { { public: Reflection(Ogre::SceneManager* sceneManager) - : mSceneMgr(sceneManager) {} + : mSceneMgr(sceneManager) + , mIsUnderwater(false) + {} virtual ~Reflection() {} virtual void setHeight (float height) {} diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 8402a5b4cd..9f29163afc 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -181,23 +181,30 @@ namespace MWScript runtime.pop(); std::vector idleList; - idleList.push_back (0); // why MW, why? + bool repeat = false; - for (int i=2; i<10 && arg0; ++i) + for(int i=1; i < 10 && arg0; ++i) { - Interpreter::Type_Integer idleValue = runtime[0].mFloat; + if(!repeat) + repeat = true; + Interpreter::Type_Integer idleValue = runtime[0].mInteger; idleList.push_back(idleValue); runtime.pop(); --arg0; } + if(arg0) + { + repeat = runtime[0].mInteger != 0; + runtime.pop(); + --arg0; + } + // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i().search (name) || store.get().search (name) || store.get().search (name) || - store.get().search (name) || + store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 81639b5be9..3a46f62d6b 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -66,8 +66,8 @@ namespace MWScript MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr()); - // Spawn a messagebox (only for items added to player's inventory) - if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer()) + // Spawn a messagebox (only for items added to player's inventory and if player is talking to someone) + if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer() ) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory std::string msgBox; @@ -82,8 +82,8 @@ namespace MWScript msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage61}"); msgBox = boost::str(boost::format(msgBox) % count % itemName); } - - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, std::vector()); + std::vector noButtons; + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); } } }; @@ -161,12 +161,15 @@ namespace MWScript } } - // Spawn a messagebox (only for items added to player's inventory) + // Spawn a messagebox (only for items removed from player's inventory) if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer()) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory std::string msgBox; int numRemoved = (originalCount - count); + if (numRemoved == 0) + return; + if(numRemoved > 1) { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); @@ -177,9 +180,8 @@ namespace MWScript msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage62}"); msgBox = boost::str (boost::format(msgBox) % itemName); } - - if (numRemoved > 0) - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, std::vector()); + std::vector noButtons; + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); } } }; 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/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 283f149de2..85d8deaecd 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -315,5 +315,28 @@ op 0x20001f8: Drop op 0x20001f9: Drop, explicit reference op 0x20001fa: DropSoulGem op 0x20001fb: DropSoulGem, explicit reference +op 0x20001fc: OnDeath +op 0x20001fd: IsWerewolf +op 0x20001fe: IsWerewolf, explicit reference +op 0x20001ff: Rotate +op 0x2000200: Rotate, explicit reference +op 0x2000201: RotateWorld +op 0x2000202: RotateWorld, explicit reference +op 0x2000203: SetAtStart +op 0x2000204: SetAtStart, explicit +op 0x2000205: OnDeath, explicit +op 0x2000206: Move +op 0x2000207: Move, explicit +op 0x2000208: MoveWorld +op 0x2000209: MoveWorld, explicit +op 0x200020a: Fall +op 0x200020b: Fall, explicit +op 0x200020c: GetStandingPC +op 0x200020d: GetStandingPC, explicit +op 0x200020e: GetStandingActor +op 0x200020f: GetStandingActor, explicit +op 0x2000210: GetStartingAngle +op 0x2000211: GetStartingAngle, explicit +op 0x2000212: GetWindSpeed -opcodes 0x20001fa-0x3ffffff unused +opcodes 0x2000213-0x3ffffff unused diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 3ef138432d..608725ae6f 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -15,12 +15,18 @@ namespace MWScript GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) { + reset(); + } + + void GlobalScripts::reset() + { + mScripts.clear(); addScript ("Main"); MWWorld::Store::iterator iter = - store.get().begin(); + mStore.get().begin(); - for (; iter != store.get().end(); ++iter) { + for (; iter != mStore.get().end(); ++iter) { addScript (iter->mScript); } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index f9f9b7ae3a..628919d1d2 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -22,6 +22,8 @@ namespace MWScript GlobalScripts (const MWWorld::ESMStore& store); + void reset(); + void addScript (const std::string& name); void removeScript (const std::string& name); diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index c74e3f1633..bba5cb4ade 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -215,19 +215,22 @@ namespace MWScript std::string InterpreterContext::getNPCRace() const { ESM::NPC npc = *mReference.get()->mBase; - return npc.mRace; + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mRace); + return race->mName; } std::string InterpreterContext::getNPCClass() const { ESM::NPC npc = *mReference.get()->mBase; - return npc.mClass; + const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mClass); + return class_->mName; } std::string InterpreterContext::getNPCFaction() const { ESM::NPC npc = *mReference.get()->mBase; - return npc.mFaction; + const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mFaction); + return faction->mName; } std::string InterpreterContext::getNPCRank() const @@ -289,19 +292,25 @@ namespace MWScript std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; - std::map ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks(); - std::map::const_iterator it = ranks.begin(); - const MWWorld::ESMStore &store = world->getStore(); const ESM::Faction *faction = store.get().find(factionId); - if(it->second < 0 || it->second > 9) - return ""; + std::map ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks(); - if(it->second <= 8) // If player is at max rank, there is no next rank - return faction->mRanks[it->second + 1]; + if (ranks.size()) + { + std::map::const_iterator it = ranks.begin(); + + if(it->second < -1 || it->second > 9) + return ""; + + if(it->second <= 8) // If player is at max rank, there is no next rank + return faction->mRanks[it->second + 1]; + else + return faction->mRanks[it->second]; + } else - return faction->mRanks[it->second]; + return faction->mRanks[0]; } int InterpreterContext::getPCBounty() const diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index f0b2758d9e..9c7b443ae1 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -89,7 +89,7 @@ namespace MWScript virtual std::string getNPCClass() const; virtual std::string getNPCFaction() const; - + virtual std::string getNPCRank() const; virtual std::string getPCName() const; diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 53f744323e..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 { @@ -15,9 +18,33 @@ namespace MWScript mFloats.clear(); mFloats.resize (script.mData.mNumFloats, 0); } - + + int Locals::getIntVar(const std::string &script, const std::string &var) + { + Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); + int index = locals.getIndex(var); + char type = locals.getType(var); + if(index != -1) + { + switch(type) + { + case 's': + return mShorts.at (index); + + case 'l': + return mLongs.at (index); + + case 'f': + return mFloats.at (index); + default: + return 0; + } + } + 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); @@ -27,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 e933c727f3..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,10 +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..5ed26119c0 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -282,10 +282,8 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); - if (world->toggleVanityMode(sActivate, true)) { - context.report( - (sActivate) ? "Vanity Mode -> On" : "Vanity Mode -> Off" - ); + if (world->toggleVanityMode(sActivate)) { + context.report(sActivate ? "Vanity Mode -> On" : "Vanity Mode -> Off"); sActivate = !sActivate; } else { context.report("Vanity Mode -> No"); @@ -399,6 +397,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); @@ -550,6 +555,50 @@ namespace MWScript } }; + template + class OpFall : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + } + }; + + template + class OpGetStandingPc : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push (MWBase::Environment::get().getWorld()->getPlayerStandingOn(ptr)); + } + }; + + template + class OpGetStandingActor : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push (MWBase::Environment::get().getWorld()->getActorStandingOn(ptr)); + } + }; + + class OpGetWindSpeed : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.push(MWBase::Environment::get().getWorld()->getWindSpeed()); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -591,6 +640,13 @@ namespace MWScript const int opcodeSetDelete = 0x20001e5; const int opcodeSetDeleteExplicit = 0x20001e6; const int opcodeGetSquareRoot = 0x20001e7; + const int opcodeFall = 0x200020a; + const int opcodeFallExplicit = 0x200020b; + const int opcodeGetStandingPc = 0x200020c; + const int opcodeGetStandingPcExplicit = 0x200020d; + const int opcodeGetStandingActor = 0x200020e; + const int opcodeGetStandingActorExplicit = 0x200020f; + const int opcodeGetWindSpeed = 0x2000212; const int opcodePlayBink = 0x20001f7; @@ -632,6 +688,10 @@ namespace MWScript extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit); extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot); + extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit); + extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit); + extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit); + extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -678,6 +738,13 @@ namespace MWScript interpreter.installSegment5 (opcodeSetDelete, new OpSetDelete); interpreter.installSegment5 (opcodeSetDeleteExplicit, new OpSetDelete); interpreter.installSegment5 (opcodeGetSquareRoot, new OpGetSquareRoot); + interpreter.installSegment5 (opcodeFall, new OpFall); + interpreter.installSegment5 (opcodeFallExplicit, new OpFall); + interpreter.installSegment5 (opcodeGetStandingPc, new OpGetStandingPc); + interpreter.installSegment5 (opcodeGetStandingPcExplicit, new OpGetStandingPc); + interpreter.installSegment5 (opcodeGetStandingActor, new OpGetStandingActor); + interpreter.installSegment5 (opcodeGetStandingActorExplicit, new OpGetStandingActor); + interpreter.installSegment5 (opcodeGetWindSpeed, new OpGetWindSpeed); } } } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index fed5877c4b..14fe5b7fd6 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -114,7 +114,7 @@ namespace MWScript } catch (const std::exception& e) { - std::cerr << "exeution of script " << name << " failed." << std::endl; + std::cerr << "execution of script " << name << " failed." << std::endl; if (mVerbose) std::cerr << "(" << e.what() << ")" << std::endl; @@ -209,6 +209,7 @@ namespace MWScript offset = script->mData.mNumShorts+script->mData.mNumLongs; size = script->mData.mNumFloats; + break; default: @@ -221,4 +222,9 @@ namespace MWScript throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId); } + + void ScriptManager::resetGlobalScripts() + { + mGlobalScripts.reset(); + } } diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index 1a856e0c58..7bb98ffbd3 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -61,6 +61,8 @@ namespace MWScript ///< Compile script with the given namen /// \return Success? + virtual void resetGlobalScripts(); + virtual std::pair compileAll(); ///< Compile all scripts /// \return count, success diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 530d44c9dd..04e89edc66 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1025,6 +1025,37 @@ namespace MWScript } }; + template + class OpOnDeath : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = + MWWorld::Class::get (ptr).getCreatureStats (ptr).hasDied(); + + if (value) + MWWorld::Class::get (ptr).getCreatureStats (ptr).clearHasDied(); + + runtime.push (value); + } + }; + + template + class OpIsWerewolf : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push(MWWorld::Class::get(ptr).getNpcStats(ptr).isWerewolf()); + } + }; + const int numberOfAttributes = 8; const int opcodeGetAttribute = 0x2000027; @@ -1113,6 +1144,10 @@ namespace MWScript const int opcodeRaiseRankExplicit = 0x20001e9; const int opcodeLowerRank = 0x20001ea; const int opcodeLowerRankExplicit = 0x20001eb; + const int opcodeOnDeath = 0x20001fc; + const int opcodeOnDeathExplicit = 0x2000205; + const int opcodeIsWerewolf = 0x20001fd; + const int opcodeIsWerewolfExplicit = 0x20001fe; void registerExtensions (Compiler::Extensions& extensions) { @@ -1227,6 +1262,10 @@ namespace MWScript extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit); extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit); extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit); + + extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit); + + extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -1341,6 +1380,12 @@ namespace MWScript interpreter.installSegment5 (opcodeRaiseRankExplicit, new OpRaiseRank); interpreter.installSegment5 (opcodeLowerRank, new OpLowerRank); interpreter.installSegment5 (opcodeLowerRankExplicit, new OpLowerRank); + + interpreter.installSegment5 (opcodeOnDeath, new OpOnDeath); + interpreter.installSegment5 (opcodeOnDeathExplicit, new OpOnDeath); + + interpreter.installSegment5 (opcodeIsWerewolf, new OpIsWerewolf); + interpreter.installSegment5 (opcodeIsWerewolfExplicit, new OpIsWerewolf); } } } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index d86a6e3486..9b7b51a5a2 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -84,21 +84,27 @@ namespace MWScript Interpreter::Type_Float angle = runtime[0].mFloat; runtime.pop(); - float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); - float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); + float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); + + float *objRot = ptr.getRefData().getPosition().rot; + + float lx = Ogre::Radian(objRot[0]).valueDegrees(); + float ly = Ogre::Radian(objRot[1]).valueDegrees(); + float lz = Ogre::Radian(objRot[2]).valueDegrees(); if (axis == "x") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); + MWBase::Environment::get().getWorld()->localRotateObject(ptr,angle-lx,ay,az); } else if (axis == "y") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,angle-ly,az); } else if (axis == "z") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,angle-lz); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -148,15 +154,15 @@ namespace MWScript if (axis=="x") { - runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees()); } else if (axis=="y") { - runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees()); } else if (axis=="z") { - runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees()); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -241,15 +247,15 @@ namespace MWScript if(axis == "x") { - runtime.push(ptr.getRefData().getPosition().pos[0]); + runtime.push(ptr.getCellRef().mPos.pos[0]); } else if(axis == "y") { - runtime.push(ptr.getRefData().getPosition().pos[1]); + runtime.push(ptr.getCellRef().mPos.pos[1]); } else if(axis == "z") { - runtime.push(ptr.getRefData().getPosition().pos[2]); + runtime.push(ptr.getCellRef().mPos.pos[2]); } else throw std::runtime_error ("invalid axis: " + axis); @@ -303,6 +309,8 @@ namespace MWScript zRot = zRot/60.; } MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + + MWWorld::Class::get(ptr).adjustPosition(ptr); } else { @@ -341,6 +349,7 @@ namespace MWScript zRot = zRot/60.; } MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + MWWorld::Class::get(ptr).adjustPosition(ptr); } }; @@ -457,6 +466,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 +516,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); @@ -525,6 +548,165 @@ namespace MWScript } }; + template + class OpRotate : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + const MWWorld::Ptr& ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); + runtime.pop(); + + float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); + + if (axis == "x") + { + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax+rotation,ay,az); + } + else if (axis == "y") + { + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay+rotation,az); + } + else if (axis == "z") + { + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,az+rotation); + } + else + throw std::runtime_error ("invalid rotation axis: " + axis); + } + }; + + template + class OpRotateWorld : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); + runtime.pop(); + + float *objRot = ptr.getRefData().getPosition().rot; + + float ax = Ogre::Radian(objRot[0]).valueDegrees(); + float ay = Ogre::Radian(objRot[1]).valueDegrees(); + float az = Ogre::Radian(objRot[2]).valueDegrees(); + + if (axis == "x") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax+rotation,ay,az); + } + else if (axis == "y") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay+rotation,az); + } + else if (axis == "z") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,az+rotation); + } + else + throw std::runtime_error ("invalid rotation axis: " + axis); + } + }; + + template + class OpSetAtStart : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + ptr.getRefData().getLocalRotation().rot[0] = 0; + ptr.getRefData().getLocalRotation().rot[1] = 0; + ptr.getRefData().getLocalRotation().rot[2] = 0; + MWBase::Environment::get().getWorld()->rotateObject(ptr, 0,0,0,true); + MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().mPos.pos[0], + ptr.getCellRef().mPos.pos[1], ptr.getCellRef().mPos.pos[2]); + + } + }; + + template + class OpMove : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + const MWWorld::Ptr& ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); + runtime.pop(); + + Ogre::Vector3 posChange; + if (axis == "x") + { + posChange=Ogre::Vector3(movement, 0, 0); + } + else if (axis == "y") + { + posChange=Ogre::Vector3(0, movement, 0); + } + else if (axis == "z") + { + posChange=Ogre::Vector3(0, 0, movement); + } + else + throw std::runtime_error ("invalid movement axis: " + axis); + + Ogre::Vector3 worldPos = ptr.getRefData().getBaseNode()->convertLocalToWorldPosition(posChange); + MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z); + } + }; + + template + class OpMoveWorld : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); + runtime.pop(); + + float *objPos = ptr.getRefData().getPosition().pos; + + if (axis == "x") + { + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]); + } + else if (axis == "y") + { + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]); + } + else if (axis == "z") + { + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement); + } + else + throw std::runtime_error ("invalid movement axis: " + axis); + } + }; + + const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -539,6 +721,8 @@ namespace MWScript const int opcodeSetPosExplicit = 0x2000193; const int opcodeGetStartingPos = 0x2000194; const int opcodeGetStartingPosExplicit = 0x2000195; + const int opcodeGetStartingAngle = 0x2000210; + const int opcodeGetStartingAngleExplicit = 0x2000211; const int opcodePosition = 0x2000196; const int opcodePositionExplicit = 0x2000197; const int opcodePositionCell = 0x2000198; @@ -551,6 +735,16 @@ namespace MWScript const int opcodePlaceAtMeExplicit = 0x200019e; const int opcodeModScale = 0x20001e3; const int opcodeModScaleExplicit = 0x20001e4; + const int opcodeRotate = 0x20001ff; + const int opcodeRotateExplicit = 0x2000200; + const int opcodeRotateWorld = 0x2000201; + const int opcodeRotateWorldExplicit = 0x2000202; + const int opcodeSetAtStart = 0x2000203; + const int opcodeSetAtStartExplicit = 0x2000204; + const int opcodeMove = 0x2000206; + const int opcodeMoveExplicit = 0x2000207; + const int opcodeMoveWorld = 0x2000208; + const int opcodeMoveWorldExplicit = 0x2000209; void registerExtensions (Compiler::Extensions& extensions) { @@ -568,6 +762,12 @@ namespace MWScript extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit); + extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit); + extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit); + extensions.registerInstruction("setatstart","",opcodeSetAtStart,opcodeSetAtStartExplicit); + extensions.registerInstruction("move","cf",opcodeMove,opcodeMoveExplicit); + extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit); + extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -597,6 +797,18 @@ namespace MWScript interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe); interpreter.installSegment5(opcodeModScale,new OpModScale); interpreter.installSegment5(opcodeModScaleExplicit,new OpModScale); + interpreter.installSegment5(opcodeRotate,new OpRotate); + interpreter.installSegment5(opcodeRotateExplicit,new OpRotate); + interpreter.installSegment5(opcodeRotateWorld,new OpRotateWorld); + interpreter.installSegment5(opcodeRotateWorldExplicit,new OpRotateWorld); + interpreter.installSegment5(opcodeSetAtStart,new OpSetAtStart); + interpreter.installSegment5(opcodeSetAtStartExplicit,new OpSetAtStart); + interpreter.installSegment5(opcodeMove,new OpMove); + interpreter.installSegment5(opcodeMoveExplicit,new OpMove); + interpreter.installSegment5(opcodeMoveWorld,new OpMoveWorld); + interpreter.installSegment5(opcodeMoveWorldExplicit,new OpMoveWorld); + interpreter.installSegment5(opcodeGetStartingAngle, new OpGetStartingAngle); + interpreter.installSegment5(opcodeGetStartingAngleExplicit, new OpGetStartingAngle); } } } diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 1dc5f9974f..690b07331c 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -586,7 +586,7 @@ void OpenAL_Sound::update() alSourcef(mSource, AL_GAIN, gain); alSourcef(mSource, AL_PITCH, pitch); - alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]); + alSource3f(mSource, AL_POSITION, mPos[0], mPos[1], mPos[2]); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); throwALerror(); @@ -606,7 +606,7 @@ void OpenAL_Sound3D::update() alSourcef(mSource, AL_GAIN, gain); alSourcef(mSource, AL_PITCH, pitch); - alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]); + alSource3f(mSource, AL_POSITION, mPos[0], mPos[1], mPos[2]); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); throwALerror(); @@ -923,10 +923,10 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 if(mContext) { ALfloat orient[6] = { - atdir.x, atdir.z, -atdir.y, - updir.x, updir.z, -updir.y + atdir.x, atdir.y, atdir.z, + updir.x, updir.y, updir.z }; - alListener3f(AL_POSITION, mPos.x, mPos.z, -mPos.y); + alListener3f(AL_POSITION, mPos.x, mPos.y, mPos.z); alListenerfv(AL_ORIENTATION, orient); throwALerror(); } 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/actiondoor.cpp b/apps/openmw/mwworld/actiondoor.cpp new file mode 100644 index 0000000000..6e3441e220 --- /dev/null +++ b/apps/openmw/mwworld/actiondoor.cpp @@ -0,0 +1,16 @@ +#include "actiondoor.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +namespace MWWorld +{ + ActionDoor::ActionDoor (const MWWorld::Ptr& object) : Action (false, object) + { + } + + void ActionDoor::executeImp (const MWWorld::Ptr& actor) + { + MWBase::Environment::get().getWorld()->activateDoor(getTarget()); + } +} diff --git a/apps/openmw/mwworld/actiondoor.hpp b/apps/openmw/mwworld/actiondoor.hpp new file mode 100644 index 0000000000..2dc5ad8c13 --- /dev/null +++ b/apps/openmw/mwworld/actiondoor.hpp @@ -0,0 +1,18 @@ +#ifndef GAME_MWWORLD_ACTIONDOOR_H +#define GAME_MWWORLD_ACTIONDOOR_H + +#include "action.hpp" +#include "ptr.hpp" + +namespace MWWorld +{ + class ActionDoor : public Action + { + virtual void executeImp (const MWWorld::Ptr& actor); + + public: + ActionDoor (const Ptr& object); + }; +} + +#endif diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index eb2ae9dca7..85e73a0560 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -18,8 +18,27 @@ namespace MWWorld void ActionEquip::executeImp (const Ptr& actor) { + MWWorld::Ptr object = getTarget(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); + std::pair result = MWWorld::Class::get (object).canBeEquipped (object, actor); + + // display error message if the player tried to equip something + if (!result.second.empty() && actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox(result.second); + + switch(result.first) + { + case 0: + return; + 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 +46,7 @@ namespace MWWorld MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { - if (*it == getTarget()) + if (*it == object) { break; } @@ -35,8 +54,6 @@ namespace MWWorld assert(it != invStore.end()); - std::string npcRace = actor.get()->mBase->mRace; - bool equipped = false; // equip the item in the first free slot @@ -44,70 +61,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}", std::vector()); - - 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}", std::vector()); - } - - else // It's boots - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}", std::vector()); - } - } - break; - } - } - - if(!allow) - break; - } - - } - // if all slots are occupied, replace the last slot if (slot == --slots.first.end()) { @@ -125,10 +78,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/actionrepair.cpp b/apps/openmw/mwworld/actionrepair.cpp new file mode 100644 index 0000000000..bd56421165 --- /dev/null +++ b/apps/openmw/mwworld/actionrepair.cpp @@ -0,0 +1,18 @@ +#include "actionrepair.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +namespace MWWorld +{ + ActionRepair::ActionRepair(const Ptr &item) + : Action(false, item) + { + } + + void ActionRepair::executeImp (const Ptr& actor) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Repair); + MWBase::Environment::get().getWindowManager()->startRepairItem(getTarget()); + } +} diff --git a/apps/openmw/mwworld/actionrepair.hpp b/apps/openmw/mwworld/actionrepair.hpp new file mode 100644 index 0000000000..1d3ef2bc11 --- /dev/null +++ b/apps/openmw/mwworld/actionrepair.hpp @@ -0,0 +1,17 @@ +#ifndef GAME_MWWORLD_ACTIONREPAIR_H +#define GAME_MWWORLD_ACTIONREPAIR_H + +#include "action.hpp" + +namespace MWWorld +{ + class ActionRepair : public Action + { + virtual void executeImp (const Ptr& actor); + + public: + ActionRepair(const MWWorld::Ptr& item); + }; +} + +#endif diff --git a/apps/openmw/mwworld/actionsoulgem.cpp b/apps/openmw/mwworld/actionsoulgem.cpp new file mode 100644 index 0000000000..6746f692f1 --- /dev/null +++ b/apps/openmw/mwworld/actionsoulgem.cpp @@ -0,0 +1,21 @@ +#include "actionsoulgem.hpp" + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" + +namespace MWWorld +{ + +ActionSoulgem::ActionSoulgem(const Ptr &object) + : Action(false, object) +{ + +} + +void ActionSoulgem::executeImp(const Ptr &actor) +{ + MWBase::Environment::get().getWindowManager()->showSoulgemDialog(getTarget()); +} + + +} diff --git a/apps/openmw/mwworld/actionsoulgem.hpp b/apps/openmw/mwworld/actionsoulgem.hpp new file mode 100644 index 0000000000..0dd5266570 --- /dev/null +++ b/apps/openmw/mwworld/actionsoulgem.hpp @@ -0,0 +1,19 @@ +#ifndef GAME_MWWORLD_ACTIONSOULGEM_H +#define GAME_MWWORLD_ACTIONSOULGEM_H + +#include "action.hpp" +#include "ptr.hpp" + +namespace MWWorld +{ + class ActionSoulgem : public Action + { + virtual void executeImp (const MWWorld::Ptr& actor); + + public: + /// @param soulgem to use + ActionSoulgem (const Ptr& object); + }; +} + +#endif diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index 8bba898ce7..4b1f70096a 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -13,8 +13,8 @@ namespace ESM namespace MWWorld { - /// List all (Ogre-)handles. - struct ListHandles + /// List all (Ogre-)handles, then reset RefData::mBaseNode to 0. + struct ListAndResetHandles { std::vector mHandles; @@ -23,6 +23,8 @@ namespace MWWorld Ogre::SceneNode* handle = data.getBaseNode(); if (handle) mHandles.push_back (handle); + + data.setBaseNode(0); return true; } }; diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index f95d30df13..cb2e49ca08 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -11,11 +11,11 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { if (cell->mData.mFlags & ESM::Cell::Interior) { - std::map::iterator result = mInteriors.find (cell->mName); + std::map::iterator result = mInteriors.find (Misc::StringUtils::lowerCase(cell->mName)); if (result==mInteriors.end()) { - result = mInteriors.insert (std::make_pair (cell->mName, Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (Misc::StringUtils::lowerCase(cell->mName), Ptr::CellStore (cell))).first; } return &result->second; @@ -36,6 +36,14 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) } } +void MWWorld::Cells::clear() +{ + mInteriors.clear(); + mExteriors.clear(); + std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair("", (MWWorld::Ptr::CellStore*)0)); + mIdCacheIndex = 0; +} + void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) { for (CellRefList::List::iterator iter ( @@ -45,7 +53,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 +63,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 +73,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); } } @@ -165,6 +173,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce else return Ptr(); } + MWWorld::Ptr ptr; if (MWWorld::LiveCellRef *ref = cell.mActivators.find (name)) @@ -206,7 +215,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce if (MWWorld::LiveCellRef *ref = cell.mLights.find (name)) ptr = Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.mLockpicks.find (name)) + if (MWWorld::LiveCellRef *ref = cell.mLockpicks.find (name)) ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mMiscItems.find (name)) @@ -246,16 +255,16 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) } // Then check cells that are already listed - for (std::map::iterator iter = mInteriors.begin(); - iter!=mInteriors.end(); ++iter) + for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); + iter!=mExteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) - return ptr; + return ptr; } - for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); - iter!=mExteriors.end(); ++iter) + for (std::map::iterator iter = mInteriors.begin(); + iter!=mInteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) @@ -266,7 +275,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) const MWWorld::Store &cells = mStore.get(); MWWorld::Store::iterator iter; - for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter) + for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter) { Ptr::CellStore *cellStore = getCellStore (&(*iter)); @@ -276,7 +285,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) return ptr; } - for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter) + for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter) { Ptr::CellStore *cellStore = getCellStore (&(*iter)); diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index f999fedde2..53c72bb754 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -37,6 +37,8 @@ namespace MWWorld public: + void clear(); + Cells (const MWWorld::ESMStore& store, std::vector& reader); ///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole /// world diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index c182f196bc..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 @@ -112,7 +76,7 @@ namespace MWWorld CellRefList mCreatureLists; CellRefList mItemLists; CellRefList mLights; - CellRefList mLockpicks; + CellRefList mLockpicks; CellRefList mMiscItems; CellRefList mNpcs; CellRefList mProbes; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 71b24b65dc..5690531e71 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -47,6 +47,16 @@ namespace MWWorld throw std::runtime_error ("class does not represent an actor"); } + bool Class::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return false; + } + + int Class::getServices(const Ptr &actor) const + { + throw std::runtime_error ("class does not have services"); + } + MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const { throw std::runtime_error ("class does not have creature stats"); @@ -127,6 +137,11 @@ namespace MWWorld return 0; } + float Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + throw std::runtime_error ("class does not support enchanting"); + } + MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const { throw std::runtime_error ("movement settings not supported by class"); @@ -137,6 +152,11 @@ namespace MWWorld return Ogre::Vector3 (0, 0, 0); } + Ogre::Vector3 Class::getRotationVector (const Ptr& ptr) const + { + return Ogre::Vector3 (0, 0, 0); + } + std::pair, bool> Class::getEquipmentSlots (const Ptr& ptr) const { return std::make_pair (std::vector(), false); @@ -157,6 +177,11 @@ namespace MWWorld throw std::runtime_error ("capacity not supported by this class"); } + float Class::getWeight(const Ptr &ptr) const + { + throw std::runtime_error ("weight not supported by this class"); + } + float Class::getEncumbrance (const MWWorld::Ptr& ptr) const { throw std::runtime_error ("encumbrance not supported by class"); @@ -167,17 +192,25 @@ namespace MWWorld return false; } - bool Class::hasDetected (const MWWorld::Ptr& ptr, const MWWorld::Ptr& ptr2) const + bool Class::hasDetected (const MWWorld::Ptr& ptr, const MWWorld::Ptr& ptr2) const { - return false; + return true; + } + + float Class::getArmorRating (const MWWorld::Ptr& ptr) const + { + throw std::runtime_error("Class does not support armor rating"); } const Class& Class::get (const std::string& key) { + if (key.empty()) + throw std::logic_error ("Class::get(): attempting to get an empty key"); + std::map >::const_iterator iter = sClasses.find (key); if (iter==sClasses.end()) - throw std::logic_error ("unknown class key: " + key); + throw std::logic_error ("Class::get(): unknown class key: " + key); return *iter->second; } @@ -187,6 +220,11 @@ namespace MWWorld return get (ptr.getTypeName()); } + bool Class::isPersistent(const Ptr &ptr) const + { + throw std::runtime_error ("class does not support persistence"); + } + void Class::registerClass (const std::string& key, boost::shared_ptr instance) { sClasses.insert (std::make_pair (key, instance)); @@ -236,6 +274,20 @@ namespace MWWorld return ""; } + 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"); + } + + std::pair Class::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + return std::make_pair (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 1a6a16ca07..b4fd84a6b4 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) @@ -150,6 +153,9 @@ namespace MWWorld ///< Return desired movement vector (determined based on movement settings, /// stance and stats). + virtual Ogre::Vector3 getRotationVector (const Ptr& ptr) const; + ///< Return desired rotations, as euler angles. + virtual std::pair, bool> getEquipmentSlots (const Ptr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? @@ -215,6 +221,9 @@ namespace MWWorld ///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) + virtual float getArmorRating (const MWWorld::Ptr& ptr) const; + ///< @return combined armor rating of this actor + virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. @@ -222,12 +231,30 @@ namespace MWWorld ///< @return the enchantment ID if the object is enchanted, otherwise an empty string /// (default implementation: return empty string) + virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + ///< @return the number of enchantment points available for possible enchanting + virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; 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 int getServices (const MWWorld::Ptr& actor) const; + virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8a7884e9e0..6682f072a4 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" @@ -38,12 +40,6 @@ namespace return sum; } - - bool compare_string_ci(std::string str1, std::string str2) - { - Misc::StringUtils::toLower(str1); - return str1 == str2; - } } MWWorld::ContainerStore::ContainerStore() : mStateId (0), mCachedWeight (0), mWeightUpToDate (false) {} @@ -62,13 +58,17 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) { - /// \todo add current weapon/armor health, remaining lockpick/repair uses, current enchantment charge here as soon as they are implemented + /// \todo add current enchantment charge here when it is implemented if ( ptr1.mCellRef->mRefID == ptr2.mCellRef->mRefID - && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks - && MWWorld::Class::get(ptr1).getEnchantment(ptr1) == "" // item with enchantment never stacks (we could revisit this later, but for now it makes selecting items in the spell window much easier) + && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks + && MWWorld::Class::get(ptr1).getEnchantment(ptr1) == "" // item with enchantment never stacks (we could revisit this later, but for now it makes selecting items in the spell window much easier) && ptr1.mCellRef->mOwner == ptr2.mCellRef->mOwner && ptr1.mCellRef->mSoul == ptr2.mCellRef->mSoul - && ptr1.mCellRef->mCharge == ptr2.mCellRef->mCharge) + // item that is already partly used up never stacks + && (!MWWorld::Class::get(ptr1).hasItemHealth(ptr1) || ptr1.mCellRef->mCharge == -1 + || MWWorld::Class::get(ptr1).getItemMaxHealth(ptr1) == ptr1.mCellRef->mCharge) + && (!MWWorld::Class::get(ptr2).hasItemHealth(ptr2) || ptr2.mCellRef->mCharge == -1 + || MWWorld::Class::get(ptr2).getItemMaxHealth(ptr2) == ptr2.mCellRef->mCharge)) return true; return false; @@ -113,33 +113,28 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) // gold needs special handling: when it is inserted into a container, the base object automatically becomes Gold_001 // this ensures that gold piles of different sizes stack with each other (also, several scripts rely on Gold_001 for detecting player gold) - if (MWWorld::Class::get(ptr).getName(ptr) == esmStore.get().find("sGold")->getString()) + if (Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_001") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_005") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_010") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - MWWorld::LiveCellRef *gold = - ptr.get(); + MWWorld::ManualRef ref(esmStore, "Gold_001"); - if (compare_string_ci(gold->mRef.mRefID, "gold_001") - || compare_string_ci(gold->mRef.mRefID, "gold_005") - || compare_string_ci(gold->mRef.mRefID, "gold_010") - || compare_string_ci(gold->mRef.mRefID, "gold_025") - || compare_string_ci(gold->mRef.mRefID, "gold_100")) + int count = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); + + ref.getPtr().getRefData().setCount(count); + for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { - MWWorld::ManualRef ref(esmStore, "Gold_001"); - - int count = (ptr.getRefData().getCount() == 1) ? gold->mBase->mData.mValue : ptr.getRefData().getCount(); - ref.getPtr().getRefData().setCount(count); - for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) + if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) { - if (compare_string_ci((*iter).get()->mRef.mRefID, "gold_001")) - { - (*iter).getRefData().setCount( (*iter).getRefData().getCount() + count); - flagAsModified(); - return iter; - } + (*iter).getRefData().setCount( (*iter).getRefData().getCount() + count); + flagAsModified(); + return iter; } - - return addImpl(ref.getPtr()); } + + return addImpl(ref.getPtr()); } // determine whether to stack or not @@ -171,7 +166,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr case Type_Clothing: clothes.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --clothes.mList.end()); break; case Type_Ingredient: ingreds.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --ingreds.mList.end()); break; case Type_Light: lights.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --lights.mList.end()); break; - case Type_Lockpick: lockpicks.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --lockpicks.mList.end()); break; + case Type_Lockpick: lockpicks.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --lockpicks.mList.end()); break; case Type_Miscellaneous: miscItems.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --miscItems.mList.end()); break; case Type_Probe: probes.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --probes.mList.end()); break; case Type_Repair: repairs.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --repairs.mList.end()); break; @@ -182,26 +177,87 @@ 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) + + try + { + 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()); + } + } + catch (std::logic_error& e) + { + // Vanilla doesn't fail on nonexistent items in levelled lists + std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl; + return; + } +} + void MWWorld::ContainerStore::clear() { for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) @@ -272,7 +328,7 @@ int MWWorld::ContainerStore::getType (const Ptr& ptr) if (ptr.getTypeName()==typeid (ESM::Light).name()) return Type_Light; - if (ptr.getTypeName()==typeid (ESM::Tool).name()) + if (ptr.getTypeName()==typeid (ESM::Lockpick).name()) return Type_Lockpick; if (ptr.getTypeName()==typeid (ESM::Miscellaneous).name()) @@ -321,7 +377,7 @@ MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *contain : mType(MWWorld::ContainerStore::Type_Ingredient), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mIngredient(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Light), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLight(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Lockpick), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLockpick(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Miscellaneous), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mMiscellaneous(iterator){} diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index e4f75d5478..559463d46f 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -44,7 +44,7 @@ namespace MWWorld MWWorld::CellRefList clothes; MWWorld::CellRefList ingreds; MWWorld::CellRefList lights; - MWWorld::CellRefList lockpicks; + MWWorld::CellRefList lockpicks; MWWorld::CellRefList miscItems; MWWorld::CellRefList probes; MWWorld::CellRefList repairs; @@ -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: @@ -78,13 +79,12 @@ namespace MWWorld ContainerStoreIterator addImpl (const Ptr& ptr); ///< Add the item to this container (no stacking) - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); - ///< @return true if the two specified objects can stack with each other - /// @note ptr1 is the item that is already in this container - public: - void fill (const ESM::InventoryList& items, const MWWorld::ESMStore& store); + virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); + ///< @return true if the two specified objects can stack with each other + + void fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store); ///< Insert items into *this. void clear(); @@ -127,7 +127,7 @@ namespace MWWorld MWWorld::CellRefList::List::iterator mClothing; MWWorld::CellRefList::List::iterator mIngredient; MWWorld::CellRefList::List::iterator mLight; - MWWorld::CellRefList::List::iterator mLockpick; + MWWorld::CellRefList::List::iterator mLockpick; MWWorld::CellRefList::List::iterator mMiscellaneous; MWWorld::CellRefList::List::iterator mProbe; MWWorld::CellRefList::List::iterator mRepair; @@ -149,7 +149,7 @@ namespace MWWorld ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 257676076c..5a4f1db541 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -27,14 +27,15 @@ void ESMStore::load(ESM::ESMReader &esm) ESM::Dialogue *dialogue = 0; + /// \todo Move this to somewhere else. ESMReader? // Cache parent esX files by tracking their indices in the global list of // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. int index = ~0; - const ESM::ESMReader::MasterList &masters = esm.getMasters(); + const std::vector &masters = esm.getMasters(); std::vector *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { - ESM::MasterData &mast = const_cast(masters[j]); + ESM::Header::MasterData &mast = const_cast(masters[j]); std::string fname = mast.name; for (int i = 0; i < esm.getIndex(); i++) { const std::string &candidate = allPlugins->at(i).getContext().filename; @@ -133,15 +134,6 @@ void ESMStore::setUp() mSkills.setUp(); mMagicEffects.setUp(); mAttributes.setUp(); - - ESM::NPC item; - item.mId = "player"; - - const ESM::NPC *pIt = mNpcs.find("player"); - assert(pIt != NULL); - - mNpcs.insert(*pIt); - mNpcs.eraseStatic(pIt->mId); } } // end namespace diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index b9b95e4f4e..d86faf7663 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -32,7 +32,7 @@ namespace MWWorld Store mCreatureLists; Store mItemLists; Store mLights; - Store mLockpicks; + Store mLockpicks; Store mMiscItems; Store mNpcs; Store mNpcChange; @@ -67,6 +67,8 @@ namespace MWWorld std::map mIds; std::map mStores; + ESM::NPC mPlayerTemplate; + unsigned int mDynamicCount; public: @@ -141,6 +143,21 @@ namespace MWWorld mStores[ESM::REC_WEAP] = &mWeapons; } + void clearDynamic () + { + for (std::map::iterator it = mStores.begin(); it != mStores.end(); ++it) + it->second->clearDynamic(); + + mNpcs.insert(mPlayerTemplate); + } + + void movePlayerRecord () + { + mPlayerTemplate = *mNpcs.find("player"); + mNpcs.eraseStatic(mPlayerTemplate.mId); + mNpcs.insert(mPlayerTemplate); + } + void load(ESM::ESMReader &esm); template @@ -171,6 +188,25 @@ namespace MWWorld return ptr; } + template + const T *insertStatic(const T &x) { + Store &store = const_cast &>(get()); + if (store.search(x.mId) != 0) { + std::ostringstream msg; + msg << "Try to override existing record '" << x.mId << "'"; + throw std::runtime_error(msg.str()); + } + T record = x; + + T *ptr = store.insertStatic(record); + for (iterator it = mStores.begin(); it != mStores.end(); ++it) { + if (it->second == &store) { + mIds[ptr->mId] = it->first; + } + } + return ptr; + } + // This method must be called once, after loading all master/plugin files. This can only be done // from the outside, so it must be public. void setUp(); @@ -312,7 +348,7 @@ namespace MWWorld } template <> - inline const Store &ESMStore::get() const { + inline const Store &ESMStore::get() const { return mLockpicks; } diff --git a/apps/openmw/mwworld/failedaction.cpp b/apps/openmw/mwworld/failedaction.cpp index ec763dba01..1db00ad06c 100644 --- a/apps/openmw/mwworld/failedaction.cpp +++ b/apps/openmw/mwworld/failedaction.cpp @@ -15,7 +15,7 @@ namespace MWWorld { if ( actor.getRefData().getHandle()=="player" && !(message.empty())) { - MWBase::Environment::get().getWindowManager() ->messageBox(message, std::vector()); + MWBase::Environment::get().getWindowManager() ->messageBox(message); } } } diff --git a/apps/openmw/mwworld/fallback.cpp b/apps/openmw/mwworld/fallback.cpp new file mode 100644 index 0000000000..569a6b50c2 --- /dev/null +++ b/apps/openmw/mwworld/fallback.cpp @@ -0,0 +1,49 @@ +#include "fallback.hpp" +#include "boost/lexical_cast.hpp" +namespace MWWorld +{ + Fallback::Fallback(const std::map& fallback):mFallbackMap(fallback) + {} + + std::string Fallback::getFallbackString(const std::string& fall) const + { + std::map::const_iterator it; + if((it = mFallbackMap.find(fall)) == mFallbackMap.end()) + { + return ""; + } + return it->second; + } + float Fallback::getFallbackFloat(const std::string& fall) const + { + std::string fallback=getFallbackString(fall); + if(fallback.empty()) + return 0; + else + return boost::lexical_cast(fallback); + } + bool Fallback::getFallbackBool(const std::string& fall) const + { + std::string fallback=getFallbackString(fall); + if(fallback.empty()) + return false; + else + return boost::lexical_cast(fallback); + } + Ogre::ColourValue Fallback::getFallbackColour(const std::string& fall) const + { + std::string sum=getFallbackString(fall); + if(sum.empty()) + return Ogre::ColourValue(0,0,0); + else + { + std::string ret[3]; + unsigned int j=0; + for(unsigned int i=0;i(ret[0])/255.f,boost::lexical_cast(ret[1])/255.f,boost::lexical_cast(ret[2])/255.f); + } + } +} diff --git a/apps/openmw/mwworld/fallback.hpp b/apps/openmw/mwworld/fallback.hpp new file mode 100644 index 0000000000..6c5802e071 --- /dev/null +++ b/apps/openmw/mwworld/fallback.hpp @@ -0,0 +1,22 @@ +#ifndef GAME_MWWORLD_FALLBACK_H +#define GAME_MWWORLD_FALLBACK_H + +#include +#include + +#include + +namespace MWWorld +{ + class Fallback + { + const std::map mFallbackMap; + public: + Fallback(const std::map& fallback); + std::string getFallbackString(const std::string& fall) const; + float getFallbackFloat(const std::string& fall) const; + bool getFallbackBool(const std::string& fall) const; + Ogre::ColourValue getFallbackColour(const std::string& fall) const; + }; +} +#endif diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 9e57910ee0..a905f8aaed 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -74,15 +74,6 @@ namespace MWWorld mVariables.insert (std::make_pair (iter->mId, std::make_pair (type, value))); } - - if (mVariables.find ("dayspassed")==mVariables.end()) - { - // vanilla Morrowind does not define dayspassed. - Data value; - value.mLong = 0; - - mVariables.insert (std::make_pair ("dayspassed", std::make_pair ('l', value))); - } } const Globals::Data& Globals::operator[] (const std::string& name) const diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index dd518ff6a4..9d07ecb76d 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).first) + { + 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 @@ -261,6 +276,8 @@ bool MWWorld::InventoryStore::stacks(const Ptr& ptr1, const Ptr& ptr2) { if (*iter != end() && ptr1 == **iter) return false; + if (*iter != end() && ptr2 == **iter) + return false; } return true; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e55eca0ae7..0801e04bc5 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(); @@ -90,8 +90,6 @@ namespace MWWorld ///< \attention This function is internal to the world model and should not be called from /// outside. - protected: - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); ///< @return true if the two specified objects can stack with each other /// @note ptr1 is the item that is already in this container 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/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 91b8cf8cd4..6616165fae 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -53,7 +53,7 @@ namespace MWWorld !create (store.get(), name) && !create (store.get(), name) && !create (store.get(), name) && - !create (store.get(), name) && + !create (store.get(), name) && !create (store.get(), name) && !create (store.get(), name) && !create (store.get(), name) && @@ -68,12 +68,12 @@ namespace MWWorld cellRef.mRefnum = -1; cellRef.mScale = 1; cellRef.mFactIndex = 0; - cellRef.mCharge = 0; - cellRef.mIntv = 0; - cellRef.mNam9 = 0; + cellRef.mCharge = -1; + cellRef.mGoldValue = 1; + cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; cellRef.mLockLevel = 0; - cellRef.mUnam = 0; + cellRef.mReferenceBlocked = 0; } const Ptr& getPtr() const diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7edd202935..7d63a2362e 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -33,23 +33,23 @@ namespace MWWorld class MovementSolver { private: - static bool stepMove(Ogre::Vector3& position, const Ogre::Vector3 &velocity, float remainingTime, + static bool stepMove(Ogre::Vector3& position, const Ogre::Quaternion& orient, const Ogre::Vector3 &velocity, float remainingTime, const Ogre::Vector3 &halfExtents, bool isInterior, OEngine::Physic::PhysicEngine *engine) { traceResults trace; // no initialization needed - newtrace(&trace, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), + newtrace(&trace, orient, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine); if(trace.fraction == 0.0f) return false; - newtrace(&trace, trace.endpos, trace.endpos + velocity*remainingTime, + newtrace(&trace, orient, trace.endpos, trace.endpos + velocity*remainingTime, halfExtents, isInterior, engine); if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope)) return false; - newtrace(&trace, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine); + newtrace(&trace, orient, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine); if(getSlope(trace.planenormal) <= sMaxSlope) { // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. @@ -88,6 +88,44 @@ 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); + halfExtents.z = 1; // we trace the feet only, so we use a very thin box + + Ogre::Vector3 newPosition = position; + + traceResults trace; //no initialization needed + + int maxHeight = 200.f; + newtrace(&trace, Ogre::Quaternion::IDENTITY, newPosition, newPosition-Ogre::Vector3(0,0,maxHeight), halfExtents, isInterior, engine); + if(trace.fraction < 1.0f) + hit = true; + newPosition = trace.endpos; + + physicActor->setOnGround(hit && getSlope(trace.planenormal) <= sMaxSlope); + physicActor->enableCollisions(wasCollisionMode); + + if (hit) + return newPosition+Ogre::Vector3(0,0,4); + else + return position; + } + static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity, OEngine::Physic::PhysicEngine *engine) { @@ -101,7 +139,7 @@ namespace MWWorld // FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why? return position + (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)* Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian( -refpos.rot[0]), Ogre::Vector3::UNIT_X)) * + Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * movement; } @@ -111,20 +149,20 @@ namespace MWWorld bool isInterior = !ptr.getCell()->isExterior(); Ogre::Vector3 halfExtents = physicActor->getHalfExtents();// + Vector3(1,1,1); physicActor->enableCollisions(false); - + Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::UNIT_Z); Ogre::Vector3 velocity; if(!gravity) { velocity = (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)* Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian( -refpos.rot[0]), Ogre::Vector3::UNIT_X)) * + Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * movement / time; } else { if(!(movement.z > 0.0f)) { - newtrace(&trace, position, position-Ogre::Vector3(0,0,4), halfExtents, isInterior, engine); + newtrace(&trace, orient, position, position-Ogre::Vector3(0,0,4), halfExtents, isInterior, engine); if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope) onground = true; } @@ -145,7 +183,7 @@ namespace MWWorld int iterations = 0; do { // trace to where character would go if there were no obstructions - newtrace(&trace, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, isInterior, engine); + newtrace(&trace, orient, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, isInterior, engine); newPosition = trace.endpos; remainingTime = remainingTime * (1.0f-trace.fraction); @@ -164,7 +202,7 @@ namespace MWWorld { // Can't walk on this. Try to step up onto it. if((gravity && !onground) || - !stepMove(newPosition, velocity, remainingTime, halfExtents, isInterior, engine)) + !stepMove(newPosition, orient, velocity, remainingTime, halfExtents, isInterior, engine)) { Ogre::Vector3 resultantDirection = trace.planenormal.crossProduct(up); resultantDirection.normalise(); @@ -182,7 +220,7 @@ namespace MWWorld if(onground) { - newtrace(&trace, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+4.0f), halfExtents, isInterior, engine); + newtrace(&trace, orient, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+4.0f), halfExtents, isInterior, engine); if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope) newPosition.z = trace.endpos.z + 2.0f; else @@ -217,14 +255,13 @@ namespace MWWorld std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance) { btVector3 dir(0, 1, 0); - dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); - dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw); dir.setX(-dir.x()); - btVector3 origin( - mPlayerData.eyepos.x, - mPlayerData.eyepos.y, - mPlayerData.eyepos.z); + btVector3 origin(mCameraData.eyepos.x, + mCameraData.eyepos.y, + mCameraData.eyepos.z); origin += dir * 5; btVector3 dest = origin + dir * queryDistance; @@ -237,14 +274,13 @@ namespace MWWorld std::vector < std::pair > PhysicsSystem::getFacedHandles (float queryDistance) { btVector3 dir(0, 1, 0); - dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); - dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw); dir.setX(-dir.x()); - btVector3 origin( - mPlayerData.eyepos.x, - mPlayerData.eyepos.y, - mPlayerData.eyepos.z); + btVector3 origin(mCameraData.eyepos.x, + mCameraData.eyepos.y, + mCameraData.eyepos.z); origin += dir * 5; btVector3 dest = origin + dir * queryDistance; @@ -297,14 +333,13 @@ namespace MWWorld return result; } - bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to) + bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to, bool raycastingObjectOnly,bool ignoreHeightMap) { btVector3 _from, _to; _from = btVector3(from.x, from.y, from.z); _to = btVector3(to.x, to.y, to.z); - std::pair result = mEngine->rayTest(_from, _to); - + std::pair result = mEngine->rayTest(_from, _to, raycastingObjectOnly,ignoreHeightMap); return !(result.first == ""); } @@ -318,7 +353,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)); @@ -346,11 +381,20 @@ namespace MWWorld } } + std::vector PhysicsSystem::getCollisions(const Ptr &ptr) + { + return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName()); + } + Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity) { 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, @@ -364,14 +408,15 @@ namespace MWWorld mEngine->removeHeightField(x, y); } - void PhysicsSystem::addObject (const Ptr& ptr) + void PhysicsSystem::addObject (const Ptr& ptr, bool placeable) { std::string mesh = MWWorld::Class::get(ptr).getModel(ptr); Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); handleToMesh[node->getName()] = mesh; - OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody(mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation()); - OEngine::Physic::RigidBody* raycastingBody = mEngine->createAndAdjustRigidBody - (mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true); + OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody( + mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, false, placeable); + OEngine::Physic::RigidBody* raycastingBody = mEngine->createAndAdjustRigidBody( + mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true, placeable); mEngine->addRigidBody(body, true, raycastingBody); } @@ -440,8 +485,13 @@ namespace MWWorld const std::string &handle = node->getName(); if(handleToMesh.find(handle) != handleToMesh.end()) { + bool placeable = false; + if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle,true)) + placeable = body->mPlaceable; + else if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle,false)) + placeable = body->mPlaceable; removeObject(handle); - addObject(ptr); + addObject(ptr, placeable); } if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) @@ -450,7 +500,7 @@ namespace MWWorld bool PhysicsSystem::toggleCollisionMode() { - for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) + for(std::map::iterator it = mEngine->mActorMap.begin(); it != mEngine->mActorMap.end();it++) { if (it->first=="player") { @@ -494,10 +544,10 @@ namespace MWWorld return true; } - void PhysicsSystem::updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw) + void PhysicsSystem::updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw) { - mPlayerData.eyepos = eyepos; - mPlayerData.pitch = pitch; - mPlayerData.yaw = yaw; + mCameraData.eyepos = eyepos; + mCameraData.pitch = pitch; + mCameraData.yaw = yaw; } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 60c8246ae3..7b48f880ee 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -29,7 +29,7 @@ namespace MWWorld PhysicsSystem (OEngine::Render::OgreRenderer &_rend); ~PhysicsSystem (); - void addObject (const MWWorld::Ptr& ptr); + void addObject (const MWWorld::Ptr& ptr, bool placeable=false); void addActor (const MWWorld::Ptr& ptr); @@ -51,6 +51,8 @@ namespace MWWorld bool toggleCollisionMode(); Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity); + std::vector getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with + Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); std::pair getFacedHandle (MWWorld::World& world, float queryDistance); std::vector < std::pair > getFacedHandles (float queryDistance); @@ -60,8 +62,8 @@ namespace MWWorld btVector3 getRayPoint(float extent, float mouseX, float mouseY); - // cast ray, return true if it hit something - bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); + // cast ray, return true if it hit something. if raycasringObjectOnlt is set to false, it ignores NPCs and objects with no collisions. + bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to, bool raycastingObjectOnly = true,bool ignoreHeightMap = false); std::pair castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len); @@ -75,13 +77,13 @@ namespace MWWorld bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); - void updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw); + void updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw); private: struct { Ogre::Vector3 eyepos; float pitch, yaw; - } mPlayerData; + } mCameraData; OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 03dd1abc79..e352b4c827 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,14 +1,21 @@ #include "player.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/character.hpp" +#include "../mwmechanics/security.hpp" + +#include "../mwrender/animation.hpp" -#include "esmstore.hpp" #include "class.hpp" namespace MWWorld @@ -25,12 +32,46 @@ namespace MWWorld playerPos[0] = playerPos[1] = playerPos[2] = 0; } + void Player::set(const ESM::NPC *player) + { + mPlayer.mBase = player; + + float* playerPos = mPlayer.mData.getPosition().pos; + 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(); @@ -42,14 +83,14 @@ namespace MWWorld if (mAutoMove) value = 1; - MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; + MWWorld::Class::get (ptr).getMovementSettings (ptr).mPosition[1] = value; } void Player::setLeftRight (int value) { MWWorld::Ptr ptr = getPlayer(); - MWWorld::Class::get (ptr).getMovementSettings (ptr).mLeftRight = value; + MWWorld::Class::get (ptr).getMovementSettings (ptr).mPosition[0] = value; } void Player::setForwardBackward (int value) @@ -61,14 +102,14 @@ namespace MWWorld if (mAutoMove) value = 1; - MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; + MWWorld::Class::get (ptr).getMovementSettings (ptr).mPosition[1] = value; } void Player::setUpDown(int value) { MWWorld::Ptr ptr = getPlayer(); - MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value; + MWWorld::Class::get (ptr).getMovementSettings (ptr).mPosition[2] = value; } void Player::setRunState(bool run) @@ -77,15 +118,6 @@ namespace MWWorld MWWorld::Class::get(ptr).setStance(ptr, MWWorld::Class::Run, run); } - void Player::toggleRunning() - { - MWWorld::Ptr ptr = getPlayer(); - - bool running = MWWorld::Class::get (ptr).getStance (ptr, MWWorld::Class::Run, true); - - MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running); - } - void Player::setSneak(bool sneak) { MWWorld::Ptr ptr = getPlayer(); @@ -93,6 +125,67 @@ namespace MWWorld MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Sneak, sneak); } + void Player::setYaw(float yaw) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[2] = yaw; + } + void Player::setPitch(float pitch) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[0] = pitch; + } + void Player::setRoll(float roll) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[1] = roll; + } + + void Player::use() + { + MWWorld::InventoryStore& store = MWWorld::Class::get(getPlayer()).getInventoryStore(getPlayer()); + MWWorld::ContainerStoreIterator equipped = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if (getDrawState() == MWMechanics::DrawState_Weapon) + { + if (equipped != store.end()) + { + MWWorld::Ptr item = *equipped; + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(getPlayer()); + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); + + if (anim->isPriorityActive(MWMechanics::Priority_Weapon)) + return; + + std::string resultMessage, resultSound; + + if (item.getTypeName() == typeid(ESM::Lockpick).name()) + { + if (!target.isEmpty()) + MWMechanics::Security(getPlayer()).pickLock(target, item, resultMessage, resultSound); + anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "start", "stop", 0.0, 0); + } + else if (item.getTypeName() == typeid(ESM::Probe).name()) + { + if (!target.isEmpty()) + MWMechanics::Security(getPlayer()).probeTrap(target, item, resultMessage, resultSound); + anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "start", "stop", 0.0, 0); + } + + if (!resultMessage.empty()) + MWBase::Environment::get().getWindowManager()->messageBox(resultMessage); + if (!resultSound.empty()) + MWBase::Environment::get().getSoundManager()->playSound(resultSound,1,1); + + // tool used up? + if (!item.getRefData().getCount()) + MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); + else + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); + } + } + } + MWMechanics::DrawState_ Player::getDrawState() { MWWorld::Ptr ptr = getPlayer(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 5f2fc3a08b..fd25f749fb 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,19 @@ namespace MWWorld Player(const ESM::NPC *player, const MWBase::World& world); - void setCell (MWWorld::CellStore *cellStore) - { - mCellStore = cellStore; - } + void set (const ESM::NPC *player); - MWWorld::Ptr getPlayer() - { - MWWorld::Ptr ptr (&mPlayer, mCellStore); - return ptr; - } + void setCell (MWWorld::CellStore *cellStore); - void setBirthSign(const std::string &sign) { - mSign = sign; - } + MWWorld::Ptr getPlayer(); - const std::string &getBirthSign() const { - return mSign; - } + void setBirthSign(const std::string &sign); + + const std::string &getBirthSign() const; void setDrawState (MWMechanics::DrawState_ state); - bool getAutoMove() const - { - return mAutoMove; - } + bool getAutoMove() const; MWMechanics::DrawState_ getDrawState(); /// \todo constness @@ -65,9 +58,15 @@ namespace MWWorld void setForwardBackward (int value); void setUpDown(int value); + void use (); + ///< Use item equipped on right hand, or fists + void setRunState(bool run); - void toggleRunning(); void setSneak(bool sneak); + + void setYaw(float yaw); + void setPitch(float pitch); + void setRoll(float roll); }; } #endif diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index d97ebcc6ea..14e2c76a41 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -68,7 +68,7 @@ namespace MWWorld Ptr::CellStore *getCell() const { - assert (mCell); + assert(mCell); return mCell; } diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 4be2878104..c1a3ae7859 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -19,6 +19,7 @@ namespace MWWorld mEnabled = refData.mEnabled; mCount = refData.mCount; mPosition = refData.mPosition; + mLocalRotation = refData.mLocalRotation; mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; } @@ -34,7 +35,11 @@ namespace MWWorld RefData::RefData (const ESM::CellRef& cellRef) : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.mPos), mCustomData (0) - {} + { + mLocalRotation.rot[0]=0; + mLocalRotation.rot[1]=0; + mLocalRotation.rot[2]=0; + } RefData::RefData (const RefData& refData) : mBaseNode(0), mCustomData (0) @@ -76,10 +81,13 @@ namespace MWWorld {} } - std::string RefData::getHandle() + const std::string &RefData::getHandle() { - if (!mBaseNode) - return ""; + if(!mBaseNode) + { + static const std::string empty; + return empty; + } return mBaseNode->getName(); } @@ -141,6 +149,11 @@ namespace MWWorld return mPosition; } + LocalRotation& RefData::getLocalRotation() + { + return mLocalRotation; + } + void RefData::setCustomData (CustomData *data) { delete mCustomData; diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 3a6e0fc9fd..642f5412c8 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -18,6 +18,10 @@ namespace ESM namespace MWWorld { + struct LocalRotation{ + float rot[3]; + }; + class CustomData; class RefData @@ -34,6 +38,8 @@ namespace MWWorld ESM::Position mPosition; + LocalRotation mLocalRotation; + CustomData *mCustomData; void copy (const RefData& refData); @@ -54,7 +60,7 @@ namespace MWWorld RefData& operator= (const RefData& refData); /// Return OGRE handle (may be empty). - std::string getHandle(); + const std::string &getHandle(); /// Return OGRE base node (can be a null pointer). Ogre::SceneNode* getBaseNode(); @@ -78,6 +84,8 @@ namespace MWWorld ESM::Position& getPosition(); + LocalRotation& getLocalRotation(); + void setCustomData (CustomData *data); ///< Set custom data (potentially replacing old custom data). The ownership of \æ data is /// transferred to this. diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 592fb5c80a..aec00a8c6d 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -41,7 +41,7 @@ namespace ++current; - if (it->mData.getCount() || it->mData.isEnabled()) + if (it->mData.getCount() && it->mData.isEnabled()) { MWWorld::Ptr ptr (&*it, &cell); @@ -49,8 +49,14 @@ namespace { rendering.addObject(ptr); class_.insertObject(ptr, physics); - MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true); + + float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); + MWBase::Environment::get().getWorld()->localRotateObject(ptr, ax, ay, az); + MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().mScale); + class_.adjustPosition(ptr); } catch (const std::exception& e) { @@ -75,31 +81,31 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; - ListHandles functor; + ListAndResetHandles functor; - (*iter)->forEach(functor); + (*iter)->forEach(functor); { // silence annoying g++ warning for (std::vector::const_iterator iter2 (functor.mHandles.begin()); - iter2!=functor.mHandles.end(); ++iter2){ - Ogre::SceneNode* node = *iter2; - mPhysics->removeObject (node->getName()); - } - - if ((*iter)->mCell->isExterior()) + iter2!=functor.mHandles.end(); ++iter2) { - ESM::Land* land = - MWBase::Environment::get().getWorld()->getStore().get().search( - (*iter)->mCell->getGridX(), - (*iter)->mCell->getGridY() - ); - if (land) - mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); + Ogre::SceneNode* node = *iter2; + mPhysics->removeObject (node->getName()); } } + if ((*iter)->mCell->isExterior()) + { + ESM::Land* land = + MWBase::Environment::get().getWorld()->getStore().get().search( + (*iter)->mCell->getGridX(), + (*iter)->mCell->getGridY() + ); + if (land) + mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); + } + mRendering.removeCell(*iter); - //mPhysics->removeObject("Unnamed_43"); MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); MWBase::Environment::get().getMechanicsManager()->drop (*iter); @@ -115,14 +121,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 +144,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 +173,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 = @@ -176,6 +186,15 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); } + void Scene::changeToVoid() + { + CellStoreCollection::iterator active = mActiveCells.begin(); + while (active!=mActiveCells.end()) + unloadCell (active++); + assert(mActiveCells.empty()); + mCurrentCell = NULL; + } + void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { Nif::NIFFile::CacheLock cachelock; @@ -355,6 +374,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 +456,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 +463,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/scene.hpp b/apps/openmw/mwworld/scene.hpp index dc08d6f9b5..3dfbd1045b 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -47,7 +47,7 @@ namespace MWWorld private: //OEngine::Render::OgreRenderer& mRenderer; - CellStore* mCurrentCell; // the cell, the player is in + CellStore* mCurrentCell; // the cell the player is in CellStoreCollection mActiveCells; bool mCellChanged; PhysicsSystem *mPhysics; @@ -85,6 +85,9 @@ namespace MWWorld void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. + void changeToVoid(); + ///< Change into a void + void markCellAsUnchanged(); void update (float duration, bool paused); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 005601cd13..ebc7ef03f6 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1,4 +1,5 @@ #include "store.hpp" +#include "esmstore.hpp" namespace MWWorld { @@ -15,8 +16,39 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) ESM::Cell *cell = new ESM::Cell; cell->mName = id; - // The cell itself takes care of some of the hairy details - cell->load(esm, *mEsmStore); + //First part of cell loading + cell->preLoad(esm); + + //Handling MovedCellRefs, there is no way to do it inside loadcell + while (esm.isNextSub("MVRF")) { + ESM::CellRef ref; + ESM::MovedCellRef cMRef; + cell->getNextMVRF(esm, cMRef); + + MWWorld::Store &cStore = const_cast&>(mEsmStore->get()); + ESM::Cell *cellAlt = const_cast(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); + + // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following + // implementation when the oher implementation works as well. + cell->getNextRef(esm, ref); + std::string lowerCase; + + std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + // Add data required to make reference appear in the correct cell. + // We should not need to test for duplicates, as this part of the code is pre-cell merge. + cell->mMovedRefs.push_back(cMRef); + // But there may be duplicates here! + ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum); + if (iter == cellAlt->mLeasedRefs.end()) + cellAlt->mLeasedRefs.push_back(ref); + else + *iter = ref; + } + + //Second part of cell loading + cell->postLoad(esm); if(cell->mData.mFlags & ESM::Cell::Interior) { @@ -62,4 +94,4 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) delete cell; } -} \ No newline at end of file +} diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index f69f606b45..b49569f247 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -21,6 +21,7 @@ namespace MWWorld virtual void load(ESM::ESMReader &esm, const std::string &id) = 0; virtual bool eraseStatic(const std::string &id) {return false;} + virtual void clearDynamic() {} }; template @@ -92,6 +93,7 @@ namespace MWWorld std::map mDynamic; typedef std::map Dynamic; + typedef std::map Static; friend class ESMStore; @@ -105,6 +107,13 @@ namespace MWWorld typedef SharedIterator iterator; + // setUp needs to be called again after + virtual void clearDynamic() + { + mDynamic.clear(); + mShared.clear(); + } + const T *search(const std::string &id) const { T item; item.mId = Misc::StringUtils::lowerCase(id); @@ -183,6 +192,20 @@ namespace MWWorld return ptr; } + T *insertStatic(const T &item) { + std::string id = Misc::StringUtils::lowerCase(item.mId); + std::pair result = + mStatic.insert(std::pair(id, item)); + T *ptr = &result.first->second; + if (result.second) { + mShared.push_back(ptr); + } else { + *ptr = item; + } + return ptr; + } + + bool eraseStatic(const std::string &id) { T item; item.mId = Misc::StringUtils::lowerCase(id); @@ -415,8 +438,18 @@ namespace MWWorld } }; - typedef std::map DynamicInt; - typedef std::map, ESM::Cell> DynamicExt; + struct DynamicExtCmp + { + bool operator()(const std::pair &left, const std::pair &right) const { + if (left.first == right.first) { + return left.second < right.second; + } + return left.first < right.first; + } + }; + + typedef std::map DynamicInt; + typedef std::map, ESM::Cell, DynamicExtCmp> DynamicExt; DynamicInt mInt; DynamicExt mExt; @@ -465,7 +498,7 @@ namespace MWWorld cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); - std::map, ESM::Cell>::const_iterator it = mExt.find(key); + DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } @@ -483,7 +516,7 @@ namespace MWWorld cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); - std::map, ESM::Cell>::const_iterator it = mExt.find(key); + DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } @@ -524,7 +557,7 @@ namespace MWWorld void setUp() { //typedef std::vector::iterator Iterator; - typedef std::map, ESM::Cell>::iterator ExtIterator; + typedef DynamicExt::iterator ExtIterator; typedef std::map::iterator IntIterator; //std::sort(mInt.begin(), mInt.end(), RecordCmp()); @@ -862,7 +895,28 @@ namespace MWWorld } void setUp() { - std::sort(mStatic.begin(), mStatic.end(), Compare()); + /// \note This method sorts indexed values for further + /// searches. Every loaded item is present in storage, but + /// latest loaded shadows any previous while searching. + /// If memory cost will be too high, it is possible to remove + /// unused values. + + Compare cmp; + + std::stable_sort(mStatic.begin(), mStatic.end(), cmp); + + typename std::vector::iterator first, next; + next = first = mStatic.begin(); + + while (first != mStatic.end() && ++next != mStatic.end()) { + while (next != mStatic.end() && !cmp(*first, *next)) { + ++next; + } + if (first != --next) { + std::swap(*first, *next); + } + first = ++next; + } } const T *search(int index) const { diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 052ae4e028..8b3c5f6ffa 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -4,7 +4,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" @@ -13,95 +12,116 @@ #include "player.hpp" #include "esmstore.hpp" +#include "fallback.hpp" using namespace Ogre; using namespace MWWorld; using namespace MWSound; -#define lerp(x, y) (x * (1-factor) + y * factor) -std::string WeatherManager::getFallback (const std::string& key) const +namespace { - std::map::const_iterator it; - if((it = mFallback.find(key)) == mFallback.end()) + float lerp (float x, float y, float factor) { - return ""; + return x * (1-factor) + y * factor; + } + + Ogre::ColourValue lerp (const Ogre::ColourValue& x, const Ogre::ColourValue& y, float factor) + { + return x * (1-factor) + y * factor; } - return it->second; -} -std::string WeatherManager::getFallbackString(const std::string& fall) const -{ - return getFallback(fall); } -float WeatherManager::getFallbackFloat(const std::string& fall) const -{ - std::string fallback=getFallbackString(fall); - return boost::lexical_cast(fallback); -} - -ColourValue WeatherManager::getFallbackColour(const std::string& fall) const -{ - std::string sum; - std::string ret[3]; - sum=getFallback(fall); - unsigned int j=0; - for(unsigned int i=0;i(ret[0])/255.f,boost::lexical_cast(ret[1])/255.f,boost::lexical_cast(ret[2])/255.f); -} void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name) { std::string upper=name; upper[0]=toupper(name[0]); - weather.mCloudsMaximumPercent = getFallbackFloat("Weather_"+upper+"_Clouds_Maximum_Percent"); - weather.mTransitionDelta = getFallbackFloat("Weather_"+upper+"_Transition_Delta"); - weather.mSkySunriseColor=getFallbackColour("Weather_"+upper+"_Sky_Sunrise_Color"); - weather.mSkyDayColor = getFallbackColour("Weather_"+upper+"_Sky_Day_Color"); - weather.mSkySunsetColor = getFallbackColour("Weather_"+upper+"_Sky_Sunset_Color"); - weather.mSkyNightColor = getFallbackColour("Weather_"+upper+"_Sky_Night_Color"); - weather.mFogSunriseColor = getFallbackColour("Weather_"+upper+"_Fog_Sunrise_Color"); - weather.mFogDayColor = getFallbackColour("Weather_"+upper+"_Fog_Day_Color"); - weather.mFogSunsetColor = getFallbackColour("Weather_"+upper+"_Fog_Sunset_Color"); - weather.mFogNightColor = getFallbackColour("Weather_"+upper+"_Fog_Night_Color"); - weather.mAmbientSunriseColor = getFallbackColour("Weather_"+upper+"_Ambient_Sunrise_Color"); - weather.mAmbientDayColor = getFallbackColour("Weather_"+upper+"_Ambient_Day_Color"); - weather.mAmbientSunsetColor = getFallbackColour("Weather_"+upper+"_Ambient_Sunset_Color"); - weather.mAmbientNightColor = getFallbackColour("Weather_"+upper+"_Ambient_Night_Color"); - weather.mSunSunriseColor = getFallbackColour("Weather_"+upper+"_Sun_Sunrise_Color"); - weather.mSunDayColor = getFallbackColour("Weather_"+upper+"_Sun_Day_Color"); - weather.mSunSunsetColor = getFallbackColour("Weather_"+upper+"_Sun_Sunset_Color"); - weather.mSunNightColor = getFallbackColour("Weather_"+upper+"_Sun_Night_Color"); - weather.mSunDiscSunsetColor = getFallbackColour("Weather_"+upper+"_Sun_Disc_Sunset_Color"); - weather.mLandFogDayDepth = getFallbackFloat("Weather_"+upper+"_Land_Fog_Day_Depth"); - weather.mLandFogNightDepth = getFallbackFloat("Weather_"+upper+"_Land_Fog_Night_Depth"); - weather.mWindSpeed = getFallbackFloat("Weather_"+upper+"_Wind_Speed"); - weather.mCloudSpeed = getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); - weather.mGlareView = getFallbackFloat("Weather_"+upper+"_Glare_View"); + weather.mCloudsMaximumPercent = mFallback->getFallbackFloat("Weather_"+upper+"_Clouds_Maximum_Percent"); + weather.mTransitionDelta = mFallback->getFallbackFloat("Weather_"+upper+"_Transition_Delta"); + weather.mSkySunriseColor=mFallback->getFallbackColour("Weather_"+upper+"_Sky_Sunrise_Color"); + weather.mSkyDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Day_Color"); + weather.mSkySunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Sunset_Color"); + weather.mSkyNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Night_Color"); + weather.mFogSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Sunrise_Color"); + weather.mFogDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Day_Color"); + weather.mFogSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Sunset_Color"); + weather.mFogNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Night_Color"); + weather.mAmbientSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Sunrise_Color"); + weather.mAmbientDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Day_Color"); + weather.mAmbientSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Sunset_Color"); + weather.mAmbientNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Night_Color"); + weather.mSunSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Sunrise_Color"); + weather.mSunDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Day_Color"); + weather.mSunSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Sunset_Color"); + weather.mSunNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Night_Color"); + weather.mSunDiscSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Disc_Sunset_Color"); + weather.mLandFogDayDepth = mFallback->getFallbackFloat("Weather_"+upper+"_Land_Fog_Day_Depth"); + weather.mLandFogNightDepth = mFallback->getFallbackFloat("Weather_"+upper+"_Land_Fog_Night_Depth"); + weather.mWindSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Wind_Speed"); + weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); + weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View"); mWeatherSettings[name] = weather; } -WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,const std::map& fallbackMap) : + +float WeatherManager::calculateHourFade (const std::string& moonName) const +{ + float fadeOutStart=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Out_Start"); + float fadeOutFinish=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Out_Finish"); + float fadeInStart=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_In_Start"); + float fadeInFinish=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_In_Finish"); + + if (mHour >= fadeOutStart && mHour <= fadeOutFinish) + return (1 - ((mHour - fadeOutStart) / (fadeOutFinish - fadeOutStart))); + if (mHour >= fadeInStart && mHour <= fadeInFinish) + return (1 - ((mHour - fadeInStart) / (fadeInFinish - fadeInStart))); + else + return 1; +} + +float WeatherManager::calculateAngleFade (const std::string& moonName, float angle) const +{ + float endAngle=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_End_Angle"); + float startAngle=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Start_Angle"); + if (angle <= startAngle && angle >= endAngle) + return (1 - ((angle - endAngle)/(startAngle-endAngle))); + else if (angle > startAngle) + return 0.f; + else + return 1.f; +} + +WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fallback* fallback) : mHour(14), mCurrentWeather("clear"), mFirstUpdate(true), mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0), mMonth(0), mDay(0), - mTimePassed(0), mFallback(fallbackMap) + mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering) { - mRendering = rendering; //Globals - mThunderSoundID0 = getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); - mThunderSoundID1 = getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_1"); - mThunderSoundID2 = getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_2"); - mThunderSoundID3 = getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_3"); - mSunriseTime = getFallbackFloat("Weather_Sunrise_Time"); - mSunsetTime = getFallbackFloat("Weather_Sunset_Time"); - mSunriseDuration = getFallbackFloat("Weather_Sunrise_Duration"); - mSunsetDuration = getFallbackFloat("Weather_Sunset_Duration"); - mWeatherUpdateTime = getFallbackFloat("Weather_Hours_Between_Weather_Changes"); - mThunderFrequency = getFallbackFloat("Weather_Thunderstorm_Thunder_Frequency"); - mThunderThreshold = getFallbackFloat("Weather_Thunderstorm_Thunder_Threshold"); + mThunderSoundID0 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); + mThunderSoundID1 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_1"); + mThunderSoundID2 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_2"); + mThunderSoundID3 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_3"); + mSunriseTime = mFallback->getFallbackFloat("Weather_Sunrise_Time"); + mSunsetTime = mFallback->getFallbackFloat("Weather_Sunset_Time"); + mSunriseDuration = mFallback->getFallbackFloat("Weather_Sunrise_Duration"); + mSunsetDuration = mFallback->getFallbackFloat("Weather_Sunset_Duration"); + mHoursBetweenWeatherChanges = mFallback->getFallbackFloat("Weather_Hours_Between_Weather_Changes"); + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; + mThunderFrequency = mFallback->getFallbackFloat("Weather_Thunderstorm_Thunder_Frequency"); + mThunderThreshold = mFallback->getFallbackFloat("Weather_Thunderstorm_Thunder_Threshold"); mThunderSoundDelay = 0.25; + + //Some useful values + /* TODO: Use pre-sunrise_time, pre-sunset_time, + * post-sunrise_time, and post-sunset_time to better + * describe sunrise/sunset time. + * These values are fallbacks attached to weather. + */ + mNightStart = mSunsetTime + mSunsetDuration; + mNightEnd = mSunriseTime - 0.5; + mDayStart = mSunriseTime + mSunriseDuration; + mDayEnd = mSunsetTime; + //Weather Weather clear; clear.mCloudTexture = "tx_sky_clear.dds"; @@ -139,64 +159,14 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,const std:: blight.mAmbientLoopSoundID = "blight"; setFallbackWeather(blight,"blight"); - /* Weather snow; snow.mCloudTexture = "tx_bm_sky_snow.dds"; - snow.mCloudsMaximumPercent = 1.0; - snow.mTransitionDelta = 0.014; - snow.mSkySunriseColor = clr(196, 91, 91); - snow.mSkyDayColor = clr(153, 158, 166); - snow.mSkySunsetColor = clr(96, 115, 134); - snow.mSkyNightColor = clr(31, 35, 39); - snow.mFogSunriseColor = clr(106, 91, 91); - snow.mFogDayColor = clr(153, 158, 166); - snow.mFogSunsetColor = clr(96, 115, 134); - snow.mFogNightColor = clr(31, 35, 39); - snow.mAmbientSunriseColor = clr(92, 84, 84); - snow.mAmbientDayColor = clr(93, 96, 105); - snow.mAmbientSunsetColor = clr(70, 79, 87); - snow.mAmbientNightColor = clr(49, 58, 68); - snow.mSunSunriseColor = clr(141, 109, 109); - snow.mSunDayColor = clr(163, 169, 183); - snow.mSunSunsetColor = clr(101, 121, 141); - snow.mSunNightColor = clr(55, 66, 77); - snow.mSunDiscSunsetColor = clr(128, 128, 128); - snow.mLandFogDayDepth = 1.0; - snow.mLandFogNightDepth = 1.2; - snow.mWindSpeed = 0; - snow.mCloudSpeed = 1.5; - snow.mGlareView = 0; - mWeatherSettings["snow"] = snow; + setFallbackWeather(snow, "snow"); Weather blizzard; blizzard.mCloudTexture = "tx_bm_sky_blizzard.dds"; - blizzard.mCloudsMaximumPercent = 1.0; - blizzard.mTransitionDelta = 0.030; - blizzard.mSkySunriseColor = clr(91, 99, 106); - blizzard.mSkyDayColor = clr(121, 133, 145); - blizzard.mSkySunsetColor = clr(108, 115, 121); - blizzard.mSkyNightColor = clr(27, 29, 31); - blizzard.mFogSunriseColor = clr(91, 99, 106); - blizzard.mFogDayColor = clr(121, 133, 145); - blizzard.mFogSunsetColor = clr(108, 115, 121); - blizzard.mFogNightColor = clr(21, 24, 28); - blizzard.mAmbientSunriseColor = clr(84, 88, 92); - blizzard.mAmbientDayColor = clr(93, 96, 105); - blizzard.mAmbientSunsetColor = clr(83, 77, 75); - blizzard.mAmbientNightColor = clr(53, 62, 70); - blizzard.mSunSunriseColor = clr(114, 128, 146); - blizzard.mSunDayColor = clr(163, 169, 183); - blizzard.mSunSunsetColor = clr(106, 114, 136); - blizzard.mSunNightColor = clr(57, 66, 74); - blizzard.mSunDiscSunsetColor = clr(128, 128, 128); - blizzard.mLandFogDayDepth = 2.8; - blizzard.mLandFogNightDepth = 3.0; - blizzard.mWindSpeed = 0.9; - blizzard.mCloudSpeed = 7.5; - blizzard.mGlareView = 0; blizzard.mAmbientLoopSoundID = "BM Blizzard"; - mWeatherSettings["blizzard"] = blizzard; - */ + setFallbackWeather(blizzard,"blizzard"); } void WeatherManager::setWeather(const String& weather, bool instant) @@ -217,12 +187,12 @@ void WeatherManager::setWeather(const String& weather, bool instant) if (mNextWeather != "") { // transition more than 50% finished? - if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600) <= 0.5) + if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600) <= 0.5) mCurrentWeather = mNextWeather; } mNextWeather = weather; - mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600; + mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600; } mFirstUpdate = false; } @@ -240,13 +210,13 @@ WeatherResult WeatherManager::getResult(const String& weather) result.mGlareView = current.mGlareView; result.mAmbientLoopSoundID = current.mAmbientLoopSoundID; result.mSunColor = current.mSunDiscSunsetColor; - - result.mNight = (mHour < 6 || mHour > 19); + + result.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); result.mFogDepth = result.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; // night - if (mHour <= 5.5f || mHour >= 21) + if (mHour <= mNightEnd || mHour >= mNightStart + 1) { result.mFogColor = current.mFogNightColor; result.mAmbientColor = current.mAmbientNightColor; @@ -256,33 +226,33 @@ WeatherResult WeatherManager::getResult(const String& weather) } // sunrise - else if (mHour >= 5.5f && mHour <= 9) + else if (mHour >= mNightEnd && mHour <= mDayStart + 1) { - if (mHour <= 6) + if (mHour <= mSunriseTime) { // fade in - float advance = 6-mHour; + float advance = mSunriseTime - mHour; float factor = advance / 0.5f; - result.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor); - result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor); - result.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor); - result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor); + result.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor, factor); + result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor, factor); + result.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor, factor); + result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor, factor); result.mNightFade = factor; } else //if (mHour >= 6) { // fade out - float advance = mHour-6; + float advance = mHour - mSunriseTime; float factor = advance / 3.f; - result.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor); - result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor); - result.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor); - result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyDayColor); + result.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor, factor); + result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor, factor); + result.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor, factor); + result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyDayColor, factor); } } // day - else if (mHour >= 9 && mHour <= 17) + else if (mHour >= mDayStart + 1 && mHour <= mDayEnd - 1) { result.mFogColor = current.mFogDayColor; result.mAmbientColor = current.mAmbientDayColor; @@ -291,27 +261,27 @@ WeatherResult WeatherManager::getResult(const String& weather) } // sunset - else if (mHour >= 17 && mHour <= 21) + else if (mHour >= mDayEnd - 1 && mHour <= mNightStart + 1) { - if (mHour <= 19) + if (mHour <= mDayEnd + 1) { // fade in - float advance = 19-mHour; + float advance = (mDayEnd + 1) - mHour; float factor = (advance / 2); - result.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor); - result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor); - result.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor); - result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyDayColor); + result.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor, factor); + result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor, factor); + result.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor, factor); + result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyDayColor, factor); } else //if (mHour >= 19) { // fade out - float advance = mHour-19; + float advance = mHour - (mDayEnd + 1); float factor = advance / 2.f; - result.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor); - result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor); - result.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor); - result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor); + result.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor, factor); + result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor, factor); + result.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor, factor); + result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor, factor); result.mNightFade = factor; } } @@ -329,19 +299,19 @@ WeatherResult WeatherManager::transition(float factor) result.mNextCloudTexture = other.mCloudTexture; result.mCloudBlendFactor = factor; - result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); - result.mFogColor = lerp(current.mFogColor, other.mFogColor); - result.mSunColor = lerp(current.mSunColor, other.mSunColor); - result.mSkyColor = lerp(current.mSkyColor, other.mSkyColor); + result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity, factor); + result.mFogColor = lerp(current.mFogColor, other.mFogColor, factor); + result.mSunColor = lerp(current.mSunColor, other.mSunColor, factor); + result.mSkyColor = lerp(current.mSkyColor, other.mSkyColor, factor); - result.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor); - result.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor); - result.mFogDepth = lerp(current.mFogDepth, other.mFogDepth); - result.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed); - result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed); - result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); - result.mGlareView = lerp(current.mGlareView, other.mGlareView); - result.mNightFade = lerp(current.mNightFade, other.mNightFade); + result.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor); + result.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor); + result.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor); + result.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor); + result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor); + result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity, factor); + result.mGlareView = lerp(current.mGlareView, other.mGlareView, factor); + result.mNightFade = lerp(current.mNightFade, other.mNightFade, factor); result.mNight = current.mNight; @@ -359,13 +329,12 @@ void WeatherManager::update(float duration) if (exterior) { - std::string regionstr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; - Misc::StringUtils::toLower(regionstr); + std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) { mCurrentRegion = regionstr; - mWeatherUpdateTime = mWeatherUpdateTime*3600; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; std::string weather = "clear"; @@ -387,19 +356,19 @@ void WeatherManager::update(float duration) float thunder = region->mData.mThunder/255.f; float ash = region->mData.mAsh/255.f; float blight = region->mData.mBlight/255.f; - //float snow = region->mData.a/255.f; - //float blizzard = region->mData.b/255.f; + float snow = region->mData.mA/255.f; + float blizzard = region->mData.mB/255.f; // re-scale to 100 percent - const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight;//+snow+blizzard; + const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight+snow+blizzard; float random = ((rand()%100)/100.f) * total; - //if (random >= snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear) - // weather = "blizzard"; - //else if (random >= blight+ash+thunder+rain+overcast+foggy+cloudy+clear) - // weather = "snow"; - /*else*/ if (random >= ash+thunder+rain+overcast+foggy+cloudy+clear) + if (random >= snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear) + weather = "blizzard"; + else if (random >= blight+ash+thunder+rain+overcast+foggy+cloudy+clear) + weather = "snow"; + else if (random >= ash+thunder+rain+overcast+foggy+cloudy+clear) weather = "blight"; else if (random >= thunder+rain+overcast+foggy+cloudy+clear) weather = "ashstorm"; @@ -434,14 +403,16 @@ void WeatherManager::update(float duration) } if (mNextWeather != "") - result = transition(1-(mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600))); + result = transition(1 - (mRemainingTransitionTime / (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600))); else result = getResult(mCurrentWeather); + mWindSpeed = result.mWindSpeed; + mRendering->configureFog(result.mFogDepth, result.mFogColor); // disable sun during night - if (mHour >= 20 || mHour <= 6.f) + if (mHour >= mNightStart || mHour <= mSunriseTime) mRendering->getSkyManager()->sunDisable(); else mRendering->getSkyManager()->sunEnable(); @@ -449,45 +420,55 @@ void WeatherManager::update(float duration) // sun angle float height; + //Day duration + float dayDuration = (mNightStart - 1) - mSunriseTime; + // rise at 6, set at 20 - if (mHour >= 6 && mHour <= 20) - height = 1-std::abs(((mHour-13)/7.f)); - else if (mHour > 20) - height = (mHour-20.f)/4.f; + if (mHour >= mSunriseTime && mHour <= mNightStart) + height = 1 - std::abs(((mHour - dayDuration) / 7.f)); + else if (mHour > mNightStart) + height = (mHour - mNightStart) / 4.f; else //if (mHour > 0 && mHour < 6) - height = 1-(mHour/6.f); + height = 1 - (mHour / mSunriseTime); int facing = (mHour > 13.f) ? 1 : -1; Vector3 final( - -(1-height)*facing, - -(1-height)*facing, + -(1 - height) * facing, + -(1 - height) * facing, height); mRendering->setSunDirection(final); - // moon calculations - float night; - if (mHour >= 14) - night = mHour-14; - else if (mHour <= 10) - night = mHour+10; + /* + * TODO: import separated fadeInStart/Finish, fadeOutStart/Finish + * for masser and secunda + */ + + float fadeOutFinish=mFallback->getFallbackFloat("Moons_Masser_Fade_Out_Finish"); + float fadeInStart=mFallback->getFallbackFloat("Moons_Masser_Fade_In_Start"); + + //moon calculations + float moonHeight; + if (mHour >= fadeInStart) + moonHeight = mHour - fadeInStart; + else if (mHour <= fadeOutFinish) + moonHeight = mHour + fadeOutFinish; else - night = 0; + moonHeight = 0; - night /= 20.f; + moonHeight /= (24.f - (fadeInStart - fadeOutFinish)); - if (night != 0) + if (moonHeight != 0) { - float moonHeight = 1-std::abs((night-0.5)*2); - int facing = (mHour > 0.f && mHour<12.f) ? 1 : -1; + int facing = (moonHeight <= 1) ? 1 : -1; Vector3 masser( - (1-moonHeight)*facing, - (1-moonHeight)*facing, + (moonHeight - 1) * facing, + (1 - moonHeight) * facing, moonHeight); Vector3 secunda( - (1-moonHeight)*facing*0.8, - (1-moonHeight)*facing*1.25, + (moonHeight - 1) * facing * 1.25, + (1 - moonHeight) * facing * 0.8, moonHeight); mRendering->getSkyManager()->setMasserDirection(masser); @@ -495,37 +476,17 @@ void WeatherManager::update(float duration) mRendering->getSkyManager()->masserEnable(); mRendering->getSkyManager()->secundaEnable(); - float hour_fade; - if (mHour >= 7.f && mHour <= 14.f) - hour_fade = 1-(mHour-7)/3.f; - else if (mHour >= 14 && mHour <= 15.f) - hour_fade = mHour-14; - else - hour_fade = 1; + float angle = (1-moonHeight) * 90.f * facing; + float masserHourFade = calculateHourFade("Masser"); + float secundaHourFade = calculateHourFade("Secunda"); + float masserAngleFade = calculateAngleFade("Masser", angle); + float secundaAngleFade = calculateAngleFade("Secunda", angle); - float secunda_angle_fade; - float masser_angle_fade; - float angle = moonHeight*90.f; + masserAngleFade *= masserHourFade; + secundaAngleFade *= secundaHourFade; - if (angle >= 30 && angle <= 50) - secunda_angle_fade = (angle-30)/20.f; - else if (angle <30) - secunda_angle_fade = 0.f; - else - secunda_angle_fade = 1.f; - - if (angle >= 40 && angle <= 50) - masser_angle_fade = (angle-40)/10.f; - else if (angle <40) - masser_angle_fade = 0.f; - else - masser_angle_fade = 1.f; - - masser_angle_fade *= hour_fade; - secunda_angle_fade *= hour_fade; - - mRendering->getSkyManager()->setMasserFade(masser_angle_fade); - mRendering->getSkyManager()->setSecundaFade(secunda_angle_fade); + mRendering->getSkyManager()->setMasserFade(masserAngleFade); + mRendering->getSkyManager()->setSecundaFade(secundaAngleFade); } else { @@ -672,6 +633,9 @@ unsigned int WeatherManager::getWeatherID() const void WeatherManager::changeWeather(const std::string& region, const unsigned int id) { + // make sure this region exists + MWBase::Environment::get().getWorld()->getStore().get().find(region); + std::string weather; if (id==0) weather = "clear"; @@ -696,5 +660,14 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int else weather = "clear"; - mRegionOverrides[region] = weather; + mRegionOverrides[Misc::StringUtils::lowerCase(region)] = weather; + + std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; + if (Misc::StringUtils::ciEqual(region, playerRegion)) + setWeather(weather); +} + +float WeatherManager::getWindSpeed() const +{ + return mWindSpeed; } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index b5bd0e10a0..081bd7f87d 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -11,6 +11,8 @@ namespace MWRender namespace MWWorld { + class Fallback; + /// Defines the actual weather that results from weather setting (see below), time of day and weather transition struct WeatherResult { @@ -112,7 +114,7 @@ namespace MWWorld class WeatherManager { public: - WeatherManager(MWRender::RenderingManager*,const std::map& fallbackMap); + WeatherManager(MWRender::RenderingManager*,MWWorld::Fallback* fallback); /** * Change the weather in the specified region @@ -129,6 +131,8 @@ namespace MWWorld void setHour(const float hour); + float getWindSpeed() const; + void setDate(const int day, const int month); void advanceTime(double hours) @@ -141,11 +145,8 @@ namespace MWWorld private: float mHour; int mDay, mMonth; - std::map mFallback; - std::string getFallback (const std::string& key) const; - std::string getFallbackString(const std::string& fall) const; - float getFallbackFloat(const std::string& fall) const; - Ogre::ColourValue getFallbackColour(const std::string& fall) const; + float mWindSpeed; + MWWorld::Fallback* mFallback; void setFallbackWeather(Weather& weather,const std::string& name); MWRender::RenderingManager* mRendering; @@ -173,15 +174,23 @@ namespace MWWorld WeatherResult transition(const float factor); WeatherResult getResult(const Ogre::String& weather); + float calculateHourFade (const std::string& moonName) const; + float calculateAngleFade (const std::string& moonName, float angle) const; + void setWeather(const Ogre::String& weather, bool instant=false); float mSunriseTime; float mSunsetTime; float mSunriseDuration; float mSunsetDuration; float mWeatherUpdateTime; + float mHoursBetweenWeatherChanges; float mThunderFrequency; float mThunderThreshold; float mThunderSoundDelay; + float mNightStart; + float mNightEnd; + float mDayStart; + float mDayEnd; std::string mThunderSoundID0; std::string mThunderSoundID1; std::string mThunderSoundID2; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a933713dd7..8b76efdaeb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -13,9 +15,9 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/movement.hpp" #include "../mwrender/sky.hpp" -#include "../mwrender/player.hpp" #include "../mwclass/door.hpp" @@ -23,6 +25,7 @@ #include "manualref.hpp" #include "cellfunctors.hpp" #include "containerstore.hpp" +#include "inventorystore.hpp" using namespace Ogre; @@ -101,7 +104,7 @@ namespace MWWorld return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mLights)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mLockpicks)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mLockpicks)) return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mMiscItems)) return Ptr (ref, &cell); @@ -154,44 +157,24 @@ namespace MWWorld mRendering->skyDisable(); } - void World::setFallbackValues (const std::map& fallbackMap) - { - mFallback = fallbackMap; - } - - std::string World::getFallback (const std::string& key) const - { - return getFallback(key, ""); - } - - std::string World::getFallback (const std::string& key, const std::string& def) const - { - std::map::const_iterator it; - if((it = mFallback.find(key)) == mFallback.end()) - { - return def; - } - return it->second; - } - World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::vector& master, const std::vector& plugins, - const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, + const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride), - mFallback (fallbackMap) + mFallback(fallbackMap), mPlayIntro(0) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); - mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine); + mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine,&mFallback); mPhysEngine->setSceneManager(renderer.getScene()); - mWeatherManager = new MWWorld::WeatherManager(mRendering,fallbackMap); + mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); int idx = 0; // NOTE: We might need to reserve one more for the running game / save. @@ -228,27 +211,113 @@ namespace MWWorld mStore.load (mEsm[idx]); } + // insert records that may not be present in all versions of MW + if (mEsm[0].getFormat() == 0) + ensureNeededRecords(); + + mStore.movePlayerRecord(); mStore.setUp(); - mPlayer = new MWWorld::Player (mStore.get().find ("player"), *this); - mRendering->attachCameraTo(mPlayer->getPlayer()); - - // global variables mGlobalVariables = new Globals (mStore); - if (newGame) - { - // set new game mark - mGlobalVariables->setInt ("chargenstate", 1); - } - - mGlobalVariables->setInt ("pcrace", 3); - mWorldScene = new Scene(*mRendering, mPhysics); - - lastTick = mTimer.getMilliseconds(); } + void World::startNewGame() + { + mWorldScene->changeToVoid(); + + mStore.clearDynamic(); + mStore.setUp(); + + mCells.clear(); + + // Rebuild player + setupPlayer(); + const ESM::NPC* playerNpc = mStore.get().find("player"); + MWWorld::Ptr player = mPlayer->getPlayer(); + renderPlayer(); + mRendering->resetCamera(); + + // removes NpcStats, ContainerStore etc + player.getRefData().setCustomData(NULL); + + // make sure to do this so that local scripts from items that were in the players inventory are removed + mLocalScripts.clear(); + + MWWorld::Class::get(player).getContainerStore(player).fill(playerNpc->mInventory, "", mStore); + MWWorld::Class::get(player).getInventoryStore(player).autoEquip(player); + + MWBase::Environment::get().getWindowManager()->updatePlayer(); + + ESM::Position pos; + const int cellSize = 8192; + pos.pos[0] = cellSize/2; + pos.pos[1] = cellSize/2; + pos.pos[2] = 0; + pos.rot[0] = 0; + pos.rot[1] = 0; + pos.rot[2] = 0; + mWorldScene->changeToExteriorCell(pos); + + + // enable collision + if(!mPhysics->toggleCollisionMode()) + mPhysics->toggleCollisionMode(); + + // FIXME: should be set to 1, but the sound manager won't pause newly started sounds + mPlayIntro = 2; + + // global variables + delete mGlobalVariables; + mGlobalVariables = new Globals (mStore); + + // set new game mark + mGlobalVariables->setInt ("chargenstate", 1); + mGlobalVariables->setInt ("pcrace", 3); + + // we don't want old weather to persist on a new game + delete mWeatherManager; + mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); + + MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); + } + + + void World::ensureNeededRecords() + { + if (!mStore.get().search("sCompanionShare")) + { + ESM::GameSetting sCompanionShare; + sCompanionShare.mId = "sCompanionShare"; + ESM::Variant value; + value.setType(ESM::VT_String); + value.setString("Companion Share"); + sCompanionShare.mValue = value; + mStore.insertStatic(sCompanionShare); + } + if (!mStore.get().search("dayspassed")) + { + // vanilla Morrowind does not define dayspassed. + ESM::Global dayspassed; + dayspassed.mId = "dayspassed"; + ESM::Variant value; + value.setType(ESM::VT_Long); + value.setInteger(1); // but the addons start counting at 1 :( + dayspassed.mValue = value; + mStore.insertStatic(dayspassed); + } + if (!mStore.get().search("fWereWolfRunMult")) + { + ESM::GameSetting fWereWolfRunMult; + fWereWolfRunMult.mId = "fWereWolfRunMult"; + ESM::Variant value; + value.setType(ESM::VT_Float); + value.setFloat(1.f); + fWereWolfRunMult.mValue = value; + mStore.insertStatic(fWereWolfRunMult); + } + } World::~World() { @@ -283,6 +352,11 @@ namespace MWWorld return 0; } + const MWWorld::Fallback *World::getFallback() const + { + return &mFallback; + } + Ptr::CellStore *World::getExterior (int x, int y) { return mCells.getExterior (x, y); @@ -687,10 +761,11 @@ namespace MWWorld return MWWorld::Ptr (); MWWorld::Ptr object = searchPtrViaHandle (result.second); - float ActivationDistance; - if (object.getTypeName ().find("NPC") != std::string::npos) + if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) + ActivationDistance = getObjectActivationDistance ()*50; + else if (object.getTypeName ().find("NPC") != std::string::npos) ActivationDistance = getNpcActivationDistance (); else ActivationDistance = getObjectActivationDistance (); @@ -720,18 +795,20 @@ 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(); bool isPlayer = ptr == mPlayer->getPlayer(); - bool haveToMove = mWorldScene->isCellActive(*currCell) || isPlayer; + bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell); if (*currCell != newCell) { - removeContainerScripts(ptr); + removeContainerScripts(ptr); if (isPlayer) { @@ -763,7 +840,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); @@ -793,12 +870,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(); @@ -811,8 +890,8 @@ namespace MWWorld void World::scaleObject (const Ptr& ptr, float scale) { - MWWorld::Class::get(ptr).adjustScale(ptr,scale); ptr.getCellRef().mScale = scale; + MWWorld::Class::get(ptr).adjustScale(ptr,scale); if(ptr.getRefData().getBaseNode() == 0) return; @@ -820,23 +899,127 @@ namespace MWWorld mPhysics->scaleObject(ptr); } + void World::rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust) + { + const float two_pi = Ogre::Math::TWO_PI; + const float pi = Ogre::Math::PI; + + float *objRot = ptr.getRefData().getPosition().rot; + if(adjust) + { + objRot[0] += rot.x; + objRot[1] += rot.y; + objRot[2] += rot.z; + } + else + { + objRot[0] = rot.x; + objRot[1] = rot.y; + objRot[2] = rot.z; + } + + if(Class::get(ptr).isActor()) + { + /* HACK? Actors shouldn't really be rotating around X (or Y), but + * currently it's done so for rotating the camera, which needs + * clamping. + */ + const float half_pi = Ogre::Math::HALF_PI; + + if(objRot[0] < -half_pi) objRot[0] = -half_pi; + else if(objRot[0] > half_pi) objRot[0] = half_pi; + } + else + { + while(objRot[0] < -pi) objRot[0] += two_pi; + while(objRot[0] > pi) objRot[0] -= two_pi; + } + + while(objRot[1] < -pi) objRot[1] += two_pi; + while(objRot[1] > pi) objRot[1] -= two_pi; + + while(objRot[2] < -pi) objRot[2] += two_pi; + while(objRot[2] > pi) objRot[2] -= two_pi; + + if(ptr.getRefData().getBaseNode() != 0) + { + mRendering->rotateObject(ptr); + mPhysics->rotateObject(ptr); + } + } + + void World::localRotateObject (const Ptr& ptr, float x, float y, float z) + { + if (ptr.getRefData().getBaseNode() != 0) { + + ptr.getRefData().getLocalRotation().rot[0]=Ogre::Degree(x).valueRadians(); + ptr.getRefData().getLocalRotation().rot[1]=Ogre::Degree(y).valueRadians(); + ptr.getRefData().getLocalRotation().rot[2]=Ogre::Degree(z).valueRadians(); + + float fullRotateRad=Ogre::Degree(360).valueRadians(); + + while(ptr.getRefData().getLocalRotation().rot[0]>=fullRotateRad) + ptr.getRefData().getLocalRotation().rot[0]-=fullRotateRad; + while(ptr.getRefData().getLocalRotation().rot[1]>=fullRotateRad) + ptr.getRefData().getLocalRotation().rot[1]-=fullRotateRad; + while(ptr.getRefData().getLocalRotation().rot[2]>=fullRotateRad) + ptr.getRefData().getLocalRotation().rot[2]-=fullRotateRad; + + while(ptr.getRefData().getLocalRotation().rot[0]<=-fullRotateRad) + ptr.getRefData().getLocalRotation().rot[0]+=fullRotateRad; + while(ptr.getRefData().getLocalRotation().rot[1]<=-fullRotateRad) + ptr.getRefData().getLocalRotation().rot[1]+=fullRotateRad; + while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad) + ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad; + + float *worldRot = ptr.getRefData().getPosition().rot; + + Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(-worldRot[0]), Ogre::Vector3::UNIT_X)* + Ogre::Quaternion(Ogre::Radian(-worldRot[1]), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian(-worldRot[2]), Ogre::Vector3::UNIT_Z)); + + Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-x).valueRadians()), Ogre::Vector3::UNIT_X)* + Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-y).valueRadians()), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-z).valueRadians()), Ogre::Vector3::UNIT_Z)); + + ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); + mPhysics->rotateObject(ptr); + } + } + + 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; + + ptr.getRefData().getPosition().pos[2] = pos.z + 20; // place slightly above. will snap down to ground with code below + + if (!isFlying(ptr)) + { + Ogre::Vector3 traced = mPhysics->traceDown(ptr); + if (traced.z < pos.z) + pos.z = traced.z; + } + + moveObject(ptr, *ptr.getCell(), pos.x, pos.y, pos.z); + } + void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) { - Ogre::Vector3 rot; - rot.x = Ogre::Degree(x).valueRadians(); - rot.y = Ogre::Degree(y).valueRadians(); - rot.z = Ogre::Degree(z).valueRadians(); - - if (mRendering->rotateObject(ptr, rot, adjust)) - { - // rotate physically iff renderer confirm so - float *objRot = ptr.getRefData().getPosition().rot; - objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; - - if (ptr.getRefData().getBaseNode() != 0) { - mPhysics->rotateObject(ptr); - } - } + rotateObjectImp(ptr, Ogre::Vector3(Ogre::Degree(x).valueRadians(), + Ogre::Degree(y).valueRadians(), + Ogre::Degree(z).valueRadians()), + adjust); } void World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) @@ -879,6 +1062,8 @@ namespace MWWorld if(duration <= 0.0f) return; + processDoors(duration); + PtrMovementList::const_iterator player(actors.end()); for(PtrMovementList::const_iterator iter(actors.begin());iter != actors.end();iter++) { @@ -888,20 +1073,87 @@ namespace MWWorld player = iter; continue; } - Ogre::Vector3 vec = mPhysics->move(iter->first, iter->second, duration, + + rotateObjectImp(iter->first, Ogre::Vector3(iter->second.mRotation), true); + + Ogre::Vector3 vec = mPhysics->move(iter->first, Ogre::Vector3(iter->second.mPosition), duration, !isSwimming(iter->first) && !isFlying(iter->first)); moveObjectImp(iter->first, vec.x, vec.y, vec.z); } if(player != actors.end()) { - Ogre::Vector3 vec = mPhysics->move(player->first, player->second, duration, + rotateObjectImp(player->first, Ogre::Vector3(player->second.mRotation), true); + + Ogre::Vector3 vec = mPhysics->move(player->first, Ogre::Vector3(player->second.mPosition), duration, !isSwimming(player->first) && !isFlying(player->first)); moveObjectImp(player->first, vec.x, vec.y, vec.z); } - // the only purpose this has currently is to update the debug drawer + mPhysEngine->stepSimulation (duration); } + bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) + { + Ogre::Vector3 a(x1,y1,z1);std::cout << x1 << " " << x2; + Ogre::Vector3 b(x2,y2,z2); + return mPhysics->castRay(a,b,false,true); + } + + void World::processDoors(float duration) + { + std::map::iterator it = mDoorStates.begin(); + while (it != mDoorStates.end()) + { + if (!mWorldScene->isCellActive(*it->first.getCell())) + mDoorStates.erase(it++); + else + { + float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees(); + float diff = duration * 90; + float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second ? 1 : -1)), 90.f); + localRotateObject(it->first, 0, 0, targetRot); + + // AABB of the door + Ogre::Vector3 min,max; + mPhysics->getObjectAABB(it->first, min, max); + Ogre::Vector3 dimensions = max-min; + + /// \todo should use convexSweepTest here + std::vector collisions = mPhysics->getCollisions(it->first); + for (std::vector::iterator cit = collisions.begin(); cit != collisions.end(); ++cit) + { + MWWorld::Ptr ptr = getPtrViaHandle(*cit); + if (MWWorld::Class::get(ptr).isActor()) + { + // we collided with an actor, we need to undo the rotation and push the door away from the actor + + // figure out on which side of the door the actor we collided with is + Ogre::Vector3 relativePos = it->first.getRefData().getBaseNode()-> + convertWorldToLocalPosition(ptr.getRefData().getBaseNode()->_getDerivedPosition()); + + float axisToCheck; + if (dimensions.x > dimensions.y) + axisToCheck = relativePos.y * boost::math::sign((min+max).y); + else + axisToCheck = relativePos.x * boost::math::sign((min+max).x); + if (axisToCheck >= 0) + targetRot = std::min(std::max(0.f, oldRot + diff*0.5f), 90.f); + else + targetRot = std::min(std::max(0.f, oldRot - diff*0.5f), 90.f); + + localRotateObject(it->first, 0, 0, targetRot); + break; + } + } + + if ((targetRot == 90.f && it->second) || targetRot == 0.f) + mDoorStates.erase(it++); + else + ++it; + } + } + } + bool World::toggleCollisionMode() { return mPhysics->toggleCollisionMode();; @@ -964,16 +1216,48 @@ namespace MWWorld return ret; } + const ESM::Armor *World::createRecord (const ESM::Armor& record) + { + return mStore.insert(record); + } + + const ESM::Weapon *World::createRecord (const ESM::Weapon& record) + { + return mStore.insert(record); + } + + const ESM::Clothing *World::createRecord (const ESM::Clothing& record) + { + return mStore.insert(record); + } + + const ESM::Enchantment *World::createRecord (const ESM::Enchantment& record) + { + return mStore.insert(record); + } + + const ESM::Book *World::createRecord (const ESM::Book& record) + { + return mStore.insert(record); + } + void World::update (float duration, bool paused) { + if (mPlayIntro) + { + --mPlayIntro; + if (mPlayIntro == 0) + mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); + } + mWeatherManager->update (duration); mWorldScene->update (duration, paused); float pitch, yaw; Ogre::Vector3 eyepos; - mRendering->getPlayerData(eyepos, pitch, yaw); - mPhysics->updatePlayerData(eyepos, pitch, yaw); + mRendering->getCameraData(eyepos, pitch, yaw); + mPhysics->updateCameraData(eyepos, pitch, yaw); performUpdateSceneQueries (); @@ -1026,6 +1310,8 @@ namespace MWWorld float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()); + if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) + results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()*50); } else { @@ -1275,7 +1561,7 @@ namespace MWWorld World::isFlying(const MWWorld::Ptr &ptr) const { const MWWorld::Class &cls = MWWorld::Class::get(ptr); - if(cls.isActor() && cls.getCreatureStats(ptr).getMagicEffects().get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0) + if(cls.isActor() && cls.getCreatureStats(ptr).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0) return true; return false; } @@ -1310,6 +1596,23 @@ namespace MWWorld return physactor && physactor->getOnGround(); } + bool World::vanityRotateCamera(float * rot) + { + return mRendering->vanityRotateCamera(rot); + } + + void World::setupPlayer() + { + const ESM::NPC *player = mStore.get().find("player"); + if (!mPlayer) + mPlayer = new MWWorld::Player(player, *this); + else + mPlayer->set(player); + + Ptr ptr = mPlayer->getPlayer(); + mRendering->setupPlayer(ptr); + } + void World::renderPlayer() { mRendering->renderPlayer(mPlayer->getPlayer()); @@ -1329,7 +1632,7 @@ namespace MWWorld Ogre::Vector3 playerPos(refdata.getPosition().pos); const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); - if(!physactor->getOnGround() || isUnderwater(currentCell, playerPos)) + if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos)) return 2; if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep)) return 1; @@ -1356,4 +1659,69 @@ namespace MWWorld { mRendering->frameStarted(dt); } + + void World::activateDoor(const MWWorld::Ptr& door) + { + if (mDoorStates.find(door) != mDoorStates.end()) + { + // if currently opening, then close, if closing, then open + mDoorStates[door] = !mDoorStates[door]; + } + else + { + if (door.getRefData().getLocalRotation().rot[2] == 0) + mDoorStates[door] = 1; // open + else + mDoorStates[door] = 0; // close + } + } + + bool World::getOpenOrCloseDoor(const Ptr &door) + { + if (mDoorStates.find(door) != mDoorStates.end()) + return !mDoorStates[door]; // if currently opening or closing, then do the opposite + return door.getRefData().getLocalRotation().rot[2] == 0; + } + + bool World::getPlayerStandingOn (const MWWorld::Ptr& object) + { + MWWorld::Ptr player = mPlayer->getPlayer(); + if (!mPhysEngine->getCharacter("player")->getOnGround()) + return false; + btVector3 from (player.getRefData().getPosition().pos[0], player.getRefData().getPosition().pos[1], player.getRefData().getPosition().pos[2]); + btVector3 to = from - btVector3(0,0,5); + std::pair result = mPhysEngine->rayTest(from, to); + return result.first == object.getRefData().getBaseNode()->getName(); + } + + bool World::getActorStandingOn (const MWWorld::Ptr& object) + { + return mPhysEngine->isAnyActorStandingOn(object.getRefData().getBaseNode()->getName()); + } + + float World::getWindSpeed() + { + if (isCellExterior() || isCellQuasiExterior()) + return mWeatherManager->getWindSpeed(); + else + return 0.f; + } + + void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) + { + std::string refId = npc.getCellRef().mRefID; + + const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); + for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) + { + MWWorld::CellRefList& containers = (*cellIt)->mContainers; + CellRefList::List& refList = containers.mList; + for (CellRefList::List::iterator container = refList.begin(); container != refList.end(); ++container) + { + MWWorld::Ptr ptr (&*container, *cellIt); + if (Misc::StringUtils::ciEqual(ptr.getCellRef().mOwner, npc.getCellRef().mRefID)) + out.push_back(ptr); + } + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e06b89f600..1baf3d4ba2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -10,6 +10,7 @@ #include "cells.hpp" #include "localscripts.hpp" #include "timestamp.hpp" +#include "fallback.hpp" #include "../mwbase/world.hpp" @@ -49,6 +50,7 @@ namespace MWWorld class World : public MWBase::World { + MWWorld::Fallback mFallback; MWRender::RenderingManager* mRendering; MWWorld::WeatherManager* mWeatherManager; @@ -82,13 +84,14 @@ namespace MWWorld float mFaced1Distance; float mFaced2Distance; int mNumFacing; - std::map mFallback; - unsigned long lastTick; - Ogre::Timer mTimer; + std::map mDoorStates; + ///< only holds doors that are currently moving. 0 means closing, 1 opening int getDaysPerMonth (int month) const; + void rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust); + bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed @@ -107,16 +110,25 @@ namespace MWWorld void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell); void PCDropped (const Ptr& item); + void processDoors(float duration); + ///< Run physics simulation and modify \a world accordingly. + + void ensureNeededRecords(); + + int mPlayIntro; + public: World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::vector& master, const std::vector& plugins, - const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, + const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride); virtual ~World(); + virtual void startNewGame(); + virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. @@ -132,11 +144,7 @@ namespace MWWorld virtual void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); - virtual void setFallbackValues (const std::map& fallbackMap); - - virtual std::string getFallback (const std::string& key) const; - - virtual std::string getFallback (const std::string& key, const std::string& def) const; + virtual const Fallback *getFallback() const; virtual Player& getPlayer(); @@ -189,6 +197,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); @@ -252,6 +263,8 @@ namespace MWWorld /// \param adjust indicates rotation should be set or adjusted virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); + virtual void localRotateObject (const Ptr& ptr, float x, float y, float z); + virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos); ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. @@ -265,6 +278,9 @@ namespace MWWorld virtual void doPhysics(const PtrMovementList &actors, float duration); ///< Run physics simulation and modify \a world accordingly. + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2); + ///< cast a Ray and return true if there is an object in the ray path. + virtual bool toggleCollisionMode(); ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. @@ -275,25 +291,44 @@ namespace MWWorld ///< \return Resulting mode virtual const ESM::Potion *createRecord (const ESM::Potion& record); - ///< Create a new recrod (of type potion) in the ESM store. + ///< Create a new record (of type potion) in the ESM store. /// \return pointer to created record virtual const ESM::Spell *createRecord (const ESM::Spell& record); - ///< Create a new recrod (of type spell) in the ESM store. + ///< Create a new record (of type spell) in the ESM store. /// \return pointer to created record virtual const ESM::Class *createRecord (const ESM::Class& record); - ///< Create a new recrod (of type class) in the ESM store. + ///< Create a new record (of type class) in the ESM store. /// \return pointer to created record virtual const ESM::Cell *createRecord (const ESM::Cell& record); - ///< Create a new recrod (of type cell) in the ESM store. + ///< Create a new record (of type cell) in the ESM store. /// \return pointer to created record virtual const ESM::NPC *createRecord(const ESM::NPC &record); - ///< Create a new recrod (of type npc) in the ESM store. + ///< Create a new record (of type npc) in the ESM store. /// \return pointer to created record + virtual const ESM::Armor *createRecord (const ESM::Armor& record); + ///< Create a new record (of type armor) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Weapon *createRecord (const ESM::Weapon& record); + ///< Create a new record (of type weapon) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Clothing *createRecord (const ESM::Clothing& record); + ///< Create a new record (of type clothing) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Enchantment *createRecord (const ESM::Enchantment& record); + ///< Create a new record (of type enchantment) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Book *createRecord (const ESM::Book& record); + ///< Create a new record (of type book) in the ESM store. + /// \return pointer to created record virtual void update (float duration, bool paused); @@ -324,8 +359,8 @@ namespace MWWorld mRendering->togglePreviewMode(enable); } - virtual bool toggleVanityMode(bool enable, bool force) { - return mRendering->toggleVanityMode(enable, force); + virtual bool toggleVanityMode(bool enable) { + return mRendering->toggleVanityMode(enable); } virtual void allowVanityMode(bool allow) { @@ -340,8 +375,23 @@ namespace MWWorld mRendering->changeVanityModeScale(factor); } + virtual bool vanityRotateCamera(float * rot); + + virtual void setupPlayer(); virtual void renderPlayer(); + virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door); + ///< if activated, should this door be opened or closed? + virtual void activateDoor(const MWWorld::Ptr& door); + ///< activate (open or close) an non-teleport door + + virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object + virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object + virtual float getWindSpeed(); + + virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out); + ///< get all containers in active cells owned by this Npc + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); virtual int canRest(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 38625fb529..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 @@ -37,9 +37,9 @@ add_component_dir (file_finder add_component_dir (esm attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst - loadinfo loadingr loadland loadlevlist loadligh loadlocks loadltex loadmgef loadmisc loadnpcc + 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 + 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/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index c97ca4562a..7d3df15908 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -313,21 +313,26 @@ public: void destroyInstance( Archive* arch) { delete arch; } }; -class DirArchiveFactory : public FileSystemArchiveFactory +class DirArchiveFactory : public ArchiveFactory { public: - const String& getType() const - { - static String name = "Dir"; - return name; - } + const String& getType() const + { + static String name = "Dir"; + return name; + } - Archive *createInstance( const String& name ) - { - return new DirArchive(name); - } + Archive *createInstance( const String& name ) + { + return new DirArchive(name); + } - void destroyInstance( Archive* arch) { delete arch; } + virtual Archive* createInstance(const String& name, bool readOnly) + { + return new DirArchive(name); + } + + void destroyInstance( Archive* arch) { delete arch; } }; diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 027f3de712..94240c5eb9 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -603,8 +603,8 @@ namespace Compiler switch (code) { - case Scanner::S_plus: pushBinaryOperator ('+'); return true; - case Scanner::S_minus: pushBinaryOperator ('-'); return true; + case Scanner::S_plus: c = '+'; break; + case Scanner::S_minus: c = '-'; break; case Scanner::S_mult: pushBinaryOperator ('*'); return true; case Scanner::S_div: pushBinaryOperator ('/'); return true; case Scanner::S_cmpEQ: c = 'e'; break; diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp index 98be2d3d1f..185af4a513 100644 --- a/components/compiler/fileparser.cpp +++ b/components/compiler/fileparser.cpp @@ -71,6 +71,19 @@ namespace Compiler return true; } + if (mState==EndNameState) + { + // optional repeated name after end statement + if (mName!=loc.mLiteral) + reportWarning ("Names for script " + mName + " do not match", loc); + + mState = EndCompleteState; + return false; // we are stopping here, because there might be more garbage on the end line, + // that we must ignore. + // + /// \todo allow this workaround to be disabled for newer scripts + } + return Parser::parseKeyword (keyword, loc, scanner); } diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 210d18dc98..40462c4881 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -98,10 +98,12 @@ namespace Compiler if (type!=' ') { - getErrorHandler().error ("catoLowern't re-declare local variable", loc); + /// \todo add option to make re-declared local variables an error + getErrorHandler().warning ("can't re-declare local variable", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); - return false; + mState = EndState; + return true; } mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'), @@ -139,7 +141,7 @@ namespace Compiler if (mState==SetMemberVarState) { - mMemberName = Misc::StringUtils::lowerCase (name); + mMemberName = name; char type = getContext().getMemberType (mMemberName, mName); if (type!=' ') 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 7f43c36a55..b45015d1e7 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -1,6 +1,7 @@ #include "scanner.hpp" +#include #include #include #include @@ -88,6 +89,10 @@ namespace Compiler } else if (c==';') { + std::string comment; + + comment += c; + while (get (c)) { if (c=='\n') @@ -95,17 +100,26 @@ 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)) { mLoc.mLiteral.clear(); return true; } + else if (c==':') + { + // treat : as a whitespace :( + mLoc.mLiteral.clear(); + return true; + } else if (std::isdigit (c)) { bool cont = false; @@ -150,10 +164,9 @@ namespace Compiler bool Scanner::scanInt (char c, Parser& parser, bool& cont) { + assert(c != '\0'); std::string value; - value += c; - bool empty = false; bool error = false; @@ -162,7 +175,6 @@ namespace Compiler if (std::isdigit (c)) { value += c; - empty = false; } else if (std::isalpha (c) || c=='_') error = true; @@ -177,7 +189,7 @@ namespace Compiler } } - if (empty || error) + if (error) return false; TokenLoc loc (mLoc); @@ -359,7 +371,18 @@ namespace Compiler else if (c==')') special = S_close; else if (c=='.') + { + // check, if this starts a float literal + if (get (c)) + { + putback (c); + + if (std::isdigit (c)) + return scanFloat ("", parser, cont); + } + special = S_member; + } else if (c=='=') { if (get (c)) @@ -415,7 +438,12 @@ namespace Compiler if (get (c)) { if (c=='=') + { special = S_cmpLE; + + if (get (c) && c!='=') // <== is a allowed as an alternative to <= :( + putback (c); + } else { putback (c); @@ -430,7 +458,12 @@ namespace Compiler if (get (c)) { if (c=='=') + { special = S_cmpGE; + + if (get (c) && c!='=') // >== is a allowed as an alternative to >= :( + putback (c); + } else { putback (c); diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp index ac0ee63f1c..5b3d244b0d 100644 --- a/components/compiler/scriptparser.cpp +++ b/components/compiler/scriptparser.cpp @@ -2,6 +2,8 @@ #include "scriptparser.hpp" #include "scanner.hpp" +#include "skipparser.hpp" +#include "errorhandler.hpp" namespace Compiler { @@ -41,6 +43,17 @@ namespace Compiler return true; } + /// \todo add an option to disable this nonsense + if (keyword==Scanner::K_endif) + { + // surplus endif + getErrorHandler().warning ("endif without matching if/elseif", loc); + + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return true; + } + if (keyword==Scanner::K_end && mEnd) { return false; diff --git a/components/esm/aipackage.cpp b/components/esm/aipackage.cpp index 1e6220c3a5..1440dbd138 100644 --- a/components/esm/aipackage.cpp +++ b/components/esm/aipackage.cpp @@ -5,6 +5,12 @@ namespace ESM { + void AIData::blank() + { + mHello = mU1 = mFight = mFlee = mAlarm = mU2 = mU3 = mU4 = 0; + mServices = 0; + } + void AIPackageList::load(ESMReader &esm) { while (esm.hasMoreSubs()) { diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index 3128fe0c61..38499b2dd8 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -19,6 +19,9 @@ namespace ESM // These are probabilities char mHello, mU1, mFight, mFlee, mAlarm, mU2, mU3, mU4; int mServices; // See the Services enum + + void blank(); + ///< Set record to default state (does not touch the ID). }; // 12 bytes struct AIWander 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/esmcommon.hpp b/components/esm/esmcommon.hpp index 335d123378..6f51c767ec 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -2,6 +2,7 @@ #define OPENMW_ESM_COMMON_H #include +#include #include #include @@ -14,24 +15,6 @@ enum Version VER_13 = 0x3fa66666 }; -enum FileType - { - FT_ESP = 0, // Plugin - FT_ESM = 1, // Master - FT_ESS = 32 // Savegame - }; - -// Used to mark special files. The original ESM files are given -// special treatment in a few places, most noticably in loading and -// filtering out "dirtly" GMST entries correctly. -enum SpecialFile - { - SF_Other, - SF_Morrowind, - SF_Tribunal, - SF_Bloodmoon - }; - /* A structure used for holding fixed-length strings. In the case of LEN=4, it can be more efficient to match the string as a 32 bit number, therefore the struct is implemented as a union with an int. @@ -41,7 +24,7 @@ union NAME_T { char name[LEN]; int32_t val; - + bool operator==(const char *str) const { for(int i=0; i NAME; @@ -70,28 +55,6 @@ typedef NAME_T<256> NAME256; #pragma pack(push) #pragma pack(1) -/// File header data for all ES files -struct HEDRstruct -{ - /* File format version. This is actually a float, the supported - versions are 1.2 and 1.3. These correspond to: - 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 - */ - int version; - int type; // 0=esp, 1=esm, 32=ess - NAME32 author; // Author's name - NAME256 desc; // File description - int records; // Number of records? Not used. -}; - -// Defines another files (esm or esp) that this file depends upon. -struct MasterData -{ - std::string name; - uint64_t size; - int index; // Position of the parent file in the global list of loaded files -}; - // Data that is only present in save game files struct SaveData { @@ -113,7 +76,6 @@ struct ESM_Context uint32_t leftRec, leftSub; size_t leftFile; NAME recName, subName; - HEDRstruct header; // When working with multiple esX files, we will generate lists of all files that // actually contribute to a specific cell. Therefore, we need to store the index // of the file belonging to this contest. See CellStore::(list/load)refs for details. diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index b4f581d7e8..0a4c1a3fee 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -15,11 +15,17 @@ ESM_Context ESMReader::getContext() return mCtx; } -ESMReader::ESMReader(void): - mBuffer(50*1024) +ESMReader::ESMReader() + : mBuffer(50*1024) + , mRecordFlags(0) { } +int ESMReader::getFormat() const +{ + return mHeader.mFormat; +} + void ESMReader::restoreContext(const ESM_Context &rc) { // Reopen the file if necessary @@ -51,18 +57,6 @@ void ESMReader::openRaw(Ogre::DataStreamPtr _esm, const std::string &name) mEsm = _esm; mCtx.filename = name; mCtx.leftFile = mEsm->size(); - - // Flag certain files for special treatment, based on the file - // name. - const char *cstr = mCtx.filename.c_str(); - if (iends(cstr, "Morrowind.esm")) - mSpf = SF_Morrowind; - else if (iends(cstr, "Tribunal.esm")) - mSpf = SF_Tribunal; - else if (iends(cstr, "Bloodmoon.esm")) - mSpf = SF_Bloodmoon; - else - mSpf = SF_Other; } void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) @@ -74,42 +68,7 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) getRecHeader(); - // Get the header - getHNT(mCtx.header, "HEDR", 300); - - // Some mods abuse the header.version field for the version of the mod instead of the version of the file format, so we can only ignore it. - - while (isNextSub("MAST")) - { - MasterData m; - m.name = getHString(); - m.size = getHNLong("DATA"); - mMasters.push_back(m); - } - - if (mCtx.header.type == FT_ESS) - { - // Savegame-related data - - // Player position etc - getHNT(mSaveData, "GMDT", 124); - - /* Image properties, five ints. Is always: - Red-mask: 0xff0000 - Blue-mask: 0x00ff00 - Green-mask: 0x0000ff - Alpha-mask: 0x000000 - Bpp: 32 - */ - getSubNameIs("SCRD"); - skipHSubSize(20); - - /* Savegame screenshot: - 128x128 pixels * 4 bytes per pixel - */ - getSubNameIs("SCRS"); - skipHSubSize(65536); - } + mHeader.load (*this); } void ESMReader::open(const std::string &file) diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index ae876edf8b..ff10a202c9 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -12,7 +12,9 @@ #include #include + #include "esmcommon.hpp" +#include "loadtes3.hpp" namespace ESM { @@ -20,15 +22,7 @@ class ESMReader { public: - ESMReader(void); - - /************************************************************************* - * - * Public type definitions - * - *************************************************************************/ - - typedef std::vector MasterList; + ESMReader(); /************************************************************************* * @@ -36,14 +30,12 @@ public: * *************************************************************************/ - int getVer() const { return mCtx.header.version; } - float getFVer() const { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } - int getSpecial() const { return mSpf; } - int getType() const { return mCtx.header.type; } - const std::string getAuthor() const { return mCtx.header.author.toString(); } - const std::string getDesc() const { return mCtx.header.desc.toString(); } - const SaveData &getSaveData() const { return mSaveData; } - const MasterList &getMasters() const { return mMasters; } + int getVer() const { return mHeader.mData.version; } + float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; } + const std::string getAuthor() const { return mHeader.mData.author.toString(); } + const std::string getDesc() const { return mHeader.mData.desc.toString(); } + const std::vector &getMasters() const { return mHeader.mMaster; } + int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } @@ -224,7 +216,7 @@ public: follows the header, ie beyond the entire record. You should use leftRec to orient yourself inside the record itself. */ - void getRecHeader() { uint32_t u; getRecHeader(u); } + void getRecHeader() { getRecHeader(mRecordFlags); } void getRecHeader(uint32_t &flags); bool hasMoreRecs() const { return mCtx.leftFile > 0; } @@ -257,19 +249,23 @@ public: /// Sets font encoder for ESM strings void setEncoder(ToUTF8::Utf8Encoder* encoder); + /// Get record flags of last record + unsigned int getRecordFlags() { return mRecordFlags; } + private: Ogre::DataStreamPtr mEsm; ESM_Context mCtx; + unsigned int mRecordFlags; + // Special file signifier (see SpecialFile enum above) - int mSpf; // Buffer for ESM strings std::vector mBuffer; - SaveData mSaveData; - MasterList mMasters; + Header mHeader; + std::vector *mGlobalReaderList; ToUTF8::Utf8Encoder* mEncoder; }; diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index b9dd5b57b6..3ea6bd350a 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -1,6 +1,8 @@ #include "esmwriter.hpp" + +#include #include -#include +#include bool count = true; @@ -9,40 +11,40 @@ namespace ESM int ESMWriter::getVersion() { - return m_header.version; + return mHeader.mData.version; } void ESMWriter::setVersion(int ver) { - m_header.version = ver; -} - -int ESMWriter::getType() -{ - return m_header.type; -} - -void ESMWriter::setType(int type) -{ - m_header.type = type; + mHeader.mData.version = ver; } void ESMWriter::setAuthor(const std::string& auth) { - strncpy((char*)&m_header.author, auth.c_str(), 32); + mHeader.mData.author.assign (auth); } void ESMWriter::setDescription(const std::string& desc) { - strncpy((char*)&m_header.desc, desc.c_str(), 256); + mHeader.mData.desc.assign (desc); +} + +void ESMWriter::setRecordCount (int count) +{ + mHeader.mData.records = count; +} + +void ESMWriter::setFormat (int format) +{ + mHeader.mFormat = format; } void ESMWriter::addMaster(const std::string& name, uint64_t size) { - MasterData d; + Header::MasterData d; d.name = name; d.size = size; - m_masters.push_back(d); + mHeader.mMaster.push_back(d); } void ESMWriter::save(const std::string& file) @@ -58,25 +60,13 @@ void ESMWriter::save(std::ostream& file) startRecord("TES3", 0); - m_header.records = 0; - writeHNT("HEDR", m_header, 300); - m_headerPos = m_stream->tellp() - (std::streampos)4; - - for (std::list::iterator it = m_masters.begin(); it != m_masters.end(); ++it) - { - writeHNCString("MAST", it->name); - writeHNT("DATA", it->size); - } + mHeader.save (*this); endRecord("TES3"); } void ESMWriter::close() { - std::cout << "Writing amount of saved records (" << m_recordCount - 1 << ")" << std::endl; - m_stream->seekp(m_headerPos); - writeT(m_recordCount-1); - m_stream->seekp(0, std::ios::end); m_stream->flush(); if (!m_records.empty()) @@ -86,7 +76,7 @@ void ESMWriter::close() void ESMWriter::startRecord(const std::string& name, uint32_t flags) { m_recordCount++; - + writeName(name); RecordData rec; rec.name = name; @@ -109,7 +99,7 @@ void ESMWriter::startSubRecord(const std::string& name) rec.size = 0; writeT(0); // Size goes here m_records.push_back(rec); - + assert(m_records.back().size == 0); } @@ -118,7 +108,7 @@ void ESMWriter::endRecord(const std::string& name) RecordData rec = m_records.back(); assert(rec.name == name); m_records.pop_back(); - + m_stream->seekp(rec.position); count = false; diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 94e173b4ce..be3ae33abe 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -1,12 +1,13 @@ #ifndef OPENMW_ESM_WRITER_H #define OPENMW_ESM_WRITER_H -#include +#include #include -#include + +#include #include "esmcommon.hpp" -#include +#include "loadtes3.hpp" namespace ESM { @@ -22,11 +23,11 @@ class ESMWriter public: int getVersion(); void setVersion(int ver); - int getType(); - void setType(int type); void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8? void setAuthor(const std::string& author); void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); void addMaster(const std::string& name, uint64_t size); @@ -80,7 +81,7 @@ public: { write((char*)&data, size); } - + void startRecord(const std::string& name, uint32_t flags); void startSubRecord(const std::string& name); void endRecord(const std::string& name); @@ -90,14 +91,13 @@ public: void write(const char* data, size_t size); private: - std::list m_masters; std::list m_records; std::ostream* m_stream; std::streampos m_headerPos; ToUTF8::Utf8Encoder* m_encoder; int m_recordCount; - HEDRstruct m_header; + Header mHeader; }; } diff --git a/components/esm/loadacti.cpp b/components/esm/loadacti.cpp index 0e214e2b62..fd022af7e6 100644 --- a/components/esm/loadacti.cpp +++ b/components/esm/loadacti.cpp @@ -17,4 +17,11 @@ void Activator::save(ESMWriter &esm) esm.writeHNCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); } + + void Activator::blank() + { + mName.clear(); + mScript.clear(); + mModel.clear(); + } } diff --git a/components/esm/loadacti.hpp b/components/esm/loadacti.hpp index 8cb335feb2..a62990590d 100644 --- a/components/esm/loadacti.hpp +++ b/components/esm/loadacti.hpp @@ -15,6 +15,9 @@ struct Activator void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } diff --git a/components/esm/loadalch.cpp b/components/esm/loadalch.cpp index a4b1bb7182..dbb69c066f 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -23,4 +23,16 @@ void Potion::save(ESMWriter &esm) esm.writeHNT("ALDT", mData, 12); mEffects.save(esm); } + + void Potion::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mAutoCalc = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + mEffects.mList.clear(); + } } diff --git a/components/esm/loadalch.hpp b/components/esm/loadalch.hpp index 1e571ac40b..3ede853424 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -30,6 +30,10 @@ struct Potion void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). + }; } #endif diff --git a/components/esm/loadappa.cpp b/components/esm/loadappa.cpp index 643fefda51..4b8d2b763c 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -36,4 +36,16 @@ void Apparatus::save(ESMWriter &esm) esm.writeHNOCString("SCRI", mScript); esm.writeHNCString("ITEX", mIcon); } + + void Apparatus::blank() + { + mData.mType = 0; + mData.mQuality = 0; + mData.mWeight = 0; + mData.mValue = 0; + mModel.clear(); + mIcon.clear(); + mScript.clear(); + mName.clear(); + } } diff --git a/components/esm/loadappa.hpp b/components/esm/loadappa.hpp index 486a559f89..ed9d335be6 100644 --- a/components/esm/loadappa.hpp +++ b/components/esm/loadappa.hpp @@ -36,6 +36,9 @@ struct Apparatus void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index 6133465330..e64c8705d7 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -50,4 +50,19 @@ void Armor::save(ESMWriter &esm) esm.writeHNOCString("ENAM", mEnchant); } + void Armor::blank() + { + mData.mType = 0; + mData.mWeight = 0; + mData.mValue = 0; + mData.mHealth = 0; + mData.mEnchant = 0; + mData.mArmor = 0; + mParts.mParts.clear(); + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + mEnchant.clear(); + } } diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index a94ae67353..eaef42be83 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -38,7 +38,9 @@ enum PartReferenceType PRT_RPauldron = 23, PRT_LPauldron = 24, PRT_Weapon = 25, - PRT_Tail = 26 + PRT_Tail = 26, + + PRT_Count = 27 }; // Reference to body parts @@ -88,6 +90,9 @@ struct Armor void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index 831ad8b641..e95a8a8603 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -9,13 +9,13 @@ namespace ESM void BodyPart::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mRace = esm.getHNString("FNAM"); esm.getHNT(mData, "BYDT", 4); } void BodyPart::save(ESMWriter &esm) { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNCString("FNAM", mRace); esm.writeHNT("BYDT", mData, 4); } diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index c91bb40bf2..3ad9b1b958 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -27,13 +27,15 @@ struct BodyPart MP_Knee = 11, MP_Upperleg = 12, MP_Clavicle = 13, - MP_Tail = 14 + MP_Tail = 14, + + MP_Count = 15 }; enum Flags { BPF_Female = 1, - BPF_Playable = 2 + BPF_NotPlayable = 2 }; enum MeshType @@ -52,7 +54,7 @@ struct BodyPart }; BYDTstruct mData; - std::string mId, mModel, mName; + std::string mId, mModel, mRace; void load(ESMReader &esm); void save(ESMWriter &esm); diff --git a/components/esm/loadbook.cpp b/components/esm/loadbook.cpp index 8ed2f122a8..3a70ac7869 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -27,4 +27,18 @@ void Book::save(ESMWriter &esm) esm.writeHNOCString("ENAM", mEnchant); } + void Book::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mIsScroll = 0; + mData.mSkillID = 0; + mData.mEnchant = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + mEnchant.clear(); + mText.clear(); + } } diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index 22201abed8..68042e246e 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -26,6 +26,9 @@ struct Book void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif 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.cpp b/components/esm/loadcell.cpp index 76a48e5ec4..779f303f3e 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -8,13 +8,10 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -#include -#include - namespace ESM { -/// Some overloaded copare operators. +/// Some overloaded compare operators. bool operator==(const MovedCellRef& ref, int pRefnum) { return (ref.mRefnum == pRefnum); @@ -43,15 +40,14 @@ void CellRef::save(ESMWriter &esm) esm.writeHNT("INDX", mFactIndex); } - if (mCharge != -1.0) { - esm.writeHNT("XCHG", mCharge); - } + if (mEnchantmentCharge != -1) + esm.writeHNT("XCHG", mEnchantmentCharge); - if (mIntv != -1) { - esm.writeHNT("INTV", mIntv); - } - if (mNam9 != 0) { - esm.writeHNT("NAM9", mNam9); + if (mCharge != -1) + esm.writeHNT("INTV", mCharge); + + if (mGoldValue != 1) { + esm.writeHNT("NAM9", mGoldValue); } if (mTeleport) @@ -66,8 +62,8 @@ void CellRef::save(ESMWriter &esm) esm.writeHNOCString("KNAM", mKey); esm.writeHNOCString("TNAM", mTrap); - if (mUnam != -1) { - esm.writeHNT("UNAM", mUnam); + if (mReferenceBlocked != -1) { + esm.writeHNT("UNAM", mReferenceBlocked); } if (mFltv != 0) { esm.writeHNT("FLTV", mFltv); @@ -133,38 +129,13 @@ void Cell::load(ESMReader &esm, bool saveContext) } } -void Cell::load(ESMReader &esm, MWWorld::ESMStore &store) +void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool { this->load(esm, false); +} - // preload moved references - while (esm.isNextSub("MVRF")) { - CellRef ref; - MovedCellRef cMRef; - getNextMVRF(esm, cMRef); - - MWWorld::Store &cStore = const_cast&>(store.get()); - ESM::Cell *cellAlt = const_cast(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); - - // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following - // implementation when the oher implementation works as well. - getNextRef(esm, ref); - std::string lowerCase; - - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - // Add data required to make reference appear in the correct cell. - // We should not need to test for duplicates, as this part of the code is pre-cell merge. - mMovedRefs.push_back(cMRef); - // But there may be duplicates here! - ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum); - if (iter == cellAlt->mLeasedRefs.end()) - cellAlt->mLeasedRefs.push_back(ref); - else - *iter = ref; - } - +void Cell::postLoad(ESMReader &esm) +{ // Save position of the cell references and move on mContextList.push_back(esm.getContext()); esm.skipRecord(); @@ -245,7 +216,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. ref.mRefnum &= 0x00ffffff; // delete old plugin ID - const ESM::ESMReader::MasterList &masters = esm.getMasters(); + const std::vector &masters = esm.getMasters(); global = masters[local-1].index + 1; ref.mRefnum |= global << 24; // insert global plugin ID } @@ -285,13 +256,15 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) ref.mFactIndex = -2; esm.getHNOT(ref.mFactIndex, "INDX"); - ref.mCharge = -1.0; - esm.getHNOT(ref.mCharge, "XCHG"); + ref.mGoldValue = 1; + ref.mCharge = -1; + ref.mEnchantmentCharge = -1; - ref.mIntv = -1; - ref.mNam9 = 0; - esm.getHNOT(ref.mIntv, "INTV"); - esm.getHNOT(ref.mNam9, "NAM9"); + esm.getHNOT(ref.mEnchantmentCharge, "XCHG"); + + esm.getHNOT(ref.mCharge, "INTV"); + + esm.getHNOT(ref.mGoldValue, "NAM9"); // Present for doors that teleport you to another cell. if (esm.isNextSub("DODT")) @@ -309,9 +282,9 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) ref.mKey = esm.getHNOString("KNAM"); ref.mTrap = esm.getHNOString("TNAM"); - ref.mUnam = -1; + ref.mReferenceBlocked = -1; ref.mFltv = 0; - esm.getHNOT(ref.mUnam, "UNAM"); + esm.getHNOT(ref.mReferenceBlocked, "UNAM"); esm.getHNOT(ref.mFltv, "FLTV"); esm.getHNOT(ref.mPos, "DATA", 24); @@ -348,11 +321,29 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) int local = (mref.mRefnum & 0xff000000) >> 24; size_t global = esm.getIndex() + 1; mref.mRefnum &= 0x00ffffff; // delete old plugin ID - const ESM::ESMReader::MasterList &masters = esm.getMasters(); + const std::vector &masters = esm.getMasters(); global = masters[local-1].index + 1; mref.mRefnum |= global << 24; // insert global plugin ID return true; } + void Cell::blank() + { + mName.clear(); + mRegion.clear(); + mWater = 0; + mWaterInt = false; + mMapColor = 0; + mNAM0 = 0; + + mData.mFlags = 0; + mData.mX = 0; + mData.mY = 0; + + mAmbi.mAmbient = 0; + mAmbi.mSunlight = 0; + mAmbi.mFog = 0; + mAmbi.mFogDensity = 0; + } } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 7db6dbe77c..eda8a5418c 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -7,93 +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; - - // Depends on context - possibly weapon health, number of uses left - // or weapon magic charge? - float mCharge; - - // I have no idea, these are present some times, often along with - // owner (ANAM) and sometimes otherwise. They are often (but not - // always) 1. INTV is big for lights (possibly a float?), might have - // something to do with remaining light "charge". - int mIntv, mNam9; - - // 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 - - // No idea - occurs ONCE in Morrowind.esm, for an activator - signed char mUnam; - - // 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. @@ -170,7 +96,8 @@ struct Cell CellRefTracker mLeasedRefs; MovedCellRefTracker mMovedRefs; - void load(ESMReader &esm, MWWorld::ESMStore &store); + void preLoad(ESMReader &esm); + void postLoad(ESMReader &esm); // This method is left in for compatibility with esmtool. Parsing moved references currently requires // passing ESMStore, bit it does not know about this parameter, so we do it this way. @@ -214,6 +141,9 @@ struct Cell * Since they are comparably rare, we use a separate method for this. */ static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index a626689504..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"); @@ -35,4 +54,18 @@ void Class::save(ESMWriter &esm) esm.writeHNOString("DESC", mDescription); } + void Class::blank() + { + mName.clear(); + mDescription.clear(); + + mData.mAttribute[0] = mData.mAttribute[1] = 0; + mData.mSpecialization = 0; + mData.mIsPlayable = 0; + mData.mCalc = 0; + + for (int i=0; i<5; ++i) + for (int i2=0; i2<2; ++i2) + mData.mSkills[i][i2] = 0; + } } diff --git a/components/esm/loadclas.hpp b/components/esm/loadclas.hpp index 264e342e63..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; @@ -65,6 +71,10 @@ struct Class 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/loadclot.cpp b/components/esm/loadclot.cpp index a37fbe8a4c..10b00970fb 100644 --- a/components/esm/loadclot.cpp +++ b/components/esm/loadclot.cpp @@ -16,7 +16,7 @@ void Clothing::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); mParts.load(esm); - + mEnchant = esm.getHNOString("ENAM"); } @@ -28,10 +28,24 @@ void Clothing::save(ESMWriter &esm) esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); - + mParts.save(esm); - + esm.writeHNOCString("ENAM", mEnchant); } + void Clothing::blank() + { + mData.mType = 0; + mData.mWeight = 0; + mData.mValue = 0; + mData.mEnchant = 0; + mParts.mParts.clear(); + mName.clear(); + mModel.clear(); + mIcon.clear(); + mIcon.clear(); + mEnchant.clear(); + mScript.clear(); + } } diff --git a/components/esm/loadclot.hpp b/components/esm/loadclot.hpp index 623983ccfa..816d03cb23 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -46,6 +46,9 @@ struct Clothing void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadcont.cpp b/components/esm/loadcont.cpp index e6ba91e7c2..853c8bd500 100644 --- a/components/esm/loadcont.cpp +++ b/components/esm/loadcont.cpp @@ -53,4 +53,13 @@ void Container::save(ESMWriter &esm) mInventory.save(esm); } + void Container::blank() + { + mName.clear(); + mModel.clear(); + mScript.clear(); + mWeight = 0; + mFlags = 0; + mInventory.mList.clear(); + } } diff --git a/components/esm/loadcont.hpp b/components/esm/loadcont.hpp index b66ca086d9..b2bbab73d0 100644 --- a/components/esm/loadcont.hpp +++ b/components/esm/loadcont.hpp @@ -47,6 +47,9 @@ struct Container void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index b59835bd64..86d05b8a57 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -7,6 +7,8 @@ namespace ESM { void Creature::load(ESMReader &esm) { + mPersistent = esm.getRecordFlags() & 0x0400; + mModel = esm.getHNString("MODL"); mOriginal = esm.getHNOString("CNAM"); mName = esm.getHNOString("FNAM"); @@ -53,4 +55,28 @@ void Creature::save(ESMWriter &esm) mAiPackage.save(esm); } + void Creature::blank() + { + mData.mType = 0; + mData.mLevel = 0; + mData.mStrength = mData.mIntelligence = mData.mWillpower = mData.mAgility = + mData.mSpeed = mData.mEndurance = mData.mPersonality = mData.mLuck = 0; + mData.mHealth = mData.mMana = mData.mFatigue = 0; + mData.mSoul = 0; + mData.mCombat = mData.mMagic = mData.mStealth = 0; + for (int i=0; i<6; ++i) mData.mAttack[i] = 0; + mData.mGold = 0; + mFlags = 0; + mScale = 0; + mModel.clear(); + mName.clear(); + mScript.clear(); + mOriginal.clear(); + mInventory.mList.clear(); + mSpells.mList.clear(); + mHasAI = false; + mAiData.blank(); + mAiData.mServices = 0; + mAiPackage.mList.clear(); + } } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 1c93d995a5..279e2ea3f4 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -69,6 +69,9 @@ struct Creature NPDTstruct mData; int mFlags; + + bool mPersistent; + float mScale; std::string mId, mModel, mName, mScript; @@ -84,6 +87,9 @@ struct Creature void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } diff --git a/components/esm/loaddoor.cpp b/components/esm/loaddoor.cpp index b8ef029c57..a4c7b7d58b 100644 --- a/components/esm/loaddoor.cpp +++ b/components/esm/loaddoor.cpp @@ -24,4 +24,12 @@ void Door::save(ESMWriter &esm) esm.writeHNOCString("ANAM", mCloseSound); } + void Door::blank() + { + mName.clear(); + mModel.clear(); + mScript.clear(); + mOpenSound.clear(); + mCloseSound.clear(); + } } diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index e992a592f6..77ffc64899 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -15,6 +15,9 @@ struct Door void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif 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/loadingr.cpp b/components/esm/loadingr.cpp index 4312dc05ba..7e31a4116d 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -24,7 +24,7 @@ void Ingredient::load(ESMReader &esm) { mData.mAttributes[i] = -1; } - + // is this relevant in cycle from 0 to 4? if (mData.mEffectID[i] != 89 && mData.mEffectID[i] != 26 && @@ -46,4 +46,20 @@ void Ingredient::save(ESMWriter &esm) esm.writeHNOCString("ITEX", mIcon); } + void Ingredient::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + for (int i=0; i<4; ++i) + { + mData.mEffectID[i] = 0; + mData.mSkills[i] = 0; + mData.mAttributes[i] = 0; + } + + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } } diff --git a/components/esm/loadingr.hpp b/components/esm/loadingr.hpp index cd63cf39ae..5e286535f4 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -29,6 +29,9 @@ struct Ingredient void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index 4cba1119bf..b54a912760 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -38,7 +38,7 @@ void LeveledListBase::save(ESMWriter &esm) esm.writeHNT("DATA", mFlags); esm.writeHNT("NNAM", mChanceNone); esm.writeHNT("INDX", mList.size()); - + for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNCString(mRecName, it->mId); @@ -46,4 +46,10 @@ void LeveledListBase::save(ESMWriter &esm) } } + void LeveledListBase::blank() + { + mFlags = 0; + mChanceNone = 0; + mList.clear(); + } } diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index b7db5db360..7339cac56f 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -15,24 +15,27 @@ class ESMWriter; * to implement it once. * * We should later implement the ability to merge leveled lists from - * several files. + * several files. */ 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 @@ -49,6 +52,9 @@ struct LeveledListBase void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; struct CreatureLevList: LeveledListBase diff --git a/components/esm/loadligh.cpp b/components/esm/loadligh.cpp index 48a56db3cf..89a2b8c65b 100644 --- a/components/esm/loadligh.cpp +++ b/components/esm/loadligh.cpp @@ -26,4 +26,18 @@ void Light::save(ESMWriter &esm) esm.writeHNOCString("SNAM", mSound); } + void Light::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mTime = 0; + mData.mRadius = 0; + mData.mColor = 0; + mData.mFlags = 0; + mSound.clear(); + mScript.clear(); + mModel.clear(); + mIcon.clear(); + mName.clear(); + } } diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp index b3d703cc26..3f0b76d6e2 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -45,6 +45,9 @@ struct Light void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadlock.cpp b/components/esm/loadlock.cpp new file mode 100644 index 0000000000..03eac52bd5 --- /dev/null +++ b/components/esm/loadlock.cpp @@ -0,0 +1,41 @@ +#include "loadlock.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + +void Lockpick::load(ESMReader &esm) +{ + mModel = esm.getHNString("MODL"); + mName = esm.getHNString("FNAM"); + + esm.getHNT(mData, "LKDT", 16); + + mScript = esm.getHNOString("SCRI"); + mIcon = esm.getHNOString("ITEX"); +} + +void Lockpick::save(ESMWriter &esm) +{ + esm.writeHNCString("MODL", mModel); + esm.writeHNCString("FNAM", mName); + + esm.writeHNT("LKDT", mData, 16); + esm.writeHNOString("SCRI", mScript); + esm.writeHNOCString("ITEX", mIcon); +} + + void Lockpick::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mQuality = 0; + mData.mUses = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } +} diff --git a/components/esm/loadlock.hpp b/components/esm/loadlock.hpp new file mode 100644 index 0000000000..953066cb2f --- /dev/null +++ b/components/esm/loadlock.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_ESM_LOCK_H +#define OPENMW_ESM_LOCK_H + +#include + +namespace ESM +{ + +class ESMReader; +class ESMWriter; + +struct Lockpick +{ + struct Data + { + float mWeight; + int mValue; + + float mQuality; + int mUses; + }; // Size = 16 + + Data mData; + std::string mId, mName, mModel, mIcon, mScript; + + void load(ESMReader &esm); + void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). +}; + +} +#endif diff --git a/components/esm/loadlocks.cpp b/components/esm/loadlocks.cpp deleted file mode 100644 index 057da595e7..0000000000 --- a/components/esm/loadlocks.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "loadlocks.hpp" - -#include "esmreader.hpp" -#include "esmwriter.hpp" - -namespace ESM -{ - -void Tool::load(ESMReader &esm) -{ - mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); - - esm.getSubName(); - NAME n = esm.retSubName(); - // The data name varies, RIDT for repair items, LKDT for lock - // picks, PBDT for probes - - esm.getHT(mData, 16); - - if (n == "RIDT") - { - mType = Type_Repair; - // Swap t.data.quality and t.data.uses for repair items (sigh) - float tmp = *((float*) &mData.mUses); - mData.mUses = *((int*) &mData.mQuality); - mData.mQuality = tmp; - } - else if (n == "LKDT") - mType = Type_Pick; - else if (n == "PBDT") - mType = Type_Probe; - - mScript = esm.getHNOString("SCRI"); - mIcon = esm.getHNOString("ITEX"); -} - -void Tool::save(ESMWriter &esm) -{ - esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); - - std::string typeName; - switch(mType) - { - case Type_Repair: typeName = "RIDT"; break; - case Type_Pick: typeName = "LKDT"; break; - case Type_Probe: typeName = "PBDT"; break; - } - - Data write = mData; - if (mType == Type_Repair) - { - float tmp = *((float*) &write.mUses); - write.mUses = *((int*) &write.mQuality); - write.mQuality = tmp; - } - - esm.writeHNT(typeName, write, 16); - esm.writeHNOString("SCRI", mScript); - esm.writeHNOCString("ITEX", mIcon); -} - - -} diff --git a/components/esm/loadlocks.hpp b/components/esm/loadlocks.hpp deleted file mode 100644 index 8e88c548e7..0000000000 --- a/components/esm/loadlocks.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef OPENMW_ESM_LOCKS_H -#define OPENMW_ESM_LOCKS_H - -#include - -namespace ESM -{ - -class ESMReader; -class ESMWriter; - -/* - * This file covers lockpicks (LOCK), probes (PROB) and armor repair - * items (REPA). These have nearly identical data structures. - */ - -struct Tool -{ - enum Type - { - Type_Pick, - Type_Probe, - Type_Repair - }; - - struct Data - { - float mWeight; - int mValue; - - float mQuality; // And when I say nearly identical structure, I - int mUses; // mean perfectly identical except that these two - // variables are swaped for repair items. Don't ask - // me why. - }; // Size = 16 - - Data mData; - Type mType; - std::string mId, mName, mModel, mIcon, mScript; - - void load(ESMReader &esm); - void save(ESMWriter &esm); -}; - -struct Probe: Tool -{ - Probe() { mType = Type_Probe; } -}; - -struct Repair: Tool -{ - Repair() { mType = Type_Repair; } -}; - -} -#endif diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index 93c59f9cbe..7efc17aec5 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -66,6 +66,158 @@ struct MagicEffect void load(ESMReader &esm); void save(ESMWriter &esm); + + + enum Effects + { + WaterBreathing = 0, + SwiftSwim = 1, + WaterWalking = 2, + Shield = 3, + FireShield = 4, + LightningShield = 5, + FrostShield = 6, + Burden = 7, + Feather = 8, + Jump = 9, + Levitate = 10, + SlowFall = 11, + Lock = 12, + Open = 13, + FireDamage = 14, + ShockDamage = 15, + FrostDamage = 16, + DrainAttribute = 17, + DrainHealth = 18, + DrainMagicka = 19, + DrainFatigue = 20, + DrainSkill = 21, + DamageAttribute = 22, + DamageHealth = 23, + DamageMagicka = 24, + DamageFatigue = 25, + DamageSkill = 26, + Poison = 27, + WeaknessToFire = 28, + WeaknessToFrost = 29, + WeaknessToShock = 30, + WeaknessToMagicka = 31, + WeaknessToCommonDisease = 32, + WeaknessToBlightDisease = 33, + WeaknessToCorprusDisease = 34, + WeaknessToPoison = 35, + WeaknessToNormalWeapons = 36, + DisintegrateWeapon = 37, + DisintegrateArmor = 38, + Invisibility = 39, + Chameleon = 40, + Light = 41, + Sanctuary = 42, + NightEye = 43, + Charm = 44, + Paralyze = 45, + Silence = 46, + Blind = 47, + Sound = 48, + CalmHumanoid = 49, + CalmCreature = 50, + FrenzyHumanoid = 51, + FrenzyCreature = 52, + DemoralizeHumanoid = 53, + DemoralizeCreature = 54, + RallyHumanoid = 55, + RallyCreature = 56, + Dispel = 57, + Soultrap = 58, + Telekinesis = 59, + Mark = 60, + Recall = 61, + DivineIntervention = 62, + AlmsiviIntervention = 63, + DetectAnimal = 64, + DetectEnchantment = 65, + DetectKey = 66, + SpellAbsorption = 67, + Reflect = 68, + CureCommonDisease = 69, + CureBlightDisease = 70, + CureCorprusDisease = 71, + CurePoison = 72, + CureParalyzation = 73, + RestoreAttribute = 74, + RestoreHealth = 75, + RestoreMagicka = 76, + RestoreFatigue = 77, + RestoreSkill = 78, + FortifyAttribute = 79, + FortifyHealth = 80, + FortifyMagicka= 81, + FortifyFatigue = 82, + FortifySkill = 83, + FortifyMaximumMagicka = 84, + AbsorbAttribute = 85, + AbsorbHealth = 86, + AbsorbMagicka = 87, + AbsorbFatigue = 88, + AbsorbSkill = 89, + ResistFire = 90, + ResistFrost = 91, + ResistShock = 92, + ResistMagicka = 93, + ResistCommonDisease = 94, + ResistBlightDisease = 95, + ResistCorprusDisease = 96, + ResistPoison = 97, + ResistNormalWeapons = 98, + ResistParalysis = 99, + RemoveCurse = 100, + TurnUndead = 101, + SummonScamp = 102, + SummonClannfear = 103, + SummonDaedroth = 104, + SummonDremora = 105, + SummonAncestralGhost = 106, + SummonSkeletalMinion = 107, + SummonBonewalker = 108, + SummonGreaterBonewalker = 109, + SummonBonelord = 110, + SummonWingedTwilight = 111, + SummonHunger = 112, + SummonGoldenSaint = 113, + SummonFlameAtronach = 114, + SummonFrostAtronach = 115, + SummonStormAtronach = 116, + FortifyAttack = 117, + CommandCreature = 118, + CommandHumanoid = 119, + BoundDagger = 120, + BoundLongsword = 121, + BoundMace = 122, + BoundBattleAxe = 123, + BoundSpear = 124, + BoundLongbow = 125, + ExtraSpell = 126, + BoundCuirass = 127, + BoundHelm = 128, + BoundBoots = 129, + BoundShield = 130, + BoundGloves = 131, + Corprus = 132, + Vampirism = 133, + SummonCenturionSphere = 134, + SunDamage = 135, + StuntedMagicka = 136, + + // Tribunal only + SummonFabricant = 137, + + // Bloodmoon only + SummonWolf = 138, + SummonBear = 139, + SummonBonewolf = 140, + SummonCreature04 = 141, + SummonCreature05 = 142 + }; }; } #endif diff --git a/components/esm/loadmisc.cpp b/components/esm/loadmisc.cpp index 3a5dded151..6006334ea4 100644 --- a/components/esm/loadmisc.cpp +++ b/components/esm/loadmisc.cpp @@ -23,4 +23,14 @@ void Miscellaneous::save(ESMWriter &esm) esm.writeHNOCString("ITEX", mIcon); } + void Miscellaneous::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mIsKey = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } } diff --git a/components/esm/loadmisc.hpp b/components/esm/loadmisc.hpp index 26d25139cb..25a9c5865a 100644 --- a/components/esm/loadmisc.hpp +++ b/components/esm/loadmisc.hpp @@ -30,6 +30,9 @@ struct Miscellaneous void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index 72d0b37362..7e17a93dc6 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -10,6 +10,8 @@ void NPC::load(ESMReader &esm) { mNpdt52.mGold = -10; + mPersistent = esm.getRecordFlags() & 0x0400; + mModel = esm.getHNOString("MODL"); mName = esm.getHNOString("FNAM"); @@ -56,7 +58,7 @@ void NPC::load(ESMReader &esm) mTransport.push_back(dodt); } else if (esm.retSubName() == 0x4d414e44) { // DNAM struct mTransport.back().mCellName = esm.getHString(); - } + } } mAiPackage.load(esm); esm.skipRecord(); @@ -71,14 +73,14 @@ void NPC::save(ESMWriter &esm) esm.writeHNCString("BNAM", mHead); esm.writeHNCString("KNAM", mHair); esm.writeHNOCString("SCRI", mScript); - + if (mNpdtType == 52) esm.writeHNT("NPDT", mNpdt52, 52); else if (mNpdtType == 12) esm.writeHNT("NPDT", mNpdt12, 12); esm.writeHNT("FLAG", mFlags); - + mInventory.save(esm); mSpells.save(esm); if (mHasAI) { @@ -93,4 +95,53 @@ void NPC::save(ESMWriter &esm) mAiPackage.save(esm); } + bool NPC::isMale() const { + return (mFlags & Female) == 0; + } + + void NPC::setIsMale(bool value) { + mFlags |= Female; + if (value) { + mFlags ^= Female; + } + } + + void NPC::blank() + { + mNpdtType = 0; + mNpdt52.mLevel = 0; + mNpdt52.mStrength = mNpdt52.mIntelligence = mNpdt52.mWillpower = mNpdt52.mAgility = + mNpdt52.mSpeed = mNpdt52.mEndurance = mNpdt52.mPersonality = mNpdt52.mLuck = 0; + for (int i=0; i<27; ++i) mNpdt52.mSkills[i] = 0; + mNpdt52.mReputation = 0; + mNpdt52.mHealth = mNpdt52.mMana = mNpdt52.mFatigue = 0; + mNpdt52.mDisposition = 0; + mNpdt52.mFactionID = 0; + mNpdt52.mRank = 0; + mNpdt52.mUnknown = 0; + mNpdt52.mGold = 0; + mNpdt12.mLevel = 0; + mNpdt12.mDisposition = 0; + mNpdt12.mReputation = 0; + mNpdt12.mRank = 0; + mNpdt12.mUnknown1 = 0; + mNpdt12.mUnknown2 = 0; + mNpdt12.mUnknown3 = 0; + mNpdt12.mGold = 0; + mFlags = 0; + mInventory.mList.clear(); + mSpells.mList.clear(); + mAiData.blank(); + mHasAI = false; + mTransport.clear(); + mAiPackage.mList.clear(); + mName.clear(); + mModel.clear(); + mRace.clear(); + mClass.clear(); + mFaction.clear(); + mScript.clear(); + mHair.clear(); + mHead.clear(); + } } diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 46be29961d..009bc5ef3b 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, @@ -62,7 +62,7 @@ struct NPC struct NPDTstruct52 { short mLevel; - char mStrength, + unsigned char mStrength, mIntelligence, mWillpower, mAgility, @@ -100,6 +100,8 @@ struct NPC int mFlags; + bool mPersistent; + InventoryList mInventory; SpellList mSpells; @@ -114,20 +116,15 @@ struct NPC // body parts std::string mHair, mHead; - // Implementation moved to load_impl.cpp void load(ESMReader &esm); void save(ESMWriter &esm); - bool isMale() const { - return (mFlags & Female) == 0; - } + bool isMale() const; - void setIsMale(bool value) { - mFlags |= Female; - if (value) { - mFlags ^= Female; - } - } + void setIsMale(bool value); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadprob.cpp b/components/esm/loadprob.cpp new file mode 100644 index 0000000000..729f8404e5 --- /dev/null +++ b/components/esm/loadprob.cpp @@ -0,0 +1,41 @@ +#include "loadprob.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + +void Probe::load(ESMReader &esm) +{ + mModel = esm.getHNString("MODL"); + mName = esm.getHNString("FNAM"); + + esm.getHNT(mData, "PBDT", 16); + + mScript = esm.getHNOString("SCRI"); + mIcon = esm.getHNOString("ITEX"); +} + +void Probe::save(ESMWriter &esm) +{ + esm.writeHNCString("MODL", mModel); + esm.writeHNCString("FNAM", mName); + + esm.writeHNT("PBDT", mData, 16); + esm.writeHNOString("SCRI", mScript); + esm.writeHNOCString("ITEX", mIcon); +} + + void Probe::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mQuality = 0; + mData.mUses = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } +} diff --git a/components/esm/loadprob.hpp b/components/esm/loadprob.hpp new file mode 100644 index 0000000000..55b896bcda --- /dev/null +++ b/components/esm/loadprob.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_ESM_PROBE_H +#define OPENMW_ESM_PROBE_H + +#include + +namespace ESM +{ + +class ESMReader; +class ESMWriter; + +struct Probe +{ + struct Data + { + float mWeight; + int mValue; + + float mQuality; + int mUses; + }; // Size = 16 + + Data mData; + std::string mId, mName, mModel, mIcon, mScript; + + void load(ESMReader &esm); + void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). +}; + +} +#endif 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/loadrepa.cpp b/components/esm/loadrepa.cpp new file mode 100644 index 0000000000..ced6daa2e9 --- /dev/null +++ b/components/esm/loadrepa.cpp @@ -0,0 +1,41 @@ +#include "loadrepa.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + +void Repair::load(ESMReader &esm) +{ + mModel = esm.getHNString("MODL"); + mName = esm.getHNString("FNAM"); + + esm.getHNT(mData, "RIDT", 16); + + mScript = esm.getHNOString("SCRI"); + mIcon = esm.getHNOString("ITEX"); +} + +void Repair::save(ESMWriter &esm) +{ + esm.writeHNCString("MODL", mModel); + esm.writeHNCString("FNAM", mName); + + esm.writeHNT("RIDT", mData, 16); + esm.writeHNOString("SCRI", mScript); + esm.writeHNOCString("ITEX", mIcon); +} + + void Repair::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mQuality = 0; + mData.mUses = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } +} diff --git a/components/esm/loadrepa.hpp b/components/esm/loadrepa.hpp new file mode 100644 index 0000000000..83812bad9c --- /dev/null +++ b/components/esm/loadrepa.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_ESM_REPA_H +#define OPENMW_ESM_REPA_H + +#include + +namespace ESM +{ + +class ESMReader; +class ESMWriter; + +struct Repair +{ + struct Data + { + float mWeight; + int mValue; + + int mUses; + float mQuality; + }; // Size = 16 + + Data mData; + std::string mId, mName, mModel, mIcon, mScript; + + void load(ESMReader &esm); + void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). +}; + +} +#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 a4d21c5912..b57645f3b8 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -1,5 +1,9 @@ #include "loadskil.hpp" +#include + +#include + #include "esmreader.hpp" #include "esmwriter.hpp" @@ -98,11 +102,44 @@ void Skill::load(ESMReader &esm) esm.getHNT(mIndex, "INDX"); esm.getHNT(mData, "SKDT", 24); mDescription = esm.getHNOString("DESC"); + + // create an ID from the index and the name (only used in the editor and likely to change in the + // future) + mId = indexToId (mIndex); } + void Skill::save(ESMWriter &esm) { esm.writeHNT("INDX", mIndex); esm.writeHNT("SKDT", mData, 24); esm.writeHNOString("DESC", mDescription); } + + void Skill::blank() + { + mData.mAttribute = 0; + mData.mSpecialization = 0; + mData.mUseValue[0] = mData.mUseValue[1] = mData.mUseValue[2] = mData.mUseValue[3] = 1.0; + mDescription.clear(); + } + + std::string Skill::indexToId (int index) + { + std::ostringstream stream; + + if (index!=-1) + { + stream << "#"; + + if (index<10) + stream << "0"; + + stream << index; + + if (index>=0 && index0) + esm.writeHNT ("FORM", mFormat); + + for (std::vector::iterator iter = mMaster.begin(); + iter != mMaster.end(); ++iter) + { + esm.writeHNCString ("MAST", iter->name); + esm.writeHNT ("DATA", iter->size); + } +} \ No newline at end of file diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp new file mode 100644 index 0000000000..b73a4c31e4 --- /dev/null +++ b/components/esm/loadtes3.hpp @@ -0,0 +1,55 @@ +#ifndef COMPONENT_ESM_TES3_H +#define COMPONENT_ESM_TES3_H + +#include + +#include "esmcommon.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + +#pragma pack(push) +#pragma pack(1) + + /// \brief File header record + struct Header + { + static const int CurrentFormat = 0; // most recent known format + + struct Data + { + /* File format version. This is actually a float, the supported + versions are 1.2 and 1.3. These correspond to: + 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 + */ + int version; + int type; // 0=esp, 1=esm, 32=ess (unused) + NAME32 author; // Author's name + NAME256 desc; // File description + int records; // Number of records? Not used. + }; + + // Defines another files (esm or esp) that this file depends upon. + struct MasterData + { + std::string name; + uint64_t size; + int index; // Position of the parent file in the global list of loaded files + }; + + Data mData; + int mFormat; + std::vector mMaster; + + void blank(); + + void load (ESMReader &esm); + void save (ESMWriter &esm); + }; +#pragma pack(pop) + +} + +#endif \ No newline at end of file diff --git a/components/esm/loadweap.cpp b/components/esm/loadweap.cpp index 18d37e56c5..2537123969 100644 --- a/components/esm/loadweap.cpp +++ b/components/esm/loadweap.cpp @@ -25,4 +25,24 @@ void Weapon::save(ESMWriter &esm) esm.writeHNOCString("ENAM", mEnchant); } + void Weapon::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mType = 0; + mData.mHealth = 0; + mData.mSpeed = 0; + mData.mReach = 0; + mData.mEnchant = 0; + mData.mChop[0] = mData.mChop[1] = 0; + mData.mSlash[0] = mData.mSlash[1] = 0; + mData.mThrust[0] = mData.mThrust[1] = 0; + mData.mFlags = 0; + + mName.clear(); + mModel.clear(); + mIcon.clear(); + mEnchant.clear(); + mScript.clear(); + } } diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index e482d3b109..b62179ccb1 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -48,7 +48,7 @@ struct Weapon short mType; short mHealth; float mSpeed, mReach; - short mEnchant; // Enchantment points + short mEnchant; // Enchantment points. The real value is mEnchant/10.f unsigned char mChop[2], mSlash[2], mThrust[2]; // Min and max int mFlags; }; // 32 bytes @@ -60,6 +60,9 @@ struct Weapon void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/records.hpp b/components/esm/records.hpp index 0662c797c1..7a0452eb3f 100644 --- a/components/esm/records.hpp +++ b/components/esm/records.hpp @@ -26,7 +26,9 @@ #include "loadland.hpp" #include "loadlevlist.hpp" #include "loadligh.hpp" -#include "loadlocks.hpp" +#include "loadlock.hpp" +#include "loadrepa.hpp" +#include "loadprob.hpp" #include "loadltex.hpp" #include "loadmgef.hpp" #include "loadmisc.hpp" diff --git a/components/esm/variant.cpp b/components/esm/variant.cpp index d25072e548..a7859d1283 100644 --- a/components/esm/variant.cpp +++ b/components/esm/variant.cpp @@ -186,7 +186,7 @@ void ESM::Variant::write (std::ostream& stream) const case VT_String: - stream << "variant string: \"" << mData->getString() << "\2"; + stream << "variant string: \"" << mData->getString() << "\""; break; } } diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index 3165341246..9f2e470b26 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include @@ -293,8 +293,8 @@ void DataFilesModel::addFiles(const QString &path) fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); + std::vector mlist = fileReader.getMasters(); - ESM::ESMReader::MasterList mlist = fileReader.getMasters(); QStringList masters; for (unsigned int i = 0; i < mlist.size(); ++i) { diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 18e5f81c1f..52f892b420 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -23,7 +23,6 @@ namespace Interpreter{ } std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context){ - unsigned int start = 0; std::ostringstream retval; for(unsigned int i = 0; i < text.length(); i++){ diff --git a/components/interpreter/runtime.cpp b/components/interpreter/runtime.cpp index dcf17d2558..8814ca7ffc 100644 --- a/components/interpreter/runtime.cpp +++ b/components/interpreter/runtime.cpp @@ -34,17 +34,20 @@ namespace Interpreter std::string Runtime::getStringLiteral (int index) const { - assert (index>=0 && index (mCode[3])); + assert (index>=0 && static_cast (mCode[3])>0); const char *literalBlock = reinterpret_cast (mCode + 4 + mCode[0] + mCode[1] + mCode[2]); + int offset = 0; + for (; index; --index) { - literalBlock += std::strlen (literalBlock) + 1; + offset += std::strlen (literalBlock+offset) + 1; + assert (offset/4 (mCode[3])); } - return literalBlock; + return literalBlock+offset; } void Runtime::configure (const Interpreter::Type_Code *code, int codeSize, Context& context) diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp new file mode 100644 index 0000000000..19a0688b26 --- /dev/null +++ b/components/misc/utf8stream.hpp @@ -0,0 +1,116 @@ +#ifndef MISC_UTF8ITER_HPP +#define MISC_UTF8ITER_HPP + +#include + +class Utf8Stream +{ +public: + + typedef uint32_t UnicodeChar; + typedef unsigned char const * Point; + + //static const unicode_char sBadChar = 0xFFFFFFFF; gcc can't handle this + static UnicodeChar sBadChar () { return UnicodeChar (0xFFFFFFFF); } + + Utf8Stream (Point begin, Point end) : + cur (begin), nxt (begin), end (end) + { + } + + Utf8Stream (std::pair range) : + cur (range.first), nxt (range.first), end (range.second) + { + } + + bool eof () const + { + return cur == end; + } + + Point current () const + { + return cur; + } + + UnicodeChar peek () + { + if (cur == nxt) + next (); + return val; + } + + UnicodeChar consume () + { + if (cur == nxt) + next (); + cur = nxt; + return val; + } + + static std::pair decode (Point cur, Point end) + { + if ((*cur & 0x80) == 0) + { + UnicodeChar chr = *cur++; + + return std::make_pair (chr, cur); + } + + int octets; + UnicodeChar chr; + + boost::tie (octets, chr) = octet_count (*cur++); + + if (octets > 5) + return std::make_pair (sBadChar(), cur); + + Point eoc = cur + octets; + + if (eoc > end) + return std::make_pair (sBadChar(), cur); + + while (cur != eoc) + { + if ((*cur & 0xC0) != 0x80) // check continuation mark + return std::make_pair (sBadChar(), cur);; + + chr = (chr << 6) | UnicodeChar ((*cur++) & 0x3F); + } + + return std::make_pair (chr, cur); + } + +private: + + static std::pair octet_count (unsigned char octet) + { + int octets; + + unsigned char mark = 0xC0; + unsigned char mask = 0xE0; + + for (octets = 1; octets <= 5; ++octets) + { + if ((octet & mask) == mark) + break; + + mark = (mark >> 1) | 0x80; + mask = (mask >> 1) | 0x80; + } + + return std::make_pair (octets, octet & ~mask); + } + + void next () + { + boost::tie (val, nxt) = decode (nxt, end); + } + + Point cur; + Point nxt; + Point end; + UnicodeChar val; +}; + +#endif diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index 36c9a82ace..6acb8ff201 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -66,12 +66,14 @@ typedef Named NiSequenceStreamHelper; class NiParticleGrowFade : public Controlled { public: + float growTime; + float fadeTime; + void read(NIFStream *nif) { Controlled::read(nif); - - // Two floats. - nif->skip(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..011e0e4452 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 { @@ -217,5 +303,28 @@ public: } }; +class NiFlipController : public Controller +{ +public: + int mTexSlot; + float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources + NiSourceTextureList mSources; + + void read(NIFStream *nif) + { + Controller::read(nif); + mTexSlot = nif->getUInt(); + /*unknown=*/nif->getUInt();/*0?*/ + mDelta = nif->getFloat(); + mSources.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + mSources.post(nif); + } +}; + } // Namespace #endif 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..cb7c2feb07 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -207,9 +207,9 @@ struct RecordFactoryEntry { static const RecordFactoryEntry recordFactories [] = { { "NiNode", &construct , RC_NiNode }, - { "AvoidNode", &construct , RC_NiNode }, - { "NiBSParticleNode", &construct , RC_NiNode }, - { "NiBSAnimationNode", &construct , RC_NiNode }, + { "AvoidNode", &construct , RC_AvoidNode }, + { "NiBSParticleNode", &construct , RC_NiBSParticleNode }, + { "NiBSAnimationNode", &construct , RC_NiBSAnimationNode }, { "NiBillboardNode", &construct , RC_NiNode }, { "NiTriShape", &construct , RC_NiTriShape }, { "NiRotatingParticles", &construct , RC_NiRotatingParticles }, @@ -235,6 +235,7 @@ static const RecordFactoryEntry recordFactories [] = { { "NiMaterialColorController", &construct , RC_NiMaterialColorController }, { "NiBSPArrayController", &construct , RC_NiBSPArrayController }, { "NiParticleSystemController", &construct , RC_NiParticleSystemController }, + { "NiFlipController", &construct , RC_NiFlipController }, { "NiAmbientLight", &construct , RC_NiLight }, { "NiDirectionalLight", &construct , RC_NiLight }, { "NiTextureEffect", &construct , RC_NiTextureEffect }, @@ -345,14 +346,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 +385,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..917bc8add3 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; }; @@ -120,13 +128,17 @@ struct NiNode : Node NodeList children; NodeList effects; - /* Known NiNode flags: - 0x01 hidden - 0x02 use mesh for collision - 0x04 use bounding box for collision (?) - 0x08 unknown, but common - 0x20, 0x40, 0x80 unknown - */ + enum Flags { + Flag_Hidden = 0x0001, + Flag_MeshCollision = 0x0002, + Flag_BBoxCollision = 0x0004 + }; + enum BSAnimFlags { + AnimFlag_AutoPlay = 0x0020 + }; + enum BSParticleFlags { + ParticleFlag_AutoPlay = 0x0020 + }; void read(NIFStream *nif) { 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..87e342dca5 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -36,9 +36,11 @@ enum RecordType { RC_MISSING = 0, RC_NiNode, + RC_AvoidNode, RC_NiTriShape, RC_NiRotatingParticles, RC_NiAutoNormalParticles, + RC_NiBSParticleNode, RC_NiCamera, RC_NiTexturingProperty, RC_NiMaterialProperty, @@ -59,6 +61,8 @@ enum RecordType RC_NiMaterialColorController, RC_NiBSPArrayController, RC_NiParticleSystemController, + RC_NiFlipController, + RC_NiBSAnimationNode, RC_NiLight, RC_NiTextureEffect, RC_NiVertWeightsExtraData, diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index c5bafea124..2ecde7f60e 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -163,6 +163,7 @@ typedef RecordPtrT NiAutoNormalParticlesDataPtr; typedef RecordListT NodeList; typedef RecordListT PropertyList; +typedef RecordListT NiSourceTextureList; } // Namespace #endif diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index af1243346a..a1e2cd8d18 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -91,35 +91,34 @@ 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; } - bool hasCollisionNode = hasRootCollisionNode(node); + cShape->mHasCollisionNode = hasRootCollisionNode(node); //do a first pass - handleNode(mesh1, node,0,hasCollisionNode,false,false); + handleNode(mesh1, node,0,false,false,false); if(mBoundingBox != NULL) { cShape->mCollisionShape = mBoundingBox; delete mesh1; } - else if (mHasShape && !cShape->mIgnore && cShape->mCollide) + else if (mHasShape && cShape->mCollide) { cShape->mCollisionShape = new TriangleMeshShape(mesh1,true); } @@ -136,14 +135,14 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) btTriangleMesh* mesh2 = new btTriangleMesh(); - handleNode(mesh2, node,0,hasCollisionNode,true,true); + handleNode(mesh2, node,0,true,true,false); if(mBoundingBox != NULL) { cShape->mRaycastingShape = mBoundingBox; delete mesh2; } - else if (mHasShape && !cShape->mIgnore) + else if (mHasShape) { cShape->mRaycastingShape = new TriangleMeshShape(mesh2,true); } @@ -174,8 +173,8 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) } void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *node, int flags, - bool hasCollisionNode, bool isCollisionNode, - bool raycasting) + bool isCollisionNode, + bool raycasting, bool isMarker) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. @@ -186,14 +185,16 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * else isCollisionNode = isCollisionNode && (node->recType != Nif::RC_RootCollisionNode); - // Marker objects: no collision + // Don't collide with AvoidNode shapes + if(node->recType == Nif::RC_AvoidNode) + flags |= 0x800; + + // Marker objects /// \todo don't do this in the editor std::string nodename = node->name; Misc::StringUtils::toLower(nodename); if (nodename.find("marker") != std::string::npos) - { - return; - } + isMarker = true; // Check for extra data Nif::Extra const *e = node; @@ -219,11 +220,12 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * // Marker objects. These are only visible in the // editor. Until and unless we add an editor component to // the engine, just skip this entire node. - return; + isMarker = true; } } - if (isCollisionNode || (!hasCollisionNode && !raycasting)) + if ( (isCollisionNode || (!cShape->mHasCollisionNode && !raycasting)) + && (!isMarker || (cShape->mHasCollisionNode && !raycasting))) { if(node->hasBounds) { @@ -246,7 +248,7 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(mesh, list[i].getPtr(), flags, hasCollisionNode, isCollisionNode, raycasting); + handleNode(mesh, list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker); } } } @@ -257,9 +259,9 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif:: assert(shape != NULL); // Interpret flags - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision + bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0; + bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0; + bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0; // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 9cb5a3e86e..7958f3b7cb 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -82,7 +82,7 @@ private: /** *Parse a node. */ - void handleNode(btTriangleMesh* mesh, Nif::Node const *node, int flags, bool hasCollisionNode, bool isCollisionNode, bool raycasting); + void handleNode(btTriangleMesh* mesh, Nif::Node const *node, int flags, bool isCollisionNode, bool raycasting, bool isMarker); /** *Helper function diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp new file mode 100644 index 0000000000..55f064c555 --- /dev/null +++ b/components/nifogre/material.cpp @@ -0,0 +1,394 @@ +#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))); + } + + 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..ca92f62d49 --- /dev/null +++ b/components/nifogre/mesh.cpp @@ -0,0 +1,387 @@ +#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(!shape->controller.empty()) + { + Nif::ControllerPtr ctrl = shape->controller; + do { + if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; + vertShadowBuffer = true; + break; + } + } while(!(ctrl=ctrl->next).empty()); + } + + 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); + + // 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::Matrix4 mat; + mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), + Ogre::Quaternion(data->bones[b].trafo.rotation)); + mat = bones[b]->getWorldTransform() * 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(); + if (numUVs) + { + size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); + + for(size_t i = 0; i < numUVs; i++) + decl->addElement(nextBuf, elemSize*i, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i); + + vbuf = hwBufMgr->createVertexBuffer(decl->getVertexSize(nextBuf), srcVerts.size(), + Ogre::HardwareBuffer::HBU_STATIC); + + std::vector allUVs; + allUVs.reserve(srcVerts.size()*numUVs); + for (size_t vert = 0; vertuvlist[i][vert]); + + vbuf->writeData(0, elemSize*srcVerts.size()*numUVs, &allUVs[0], true); + + 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) + { + Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName); + + 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 67919f7044..f9344caa4d 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -25,1123 +25,741 @@ #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; - -namespace std -{ - -// TODO: Do something useful -ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) -{ return o; } - -} +#include "skeleton.hpp" +#include "material.hpp" +#include "mesh.hpp" 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; + if(mDeltaInput) + mDeltaCount = mPhase; + } - MaxMinFinder() + virtual Ogre::Real calculate(Ogre::Real value) + { + if(mDeltaInput) { - min = std::numeric_limits::infinity(); - max = -min; + mDeltaCount += value*mFrequency; + if(mDeltaCount < mStartTime) + mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount, + mStopTime - mStartTime); + 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; + + bool calculate(Ogre::Real time) const { - 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::Quaternion getRotation(float time) const + { return Ogre::Quaternion(); } + + virtual Ogre::Vector3 getTranslation(float time) const + { return Ogre::Vector3(0.0f); } + + virtual Ogre::Vector3 getScale(float time) const + { return Ogre::Vector3(1.0f); } + + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.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; - } - - 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) + static float interpKey(const Nif::FloatKeyList::VecType &keys, float time) { - if(lastkeyiter->second.find(':') == currentgroup.length() && - lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0) - break; - } + if(time <= keys.front().mTime) + return keys.front().mValue; - 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") - { - if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) + Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) { - 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(); + 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.back().mValue; } - } - Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - return skelMgr.create(name, group, true, &sLoaders[name]); -} + static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time) + { + if(time <= keys.front().mTime) + return keys.front().mValue; + Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::Vector3KeyList::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.back().mValue; + } + + static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time) + { + if(time <= keys.front().mTime) + return keys.front().mValue; + + Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::QuaternionKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue); + } + return keys.back().mValue; + } + + public: + Value(Ogre::Node *target, const Nif::NiKeyframeData *data) + : NodeTargetValue(target) + , mRotations(data->mRotations) + , mTranslations(data->mTranslations) + , mScales(data->mScales) + { } + + virtual Ogre::Quaternion getRotation(float time) const + { + if(mRotations.mKeys.size() > 0) + return interpKey(mRotations.mKeys, time); + return mNode->getOrientation(); + } + + virtual Ogre::Vector3 getTranslation(float time) const + { + if(mTranslations.mKeys.size() > 0) + return interpKey(mTranslations.mKeys, time); + return mNode->getPosition(); + } + + virtual Ogre::Vector3 getScale(float time) const + { + if(mScales.mKeys.size() > 0) + return Ogre::Vector3(interpKey(mScales.mKeys, time)); + return mNode->getScale(); + } + + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.0f; + } + + virtual void setValue(Ogre::Real time) + { + if(mRotations.mKeys.size() > 0) + mNode->setOrientation(interpKey(mRotations.mKeys, time)); + if(mTranslations.mKeys.size() > 0) + mNode->setPosition(interpKey(mTranslations.mKeys, time)); + if(mScales.mKeys.size() > 0) + mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time))); + } + }; + + typedef DefaultFunction Function; }; -NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders; - -// Conversion of blend / test mode from NIF -static const char *getBlendFactor(int mode) +class UVController { - 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) + class Value : public Ogre::ControllerValue { - const Nif::NiSourceTexture *st = texprop->textures[0].texture.getPtr(); - if(st->external) + 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) { - /* 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\\"; + if(keys.mKeys.size() == 0) + return def; - texName = st->filename; - Misc::StringUtils::toLower(texName); + if(time <= keys.mKeys.front().mTime) + return keys.mKeys.front().mValue; - 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) + Nif::FloatKeyList::VecType::const_iterator iter(keys.mKeys.begin()+1); + for(;iter != keys.mKeys.end();iter++) { - // since we know all (GOTY edition or less) textures end - // in .dds, we change the extension - texName.replace(pos, texName.length(), ".dds"); + if(iter->mTime < time) + continue; - // 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)) + 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, const 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()) { - texName = st->filename; - Misc::StringUtils::toLower(texName); - if(texName.compare(0, sizeof(path)-1, path) != 0) - texName = path + texName; + Ogre::Pass *pass = passes.getNext(); + Ogre::TextureUnitState *tex = pass->getTextureUnitState(0); + tex->setTextureScroll(uTrans, vTrans); + tex->setTextureScale(uScale, vScale); } } } - 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 ParticleSystemController { - 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::ParticleSystem *mParticleSys; + float mEmitStart; + float mEmitStop; + + public: + Value(Ogre::ParticleSystem *psys, const Nif::NiParticleSystemController *pctrl) + : mParticleSys(psys) + , mEmitStart(pctrl->startTime) + , mEmitStop(pctrl->stopTime) + { + } + + Ogre::Real getValue() const + { return 0.0f; } + + void setValue(Ogre::Real value) + { + mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop); + } + }; + + typedef DefaultFunction Function; +}; + +class GeomMorpherController +{ +public: + class Value : public Ogre::ControllerValue + { + private: + Ogre::SubEntity *mSubEntity; + std::vector mMorphs; + + public: + Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) + : mSubEntity(subent) + , mMorphs(data->mMorphs) + { } + + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.0f; + } + + virtual void setValue(Ogre::Real value) + { + // TODO: Implement + } + }; + + typedef DefaultFunction Function; +}; + + +/** Object creator for NIFs. This is the main class responsible for creating + * "live" Ogre objects (entities, particle systems, controllers, etc) from + * their NIF equivalents. + */ +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 createEntity(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + const Nif::Node *node, int flags, int animflags) { - 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) + 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::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&Nif::NiNode::Flag_Hidden)); + + objectlist.mEntities.push_back(entity); + if(objectlist.mSkelBase) { - // 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(), 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); + if(entity->hasSkeleton()) + entity->shareSkeletonInstanceWith(objectlist.mSkelBase); 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; - } + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity); } } - return false; + + 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((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); + + Ogre::SubEntity *subent = entity->getSubEntity(0); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + ctrl = ctrl->next; + } } - 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 createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) { - Ogre::Mesh *mesh = dynamic_cast(resource); - OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!"); + Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); + emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, + partctrl->velocity + partctrl->velocityRandom*0.5f); + emitter->setEmissionRate(partctrl->emitRate); + emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f, + partctrl->lifetime + partctrl->lifetimeRandom*0.5f); + emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); + emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); + emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); + emitter->setParameter("vertical_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalDir).valueDegrees())); + emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees())); + emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees())); + emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees())); - Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); - if(mShapeIndex >= nif->numRecords()) + Nif::ExtraPtr e = partctrl->extra; + while(!e.empty()) { - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - if(!skelMgr->getByName(mName).isNull()) - mesh->setSkeletonName(mName); - return; + if(e->recType == Nif::RC_NiParticleGrowFade) + { + const Nif::NiParticleGrowFade *gf = static_cast(e.getPtr()); + + Ogre::ParticleAffector *affector = partsys->addAffector("GrowFade"); + affector->setParameter("grow_time", Ogre::StringConverter::toString(gf->growTime)); + affector->setParameter("fade_time", Ogre::StringConverter::toString(gf->fadeTime)); + } + else if(e->recType == Nif::RC_NiGravity) + { + const Nif::NiGravity *gr = static_cast(e.getPtr()); + + Ogre::ParticleAffector *affector = partsys->addAffector("Gravity"); + affector->setParameter("force", Ogre::StringConverter::toString(gr->mForce)); + affector->setParameter("force_type", (gr->mType==0) ? "wind" : "point"); + affector->setParameter("direction", Ogre::StringConverter::toString(gr->mDirection)); + affector->setParameter("position", Ogre::StringConverter::toString(gr->mPosition)); + } + else if(e->recType == Nif::RC_NiParticleColorModifier) + { + const Nif::NiParticleColorModifier *cl = static_cast(e.getPtr()); + const Nif::NiColorData *clrdata = cl->data.getPtr(); + + Ogre::ParticleAffector *affector = partsys->addAffector("ColourInterpolator"); + size_t num_colors = std::min(6, clrdata->mKeyList.mKeys.size()); + for(size_t i = 0;i < num_colors;i++) + { + Ogre::ColourValue color; + color.r = clrdata->mKeyList.mKeys[i].mValue[0]; + color.g = clrdata->mKeyList.mKeys[i].mValue[1]; + color.b = clrdata->mKeyList.mKeys[i].mValue[2]; + color.a = clrdata->mKeyList.mKeys[i].mValue[3]; + affector->setParameter("colour"+Ogre::StringConverter::toString(i), + Ogre::StringConverter::toString(color)); + affector->setParameter("time"+Ogre::StringConverter::toString(i), + Ogre::StringConverter::toString(clrdata->mKeyList.mKeys[i].mTime)); + } + } + else if(e->recType == Nif::RC_NiParticleRotation) + { + // TODO: Implement (Ogre::RotationAffector?) + } + else + warn("Unhandled particle modifier "+e->recName); + e = e->extra; + } + } + + static void createParticleSystem(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + const Nif::Node *partnode, int flags, int partflags) + { + 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(); + + std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); + if(partnode->name.length() > 0) + fullname += "@type="+partnode->name; + Misc::StringUtils::toLower(fullname); + + Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem(); + + 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); + // TODO: There is probably a field or flag to specify this, as some + // particle effects have it and some don't. + partsys->setKeepParticlesInLocalSpace(true); + + Nif::ControllerPtr ctrl = partnode->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiParticleSystemController) + { + const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); + + createParticleEmitterAffectors(partsys, partctrl); + if(!partctrl->emitter.empty() && !partsys->isAttached()) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); + } + + Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl)); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + ctrl = ctrl->next; } - const Nif::Node *node = dynamic_cast(nif->getRecord(0)); - findTriShape(mesh, node, NULL, NULL, NULL, NULL, NULL, NULL); + if(!partsys->isAttached()) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); + } + + partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); + objectlist.mParticles.push_back(partsys); } - void createMeshes(const Nif::Node *node, MeshInfoList &meshes, int flags=0) + + static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags) { - // Do not create meshes for the collision shape (includes all children) + do { + 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((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + 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((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + } + ctrl = ctrl->next; + } while(!ctrl.empty()); + } + + + static void 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; + } + } + } + + + static void createObjects(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, const Nif::Node *node, + ObjectList &objectlist, int flags, int animflags, int partflags) + { + // 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; + if(node->recType == Nif::RC_NiBSAnimationNode) + animflags |= node->flags; + else if(node->recType == Nif::RC_NiBSParticleNode) + partflags |= node->flags; + else + 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_NiTextKeyExtraData) { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); + extractTextKeys(tk, objectlist.mTextKeys[trgtid]); + } + else 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. - flags |= 0x01; + flags |= 0x80000000; } } + e = e->extra; } - if(node->recType == Nif::RC_NiTriShape && !(flags&0x01)) // Not hidden + if(!node->controller.empty()) + createNodeControllers(name, node->controller, objectlist, animflags); + + if(node->recType == Nif::RC_NiCamera) { - const Nif::NiTriShape *shape = dynamic_cast(node); + /* Ignored */ + } - Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex); - if(shape->name.length() > 0) - fullname += "@shape="+shape->name; + if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) + { + createEntity(name, group, sceneMgr, objectlist, node, flags, animflags); + } - 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); - } - - meshes.push_back(MeshInfo(mesh->getName(), shape->name)); + if((node->recType == Nif::RC_NiAutoNormalParticles || + node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) + { + createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags); } const Nif::NiNode *ninode = dynamic_cast(node); @@ -1151,146 +769,151 @@ 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, animflags, partflags); } } } - 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); - + * other entities are attached to bones and not skinned. */ 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, int flags=0) + { + Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + if(nif->numRoots() < 1) + { + nif->warn("Found no root nodes in "+name+"."); + return; + } + + 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, flags, 0, 0); + } + + static void loadKf(Ogre::Skeleton *skel, const std::string &name, + TextKeyMap &textKeys, std::vector > &ctrls) + { + Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + if(nif->numRoots() < 1) + { + nif->warn("Found no root nodes in "+name+"."); + return; + } + + const Nif::Record *r = nif->getRoot(0); + assert(r != NULL); + + if(r->recType != Nif::RC_NiSequenceStreamHelper) + { + nif->warn("First root was not a NiSequenceStreamHelper, but a "+ + r->recName+"."); + return; + } + const Nif::NiSequenceStreamHelper *seq = static_cast(r); + + Nif::ExtraPtr extra = seq->extra; + if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) + { + nif->warn("First extra data was not a NiTextKeyExtraData, but a "+ + (extra.empty() ? std::string("nil") : extra->recName)+"."); + return; + } + + extractTextKeys(static_cast(extra.getPtr()), textKeys); + + extra = extra->extra; + Nif::ControllerPtr ctrl = seq->controller; + for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) + { + if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) + { + nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName); + continue; + } + + const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + + if(key->data.empty()) + continue; + if(!skel->hasBone(strdata->string)) + continue; + + Ogre::Bone *trgtbone = skel->getBone(strdata->string); + Ogre::ControllerValueRealPtr srcval; + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false)); + + ctrls.push_back(Ogre::Controller(srcval, dstval, func)); } - meshes.push_back(MeshInfo(mesh->getName(), node->name)); } }; -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 +922,62 @@ 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::SceneNode *parentNode, 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(parentNode->getCreator(), objectlist, name, group, 0xC0000000); - Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); - if(nif->numRecords() < 1) - { - nif->warn("Found no NIF records in "+name+"."); - return skel; - } + if(objectlist.mSkelBase) + parentNode->attachObject(objectlist.mSkelBase); - // 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) +void Loader::createKfControllers(Ogre::Entity *skelBase, + const std::string &name, + TextKeyMap &textKeys, + std::vector > &ctrls) { - 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(); + NIFObjectLoader::loadKf(skelBase->getSkeleton(), name, textKeys, ctrls); } -*/ - -} // nsmaepace NifOgre +} // namespace NifOgre diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index b8b2e3c007..45f3cbcd8a 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,53 +39,63 @@ 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) + std::map mTextKeys; + + 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 EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, - Ogre::SceneNode *parentNode, - std::string name, - const std::string &group="General"); + static ObjectList createObjects(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneNode *parentNode, + std::string name, + const std::string &group="General"); - static EntityList createEntities(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 Ogre::SkeletonPtr getSkeleton(std::string name, const std::string &group="General"); + static ObjectList createObjectBase(Ogre::SceneNode *parentNode, + std::string name, + const std::string &group="General"); + + static void createKfControllers(Ogre::Entity *skelBase, + const std::string &name, + TextKeyMap &textKeys, + std::vector > &ctrls); }; -} - -namespace std +// FIXME: Should be with other general Ogre extensions. +template +class NodeTargetValue : public Ogre::ControllerValue { +protected: + Ogre::Node *mNode; -// These operators allow extra data types to be stored in an Ogre::Any -// object, which can then be stored in user object bindings on the nodes +public: + NodeTargetValue(Ogre::Node *target) : mNode(target) + { } -ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&); + virtual Ogre::Quaternion getRotation(T value) const = 0; + virtual Ogre::Vector3 getTranslation(T value) const = 0; + virtual Ogre::Vector3 getScale(T value) const = 0; + + 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..04bffdeab4 --- /dev/null +++ b/components/nifogre/skeleton.cpp @@ -0,0 +1,153 @@ +#include "skeleton.hpp" + +#include +#include +#include +#include + +#include +#include + +namespace NifOgre +{ + +void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, 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_NiBSAnimationNode || /* Handled in the object loader */ + node->recType == Nif::RC_NiBSParticleNode || + 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_NiParticleSystemController || + ctrl->recType == Nif::RC_NiVisController || + ctrl->recType == Nif::RC_NiUVController || + ctrl->recType == Nif::RC_NiKeyframeController || + ctrl->recType == Nif::RC_NiGeomMorpherController + )) + warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); + ctrl = ctrl->next; + } + + 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(), 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)); + + try { + buildBones(skel, node); + } + catch(std::exception &e) { + std::cerr<< "Exception while loading "<getName() <boneTrafo) + return true; + + if(!node->controller.empty() || node->name == "AttachLight") + return true; + + 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()) + { + if(needSkeleton(children[i].getPtr())) + return true; + } + } + return false; + } + if(node->recType == Nif::RC_NiTriShape) + return false; + + return true; +} + +Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) +{ + bool forceskel = false; + std::string::size_type extpos = name.rfind('.'); + if(extpos != std::string::npos && name.compare(extpos, name.size()-extpos, ".nif") == 0) + { + Ogre::ResourceGroupManager &resMgr = Ogre::ResourceGroupManager::getSingleton(); + forceskel = resMgr.resourceExistsInAnyGroup(name.substr(0, extpos)+".kf"); + } + + if(forceskel || needSkeleton(node)) + { + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + return skelMgr.create(name, group, true, &sLoaders[name]); + } + + return Ogre::SkeletonPtr(); +} + +// 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..9ec3a0c825 --- /dev/null +++ b/components/nifogre/skeleton.hpp @@ -0,0 +1,62 @@ +#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(); + } + + void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL); + + static bool needSkeleton(const Nif::Node *node); + + // 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..390a2d721d 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) @@ -51,12 +52,13 @@ Paul McElroy (Greendogo) Pieter van der Kloet (pvdk) Radu-Marius Popovici (rpopovici) Roman Melnik (Kromgart) +Roman Proskuryakov (humbug) Sandy Carter (bwrsandman) Sebastian Wick (swick) Sergey Shambir Sylvain Thesnieres (Garvek) Tom Mason (wheybags) - +Torben Leif Carrington (TorbenC) Packagers: Alexander Olofsson (Ace) - Windows diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp index 841a41cd39..29a4768aa0 100644 --- a/extern/oics/tinyxml.cpp +++ b/extern/oics/tinyxml.cpp @@ -706,9 +706,9 @@ void TiXmlElement::SetDoubleAttribute( const char * name, double val ) { char buf[256]; #if defined(TIXML_SNPRINTF) - TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); #else - sprintf( buf, "%f", val ); + sprintf( buf, "%f", val ); #endif SetAttribute( name, buf ); } @@ -1266,9 +1266,9 @@ void TiXmlAttribute::SetDoubleValue( double _value ) { char buf [256]; #if defined(TIXML_SNPRINTF) - TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + TIXML_SNPRINTF( buf, sizeof(buf), "%f", _value); #else - sprintf (buf, "%lf", _value); + sprintf (buf, "%f", _value); #endif SetValue (buf); } diff --git a/extern/shiny/CMakeLists.txt b/extern/shiny/CMakeLists.txt index 6eadcc1676..daf2a9df8d 100644 --- a/extern/shiny/CMakeLists.txt +++ b/extern/shiny/CMakeLists.txt @@ -9,8 +9,6 @@ set(SHINY_LIBRARY "shiny") set(SHINY_OGREPLATFORM_LIBRARY "shiny.OgrePlatform") # Sources -file(GLOB SOURCE_FILES Main/*.cpp ) - set(SOURCE_FILES Main/Factory.cpp Main/MaterialInstance.cpp @@ -24,26 +22,6 @@ set(SOURCE_FILES Main/ShaderSet.cpp ) -if (DEFINED SHINY_USE_WAVE_SYSTEM_INSTALL) - # use system install -else() - list(APPEND SOURCE_FILES - Preprocessor/aq.cpp - Preprocessor/cpp_re.cpp - Preprocessor/instantiate_cpp_literalgrs.cpp - Preprocessor/instantiate_cpp_exprgrammar.cpp - Preprocessor/instantiate_cpp_grammar.cpp - Preprocessor/instantiate_defined_grammar.cpp - Preprocessor/instantiate_predef_macros.cpp - Preprocessor/instantiate_re2c_lexer.cpp - Preprocessor/instantiate_re2c_lexer_str.cpp - Preprocessor/token_ids.cpp - ) - - # Don't use thread-safe boost::wave. Results in a huge speed-up for the preprocessor. - add_definitions(-DBOOST_WAVE_SUPPORT_THREADING=0) -endif() - set(OGRE_PLATFORM_SOURCE_FILES Platforms/Ogre/OgreGpuProgram.cpp Platforms/Ogre/OgreMaterial.cpp @@ -57,12 +35,20 @@ file(GLOB OGRE_PLATFORM_SOURCE_FILES Platforms/Ogre/*.cpp) add_library(${SHINY_LIBRARY} STATIC ${SOURCE_FILES}) +set(SHINY_LIBRARIES ${SHINY_LIBRARY}) + if (SHINY_BUILD_OGRE_PLATFORM) add_library(${SHINY_OGREPLATFORM_LIBRARY} STATIC ${OGRE_PLATFORM_SOURCE_FILES}) + set(SHINY_LIBRARIES ${SHINY_LIBRARIES} ${SHINY_OGREPLATFORM_LIBRARY}) endif() +set(SHINY_LIBRARY ${SHINY_LIBRARY} PARENT_SCOPE) + +if (DEFINED SHINY_BUILD_MATERIAL_EDITOR) + add_subdirectory(Editor) + + set(SHINY_BUILD_EDITOR_FLAG ${SHINY_BUILD_EDITOR_FLAG} PARENT_SCOPE) +endif() link_directories(${CMAKE_CURRENT_BINARY_DIR}) - -set(SHINY_LIBRARY ${SHINY_LIBRARY} PARENT_SCOPE) -set(SHINY_OGREPLATFORM_LIBRARY ${SHINY_OGREPLATFORM_LIBRARY} PARENT_SCOPE) +set(SHINY_LIBRARIES ${SHINY_LIBRARIES} PARENT_SCOPE) diff --git a/extern/shiny/Docs/Configurations.dox b/extern/shiny/Docs/Configurations.dox index affd914237..570e81d4af 100644 --- a/extern/shiny/Docs/Configurations.dox +++ b/extern/shiny/Docs/Configurations.dox @@ -21,7 +21,7 @@ } \endcode - \note You may also create configurations using sh::Factory::registerConfiguration. + \note You may also create configurations using sh::Factory::createConfiguration. The active Configuration is controlled by the active material scheme in Ogre. So, in order to use the configuration "reflection_targets" for your reflection renders, simply call \code 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/Docs/Materials.dox b/extern/shiny/Docs/Materials.dox index 2dae60560d..91e9be4b30 100644 --- a/extern/shiny/Docs/Materials.dox +++ b/extern/shiny/Docs/Materials.dox @@ -84,7 +84,8 @@ - There is no entry_point property because the entry point is always \a main. - Both profiles_cg and profiles_hlsl are a list of shader profiles. The first profile that is supported is automatically picked. GLSL does not have shader profiles. - Now, let's get into writing our shader! As you can guess from above, the filename should be 'example.shader' + Now, let's get into writing our shader! As you can guess from above, the filename should be 'example.shader'. + Make sure to also copy the 'core.h' file to the same location. It is included in shiny's source tree under 'Extra/'. \code #include "core.h" @@ -93,7 +94,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) SH_START_PROGRAM { diff --git a/extern/shiny/Editor/Actions.cpp b/extern/shiny/Editor/Actions.cpp new file mode 100644 index 0000000000..135e819878 --- /dev/null +++ b/extern/shiny/Editor/Actions.cpp @@ -0,0 +1,195 @@ +#include "Actions.hpp" + +#include "../Main/Factory.hpp" + +namespace sh +{ + + void ActionDeleteMaterial::execute() + { + sh::Factory::getInstance().destroyMaterialInstance(mName); + } + + void ActionCloneMaterial::execute() + { + sh::MaterialInstance* sourceMaterial = sh::Factory::getInstance().getMaterialInstance(mSourceName); + std::string sourceMaterialParent = static_cast(sourceMaterial->getParent())->getName(); + sh::MaterialInstance* material = sh::Factory::getInstance().createMaterialInstance( + mDestName, sourceMaterialParent); + sourceMaterial->copyAll(material, sourceMaterial, false); + + material->setSourceFile(sourceMaterial->getSourceFile()); + } + + void ActionSaveAll::execute() + { + sh::Factory::getInstance().saveAll(); + } + + void ActionChangeGlobalSetting::execute() + { + sh::Factory::getInstance().setGlobalSetting(mName, mNewValue); + } + + void ActionCreateConfiguration::execute() + { + sh::Configuration newConfiguration; + sh::Factory::getInstance().createConfiguration(mName); + } + + void ActionDeleteConfiguration::execute() + { + sh::Factory::getInstance().destroyConfiguration(mName); + } + + void ActionChangeConfiguration::execute() + { + sh::Configuration* c = sh::Factory::getInstance().getConfiguration(mName); + c->setProperty(mKey, sh::makeProperty(new sh::StringValue(mValue))); + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteConfigurationProperty::execute() + { + sh::Configuration* c = sh::Factory::getInstance().getConfiguration(mName); + c->deleteProperty(mKey); + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionSetMaterialProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + m->setProperty(mKey, sh::makeProperty(mValue)); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteMaterialProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + m->deleteProperty(mKey); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionCreatePass::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + m->createPass(); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeletePass::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + m->deletePass(mPassIndex); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionSetPassProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).setProperty (mKey, sh::makeProperty(mValue)); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeletePassProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).deleteProperty(mKey); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionSetShaderProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).mShaderProperties.setProperty (mKey, sh::makeProperty(mValue)); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteShaderProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).mShaderProperties.deleteProperty (mKey); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionSetTextureProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + m->getPasses()->at(mPassIndex).mTexUnits.at(mTextureIndex).setProperty(mKey, sh::makeProperty(mValue)); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteTextureProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + m->getPasses()->at(mPassIndex).mTexUnits.at(mTextureIndex).deleteProperty(mKey); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionCreateTextureUnit::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).createTextureUnit(mTexUnitName); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteTextureUnit::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + + m->getPasses()->at(mPassIndex).mTexUnits.erase(m->getPasses()->at(mPassIndex).mTexUnits.begin() + mTextureIndex); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionMoveTextureUnit::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + if (!mMoveUp) + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex+1); + + std::vector textures = m->getPasses()->at(mPassIndex).mTexUnits; + if (mMoveUp) + std::swap(textures[mTextureIndex-1], textures[mTextureIndex]); + else + std::swap(textures[mTextureIndex+1], textures[mTextureIndex]); + m->getPasses()->at(mPassIndex).mTexUnits = textures; + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionChangeTextureUnitName::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + + m->getPasses()->at(mPassIndex).mTexUnits[mTextureIndex].setName(mTexUnitName); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } +} diff --git a/extern/shiny/Editor/Actions.hpp b/extern/shiny/Editor/Actions.hpp new file mode 100644 index 0000000000..e5cb6df6a5 --- /dev/null +++ b/extern/shiny/Editor/Actions.hpp @@ -0,0 +1,307 @@ +#ifndef SH_ACTIONS_H +#define SH_ACTIONS_H + +#include + +namespace sh +{ + + class Action + { + public: + virtual void execute() = 0; + virtual ~Action(); + }; + + class ActionDeleteMaterial : public Action + { + public: + ActionDeleteMaterial(const std::string& name) + : mName(name) {} + + virtual void execute(); + private: + std::string mName; + }; + + class ActionCloneMaterial : public Action + { + public: + ActionCloneMaterial(const std::string& sourceName, const std::string& destName) + : mSourceName(sourceName), mDestName(destName) {} + + virtual void execute(); + private: + std::string mSourceName; + std::string mDestName; + }; + + class ActionSaveAll : public Action + { + public: + virtual void execute(); + }; + + class ActionChangeGlobalSetting : public Action + { + public: + ActionChangeGlobalSetting(const std::string& name, const std::string& newValue) + : mName(name), mNewValue(newValue) {} + + virtual void execute(); + private: + std::string mName; + std::string mNewValue; + }; + + // configuration + + class ActionCreateConfiguration : public Action + { + public: + ActionCreateConfiguration(const std::string& name) + : mName(name) {} + + virtual void execute(); + private: + std::string mName; + + }; + + class ActionDeleteConfiguration : public Action + { + public: + ActionDeleteConfiguration(const std::string& name) + : mName(name) {} + + virtual void execute(); + private: + std::string mName; + + }; + + class ActionChangeConfiguration : public Action + { + public: + ActionChangeConfiguration (const std::string& name, const std::string& key, const std::string& value) + : mName(name), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + std::string mKey; + std::string mValue; + }; + + class ActionDeleteConfigurationProperty : public Action + { + public: + ActionDeleteConfigurationProperty (const std::string& name, const std::string& key) + : mName(name), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + std::string mKey; + }; + + // material + + class ActionSetMaterialProperty : public Action + { + public: + ActionSetMaterialProperty (const std::string& name, const std::string& key, const std::string& value) + : mName(name), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + std::string mKey; + std::string mValue; + }; + + class ActionDeleteMaterialProperty : public Action + { + public: + ActionDeleteMaterialProperty (const std::string& name, const std::string& key) + : mName(name), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + std::string mKey; + }; + + // pass + + class ActionCreatePass : public Action + { + public: + ActionCreatePass (const std::string& name) + : mName(name) {} + + virtual void execute(); + private: + std::string mName; + }; + + class ActionDeletePass : public Action + { + public: + ActionDeletePass (const std::string& name, int passIndex) + : mName(name), mPassIndex(passIndex) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + }; + + class ActionSetPassProperty : public Action + { + public: + ActionSetPassProperty (const std::string& name, int passIndex, const std::string& key, const std::string& value) + : mName(name), mPassIndex(passIndex), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + std::string mKey; + std::string mValue; + }; + + class ActionDeletePassProperty : public Action + { + public: + ActionDeletePassProperty (const std::string& name, int passIndex, const std::string& key) + : mName(name), mPassIndex(passIndex), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + std::string mKey; + }; + + // shader + + class ActionSetShaderProperty : public Action + { + public: + ActionSetShaderProperty (const std::string& name, int passIndex, const std::string& key, const std::string& value) + : mName(name), mPassIndex(passIndex), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + std::string mKey; + std::string mValue; + }; + + class ActionDeleteShaderProperty : public Action + { + public: + ActionDeleteShaderProperty (const std::string& name, int passIndex, const std::string& key) + : mName(name), mPassIndex(passIndex), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + std::string mKey; + }; + + // texture unit + + class ActionChangeTextureUnitName : public Action + { + public: + ActionChangeTextureUnitName (const std::string& name, int passIndex, int textureIndex, const std::string& texUnitName) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mTexUnitName(texUnitName) {} + + virtual void execute(); + + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + std::string mTexUnitName; + }; + + class ActionCreateTextureUnit : public Action + { + public: + ActionCreateTextureUnit (const std::string& name, int passIndex, const std::string& texUnitName) + : mName(name), mPassIndex(passIndex), mTexUnitName(texUnitName) {} + + virtual void execute(); + + private: + std::string mName; + int mPassIndex; + std::string mTexUnitName; + }; + + class ActionDeleteTextureUnit : public Action + { + public: + ActionDeleteTextureUnit (const std::string& name, int passIndex, int textureIndex) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex) {} + + virtual void execute(); + + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + }; + + class ActionMoveTextureUnit : public Action + { + public: + ActionMoveTextureUnit (const std::string& name, int passIndex, int textureIndex, bool moveUp) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mMoveUp(moveUp) {} + + virtual void execute(); + + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + bool mMoveUp; + }; + + class ActionSetTextureProperty : public Action + { + public: + ActionSetTextureProperty (const std::string& name, int passIndex, int textureIndex, const std::string& key, const std::string& value) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + std::string mKey; + std::string mValue; + }; + + class ActionDeleteTextureProperty : public Action + { + public: + ActionDeleteTextureProperty (const std::string& name, int passIndex, int textureIndex, const std::string& key) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + std::string mKey; + }; + +} + +#endif diff --git a/extern/shiny/Editor/AddPropertyDialog.cpp b/extern/shiny/Editor/AddPropertyDialog.cpp new file mode 100644 index 0000000000..71b47feb19 --- /dev/null +++ b/extern/shiny/Editor/AddPropertyDialog.cpp @@ -0,0 +1,31 @@ +#include "AddPropertyDialog.hpp" +#include "ui_addpropertydialog.h" + +AddPropertyDialog::AddPropertyDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::AddPropertyDialog) + , mType(0) +{ + ui->setupUi(this); + + connect(ui->buttonBox, SIGNAL(accepted()), + this, SLOT(accepted())); + connect(ui->buttonBox, SIGNAL(rejected()), + this, SLOT(rejected())); +} + +void AddPropertyDialog::accepted() +{ + mName = ui->lineEdit->text(); + mType = ui->comboBox->currentIndex(); +} + +void AddPropertyDialog::rejected() +{ + mName = ""; +} + +AddPropertyDialog::~AddPropertyDialog() +{ + delete ui; +} diff --git a/extern/shiny/Editor/AddPropertyDialog.h b/extern/shiny/Editor/AddPropertyDialog.h new file mode 100644 index 0000000000..c1d2c960b5 --- /dev/null +++ b/extern/shiny/Editor/AddPropertyDialog.h @@ -0,0 +1,22 @@ +#ifndef ADDPROPERTYDIALOG_H +#define ADDPROPERTYDIALOG_H + +#include + +namespace Ui { +class AddPropertyDialog; +} + +class AddPropertyDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddPropertyDialog(QWidget *parent = 0); + ~AddPropertyDialog(); + +private: + Ui::AddPropertyDialog *ui; +}; + +#endif // ADDPROPERTYDIALOG_H diff --git a/extern/shiny/Editor/AddPropertyDialog.hpp b/extern/shiny/Editor/AddPropertyDialog.hpp new file mode 100644 index 0000000000..b4e19b0870 --- /dev/null +++ b/extern/shiny/Editor/AddPropertyDialog.hpp @@ -0,0 +1,29 @@ +#ifndef ADDPROPERTYDIALOG_H +#define ADDPROPERTYDIALOG_H + +#include + +namespace Ui { +class AddPropertyDialog; +} + +class AddPropertyDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddPropertyDialog(QWidget *parent = 0); + ~AddPropertyDialog(); + + int mType; + QString mName; + +public slots: + void accepted(); + void rejected(); + +private: + Ui::AddPropertyDialog *ui; +}; + +#endif // ADDPROPERTYDIALOG_H diff --git a/extern/shiny/Editor/CMakeLists.txt b/extern/shiny/Editor/CMakeLists.txt new file mode 100644 index 0000000000..eead159f08 --- /dev/null +++ b/extern/shiny/Editor/CMakeLists.txt @@ -0,0 +1,61 @@ +set(SHINY_EDITOR_LIBRARY "shiny.Editor") + +find_package(Qt4) + +if (QT_FOUND) + + add_definitions(-DSHINY_BUILD_MATERIAL_EDITOR=1) + set (SHINY_BUILD_EDITOR_FLAG -DSHINY_BUILD_MATERIAL_EDITOR=1 PARENT_SCOPE) + + set(QT_USE_QTGUI 1) + + # Headers that must be preprocessed + set(SHINY_EDITOR_HEADER_MOC + MainWindow.hpp + NewMaterialDialog.hpp + AddPropertyDialog.hpp + PropertySortModel.hpp + ) + + set(SHINY_EDITOR_UI + mainwindow.ui + newmaterialdialog.ui + addpropertydialog.ui + ) + + QT4_WRAP_CPP(MOC_SRCS ${SHINY_EDITOR_HEADER_MOC}) + QT4_WRAP_UI(UI_HDRS ${SHINY_EDITOR_UI}) + + set(SOURCE_FILES + NewMaterialDialog.cpp + AddPropertyDialog.cpp + ColoredTabWidget.hpp + MainWindow.cpp + Editor.cpp + Actions.cpp + Query.cpp + PropertySortModel.cpp + ${SHINY_EDITOR_UI} # Just to have them in the IDE's file explorer + ) + + include(${QT_USE_FILE}) + + set (CMAKE_INCLUDE_CURRENT_DIR "true") + + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + + add_library(${SHINY_EDITOR_LIBRARY} STATIC ${SOURCE_FILES} ${MOC_SRCS} ${UI_HDRS}) + + set(SHINY_LIBRARIES ${SHINY_LIBRARIES} + ${SHINY_EDITOR_LIBRARY} + ${QT_LIBRARIES} + ) + set(SHINY_LIBRARIES ${SHINY_LIBRARIES} PARENT_SCOPE) + +else (QT_FOUND) + + add_definitions(-DSHINY_BUILD_MATERIAL_EDITOR=0) + set (SHINY_BUILD_EDITOR_FLAG -DSHINY_BUILD_MATERIAL_EDITOR=0 PARENT_SCOPE) + message(STATUS "QT4 was not found. You will not be able to use the material editor.") + +endif(QT_FOUND) diff --git a/extern/shiny/Editor/ColoredTabWidget.hpp b/extern/shiny/Editor/ColoredTabWidget.hpp new file mode 100644 index 0000000000..0bf30f6dda --- /dev/null +++ b/extern/shiny/Editor/ColoredTabWidget.hpp @@ -0,0 +1,24 @@ +#ifndef SHINY_EDITOR_COLOREDTABWIDGET_H +#define SHINY_EDITOR_COLOREDTABWIDGET_H + +#include + +namespace sh +{ + +/// Makes tabBar() public to allow changing tab title colors. +class ColoredTabWidget : public QTabWidget +{ +public: + ColoredTabWidget(QWidget* parent = 0) + : QTabWidget(parent) {} + + QTabBar* tabBar() + { + return QTabWidget::tabBar(); + } +}; + +} + +#endif diff --git a/extern/shiny/Editor/Editor.cpp b/extern/shiny/Editor/Editor.cpp new file mode 100644 index 0000000000..8c58d0e66d --- /dev/null +++ b/extern/shiny/Editor/Editor.cpp @@ -0,0 +1,117 @@ +#include "Editor.hpp" + + +#include +#include + +#include + +#include "../Main/Factory.hpp" + +#include "MainWindow.hpp" + +namespace sh +{ + + Editor::Editor() + : mMainWindow(NULL) + , mApplication(NULL) + , mInitialized(false) + , mThread(NULL) + { + } + + Editor::~Editor() + { + if (mMainWindow) + mMainWindow->mRequestExit = true; + + if (mThread) + mThread->join(); + delete mThread; + } + + void Editor::show() + { + if (!mInitialized) + { + mInitialized = true; + + mThread = new boost::thread(boost::bind(&Editor::runThread, this)); + } + else + { + if (mMainWindow) + mMainWindow->mRequestShowWindow = true; + } + } + + void Editor::runThread() + { + int argc = 0; + char** argv = NULL; + mApplication = new QApplication(argc, argv); + mApplication->setQuitOnLastWindowClosed(false); + mMainWindow = new MainWindow(); + mMainWindow->mSync = &mSync; + mMainWindow->show(); + + mApplication->exec(); + + delete mApplication; + } + + void Editor::update() + { + sh::Factory::getInstance().doMonitorShaderFiles(); + + if (!mMainWindow) + return; + + + { + boost::mutex::scoped_lock lock(mSync.mActionMutex); + + // execute pending actions + while (mMainWindow->mActionQueue.size()) + { + Action* action = mMainWindow->mActionQueue.front(); + action->execute(); + delete action; + mMainWindow->mActionQueue.pop(); + } + } + { + boost::mutex::scoped_lock lock(mSync.mQueryMutex); + + // execute pending queries + for (std::vector::iterator it = mMainWindow->mQueries.begin(); it != mMainWindow->mQueries.end(); ++it) + { + Query* query = *it; + if (!query->mDone) + query->execute(); + } + } + + boost::mutex::scoped_lock lock2(mSync.mUpdateMutex); + + // update the list of materials + mMainWindow->mState.mMaterialList.clear(); + sh::Factory::getInstance().listMaterials(mMainWindow->mState.mMaterialList); + + // update global settings + mMainWindow->mState.mGlobalSettingsMap.clear(); + sh::Factory::getInstance().listGlobalSettings(mMainWindow->mState.mGlobalSettingsMap); + + // update configuration list + mMainWindow->mState.mConfigurationList.clear(); + sh::Factory::getInstance().listConfigurationNames(mMainWindow->mState.mConfigurationList); + + // update shader list + mMainWindow->mState.mShaderSets.clear(); + sh::Factory::getInstance().listShaderSets(mMainWindow->mState.mShaderSets); + + mMainWindow->mState.mErrors += sh::Factory::getInstance().getErrorLog(); + } + +} diff --git a/extern/shiny/Editor/Editor.hpp b/extern/shiny/Editor/Editor.hpp new file mode 100644 index 0000000000..2b1e8040d0 --- /dev/null +++ b/extern/shiny/Editor/Editor.hpp @@ -0,0 +1,73 @@ +#ifndef SH_EDITOR_H +#define SH_EDITOR_H + +#if SHINY_BUILD_MATERIAL_EDITOR +class QApplication; + +#include +#include + +namespace boost +{ + class thread; +} + +namespace sh +{ + class MainWindow; + + struct SynchronizationState + { + boost::mutex mUpdateMutex; + boost::mutex mActionMutex; + boost::mutex mQueryMutex; + }; + + class Editor + { + public: + + Editor(); + ~Editor(); + + void show(); + void update(); + + + private: + bool mInitialized; + + MainWindow* mMainWindow; + QApplication* mApplication; + + SynchronizationState mSync; + + boost::thread* mThread; + + void runThread(); + + void processShowWindow(); + }; + +} + +#else + +// Dummy implementation, so that the user's code does not have to be polluted with #ifdefs +namespace sh +{ + + class Editor + { + public: + Editor() {} + ~Editor() {} + void show() {} + void update() {} + + }; +} + +#endif + +#endif diff --git a/extern/shiny/Editor/MainWindow.cpp b/extern/shiny/Editor/MainWindow.cpp new file mode 100644 index 0000000000..a2c52dc2f8 --- /dev/null +++ b/extern/shiny/Editor/MainWindow.cpp @@ -0,0 +1,952 @@ +#include "MainWindow.hpp" +#include "ui_mainwindow.h" + +#include + +#include +#include + +#include +#include + +#include "Editor.hpp" +#include "ColoredTabWidget.hpp" +#include "AddPropertyDialog.hpp" + +sh::MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , mRequestShowWindow(false) + , mRequestExit(false) + , mIgnoreGlobalSettingChange(false) + , mIgnoreConfigurationChange(false) + , mIgnoreMaterialChange(false) + , mIgnoreMaterialPropertyChange(false) +{ + ui->setupUi(this); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(onIdle())); + timer->start(50); + + QList sizes; + sizes << 250; + sizes << 550; + ui->splitter->setSizes(sizes); + + mMaterialModel = new QStringListModel(this); + + mMaterialProxyModel = new QSortFilterProxyModel(this); + mMaterialProxyModel->setSourceModel(mMaterialModel); + mMaterialProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mMaterialProxyModel->setDynamicSortFilter(true); + mMaterialProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + ui->materialList->setModel(mMaterialProxyModel); + ui->materialList->setSelectionMode(QAbstractItemView::SingleSelection); + ui->materialList->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->materialList->setAlternatingRowColors(true); + + connect(ui->materialList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(onMaterialSelectionChanged(QModelIndex,QModelIndex))); + + mMaterialPropertyModel = new QStandardItemModel(0, 2, this); + mMaterialPropertyModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name"))); + mMaterialPropertyModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value"))); + connect(mMaterialPropertyModel, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(onMaterialPropertyChanged(QStandardItem*))); + + mMaterialSortModel = new PropertySortModel(this); + mMaterialSortModel->setSourceModel(mMaterialPropertyModel); + mMaterialSortModel->setDynamicSortFilter(true); + mMaterialSortModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + ui->materialView->setModel(mMaterialSortModel); + ui->materialView->setContextMenuPolicy(Qt::CustomContextMenu); + ui->materialView->setAlternatingRowColors(true); + ui->materialView->setSortingEnabled(true); + connect(ui->materialView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(onContextMenuRequested(QPoint))); + + mGlobalSettingsModel = new QStandardItemModel(0, 2, this); + mGlobalSettingsModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name"))); + mGlobalSettingsModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value"))); + connect(mGlobalSettingsModel, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(onGlobalSettingChanged(QStandardItem*))); + + ui->globalSettingsView->setModel(mGlobalSettingsModel); + ui->globalSettingsView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); + ui->globalSettingsView->verticalHeader()->hide(); + ui->globalSettingsView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + ui->globalSettingsView->setSelectionMode(QAbstractItemView::SingleSelection); + + ui->configurationList->setSelectionMode(QAbstractItemView::SingleSelection); + ui->configurationList->setEditTriggers(QAbstractItemView::NoEditTriggers); + connect(ui->configurationList, SIGNAL(currentTextChanged(QString)), + this, SLOT(onConfigurationSelectionChanged(QString))); + + mConfigurationModel = new QStandardItemModel(0, 2, this); + mConfigurationModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name"))); + mConfigurationModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value"))); + connect(mConfigurationModel, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(onConfigurationChanged(QStandardItem*))); + + ui->configurationView->setModel(mConfigurationModel); + ui->configurationView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); + ui->configurationView->verticalHeader()->hide(); + ui->configurationView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + ui->configurationView->setSelectionMode(QAbstractItemView::SingleSelection); +} + +sh::MainWindow::~MainWindow() +{ + delete ui; +} + +void sh::MainWindow::closeEvent(QCloseEvent *event) +{ + this->hide(); + event->ignore(); +} + +void sh::MainWindow::onIdle() +{ + if (mRequestShowWindow) + { + mRequestShowWindow = false; + show(); + } + + if (mRequestExit) + { + QApplication::exit(); + return; + } + + boost::mutex::scoped_lock lock(mSync->mUpdateMutex); + + + mIgnoreMaterialChange = true; + QString selected; + + QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex(); + if (selectedIndex.isValid()) + selected = mMaterialModel->data(selectedIndex, Qt::DisplayRole).toString(); + + QStringList list; + + for (std::vector::const_iterator it = mState.mMaterialList.begin(); it != mState.mMaterialList.end(); ++it) + { + list.push_back(QString::fromStdString(*it)); + } + + if (mMaterialModel->stringList() != list) + { + mMaterialModel->setStringList(list); + + // quick hack to keep our selection when the model has changed + if (!selected.isEmpty()) + for (int i=0; irowCount(); ++i) + { + const QModelIndex& index = mMaterialModel->index(i,0); + if (mMaterialModel->data(index, Qt::DisplayRole).toString() == selected) + { + ui->materialList->setCurrentIndex(index); + break; + } + } + } + mIgnoreMaterialChange = false; + + mIgnoreGlobalSettingChange = true; + for (std::map::const_iterator it = mState.mGlobalSettingsMap.begin(); + it != mState.mGlobalSettingsMap.end(); ++it) + { + QList list = mGlobalSettingsModel->findItems(QString::fromStdString(it->first)); + if (!list.empty()) // item was already there + { + // if it changed, set the value column + if (mGlobalSettingsModel->data(mGlobalSettingsModel->index(list.front()->row(), 1)).toString() + != QString::fromStdString(it->second)) + { + mGlobalSettingsModel->setItem(list.front()->row(), 1, new QStandardItem(QString::fromStdString(it->second))); + } + } + else // item wasn't there; insert new row + { + QList toAdd; + QStandardItem* name = new QStandardItem(QString::fromStdString(it->first)); + name->setFlags(name->flags() &= ~Qt::ItemIsEditable); + QStandardItem* value = new QStandardItem(QString::fromStdString(it->second)); + toAdd.push_back(name); + toAdd.push_back(value); + mGlobalSettingsModel->appendRow(toAdd); + } + } + mIgnoreGlobalSettingChange = false; + + + mIgnoreConfigurationChange = true; + QList selected_ = ui->configurationList->selectedItems(); + QString selectedStr; + if (selected_.size()) + selectedStr = selected_.front()->text(); + + ui->configurationList->clear(); + + for (std::vector::const_iterator it = mState.mConfigurationList.begin(); it != mState.mConfigurationList.end(); ++it) + ui->configurationList->addItem(QString::fromStdString(*it)); + + if (!selectedStr.isEmpty()) + for (int i=0; iconfigurationList->count(); ++i) + { + if (ui->configurationList->item(i)->text() == selectedStr) + { + ui->configurationList->setCurrentItem(ui->configurationList->item(i), QItemSelectionModel::ClearAndSelect); + } + } + + mIgnoreConfigurationChange = false; + + if (!mState.mErrors.empty()) + { + ui->errorLog->append(QString::fromStdString(mState.mErrors)); + mState.mErrors = ""; + QColor color = ui->tabWidget->palette().color(QPalette::Normal, QPalette::Link); + ui->tabWidget->tabBar()->setTabTextColor(3, color); + } + + + // process query results + boost::mutex::scoped_lock lock2(mSync->mQueryMutex); + for (std::vector::iterator it = mQueries.begin(); it != mQueries.end();) + { + if ((*it)->mDone) + { + if (typeid(**it) == typeid(ConfigurationQuery)) + buildConfigurationModel(static_cast(*it)); + else if (typeid(**it) == typeid(MaterialQuery)) + buildMaterialModel(static_cast(*it)); + else if (typeid(**it) == typeid(MaterialPropertyQuery)) + { + MaterialPropertyQuery* q = static_cast(*it); + mIgnoreMaterialPropertyChange = true; + if (getSelectedMaterial().toStdString() == q->mName) + { + for (int i=0; irowCount(); ++i) + { + if (mMaterialPropertyModel->item(i,0)->text() == QString::fromStdString(q->mPropertyName)) + { + mMaterialPropertyModel->item(i,1)->setText(QString::fromStdString(q->mValue)); + if (mMaterialPropertyModel->item(i,1)->isCheckable()) + mMaterialPropertyModel->item(i,1)->setCheckState ((q->mValue == "true") + ? Qt::Checked : Qt::Unchecked); + } + } + } + mIgnoreMaterialPropertyChange = false; + } + + delete *it; + it = mQueries.erase(it); + } + else + ++it; + } +} + +void sh::MainWindow::onMaterialSelectionChanged (const QModelIndex & current, const QModelIndex & previous) +{ + if (mIgnoreMaterialChange) + return; + + QString name = getSelectedMaterial(); + if (!name.isEmpty()) + requestQuery(new sh::MaterialQuery(name.toStdString())); +} + +QString sh::MainWindow::getSelectedMaterial() +{ + QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex(); + if (!selectedIndex.isValid()) + return QString(""); + + return mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString(); +} + +void sh::MainWindow::onConfigurationSelectionChanged (const QString& current) +{ + if (mIgnoreConfigurationChange) + return; + requestQuery(new sh::ConfigurationQuery(current.toStdString())); +} + +void sh::MainWindow::onGlobalSettingChanged(QStandardItem *item) +{ + if (mIgnoreGlobalSettingChange) + return; // we are only interested in changes by the user, not by the backend. + + std::string name = mGlobalSettingsModel->data(mGlobalSettingsModel->index(item->row(), 0)).toString().toStdString(); + std::string value = mGlobalSettingsModel->data(mGlobalSettingsModel->index(item->row(), 1)).toString().toStdString(); + + queueAction(new sh::ActionChangeGlobalSetting(name, value)); +} + +void sh::MainWindow::onConfigurationChanged (QStandardItem* item) +{ + QList items = ui->configurationList->selectedItems(); + if (items.size()) + { + std::string name = items.front()->text().toStdString(); + std::string key = mConfigurationModel->data(mConfigurationModel->index(item->row(), 0)).toString().toStdString(); + std::string value = mConfigurationModel->data(mConfigurationModel->index(item->row(), 1)).toString().toStdString(); + + queueAction(new sh::ActionChangeConfiguration(name, key, value)); + + requestQuery(new sh::ConfigurationQuery(name)); + } +} + +void sh::MainWindow::on_lineEdit_textEdited(const QString &arg1) +{ + mMaterialProxyModel->setFilterFixedString(arg1); +} + +void sh::MainWindow::on_actionSave_triggered() +{ + queueAction (new sh::ActionSaveAll()); +} + +void sh::MainWindow::on_actionNewMaterial_triggered() +{ + +} + +void sh::MainWindow::on_actionDeleteMaterial_triggered() +{ + QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex(); + QString name = mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString(); + + queueAction (new sh::ActionDeleteMaterial(name.toStdString())); +} + +void sh::MainWindow::queueAction(Action* action) +{ + boost::mutex::scoped_lock lock(mSync->mActionMutex); + mActionQueue.push(action); +} + +void sh::MainWindow::requestQuery(Query *query) +{ + boost::mutex::scoped_lock lock(mSync->mActionMutex); + mQueries.push_back(query); +} + +void sh::MainWindow::on_actionQuit_triggered() +{ + hide(); +} + +void sh::MainWindow::on_actionNewConfiguration_triggered() +{ + QInputDialog dialog(this); + + QString text = QInputDialog::getText(this, tr("New Configuration"), + tr("Configuration name:")); + + if (!text.isEmpty()) + { + queueAction(new ActionCreateConfiguration(text.toStdString())); + } +} + +void sh::MainWindow::on_actionDeleteConfiguration_triggered() +{ + QList items = ui->configurationList->selectedItems(); + if (items.size()) + queueAction(new ActionDeleteConfiguration(items.front()->text().toStdString())); +} + +void sh::MainWindow::on_actionDeleteConfigurationProperty_triggered() +{ + QList items = ui->configurationList->selectedItems(); + if (items.empty()) + return; + std::string configurationName = items.front()->text().toStdString(); + + QModelIndex current = ui->configurationView->currentIndex(); + if (!current.isValid()) + return; + + std::string propertyName = mConfigurationModel->data(mConfigurationModel->index(current.row(), 0)).toString().toStdString(); + + queueAction(new sh::ActionDeleteConfigurationProperty(configurationName, propertyName)); + requestQuery(new sh::ConfigurationQuery(configurationName)); +} + +void sh::MainWindow::on_actionCloneMaterial_triggered() +{ + QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex(); + QString name = mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString(); + if (name.isEmpty()) + return; + + QInputDialog dialog(this); + + QString text = QInputDialog::getText(this, tr("Clone material"), + tr("Name:")); + + if (!text.isEmpty()) + { + queueAction(new ActionCloneMaterial(name.toStdString(), text.toStdString())); + } +} + +void sh::MainWindow::onContextMenuRequested(const QPoint &point) +{ + QPoint globalPos = ui->materialView->viewport()->mapToGlobal(point); + + QMenu menu; + + QList actions; + actions.push_back(ui->actionNewProperty); + actions.push_back(ui->actionDeleteProperty); + actions.push_back(ui->actionCreatePass); + actions.push_back(ui->actionCreateTextureUnit); + menu.addActions(actions); + + menu.exec(globalPos); +} + +void sh::MainWindow::getContext(QModelIndex index, int* passIndex, int* textureIndex, bool* isInPass, bool* isInTextureUnit) +{ + if (passIndex) + { + *passIndex = 0; + if (isInPass) + *isInPass = false; + QModelIndex passModelIndex = index; + // go up until we find the pass item. + while (getPropertyKey(passModelIndex) != "pass" && passModelIndex.isValid()) + passModelIndex = passModelIndex.parent(); + + if (passModelIndex.isValid()) + { + if (passModelIndex.column() != 0) + passModelIndex = passModelIndex.parent().child(passModelIndex.row(), 0); + for (int i=0; irowCount(); ++i) + { + if (mMaterialPropertyModel->data(mMaterialPropertyModel->index(i, 0)).toString() == QString("pass")) + { + if (mMaterialPropertyModel->index(i, 0) == passModelIndex) + { + if (isInPass) + *isInPass = true; + break; + } + ++(*passIndex); + } + } + } + } + if (textureIndex) + { + *textureIndex = 0; + if (isInTextureUnit) + *isInTextureUnit = false; + QModelIndex texModelIndex = index; + // go up until we find the texture_unit item. + while (getPropertyKey(texModelIndex) != "texture_unit" && texModelIndex.isValid()) + texModelIndex = texModelIndex.parent(); + if (texModelIndex.isValid()) + { + if (texModelIndex.column() != 0) + texModelIndex = texModelIndex.parent().child(texModelIndex.row(), 0); + for (int i=0; irowCount(texModelIndex.parent()); ++i) + { + if (texModelIndex.parent().child(i, 0).data().toString() == QString("texture_unit")) + { + if (texModelIndex.parent().child(i, 0) == texModelIndex) + { + if (isInTextureUnit) + *isInTextureUnit = true; + break; + } + ++(*textureIndex); + } + } + } + } +} + +std::string sh::MainWindow::getPropertyKey(QModelIndex index) +{ + if (!index.parent().isValid()) + return mMaterialPropertyModel->data(mMaterialPropertyModel->index(index.row(), 0)).toString().toStdString(); + else + return index.parent().child(index.row(), 0).data().toString().toStdString(); +} + +std::string sh::MainWindow::getPropertyValue(QModelIndex index) +{ + if (!index.parent().isValid()) + return mMaterialPropertyModel->data(mMaterialPropertyModel->index(index.row(), 1)).toString().toStdString(); + else + return index.parent().child(index.row(), 1).data().toString().toStdString(); +} + +void sh::MainWindow::onMaterialPropertyChanged(QStandardItem *item) +{ + if (mIgnoreMaterialPropertyChange) + return; + + QString material = getSelectedMaterial(); + if (material.isEmpty()) + return; + + // handle checkboxes being checked/unchecked + std::string value = getPropertyValue(item->index()); + if (item->data(Qt::UserRole).toInt() == MaterialProperty::Boolean) + { + if (item->checkState() == Qt::Checked && value != "true") + value = "true"; + else if (item->checkState() == Qt::Unchecked && value == "true") + value = "false"; + item->setText(QString::fromStdString(value)); + } + + // handle inherited properties being changed, i.e. overridden by the current (derived) material + if (item->data(Qt::UserRole+1).toInt() == MaterialProperty::Inherited_Unchanged) + { + QColor normalColor = ui->materialView->palette().color(QPalette::Normal, QPalette::WindowText); + mIgnoreMaterialPropertyChange = true; + mMaterialPropertyModel->item(item->index().row(), 0) + ->setData(QVariant(MaterialProperty::Inherited_Changed), Qt::UserRole+1); + mMaterialPropertyModel->item(item->index().row(), 0) + ->setData(normalColor, Qt::ForegroundRole); + mMaterialPropertyModel->item(item->index().row(), 1) + ->setData(QVariant(MaterialProperty::Inherited_Changed), Qt::UserRole+1); + mMaterialPropertyModel->item(item->index().row(), 1) + ->setData(normalColor, Qt::ForegroundRole); + mIgnoreMaterialPropertyChange = false; + + ui->materialView->scrollTo(mMaterialSortModel->mapFromSource(item->index())); + } + + if (!item->index().parent().isValid()) + { + // top level material property + queueAction(new ActionSetMaterialProperty( + material.toStdString(), getPropertyKey(item->index()), value)); + } + else if (getPropertyKey(item->index()) == "texture_unit") + { + // texture unit name changed + int passIndex, textureIndex; + getContext(item->index(), &passIndex, &textureIndex); + std::cout << "passIndex " << passIndex << " " << textureIndex << std::endl; + + queueAction(new ActionChangeTextureUnitName( + material.toStdString(), passIndex, textureIndex, value)); + + } + else if (item->index().parent().data().toString() == "pass") + { + // pass property + int passIndex; + getContext(item->index(), &passIndex, NULL); + /// \todo if shaders are changed, check that the material provides all properties needed by the shader + queueAction(new ActionSetPassProperty( + material.toStdString(), passIndex, getPropertyKey(item->index()), value)); + } + else if (item->index().parent().data().toString() == "shader_properties") + { + // shader property + int passIndex; + getContext(item->index(), &passIndex, NULL); + queueAction(new ActionSetShaderProperty( + material.toStdString(), passIndex, getPropertyKey(item->index()), value)); + } + else if (item->index().parent().data().toString() == "texture_unit") + { + // texture property + int passIndex, textureIndex; + getContext(item->index(), &passIndex, &textureIndex); + queueAction(new ActionSetTextureProperty( + material.toStdString(), passIndex, textureIndex, getPropertyKey(item->index()), value)); + } +} + +void sh::MainWindow::buildMaterialModel(MaterialQuery *data) +{ + mMaterialPropertyModel->clear(); + + mMaterialPropertyModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name"))); + mMaterialPropertyModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value"))); + + for (std::map::const_iterator it = data->mProperties.begin(); + it != data->mProperties.end(); ++it) + { + addProperty(mMaterialPropertyModel->invisibleRootItem(), it->first, it->second); + } + + for (std::vector::iterator it = data->mPasses.begin(); + it != data->mPasses.end(); ++it) + { + QStandardItem* passItem = new QStandardItem (QString("pass")); + passItem->setFlags(passItem->flags() &= ~Qt::ItemIsEditable); + passItem->setData(QVariant(static_cast(MaterialProperty::Object)), Qt::UserRole); + + if (it->mShaderProperties.size()) + { + QStandardItem* shaderPropertiesItem = new QStandardItem (QString("shader_properties")); + shaderPropertiesItem->setFlags(shaderPropertiesItem->flags() &= ~Qt::ItemIsEditable); + shaderPropertiesItem->setData(QVariant(static_cast(MaterialProperty::Object)), Qt::UserRole); + + for (std::map::iterator pit = it->mShaderProperties.begin(); + pit != it->mShaderProperties.end(); ++pit) + { + addProperty(shaderPropertiesItem, pit->first, pit->second); + } + passItem->appendRow(shaderPropertiesItem); + } + + for (std::map::iterator pit = it->mProperties.begin(); + pit != it->mProperties.end(); ++pit) + { + addProperty(passItem, pit->first, pit->second); + } + + for (std::vector::iterator tIt = it->mTextureUnits.begin(); + tIt != it->mTextureUnits.end(); ++tIt) + { + QStandardItem* unitItem = new QStandardItem (QString("texture_unit")); + unitItem->setFlags(unitItem->flags() &= ~Qt::ItemIsEditable); + unitItem->setData(QVariant(static_cast(MaterialProperty::Object)), Qt::UserRole); + QStandardItem* nameItem = new QStandardItem (QString::fromStdString(tIt->mName)); + nameItem->setData(QVariant(static_cast(MaterialProperty::Object)), Qt::UserRole); + + QList texUnit; + texUnit << unitItem << nameItem; + + for (std::map::iterator pit = tIt->mProperties.begin(); + pit != tIt->mProperties.end(); ++pit) + { + addProperty(unitItem, pit->first, pit->second); + } + + passItem->appendRow(texUnit); + } + + QList toAdd; + toAdd << passItem; + toAdd << new QStandardItem(QString("")); + mMaterialPropertyModel->appendRow(toAdd); + } + + ui->materialView->expandAll(); + ui->materialView->resizeColumnToContents(0); + ui->materialView->resizeColumnToContents(1); +} + +void sh::MainWindow::addProperty(QStandardItem *parent, const std::string &key, MaterialProperty value, bool scrollTo) +{ + QList toAdd; + QStandardItem* keyItem = new QStandardItem(QString::fromStdString(key)); + keyItem->setFlags(keyItem->flags() &= ~Qt::ItemIsEditable); + keyItem->setData(QVariant(value.mType), Qt::UserRole); + keyItem->setData(QVariant(value.mSource), Qt::UserRole+1); + toAdd.push_back(keyItem); + + QStandardItem* valueItem = NULL; + if (value.mSource != MaterialProperty::None) + { + valueItem = new QStandardItem(QString::fromStdString(value.mValue)); + valueItem->setData(QVariant(value.mType), Qt::UserRole); + valueItem->setData(QVariant(value.mSource), Qt::UserRole+1); + toAdd.push_back(valueItem); + } + + + if (value.mSource == MaterialProperty::Inherited_Unchanged) + { + QColor color = ui->configurationView->palette().color(QPalette::Disabled, QPalette::WindowText); + keyItem->setData(color, Qt::ForegroundRole); + if (valueItem) + valueItem->setData(color, Qt::ForegroundRole); + } + if (value.mType == MaterialProperty::Boolean && valueItem) + { + valueItem->setCheckable(true); + valueItem->setCheckState((value.mValue == "true") ? Qt::Checked : Qt::Unchecked); + } + + parent->appendRow(toAdd); + + if (scrollTo) + ui->materialView->scrollTo(mMaterialSortModel->mapFromSource(keyItem->index())); +} + +void sh::MainWindow::buildConfigurationModel(ConfigurationQuery *data) +{ + while (mConfigurationModel->rowCount()) + mConfigurationModel->removeRow(0); + for (std::map::iterator it = data->mProperties.begin(); + it != data->mProperties.end(); ++it) + { + QList toAdd; + QStandardItem* name = new QStandardItem(QString::fromStdString(it->first)); + name->setFlags(name->flags() &= ~Qt::ItemIsEditable); + QStandardItem* value = new QStandardItem(QString::fromStdString(it->second)); + toAdd.push_back(name); + toAdd.push_back(value); + mConfigurationModel->appendRow(toAdd); + } + + // add items that are in global settings, but not in this configuration (with a "inactive" color) + for (std::map::const_iterator it = mState.mGlobalSettingsMap.begin(); + it != mState.mGlobalSettingsMap.end(); ++it) + { + if (data->mProperties.find(it->first) == data->mProperties.end()) + { + QColor color = ui->configurationView->palette().color(QPalette::Disabled, QPalette::WindowText); + QList toAdd; + QStandardItem* name = new QStandardItem(QString::fromStdString(it->first)); + name->setFlags(name->flags() &= ~Qt::ItemIsEditable); + name->setData(color, Qt::ForegroundRole); + QStandardItem* value = new QStandardItem(QString::fromStdString(it->second)); + value->setData(color, Qt::ForegroundRole); + toAdd.push_back(name); + toAdd.push_back(value); + mConfigurationModel->appendRow(toAdd); + } + } +} + +void sh::MainWindow::on_actionCreatePass_triggered() +{ + QString material = getSelectedMaterial(); + if (!material.isEmpty()) + { + addProperty(mMaterialPropertyModel->invisibleRootItem(), + "pass", MaterialProperty("", MaterialProperty::Object, MaterialProperty::None), true); + + queueAction (new ActionCreatePass(material.toStdString())); + } +} + +void sh::MainWindow::on_actionDeleteProperty_triggered() +{ + QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex()); + QString material = getSelectedMaterial(); + if (material.isEmpty()) + return; + + mIgnoreMaterialPropertyChange = true; + + if (getPropertyKey(selectedIndex) == "pass") + { + // delete whole pass + int passIndex; + getContext(selectedIndex, &passIndex, NULL); + if (passIndex == 0) + { + QMessageBox msgBox; + msgBox.setText("The first pass can not be deleted."); + msgBox.exec(); + } + else + { + queueAction(new ActionDeletePass(material.toStdString(), passIndex)); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + } + else if (getPropertyKey(selectedIndex) == "texture_unit") + { + // delete whole texture unit + int passIndex, textureIndex; + getContext(selectedIndex, &passIndex, &textureIndex); + queueAction(new ActionDeleteTextureUnit(material.toStdString(), passIndex, textureIndex)); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + else if (!selectedIndex.parent().isValid()) + { + // top level material property + MaterialProperty::Source source = static_cast( + mMaterialPropertyModel->itemFromIndex(selectedIndex)->data(Qt::UserRole+1).toInt()); + if (source == MaterialProperty::Inherited_Unchanged) + { + QMessageBox msgBox; + msgBox.setText("Inherited properties can not be deleted."); + msgBox.exec(); + } + else + { + queueAction(new ActionDeleteMaterialProperty( + material.toStdString(), getPropertyKey(selectedIndex))); + std::cout << "source is " << source << std::endl; + if (source == MaterialProperty::Inherited_Changed) + { + QColor inactiveColor = ui->materialView->palette().color(QPalette::Disabled, QPalette::WindowText); + mMaterialPropertyModel->item(selectedIndex.row(), 0) + ->setData(QVariant(MaterialProperty::Inherited_Unchanged), Qt::UserRole+1); + mMaterialPropertyModel->item(selectedIndex.row(), 0) + ->setData(inactiveColor, Qt::ForegroundRole); + mMaterialPropertyModel->item(selectedIndex.row(), 1) + ->setData(QVariant(MaterialProperty::Inherited_Unchanged), Qt::UserRole+1); + mMaterialPropertyModel->item(selectedIndex.row(), 1) + ->setData(inactiveColor, Qt::ForegroundRole); + + // make sure to update the property's value + requestQuery(new sh::MaterialPropertyQuery(material.toStdString(), getPropertyKey(selectedIndex))); + } + else + mMaterialPropertyModel->removeRow(selectedIndex.row()); + } + } + else if (selectedIndex.parent().data().toString() == "pass") + { + // pass property + int passIndex; + getContext(selectedIndex, &passIndex, NULL); + queueAction(new ActionDeletePassProperty( + material.toStdString(), passIndex, getPropertyKey(selectedIndex))); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + else if (selectedIndex.parent().data().toString() == "shader_properties") + { + // shader property + int passIndex; + getContext(selectedIndex, &passIndex, NULL); + queueAction(new ActionDeleteShaderProperty( + material.toStdString(), passIndex, getPropertyKey(selectedIndex))); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + else if (selectedIndex.parent().data().toString() == "texture_unit") + { + // texture property + int passIndex, textureIndex; + getContext(selectedIndex, &passIndex, &textureIndex); + queueAction(new ActionDeleteTextureProperty( + material.toStdString(), passIndex, textureIndex, getPropertyKey(selectedIndex))); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + mIgnoreMaterialPropertyChange = false; +} + +void sh::MainWindow::on_actionNewProperty_triggered() +{ + QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex()); + QString material = getSelectedMaterial(); + if (material.isEmpty()) + return; + + AddPropertyDialog* dialog = new AddPropertyDialog(this); + dialog->exec(); + QString propertyName = dialog->mName; + QString defaultValue = ""; + + /// \todo check if this property name exists already + + if (!propertyName.isEmpty()) + { + int passIndex, textureIndex; + bool isInPass, isInTextureUnit; + getContext(selectedIndex, &passIndex, &textureIndex, &isInPass, &isInTextureUnit); + + QList items; + QStandardItem* keyItem = new QStandardItem(propertyName); + keyItem->setFlags(keyItem->flags() &= ~Qt::ItemIsEditable); + items << keyItem; + items << new QStandardItem(defaultValue); + + // figure out which item the new property should be a child of + QModelIndex parentIndex = selectedIndex; + if (selectedIndex.data(Qt::UserRole) != MaterialProperty::Object) + parentIndex = selectedIndex.parent(); + QStandardItem* parentItem; + if (!parentIndex.isValid()) + parentItem = mMaterialPropertyModel->invisibleRootItem(); + else + parentItem = mMaterialPropertyModel->itemFromIndex(parentIndex); + + if (isInTextureUnit) + { + queueAction(new ActionSetTextureProperty( + material.toStdString(), passIndex, textureIndex, propertyName.toStdString(), defaultValue.toStdString())); + } + else if (isInPass) + { + if (selectedIndex.parent().child(selectedIndex.row(),0).data().toString() == "shader_properties" + || selectedIndex.parent().data().toString() == "shader_properties") + { + queueAction(new ActionSetShaderProperty( + material.toStdString(), passIndex, propertyName.toStdString(), defaultValue.toStdString())); + } + else + { + queueAction(new ActionSetPassProperty( + material.toStdString(), passIndex, propertyName.toStdString(), defaultValue.toStdString())); + } + } + else + { + queueAction(new ActionSetMaterialProperty( + material.toStdString(), propertyName.toStdString(), defaultValue.toStdString())); + } + + addProperty(parentItem, propertyName.toStdString(), + MaterialProperty (defaultValue.toStdString(), MaterialProperty::Misc, MaterialProperty::Normal), true); + + /// \todo scroll to newly added property + } +} + +void sh::MainWindow::on_actionCreateTextureUnit_triggered() +{ + QString material = getSelectedMaterial(); + if (material.isEmpty()) + return; + + QInputDialog dialog(this); + + QString text = QInputDialog::getText(this, tr("New texture unit"), + tr("Texture unit name (for referencing in shaders):")); + if (!text.isEmpty()) + { + QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex()); + int passIndex; + getContext(selectedIndex, &passIndex, NULL); + queueAction(new ActionCreateTextureUnit(material.toStdString(), passIndex, text.toStdString())); + + // add to model + int index = 0; + for (int i=0; irowCount(); ++i) + { + if (mMaterialPropertyModel->data(mMaterialPropertyModel->index(i, 0)).toString() == QString("pass")) + { + if (index == passIndex) + { + addProperty(mMaterialPropertyModel->itemFromIndex(mMaterialPropertyModel->index(i, 0)), + "texture_unit", MaterialProperty(text.toStdString(), MaterialProperty::Object), true); + break; + } + + ++index; + } + } + } +} + +void sh::MainWindow::on_clearButton_clicked() +{ + ui->errorLog->clear(); +} + +void sh::MainWindow::on_tabWidget_currentChanged(int index) +{ + QColor color = ui->tabWidget->palette().color(QPalette::Normal, QPalette::WindowText); + + if (index == 3) + ui->tabWidget->tabBar()->setTabTextColor(3, color); +} diff --git a/extern/shiny/Editor/MainWindow.hpp b/extern/shiny/Editor/MainWindow.hpp new file mode 100644 index 0000000000..3f0dc295c0 --- /dev/null +++ b/extern/shiny/Editor/MainWindow.hpp @@ -0,0 +1,139 @@ +#ifndef SHINY_EDITOR_MAINWINDOW_HPP +#define SHINY_EDITOR_MAINWINDOW_HPP + +#include + +#include +#include +#include + +#include + +#include "Actions.hpp" +#include "Query.hpp" + +#include "PropertySortModel.hpp" + +namespace Ui { +class MainWindow; +} + +namespace sh +{ + +struct SynchronizationState; + + +/** + * @brief A snapshot of the material system's state. Lock the mUpdateMutex before accessing. + */ +struct MaterialSystemState +{ + std::vector mMaterialList; + + std::map mGlobalSettingsMap; + + std::vector mConfigurationList; + + std::vector mMaterialFiles; + std::vector mConfigurationFiles; + + std::vector mShaderSets; + + std::string mErrors; +}; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + + // really should be an std::atomic + volatile bool mRequestShowWindow; + // dito + volatile bool mRequestExit; + + SynchronizationState* mSync; + + /// \todo Is there a better way to ignore manual model changes? + bool mIgnoreGlobalSettingChange; + bool mIgnoreConfigurationChange; + bool mIgnoreMaterialChange; + bool mIgnoreMaterialPropertyChange; + + std::queue mActionQueue; + std::vector mQueries; + + MaterialSystemState mState; + +private: + Ui::MainWindow *ui; + + // material tab + QStringListModel* mMaterialModel; + QSortFilterProxyModel* mMaterialProxyModel; + + QStandardItemModel* mMaterialPropertyModel; + PropertySortModel* mMaterialSortModel; + + // global settings tab + QStandardItemModel* mGlobalSettingsModel; + + // configuration tab + QStandardItemModel* mConfigurationModel; + + void queueAction(Action* action); + void requestQuery(Query* query); + + void buildMaterialModel (MaterialQuery* data); + void buildConfigurationModel (ConfigurationQuery* data); + + QString getSelectedMaterial(); + + /// get the context of an index in the material property model + void getContext(QModelIndex index, int* passIndex, int* textureIndex, bool* isInPass=NULL, bool* isInTextureUnit=NULL); + + std::string getPropertyKey(QModelIndex index); + std::string getPropertyValue(QModelIndex index); + + void addProperty (QStandardItem* parent, const std::string& key, MaterialProperty value, bool scrollTo=false); + +protected: + void closeEvent(QCloseEvent *event); + +public slots: + void onIdle(); + + void onMaterialSelectionChanged (const QModelIndex & current, const QModelIndex & previous); + void onConfigurationSelectionChanged (const QString& current); + + void onGlobalSettingChanged (QStandardItem* item); + void onConfigurationChanged (QStandardItem* item); + void onMaterialPropertyChanged (QStandardItem* item); + + void onContextMenuRequested(const QPoint& point); + +private slots: + void on_lineEdit_textEdited(const QString &arg1); + void on_actionSave_triggered(); + void on_actionNewMaterial_triggered(); + void on_actionDeleteMaterial_triggered(); + void on_actionQuit_triggered(); + void on_actionNewConfiguration_triggered(); + void on_actionDeleteConfiguration_triggered(); + void on_actionDeleteConfigurationProperty_triggered(); + void on_actionCloneMaterial_triggered(); + void on_actionCreatePass_triggered(); + void on_actionDeleteProperty_triggered(); + void on_actionNewProperty_triggered(); + void on_actionCreateTextureUnit_triggered(); + void on_clearButton_clicked(); + void on_tabWidget_currentChanged(int index); +}; + +} + +#endif // MAINWINDOW_HPP diff --git a/extern/shiny/Editor/NewMaterialDialog.cpp b/extern/shiny/Editor/NewMaterialDialog.cpp new file mode 100644 index 0000000000..f1a716a9f0 --- /dev/null +++ b/extern/shiny/Editor/NewMaterialDialog.cpp @@ -0,0 +1,14 @@ +#include "NewMaterialDialog.hpp" +#include "ui_newmaterialdialog.h" + +NewMaterialDialog::NewMaterialDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::NewMaterialDialog) +{ + ui->setupUi(this); +} + +NewMaterialDialog::~NewMaterialDialog() +{ + delete ui; +} diff --git a/extern/shiny/Editor/NewMaterialDialog.hpp b/extern/shiny/Editor/NewMaterialDialog.hpp new file mode 100644 index 0000000000..2a20bbb395 --- /dev/null +++ b/extern/shiny/Editor/NewMaterialDialog.hpp @@ -0,0 +1,22 @@ +#ifndef NEWMATERIALDIALOG_HPP +#define NEWMATERIALDIALOG_HPP + +#include + +namespace Ui { +class NewMaterialDialog; +} + +class NewMaterialDialog : public QDialog +{ + Q_OBJECT + +public: + explicit NewMaterialDialog(QWidget *parent = 0); + ~NewMaterialDialog(); + +private: + Ui::NewMaterialDialog *ui; +}; + +#endif // NEWMATERIALDIALOG_HPP diff --git a/extern/shiny/Editor/PropertySortModel.cpp b/extern/shiny/Editor/PropertySortModel.cpp new file mode 100644 index 0000000000..637fe11b02 --- /dev/null +++ b/extern/shiny/Editor/PropertySortModel.cpp @@ -0,0 +1,35 @@ +#include "PropertySortModel.hpp" + +#include "Query.hpp" + +#include +sh::PropertySortModel::PropertySortModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +bool sh::PropertySortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + if (left.data(Qt::UserRole+1).toInt() != 0 && right.data(Qt::UserRole+1).toInt() != 0) + { + int sourceL = left.data(Qt::UserRole+1).toInt(); + int sourceR = right.data(Qt::UserRole+1).toInt(); + + if (sourceL > sourceR) + return true; + else if (sourceR > sourceL) + return false; + } + + int typeL = left.data(Qt::UserRole).toInt(); + int typeR = right.data(Qt::UserRole).toInt(); + + if (typeL > typeR) + return true; + else if (typeR > typeL) + return false; + + QString nameL = left.data().toString(); + QString nameR = right.data().toString(); + return nameL > nameR; +} diff --git a/extern/shiny/Editor/PropertySortModel.hpp b/extern/shiny/Editor/PropertySortModel.hpp new file mode 100644 index 0000000000..f96927764e --- /dev/null +++ b/extern/shiny/Editor/PropertySortModel.hpp @@ -0,0 +1,21 @@ +#ifndef SHINY_EDITOR_PROPERTYSORTMODEL_H +#define SHINY_EDITOR_PROPERTYSORTMODEL_H + +#include + +namespace sh +{ + + class PropertySortModel : public QSortFilterProxyModel + { + Q_OBJECT + + public: + PropertySortModel(QObject* parent); + protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + }; + +} + +#endif diff --git a/extern/shiny/Editor/Query.cpp b/extern/shiny/Editor/Query.cpp new file mode 100644 index 0000000000..ccf07b62bc --- /dev/null +++ b/extern/shiny/Editor/Query.cpp @@ -0,0 +1,134 @@ +#include "Query.hpp" + +#include "../Main/Factory.hpp" + +namespace sh +{ + +void Query::execute() +{ + executeImpl(); + mDone = true; +} + +ConfigurationQuery::ConfigurationQuery(const std::string &name) + : mName(name) +{ +} + +void ConfigurationQuery::executeImpl() +{ + sh::Factory::getInstance().listConfigurationSettings(mName, mProperties); +} + +void MaterialQuery::executeImpl() +{ + sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(mName); + + if (instance->getParent()) + mParent = static_cast(instance->getParent())->getName(); + + // add the inherited properties + sh::PropertySetGet* parent = instance; + std::vector inheritedPropertiesVector; + while (parent->getParent()) + { + parent = parent->getParent(); + const sh::PropertyMap& parentProperties = parent->listProperties(); + + for (PropertyMap::const_iterator it = parentProperties.begin(); it != parentProperties.end(); ++it) + { + MaterialProperty::Source source = MaterialProperty::Inherited_Unchanged; + MaterialProperty::Type type = getType(it->first, parent->getProperty(it->first)); + mProperties[it->first] = MaterialProperty ( + retrieveValue(parent->getProperty(it->first), NULL).get(), + type, source); + inheritedPropertiesVector.push_back(it->first); + } + } + + // add our properties + const sh::PropertyMap& ourProperties = instance->listProperties(); + for (PropertyMap::const_iterator it = ourProperties.begin(); it != ourProperties.end(); ++it) + { + MaterialProperty::Source source = + (std::find(inheritedPropertiesVector.begin(), inheritedPropertiesVector.end(), it->first) + != inheritedPropertiesVector.end()) ? + MaterialProperty::Inherited_Changed : MaterialProperty::Normal; + MaterialProperty::Type type = getType(it->first, instance->getProperty(it->first)); + mProperties[it->first] = MaterialProperty ( + retrieveValue(instance->getProperty(it->first), NULL).get(), + type, source); + } + + std::vector* passes = instance->getPasses(); + for (std::vector::iterator it = passes->begin(); it != passes->end(); ++it) + { + mPasses.push_back(PassInfo()); + + const sh::PropertyMap& passProperties = it->listProperties(); + for (PropertyMap::const_iterator pit = passProperties.begin(); pit != passProperties.end(); ++pit) + { + PropertyValuePtr property = it->getProperty(pit->first); + MaterialProperty::Type type = getType(pit->first, property); + if (typeid(*property).name() == typeid(sh::LinkedValue).name()) + mPasses.back().mProperties[pit->first] = MaterialProperty("$" + property->_getStringValue(), type); + else + mPasses.back().mProperties[pit->first] = MaterialProperty( + retrieveValue(property, NULL).get(), type); + } + + const sh::PropertyMap& shaderProperties = it->mShaderProperties.listProperties(); + for (PropertyMap::const_iterator pit = shaderProperties.begin(); pit != shaderProperties.end(); ++pit) + { + PropertyValuePtr property = it->mShaderProperties.getProperty(pit->first); + MaterialProperty::Type type = getType(pit->first, property); + if (typeid(*property).name() == typeid(sh::LinkedValue).name()) + mPasses.back().mShaderProperties[pit->first] = MaterialProperty("$" + property->_getStringValue(), type); + else + mPasses.back().mShaderProperties[pit->first] = MaterialProperty( + retrieveValue(property, NULL).get(), type); + } + + std::vector* texUnits = &it->mTexUnits; + for (std::vector::iterator tIt = texUnits->begin(); tIt != texUnits->end(); ++tIt) + { + mPasses.back().mTextureUnits.push_back(TextureUnitInfo()); + mPasses.back().mTextureUnits.back().mName = tIt->getName(); + const sh::PropertyMap& unitProperties = tIt->listProperties(); + for (PropertyMap::const_iterator pit = unitProperties.begin(); pit != unitProperties.end(); ++pit) + { + PropertyValuePtr property = tIt->getProperty(pit->first); + MaterialProperty::Type type = getType(pit->first, property); + if (typeid(*property).name() == typeid(sh::LinkedValue).name()) + mPasses.back().mTextureUnits.back().mProperties[pit->first] = MaterialProperty( + "$" + property->_getStringValue(), MaterialProperty::Linked); + else + mPasses.back().mTextureUnits.back().mProperties[pit->first] = MaterialProperty( + retrieveValue(property, NULL).get(), type); + } + } + } +} + +MaterialProperty::Type MaterialQuery::getType(const std::string &key, PropertyValuePtr value) +{ + if (typeid(*value).name() == typeid(sh::LinkedValue).name()) + return MaterialProperty::Linked; + + if (key == "vertex_program" || key == "fragment_program") + return MaterialProperty::Shader; + + std::string valueStr = retrieveValue(value, NULL).get(); + + if (valueStr == "false" || valueStr == "true") + return MaterialProperty::Boolean; +} + +void MaterialPropertyQuery::executeImpl() +{ + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + mValue = retrieveValue(m->getProperty(mPropertyName), m).get(); +} + +} diff --git a/extern/shiny/Editor/Query.hpp b/extern/shiny/Editor/Query.hpp new file mode 100644 index 0000000000..7aec684880 --- /dev/null +++ b/extern/shiny/Editor/Query.hpp @@ -0,0 +1,121 @@ +#ifndef SH_QUERY_H +#define SH_QUERY_H + +#include +#include +#include + +#include "../Main/PropertyBase.hpp" + +namespace sh +{ + +class Query +{ +public: + Query() + : mDone(false) {} + virtual ~Query(); + + void execute(); + + bool mDone; + +protected: + virtual void executeImpl() = 0; +}; + +class ConfigurationQuery : public Query +{ +public: + ConfigurationQuery(const std::string& name); + + std::map mProperties; +protected: + std::string mName; + virtual void executeImpl(); +}; + + +struct MaterialProperty +{ + + enum Type + { + Texture, + Color, + Boolean, + Shader, + Misc, + Linked, + Object // child object, i.e. pass, texture unit, shader properties + }; + + enum Source + { + Normal, + Inherited_Changed, + Inherited_Unchanged, + None // there is no property source (e.g. a pass, which does not have a name) + }; + + MaterialProperty() {} + MaterialProperty (const std::string& value, Type type, Source source=Normal) + : mValue(value), mType(type), mSource(source) {} + + std::string mValue; + Type mType; + Source mSource; +}; + + +struct TextureUnitInfo +{ + std::string mName; + std::map mProperties; +}; + +struct PassInfo +{ + std::map mShaderProperties; + + std::map mProperties; + std::vector mTextureUnits; +}; + +class MaterialQuery : public Query +{ +public: + MaterialQuery(const std::string& name) + : mName(name) {} + + std::string mParent; + std::vector mPasses; + std::map mProperties; + +protected: + std::string mName; + virtual void executeImpl(); + + MaterialProperty::Type getType (const std::string& key, PropertyValuePtr value); +}; + +class MaterialPropertyQuery : public Query +{ +public: + MaterialPropertyQuery(const std::string& name, const std::string& propertyName) + : mName(name), mPropertyName(propertyName) + { + } + + std::string mValue; + + std::string mName; + std::string mPropertyName; +protected: + virtual void executeImpl(); +}; + +} + +#endif diff --git a/extern/shiny/Editor/addpropertydialog.ui b/extern/shiny/Editor/addpropertydialog.ui new file mode 100644 index 0000000000..63de7d1415 --- /dev/null +++ b/extern/shiny/Editor/addpropertydialog.ui @@ -0,0 +1,118 @@ + + + AddPropertyDialog + + + + 0 + 0 + 257 + 133 + + + + Dialog + + + + + + + + + + + + + Property name + + + + + + + Editing widget + + + + + + + + Checkbox + + + + + Shader + + + + + Color + + + + + Texture + + + + + Other + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + AddPropertyDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddPropertyDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/extern/shiny/Editor/mainwindow.ui b/extern/shiny/Editor/mainwindow.ui new file mode 100644 index 0000000000..b27c8357de --- /dev/null +++ b/extern/shiny/Editor/mainwindow.ui @@ -0,0 +1,420 @@ + + + MainWindow + + + + 0 + 0 + 647 + 512 + + + + MainWindow + + + + + + + 0 + + + + + + + Materials + + + + + + Qt::Horizontal + + + + + + + + + + Search + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + + + + + + + + 1 + 0 + + + + + + + + + 1 + 0 + + + + + 16 + 16 + + + + Qt::ToolButtonIconOnly + + + + + + + + + + + + + + Global settings + + + + + + + + + + Configurations + + + + + + Qt::Horizontal + + + + + + + + + + + 16 + 16 + + + + + + + + + + + + + + + + + + 16 + 16 + + + + + + + + + + + + + + Errors + + + + + + + + true + + + + + + + + 0 + 0 + + + + Clear + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 647 + 25 + + + + false + + + + File + + + + + + + Material + + + + + + + + + History + + + + + + + + + + + + + + Quit + + + + + + + + + + Save + + + Save all + + + + + + + + + + Delete + + + Delete selected material + + + + + Change parent... + + + + + + + + + + New + + + Create a new material + + + + + + + + + + Clone + + + Clone selected material + + + + + + + + + + Delete + + + Delete selected configuration + + + Del + + + + + + + + + + New + + + Create a new configuration + + + + + + + + + + Delete + + + Delete property + + + + + + + + + + Delete + + + Delete item + + + + + + + + + + New property + + + + + + + + + + Create pass + + + + + + + + + + Create texture unit + + + + + + sh::ColoredTabWidget + QTabWidget +

ColoredTabWidget.hpp
+ 1 + + + + + diff --git a/extern/shiny/Editor/newmaterialdialog.ui b/extern/shiny/Editor/newmaterialdialog.ui new file mode 100644 index 0000000000..f24561cf79 --- /dev/null +++ b/extern/shiny/Editor/newmaterialdialog.ui @@ -0,0 +1,98 @@ + + + NewMaterialDialog + + + + 0 + 0 + 385 + 198 + + + + Dialog + + + + + + + + Name + + + + + + + Parent material + + + + + + + + + + + + + File + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + NewMaterialDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + NewMaterialDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/extern/shiny/Main/Factory.cpp b/extern/shiny/Main/Factory.cpp index 40c695fd4a..c27848b5f0 100644 --- a/extern/shiny/Main/Factory.cpp +++ b/extern/shiny/Main/Factory.cpp @@ -51,8 +51,6 @@ namespace sh { assert(mCurrentLanguage != Language_None); - bool removeBinaryCache = false; - if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt")) { std::ifstream file; @@ -86,8 +84,9 @@ namespace sh break; } - PropertySetGet newConfiguration; + Configuration newConfiguration; newConfiguration.setParent(&mGlobalSettings); + newConfiguration.setSourceFile (it->second->mFileName); std::vector props = it->second->getChildren(); for (std::vector::const_iterator propIt = props.begin(); propIt != props.end(); ++propIt) @@ -137,82 +136,7 @@ namespace sh } // load shader sets - { - ScriptLoader shaderSetLoader(".shaderset"); - ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath()); - std::map nodes = shaderSetLoader.getAllConfigScripts(); - for (std::map ::const_iterator it = nodes.begin(); - it != nodes.end(); ++it) - { - if (!(it->second->getName() == "shader_set")) - { - std::cerr << "sh::Factory: Warning: Unsupported root node type \"" << it->second->getName() << "\" for file type .shaderset" << std::endl; - break; - } - - if (!it->second->findChild("profiles_cg")) - throw std::runtime_error ("missing \"profiles_cg\" field for \"" + it->first + "\""); - if (!it->second->findChild("profiles_hlsl")) - throw std::runtime_error ("missing \"profiles_hlsl\" field for \"" + it->first + "\""); - if (!it->second->findChild("source")) - throw std::runtime_error ("missing \"source\" field for \"" + it->first + "\""); - if (!it->second->findChild("type")) - throw std::runtime_error ("missing \"type\" field for \"" + it->first + "\""); - - std::vector profiles_cg; - boost::split (profiles_cg, it->second->findChild("profiles_cg")->getValue(), boost::is_any_of(" ")); - std::string cg_profile; - for (std::vector::iterator it2 = profiles_cg.begin(); it2 != profiles_cg.end(); ++it2) - { - if (mPlatform->isProfileSupported(*it2)) - { - cg_profile = *it2; - break; - } - } - - std::vector profiles_hlsl; - boost::split (profiles_hlsl, it->second->findChild("profiles_hlsl")->getValue(), boost::is_any_of(" ")); - std::string hlsl_profile; - for (std::vector::iterator it2 = profiles_hlsl.begin(); it2 != profiles_hlsl.end(); ++it2) - { - if (mPlatform->isProfileSupported(*it2)) - { - hlsl_profile = *it2; - break; - } - } - - std::string sourceAbsolute = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue(); - std::string sourceRelative = it->second->findChild("source")->getValue(); - - ShaderSet newSet (it->second->findChild("type")->getValue(), cg_profile, hlsl_profile, - sourceAbsolute, - mPlatform->getBasePath(), - it->first, - &mGlobalSettings); - - int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceAbsolute)); - mShadersLastModifiedNew[sourceRelative] = lastModified; - if (mShadersLastModified.find(sourceRelative) != mShadersLastModified.end()) - { - if (mShadersLastModified[sourceRelative] != lastModified) - { - // delete any outdated shaders based on this shader set - if (removeCache (it->first)) - removeBinaryCache = true; - } - } - else - { - // if we get here, this is either the first run or a new shader file was added - // in both cases we can safely delete - if (removeCache (it->first)) - removeBinaryCache = true; - } - mShaderSets.insert(std::make_pair(it->first, newSet)); - } - } + bool removeBinaryCache = reloadShaders(); // load materials { @@ -315,6 +239,8 @@ namespace sh Factory::~Factory () { + mShaderSets.clear(); + if (mPlatform->supportsShaderSerialization () && mWriteMicrocodeCache) { std::string file = mPlatform->getCacheFolder () + "/" + mBinaryCacheName; @@ -367,15 +293,16 @@ namespace sh while (i>0) { --i; - m->createForConfiguration (configuration, i); - - if (mListener) + if (m->createForConfiguration (configuration, i) && mListener) mListener->materialCreated (m, configuration, i); + else + return NULL; } - m->createForConfiguration (configuration, lodIndex); - if (mListener) + if (m->createForConfiguration (configuration, lodIndex) && mListener) mListener->materialCreated (m, configuration, lodIndex); + else + return NULL; } return m; } @@ -439,6 +366,12 @@ namespace sh ShaderSet* Factory::getShaderSet (const std::string& name) { + if (mShaderSets.find(name) == mShaderSets.end()) + { + std::stringstream msg; + msg << "Shader '" << name << "' not found"; + throw std::runtime_error(msg.str()); + } return &mShaderSets.find(name)->second; } @@ -466,6 +399,14 @@ namespace sh } } + void Factory::notifyConfigurationChanged() + { + for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) + { + it->second.destroyAll(); + } + } + MaterialInstance* Factory::getMaterialInstance (const std::string& name) { return findInstance(name); @@ -493,17 +434,21 @@ namespace sh return ""; } - PropertySetGet* Factory::getConfiguration (const std::string& name) + Configuration* Factory::getConfiguration (const std::string& name) { return &mConfigurations[name]; } - void Factory::registerConfiguration (const std::string& name, PropertySetGet configuration) + void Factory::createConfiguration (const std::string& name) { - mConfigurations[name] = configuration; mConfigurations[name].setParent (&mGlobalSettings); } + void Factory::destroyConfiguration(const std::string &name) + { + mConfigurations.erase(name); + } + void Factory::registerLodConfiguration (int index, PropertySetGet configuration) { mLodConfigurations[index] = configuration; @@ -571,17 +516,93 @@ namespace sh return p; } - void Factory::saveMaterials (const std::string& filename) + void Factory::saveAll () { - std::ofstream file; - file.open (filename.c_str ()); - + std::map files; for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) { - it->second.save(file); + if (it->second.getSourceFile().empty()) + continue; + if (files.find(it->second.getSourceFile()) == files.end()) + { + /// \todo check if this is actually the same file, since there can be different paths to the same file + std::ofstream* stream = new std::ofstream(); + stream->open (it->second.getSourceFile().c_str()); + + files[it->second.getSourceFile()] = stream; + } + it->second.save (*files[it->second.getSourceFile()]); } - file.close(); + for (std::map::iterator it = files.begin(); it != files.end(); ++it) + { + delete it->second; + } + files.clear(); + + for (ConfigurationMap::iterator it = mConfigurations.begin(); it != mConfigurations.end(); ++it) + { + if (it->second.getSourceFile().empty()) + continue; + if (files.find(it->second.getSourceFile()) == files.end()) + { + /// \todo check if this is actually the same file, since there can be different paths to the same file + std::ofstream* stream = new std::ofstream(); + stream->open (it->second.getSourceFile().c_str()); + + files[it->second.getSourceFile()] = stream; + } + it->second.save (it->first, *files[it->second.getSourceFile()]); + } + + for (std::map::iterator it = files.begin(); it != files.end(); ++it) + { + delete it->second; + } + } + + void Factory::listMaterials(std::vector &out) + { + for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) + { + out.push_back(it->first); + } + } + + void Factory::listGlobalSettings(std::map &out) + { + const PropertyMap& properties = mGlobalSettings.listProperties(); + + for (PropertyMap::const_iterator it = properties.begin(); it != properties.end(); ++it) + { + out[it->first] = retrieveValue(mGlobalSettings.getProperty(it->first), NULL).get(); + } + } + + void Factory::listConfigurationSettings(const std::string& name, std::map &out) + { + const PropertyMap& properties = mConfigurations[name].listProperties(); + + for (PropertyMap::const_iterator it = properties.begin(); it != properties.end(); ++it) + { + out[it->first] = retrieveValue(mConfigurations[name].getProperty(it->first), NULL).get(); + } + } + + void Factory::listConfigurationNames(std::vector &out) + { + for (ConfigurationMap::const_iterator it = mConfigurations.begin(); it != mConfigurations.end(); ++it) + { + out.push_back(it->first); + } + } + + void Factory::listShaderSets(std::vector &out) + { + for (ShaderSetMap::const_iterator it = mShaderSets.begin(); it != mShaderSets.end(); ++it) + { + out.push_back(it->first); + } } void Factory::_ensureMaterial(const std::string& name, const std::string& configuration) @@ -630,4 +651,147 @@ namespace sh } return ret; } + + bool Factory::reloadShaders() + { + mShaderSets.clear(); + notifyConfigurationChanged(); + + bool removeBinaryCache = false; + ScriptLoader shaderSetLoader(".shaderset"); + ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath()); + std::map nodes = shaderSetLoader.getAllConfigScripts(); + for (std::map ::const_iterator it = nodes.begin(); + it != nodes.end(); ++it) + { + if (!(it->second->getName() == "shader_set")) + { + std::cerr << "sh::Factory: Warning: Unsupported root node type \"" << it->second->getName() << "\" for file type .shaderset" << std::endl; + break; + } + + if (!it->second->findChild("profiles_cg")) + throw std::runtime_error ("missing \"profiles_cg\" field for \"" + it->first + "\""); + if (!it->second->findChild("profiles_hlsl")) + throw std::runtime_error ("missing \"profiles_hlsl\" field for \"" + it->first + "\""); + if (!it->second->findChild("source")) + throw std::runtime_error ("missing \"source\" field for \"" + it->first + "\""); + if (!it->second->findChild("type")) + throw std::runtime_error ("missing \"type\" field for \"" + it->first + "\""); + + std::vector profiles_cg; + boost::split (profiles_cg, it->second->findChild("profiles_cg")->getValue(), boost::is_any_of(" ")); + std::string cg_profile; + for (std::vector::iterator it2 = profiles_cg.begin(); it2 != profiles_cg.end(); ++it2) + { + if (mPlatform->isProfileSupported(*it2)) + { + cg_profile = *it2; + break; + } + } + + std::vector profiles_hlsl; + boost::split (profiles_hlsl, it->second->findChild("profiles_hlsl")->getValue(), boost::is_any_of(" ")); + std::string hlsl_profile; + for (std::vector::iterator it2 = profiles_hlsl.begin(); it2 != profiles_hlsl.end(); ++it2) + { + if (mPlatform->isProfileSupported(*it2)) + { + hlsl_profile = *it2; + break; + } + } + + std::string sourceAbsolute = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue(); + std::string sourceRelative = it->second->findChild("source")->getValue(); + + ShaderSet newSet (it->second->findChild("type")->getValue(), cg_profile, hlsl_profile, + sourceAbsolute, + mPlatform->getBasePath(), + it->first, + &mGlobalSettings); + + int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceAbsolute)); + mShadersLastModifiedNew[sourceRelative] = lastModified; + if (mShadersLastModified.find(sourceRelative) != mShadersLastModified.end()) + { + if (mShadersLastModified[sourceRelative] != lastModified) + { + // delete any outdated shaders based on this shader set + if (removeCache (it->first)) + removeBinaryCache = true; + } + } + else + { + // if we get here, this is either the first run or a new shader file was added + // in both cases we can safely delete + if (removeCache (it->first)) + removeBinaryCache = true; + } + mShaderSets.insert(std::make_pair(it->first, newSet)); + } + + // new is now current + mShadersLastModified = mShadersLastModifiedNew; + + return removeBinaryCache; + } + + void Factory::doMonitorShaderFiles() + { + bool reload=false; + ScriptLoader shaderSetLoader(".shaderset"); + ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath()); + std::map nodes = shaderSetLoader.getAllConfigScripts(); + for (std::map ::const_iterator it = nodes.begin(); + it != nodes.end(); ++it) + { + + std::string sourceAbsolute = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue(); + std::string sourceRelative = it->second->findChild("source")->getValue(); + + int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceAbsolute)); + if (mShadersLastModified.find(sourceRelative) != mShadersLastModified.end()) + { + if (mShadersLastModified[sourceRelative] != lastModified) + { + reload=true; + break; + } + } + } + if (reload) + reloadShaders(); + } + + void Factory::logError(const std::string &msg) + { + mErrorLog << msg << '\n'; + } + + std::string Factory::getErrorLog() + { + std::string errors = mErrorLog.str(); + mErrorLog.str(""); + return errors; + } + + void Factory::unloadUnreferencedMaterials() + { + for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) + { + if (it->second.getMaterial()->isUnreferenced()) + it->second.destroyAll(); + } + } + + void Configuration::save(const std::string& name, std::ofstream &stream) + { + stream << "configuration " << name << '\n'; + stream << "{\n"; + PropertySetGet::save(stream, "\t"); + stream << "}\n"; + } } diff --git a/extern/shiny/Main/Factory.hpp b/extern/shiny/Main/Factory.hpp index 846860b89a..97984609ea 100644 --- a/extern/shiny/Main/Factory.hpp +++ b/extern/shiny/Main/Factory.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "MaterialInstance.hpp" #include "ShaderSet.hpp" @@ -12,9 +13,21 @@ namespace sh { class Platform; + class Configuration : public PropertySetGet + { + public: + void setSourceFile (const std::string& file) { mSourceFile = file ; } + std::string getSourceFile () { return mSourceFile; } + + void save(const std::string& name, std::ofstream &stream); + + private: + std::string mSourceFile; + }; + typedef std::map MaterialMap; typedef std::map ShaderSetMap; - typedef std::map ConfigurationMap; + typedef std::map ConfigurationMap; typedef std::map LodConfigurationMap; typedef std::map LastModifiedMap; @@ -81,8 +94,8 @@ namespace sh /// Get a MaterialInstance by name MaterialInstance* getMaterialInstance (const std::string& name); - /// Register a configuration, which can then be used by switching the active material scheme - void registerConfiguration (const std::string& name, PropertySetGet configuration); + /// Create a configuration, which can then be altered by using Factory::getConfiguration + void createConfiguration (const std::string& name); /// Register a lod configuration, which can then be used by setting up lod distance values for the material \n /// 0 refers to highest lod, so use 1 or higher as index parameter @@ -125,8 +138,48 @@ namespace sh /// \note The default is off (no cache reading) void setReadMicrocodeCache(bool read) { mReadMicrocodeCache = read; } - /// Saves all the materials that were initially loaded from the file with this name - void saveMaterials (const std::string& filename); + /// Lists all materials currently registered with the factory. Whether they are + /// loaded or not does not matter. + void listMaterials (std::vector& out); + + /// Lists current name & value of all global settings. + void listGlobalSettings (std::map& out); + + /// Lists configuration names. + void listConfigurationNames (std::vector& out); + + /// Lists current name & value of settings for a given configuration. + void listConfigurationSettings (const std::string& name, std::map& out); + + /// Lists shader sets. + void listShaderSets (std::vector& out); + + /// \note This only works if microcode caching is disabled, as there is currently no way to remove the cache + /// through the Ogre API. Luckily, this is already fixed in Ogre 1.9. + bool reloadShaders(); + + /// Calls reloadShaders() if shader files have been modified since the last reload. + /// \note This only works if microcode caching is disabled, as there is currently no way to remove the cache + /// through the Ogre API. Luckily, this is already fixed in Ogre 1.9. + void doMonitorShaderFiles(); + + /// Unloads all materials that are currently not referenced. This will not unload the textures themselves, + /// but it will let go of the SharedPtr's to the textures, so that you may unload them if you so desire. \n + /// A good time to call this would be after a new level has been loaded, but just calling it occasionally after a period + /// of time should work just fine too. + void unloadUnreferencedMaterials(); + + void destroyConfiguration (const std::string& name); + + void notifyConfigurationChanged(); + + /// Saves all materials and configurations, by default to the file they were loaded from. + /// If you wish to save them elsewhere, use setSourceFile first. + void saveAll (); + + /// Returns the error log as a string, then clears it. + /// Note: Errors are also written to the standard error output, or thrown if they are fatal. + std::string getErrorLog (); static Factory& getInstance(); ///< Return instance of this class. @@ -137,11 +190,13 @@ namespace sh /// You will probably never have to use this. void _ensureMaterial(const std::string& name, const std::string& configuration); + + Configuration* getConfiguration (const std::string& name); + private: MaterialInstance* requestMaterial (const std::string& name, const std::string& configuration, unsigned short lodIndex); ShaderSet* getShaderSet (const std::string& name); - PropertySetGet* getConfiguration (const std::string& name); Platform* getPlatform (); PropertySetGet* getCurrentGlobalSettings(); @@ -163,6 +218,8 @@ namespace sh std::map mTextureAliasInstances; + void logError (const std::string& msg); + friend class Platform; friend class MaterialInstance; friend class ShaderInstance; @@ -179,6 +236,7 @@ namespace sh bool mWriteMicrocodeCache; bool mReadSourceCache; bool mWriteSourceCache; + std::stringstream mErrorLog; MaterialMap mMaterials; ShaderSetMap mShaderSets; diff --git a/extern/shiny/Main/Language.hpp b/extern/shiny/Main/Language.hpp index 20bf8ed61c..6b271cb86a 100644 --- a/extern/shiny/Main/Language.hpp +++ b/extern/shiny/Main/Language.hpp @@ -8,6 +8,7 @@ namespace sh Language_CG, Language_HLSL, Language_GLSL, + Language_GLSLES, Language_Count, Language_None }; diff --git a/extern/shiny/Main/MaterialInstance.cpp b/extern/shiny/Main/MaterialInstance.cpp index 0f8bcdda78..b8032d681e 100644 --- a/extern/shiny/Main/MaterialInstance.cpp +++ b/extern/shiny/Main/MaterialInstance.cpp @@ -1,6 +1,7 @@ #include "MaterialInstance.hpp" #include +#include #include "Factory.hpp" #include "ShaderSet.hpp" @@ -12,6 +13,7 @@ namespace sh , mShadersEnabled(true) , mFactory(f) , mListener(NULL) + , mFailedToCreate(false) { } @@ -46,6 +48,7 @@ namespace sh return; mMaterial->removeAll(); mTexUnits.clear(); + mFailedToCreate = false; } void MaterialInstance::setProperty (const std::string& name, PropertyValuePtr value) @@ -54,118 +57,136 @@ namespace sh destroyAll(); // trigger updates } - void MaterialInstance::createForConfiguration (const std::string& configuration, unsigned short lodIndex) + bool MaterialInstance::createForConfiguration (const std::string& configuration, unsigned short lodIndex) { - bool res = mMaterial->createConfiguration(configuration, lodIndex); - if (!res) - return; // listener was false positive + if (mFailedToCreate) + return false; + try{ + mMaterial->ensureLoaded(); + bool res = mMaterial->createConfiguration(configuration, lodIndex); + if (!res) + return false; // listener was false positive - if (mListener) - mListener->requestedConfiguration (this, configuration); + if (mListener) + mListener->requestedConfiguration (this, configuration); - mFactory->setActiveConfiguration (configuration); - mFactory->setActiveLodLevel (lodIndex); + mFactory->setActiveConfiguration (configuration); + mFactory->setActiveLodLevel (lodIndex); - bool allowFixedFunction = true; - if (!mShadersEnabled && hasProperty("allow_fixed_function")) - { - allowFixedFunction = retrieveValue(getProperty("allow_fixed_function"), NULL).get(); - } - - bool useShaders = mShadersEnabled || !allowFixedFunction; - - // get passes of the top-most parent - PassVector passes = getPasses(); - if (passes.size() == 0) - throw std::runtime_error ("material \"" + mName + "\" does not have any passes"); - - for (PassVector::iterator it = passes.begin(); it != passes.end(); ++it) - { - boost::shared_ptr pass = mMaterial->createPass (configuration, lodIndex); - it->copyAll (pass.get(), this); - - // texture samplers used in the shaders - std::vector usedTextureSamplersVertex; - std::vector usedTextureSamplersFragment; - - PropertySetGet* context = this; - - // create or retrieve shaders - bool hasVertex = it->hasProperty("vertex_program"); - bool hasFragment = it->hasProperty("fragment_program"); - if (useShaders) + bool allowFixedFunction = true; + if (!mShadersEnabled && hasProperty("allow_fixed_function")) { - it->setContext(context); - it->mShaderProperties.setContext(context); - if (hasVertex) + allowFixedFunction = retrieveValue(getProperty("allow_fixed_function"), NULL).get(); + } + + bool useShaders = mShadersEnabled || !allowFixedFunction; + + // get passes of the top-most parent + PassVector* passes = getParentPasses(); + if (passes->empty()) + throw std::runtime_error ("material \"" + mName + "\" does not have any passes"); + + for (PassVector::iterator it = passes->begin(); it != passes->end(); ++it) + { + boost::shared_ptr pass = mMaterial->createPass (configuration, lodIndex); + it->copyAll (pass.get(), this); + + // texture samplers used in the shaders + std::vector usedTextureSamplersVertex; + std::vector usedTextureSamplersFragment; + + PropertySetGet* context = this; + + // create or retrieve shaders + bool hasVertex = it->hasProperty("vertex_program") + && !retrieveValue(it->getProperty("vertex_program"), context).get().empty(); + bool hasFragment = it->hasProperty("fragment_program") + && !retrieveValue(it->getProperty("fragment_program"), context).get().empty(); + if (useShaders) { - ShaderSet* vertex = mFactory->getShaderSet(retrieveValue(it->getProperty("vertex_program"), context).get()); - ShaderInstance* v = vertex->getInstance(&it->mShaderProperties); - if (v) + it->setContext(context); + it->mShaderProperties.setContext(context); + if (hasVertex) { - pass->assignProgram (GPT_Vertex, v->getName()); - v->setUniformParameters (pass, &it->mShaderProperties); - - std::vector sharedParams = v->getSharedParameters (); - for (std::vector::iterator it = sharedParams.begin(); it != sharedParams.end(); ++it) + ShaderSet* vertex = mFactory->getShaderSet(retrieveValue(it->getProperty("vertex_program"), context).get()); + ShaderInstance* v = vertex->getInstance(&it->mShaderProperties); + if (v) { - pass->addSharedParameter (GPT_Vertex, *it); - } + pass->assignProgram (GPT_Vertex, v->getName()); + v->setUniformParameters (pass, &it->mShaderProperties); - std::vector vector = v->getUsedSamplers (); - usedTextureSamplersVertex.insert(usedTextureSamplersVertex.end(), vector.begin(), vector.end()); + std::vector sharedParams = v->getSharedParameters (); + for (std::vector::iterator it2 = sharedParams.begin(); it2 != sharedParams.end(); ++it2) + { + pass->addSharedParameter (GPT_Vertex, *it2); + } + + std::vector vector = v->getUsedSamplers (); + usedTextureSamplersVertex.insert(usedTextureSamplersVertex.end(), vector.begin(), vector.end()); + } + } + if (hasFragment) + { + ShaderSet* fragment = mFactory->getShaderSet(retrieveValue(it->getProperty("fragment_program"), context).get()); + ShaderInstance* f = fragment->getInstance(&it->mShaderProperties); + if (f) + { + pass->assignProgram (GPT_Fragment, f->getName()); + f->setUniformParameters (pass, &it->mShaderProperties); + + std::vector sharedParams = f->getSharedParameters (); + for (std::vector::iterator it2 = sharedParams.begin(); it2 != sharedParams.end(); ++it2) + { + pass->addSharedParameter (GPT_Fragment, *it2); + } + + std::vector vector = f->getUsedSamplers (); + usedTextureSamplersFragment.insert(usedTextureSamplersFragment.end(), vector.begin(), vector.end()); + } } } - if (hasFragment) + + // create texture units + std::vector* texUnits = &it->mTexUnits; + int i=0; + for (std::vector::iterator texIt = texUnits->begin(); texIt != texUnits->end(); ++texIt ) { - ShaderSet* fragment = mFactory->getShaderSet(retrieveValue(it->getProperty("fragment_program"), context).get()); - ShaderInstance* f = fragment->getInstance(&it->mShaderProperties); - if (f) + // only create those that are needed by the shader, OR those marked to be created in fixed function pipeline if shaders are disabled + bool foundVertex = std::find(usedTextureSamplersVertex.begin(), usedTextureSamplersVertex.end(), texIt->getName()) != usedTextureSamplersVertex.end(); + bool foundFragment = std::find(usedTextureSamplersFragment.begin(), usedTextureSamplersFragment.end(), texIt->getName()) != usedTextureSamplersFragment.end(); + if ( (foundVertex || foundFragment) + || (((!useShaders || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue(texIt->getProperty("create_in_ffp"), this).get())) { - pass->assignProgram (GPT_Fragment, f->getName()); - f->setUniformParameters (pass, &it->mShaderProperties); + boost::shared_ptr texUnit = pass->createTextureUnitState (texIt->getName()); + texIt->copyAll (texUnit.get(), context); - std::vector sharedParams = f->getSharedParameters (); - for (std::vector::iterator it = sharedParams.begin(); it != sharedParams.end(); ++it) + mTexUnits.push_back(texUnit); + + // set texture unit indices (required by GLSL) + if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) { - pass->addSharedParameter (GPT_Fragment, *it); - } + pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i); - std::vector vector = f->getUsedSamplers (); - usedTextureSamplersFragment.insert(usedTextureSamplersFragment.end(), vector.begin(), vector.end()); + ++i; + } } } } - // create texture units - std::vector texUnits = it->getTexUnits(); - int i=0; - for (std::vector::iterator texIt = texUnits.begin(); texIt != texUnits.end(); ++texIt ) - { - // only create those that are needed by the shader, OR those marked to be created in fixed function pipeline if shaders are disabled - bool foundVertex = std::find(usedTextureSamplersVertex.begin(), usedTextureSamplersVertex.end(), texIt->getName()) != usedTextureSamplersVertex.end(); - bool foundFragment = std::find(usedTextureSamplersFragment.begin(), usedTextureSamplersFragment.end(), texIt->getName()) != usedTextureSamplersFragment.end(); - if ( (foundVertex || foundFragment) - || (((!useShaders || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue(texIt->getProperty("create_in_ffp"), this).get())) - { - boost::shared_ptr texUnit = pass->createTextureUnitState (); - texIt->copyAll (texUnit.get(), context); + if (mListener) + mListener->createdConfiguration (this, configuration); + return true; - mTexUnits.push_back(texUnit); - - // set texture unit indices (required by GLSL) - if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) - { - pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i); - - ++i; - } - } - } + } catch (std::runtime_error& e) + { + destroyAll(); + mFailedToCreate = true; + std::stringstream msg; + msg << "Error while creating material " << mName << ": " << e.what(); + std::cerr << msg.str() << std::endl; + mFactory->logError(msg.str()); + return false; } - - if (mListener) - mListener->createdConfiguration (this, configuration); } Material* MaterialInstance::getMaterial () @@ -180,12 +201,23 @@ namespace sh return &mPasses.back(); } - PassVector MaterialInstance::getPasses() + void MaterialInstance::deletePass(unsigned int index) + { + assert(mPasses.size() > index); + mPasses.erase(mPasses.begin()+index); + } + + PassVector* MaterialInstance::getParentPasses() { if (mParent) - return static_cast(mParent)->getPasses(); + return static_cast(mParent)->getParentPasses(); else - return mPasses; + return &mPasses; + } + + PassVector* MaterialInstance::getPasses() + { + return &mPasses; } void MaterialInstance::setShadersEnabled (bool enabled) @@ -206,7 +238,7 @@ namespace sh if (mParent) { - stream << "\t" << static_cast(mParent)->getName() << "\n"; + stream << "\t" << "parent " << static_cast(mParent)->getName() << "\n"; } const PropertyMap& properties = listProperties (); @@ -215,6 +247,14 @@ namespace sh stream << "\t" << it->first << " " << retrieveValue(getProperty(it->first), NULL).get() << "\n"; } + for (PassVector::iterator it = mPasses.begin(); it != mPasses.end(); ++it) + { + stream << "\tpass" << '\n'; + stream << "\t{" << '\n'; + it->save(stream); + stream << "\t}" << '\n'; + } + stream << "}\n"; } } diff --git a/extern/shiny/Main/MaterialInstance.hpp b/extern/shiny/Main/MaterialInstance.hpp index 000f9d60c9..72c78c7b7c 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(){} }; /** @@ -40,8 +41,12 @@ namespace sh MaterialInstance (const std::string& name, Factory* f); virtual ~MaterialInstance (); + PassVector* getParentPasses(); ///< gets the passes of the top-most parent + + PassVector* getPasses(); ///< get our passes (for derived materials, none) + MaterialInstancePass* createPass (); - PassVector getPasses(); ///< gets the passes of the top-most parent + void deletePass (unsigned int index); /// @attention Because the backend material passes are created on demand, the returned material here might not contain anything yet! /// The only place where you should use this method, is for the MaterialInstance given by the MaterialListener::materialCreated event! @@ -54,25 +59,25 @@ namespace sh virtual void setProperty (const std::string& name, PropertyValuePtr value); - private: - void setParentInstance (const std::string& name); - std::string getParentInstance (); - - void create (Platform* platform); - void createForConfiguration (const std::string& configuration, unsigned short lodIndex); - - void destroyAll (); - - void setShadersEnabled (bool enabled); - void setSourceFile(const std::string& sourceFile) { mSourceFile = sourceFile; } std::string getSourceFile() { return mSourceFile; } ///< get the name of the file this material was read from, or empty if it was created dynamically by code + private: + void setParentInstance (const std::string& name); + std::string getParentInstance (); + + void create (Platform* platform); + bool createForConfiguration (const std::string& configuration, unsigned short lodIndex); + + void destroyAll (); + + void setShadersEnabled (bool enabled); + void save (std::ofstream& stream); - ///< this will only save the properties, not the passes and texture units, and as such - /// is only intended to be used for derived materials + + bool mFailedToCreate; friend class Factory; diff --git a/extern/shiny/Main/MaterialInstancePass.cpp b/extern/shiny/Main/MaterialInstancePass.cpp index b14476f4e7..a628cd64c7 100644 --- a/extern/shiny/Main/MaterialInstancePass.cpp +++ b/extern/shiny/Main/MaterialInstancePass.cpp @@ -1,5 +1,7 @@ #include "MaterialInstancePass.hpp" +#include + namespace sh { @@ -9,8 +11,25 @@ namespace sh return &mTexUnits.back(); } - std::vector MaterialInstancePass::getTexUnits () + void MaterialInstancePass::save(std::ofstream &stream) { - return mTexUnits; + if (mShaderProperties.listProperties().size()) + { + stream << "\t\t" << "shader_properties" << '\n'; + stream << "\t\t{\n"; + mShaderProperties.save(stream, "\t\t\t"); + stream << "\t\t}\n"; + } + + PropertySetGet::save(stream, "\t\t"); + + for (std::vector ::iterator it = mTexUnits.begin(); + it != mTexUnits.end(); ++it) + { + stream << "\t\ttexture_unit " << it->getName() << '\n'; + stream << "\t\t{\n"; + it->save(stream, "\t\t\t"); + stream << "\t\t}\n"; + } } } diff --git a/extern/shiny/Main/MaterialInstancePass.hpp b/extern/shiny/Main/MaterialInstancePass.hpp index 7d7330f705..3d83d8fa35 100644 --- a/extern/shiny/Main/MaterialInstancePass.hpp +++ b/extern/shiny/Main/MaterialInstancePass.hpp @@ -18,10 +18,10 @@ namespace sh public: MaterialInstanceTextureUnit* createTextureUnit (const std::string& name); + void save (std::ofstream& stream); + PropertySetGet mShaderProperties; - std::vector getTexUnits (); - private: std::vector mTexUnits; }; } diff --git a/extern/shiny/Main/MaterialInstanceTextureUnit.hpp b/extern/shiny/Main/MaterialInstanceTextureUnit.hpp index ae9f54fd2d..5ca400fd49 100644 --- a/extern/shiny/Main/MaterialInstanceTextureUnit.hpp +++ b/extern/shiny/Main/MaterialInstanceTextureUnit.hpp @@ -18,6 +18,7 @@ namespace sh public: MaterialInstanceTextureUnit (const std::string& name); std::string getName() const; + void setName (const std::string& name) { mName = name; } private: std::string mName; }; diff --git a/extern/shiny/Main/Platform.cpp b/extern/shiny/Main/Platform.cpp index 94b4f872ae..3eb7f4ad34 100644 --- a/extern/shiny/Main/Platform.cpp +++ b/extern/shiny/Main/Platform.cpp @@ -9,7 +9,7 @@ namespace sh Platform::Platform (const std::string& basePath) : mBasePath(basePath) , mCacheFolder("./") - , mShaderCachingEnabled(false) + , mFactory(NULL) { } @@ -57,11 +57,6 @@ namespace sh mCacheFolder = folder; } - void Platform::setShaderCachingEnabled (bool enabled) - { - mShaderCachingEnabled = enabled; - } - std::string Platform::getCacheFolder() const { return mCacheFolder; diff --git a/extern/shiny/Main/Platform.hpp b/extern/shiny/Main/Platform.hpp index 1b095e9576..24afea03ba 100644 --- a/extern/shiny/Main/Platform.hpp +++ b/extern/shiny/Main/Platform.hpp @@ -24,6 +24,7 @@ namespace sh class GpuProgram { public: + virtual ~GpuProgram() {} virtual bool getSupported () = 0; ///< @return true if the compilation was successful /// @param name name of the uniform in the shader @@ -35,8 +36,7 @@ namespace sh class TextureUnitState : public PropertySet { public: - virtual ~TextureUnitState(); - + virtual ~TextureUnitState(); virtual void setTextureName (const std::string& textureName) = 0; protected: @@ -46,7 +46,7 @@ namespace sh class Pass : public PropertySet { public: - virtual boost::shared_ptr createTextureUnitState () = 0; + virtual boost::shared_ptr createTextureUnitState (const std::string& name) = 0; virtual void assignProgram (GpuProgramType type, const std::string& name) = 0; /// @param type gpu program type @@ -68,6 +68,9 @@ namespace sh virtual bool createConfiguration (const std::string& name, unsigned short lodIndex) = 0; ///< @return false if already exists virtual void removeAll () = 0; ///< remove all configurations + virtual bool isUnreferenced() = 0; + virtual void ensureLoaded() = 0; + virtual void setLodLevels (const std::string& lodLevels) = 0; virtual void setShadowCasterMaterial (const std::string& name) = 0; @@ -79,8 +82,6 @@ namespace sh Platform (const std::string& basePath); virtual ~Platform (); - void setShaderCachingEnabled (bool enabled); - /// set the folder to use for shader caching void setCacheFolder (const std::string& folder); @@ -93,6 +94,8 @@ namespace sh const std::string& name, const std::string& profile, const std::string& source, Language lang) = 0; + virtual void destroyGpuProgram (const std::string& name) = 0; + virtual void setSharedParameter (const std::string& name, PropertyValuePtr value) = 0; virtual bool isProfileSupported (const std::string& profile) = 0; @@ -105,6 +108,7 @@ namespace sh friend class Factory; friend class MaterialInstance; friend class ShaderInstance; + friend class ShaderSet; protected: /** @@ -131,9 +135,6 @@ namespace sh std::string mCacheFolder; Factory* mFactory; - protected: - bool mShaderCachingEnabled; - private: void setFactory (Factory* factory); diff --git a/extern/shiny/Main/PropertyBase.cpp b/extern/shiny/Main/PropertyBase.cpp index 0c39e5c1e7..8592712fa9 100644 --- a/extern/shiny/Main/PropertyBase.cpp +++ b/extern/shiny/Main/PropertyBase.cpp @@ -6,6 +6,8 @@ #include #include +#include + namespace sh { @@ -39,8 +41,9 @@ namespace sh mValue = false; else { - std::cerr << "sh::BooleanValue: Warning: Unrecognized value \"" << in << "\" for property value of type BooleanValue" << std::endl; - mValue = false; + std::stringstream msg; + msg << "sh::BooleanValue: Warning: Unrecognized value \"" << in << "\" for property value of type BooleanValue"; + throw std::runtime_error(msg.str()); } } @@ -183,12 +186,16 @@ namespace sh void PropertySet::setProperty (const std::string& name, PropertyValuePtr &value, PropertySetGet* context) { if (!setPropertyOverride (name, value, context)) - std::cerr << "sh::PropertySet: Warning: No match for property with name '" << name << "'" << std::endl; + { + std::stringstream msg; + msg << "sh::PropertySet: Warning: No match for property with name '" << name << "'"; + throw std::runtime_error(msg.str()); + } } bool PropertySet::setPropertyOverride (const std::string& name, PropertyValuePtr &value, PropertySetGet* context) { - // if we got here, none of the sub-classes was able to make use of the property + // if we got here, none of the sub-classes were able to make use of the property return false; } @@ -226,6 +233,11 @@ namespace sh mProperties [name] = value; } + void PropertySetGet::deleteProperty(const std::string &name) + { + mProperties.erase(name); + } + PropertyValuePtr& PropertySetGet::getProperty (const std::string& name) { bool found = (mProperties.find(name) != mProperties.end()); @@ -241,7 +253,7 @@ namespace sh return mProperties[name]; } - bool PropertySetGet::hasProperty (const std::string& name) + bool PropertySetGet::hasProperty (const std::string& name) const { bool found = (mProperties.find(name) != mProperties.end()); @@ -256,13 +268,35 @@ namespace sh return true; } - void PropertySetGet::copyAll (PropertySet* target, PropertySetGet* context) + void PropertySetGet::copyAll (PropertySet* target, PropertySetGet* context, bool copyParent) { - if (mParent) + if (mParent && copyParent) mParent->copyAll (target, context); for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it) { target->setProperty(it->first, it->second, context); } } + + void PropertySetGet::copyAll (PropertySetGet* target, PropertySetGet* context, bool copyParent) + { + if (mParent && copyParent) + mParent->copyAll (target, context); + for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it) + { + std::string val = retrieveValue(it->second, this).get(); + target->setProperty(it->first, sh::makeProperty(new sh::StringValue(val))); + } + } + + void PropertySetGet::save(std::ofstream &stream, const std::string& indentation) + { + for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it) + { + if (typeid( *(it->second) ) == typeid(LinkedValue)) + stream << indentation << it->first << " " << "$" + static_cast(&*(it->second))->_getStringValue() << '\n'; + else + stream << indentation << it->first << " " << retrieveValue(it->second, this).get() << '\n'; + } + } } diff --git a/extern/shiny/Main/PropertyBase.hpp b/extern/shiny/Main/PropertyBase.hpp index 240acce81b..13809443e2 100644 --- a/extern/shiny/Main/PropertyBase.hpp +++ b/extern/shiny/Main/PropertyBase.hpp @@ -133,6 +133,7 @@ namespace sh class PropertySet { public: + virtual ~PropertySet() {} void setProperty (const std::string& name, PropertyValuePtr& value, PropertySetGet* context); protected: @@ -151,18 +152,26 @@ namespace sh virtual ~PropertySetGet() {} - void copyAll (PropertySet* target, PropertySetGet* context); ///< call setProperty for each property/value pair stored in \a this + void save (std::ofstream& stream, const std::string& indentation); + + void copyAll (PropertySet* target, PropertySetGet* context, bool copyParent=true); + ///< call setProperty for each property/value pair stored in \a this + void copyAll (PropertySetGet* target, PropertySetGet* context, bool copyParent=true); + ///< call setProperty for each property/value pair stored in \a this void setParent (PropertySetGet* parent); + PropertySetGet* getParent () { return mParent; } void setContext (PropertySetGet* context); PropertySetGet* getContext(); virtual void setProperty (const std::string& name, PropertyValuePtr value); PropertyValuePtr& getProperty (const std::string& name); + void deleteProperty (const std::string& name); + const PropertyMap& listProperties() { return mProperties; } - bool hasProperty (const std::string& name); + bool hasProperty (const std::string& name) const; private: PropertyMap mProperties; @@ -225,7 +234,7 @@ namespace sh template /// Create a property of any type - /// Example: sh::makeProperty\ (new sh::Vector4(1, 1, 1, 1)) + /// Example: sh::makeProperty (new sh::Vector4(1, 1, 1, 1)) inline PropertyValuePtr makeProperty (T* p) { return PropertyValuePtr ( static_cast(p) ); diff --git a/extern/shiny/Main/ScriptLoader.cpp b/extern/shiny/Main/ScriptLoader.cpp index 93d728b023..bafe07fc8e 100644 --- a/extern/shiny/Main/ScriptLoader.cpp +++ b/extern/shiny/Main/ScriptLoader.cpp @@ -24,6 +24,10 @@ namespace sh } ScriptLoader::ScriptLoader(const std::string& fileEnding) + : mLoadOrder(0) + , mToken(TOKEN_NewLine) + , mLastToken(TOKEN_NewLine) + { mFileEnding = fileEnding; } @@ -36,7 +40,7 @@ namespace sh void ScriptLoader::clearScriptList() { std::map ::iterator i; - for (i = m_scriptList.begin(); i != m_scriptList.end(); i++) + for (i = m_scriptList.begin(); i != m_scriptList.end(); ++i) { delete i->second; } @@ -293,7 +297,7 @@ namespace sh { //Delete all children std::vector::iterator i; - for (i = mChildren.begin(); i != mChildren.end(); i++) + for (i = mChildren.begin(); i != mChildren.end(); ++i) { ScriptNode *node = *i; node->mRemoveSelf = false; @@ -323,15 +327,24 @@ namespace sh ScriptNode *ScriptNode::findChild(const std::string &name, bool recursive) { - int indx, prevC, nextC; + int indx; int childCount = (int)mChildren.size(); if (mLastChildFound != -1) { //If possible, try checking the nodes neighboring the last successful search //(often nodes searched for in sequence, so this will boost search speeds). - prevC = mLastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1; - nextC = mLastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1; + int prevC = mLastChildFound-1; + if (prevC < 0) + prevC = 0; + else if (prevC >= childCount) + prevC = childCount-1; + int nextC = mLastChildFound+1; + if (nextC < 0) + nextC = 0; + else if (nextC >= childCount) + nextC = childCount-1; + for (indx = prevC; indx <= nextC; ++indx) { ScriptNode *node = mChildren[indx]; diff --git a/extern/shiny/Main/ShaderInstance.cpp b/extern/shiny/Main/ShaderInstance.cpp index 1539128aba..270aaba68f 100644 --- a/extern/shiny/Main/ShaderInstance.cpp +++ b/extern/shiny/Main/ShaderInstance.cpp @@ -22,8 +22,11 @@ namespace return "SH_CG"; else if (lang == sh::Language_HLSL) return "SH_HLSL"; - else //if (lang == sh::Language_GLSL) + else if (lang == sh::Language_GLSL) return "SH_GLSL"; + else if (lang == sh::Language_GLSLES) + return "SH_GLSLES"; + throw std::runtime_error("invalid language"); } char getComponent(int num) @@ -194,13 +197,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 +210,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..8fb530d394 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,12 +27,24 @@ 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(); parse(); } + ShaderSet::~ShaderSet() + { + for (ShaderInstanceMap::iterator it = mInstances.begin(); it != mInstances.end(); ++it) + { + sh::Factory::getInstance().getPlatform()->destroyGpuProgram(it->second.getName()); + } + } + void ShaderSet::parse() { std::string currentToken; @@ -52,6 +65,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 +154,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..988c6769fa 100644 --- a/extern/shiny/Main/ShaderSet.hpp +++ b/extern/shiny/Main/ShaderSet.hpp @@ -21,6 +21,7 @@ namespace sh public: ShaderSet (const std::string& type, const std::string& cgProfile, const std::string& hlslProfile, const std::string& sourceFile, const std::string& basePath, const std::string& name, PropertySetGet* globalSettingsPtr); + ~ShaderSet(); /// Retrieve a shader instance for the given properties. \n /// If a \a ShaderInstance with the same properties exists already, simply returns this instance. \n @@ -53,6 +54,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/extern/shiny/Platforms/Ogre/OgreGpuProgram.cpp b/extern/shiny/Platforms/Ogre/OgreGpuProgram.cpp index fe5aa2fe77..e718540197 100644 --- a/extern/shiny/Platforms/Ogre/OgreGpuProgram.cpp +++ b/extern/shiny/Platforms/Ogre/OgreGpuProgram.cpp @@ -28,9 +28,8 @@ namespace sh t = Ogre::GPT_FRAGMENT_PROGRAM; mProgram = mgr.createProgram(name, resourceGroup, lang, t); - if (lang != "glsl") + if (lang != "glsl" && lang != "glsles") mProgram->setParameter("entry_point", "main"); - if (lang == "hlsl") mProgram->setParameter("target", profile); else if (lang == "cg") diff --git a/extern/shiny/Platforms/Ogre/OgreMaterial.cpp b/extern/shiny/Platforms/Ogre/OgreMaterial.cpp index 4a550b8bff..77091fe03d 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterial.cpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterial.cpp @@ -15,6 +15,7 @@ namespace sh OgreMaterial::OgreMaterial (const std::string& name, const std::string& resourceGroup) : Material() { + mName = name; assert (Ogre::MaterialManager::getSingleton().getByName(name).isNull() && "Material already exists"); mMaterial = Ogre::MaterialManager::getSingleton().create (name, resourceGroup); mMaterial->removeAllTechniques(); @@ -22,9 +23,22 @@ namespace sh mMaterial->compile(); } + void OgreMaterial::ensureLoaded() + { + if (mMaterial.isNull()) + mMaterial = Ogre::MaterialManager::getSingleton().getByName(mName); + } + + bool OgreMaterial::isUnreferenced() + { + // Resource system internals hold 3 shared pointers, we hold one, so usecount of 4 means unused + return (!mMaterial.isNull() && mMaterial.useCount() <= Ogre::ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS+1); + } + OgreMaterial::~OgreMaterial() { - Ogre::MaterialManager::getSingleton().remove(mMaterial->getName()); + if (!mMaterial.isNull()) + Ogre::MaterialManager::getSingleton().remove(mMaterial->getName()); } boost::shared_ptr OgreMaterial::createPass (const std::string& configuration, unsigned short lodIndex) @@ -34,6 +48,8 @@ namespace sh void OgreMaterial::removeAll () { + if (mMaterial.isNull()) + return; mMaterial->removeAllTechniques(); mMaterial->createTechnique()->setSchemeName (sDefaultTechniqueName); mMaterial->compile(); diff --git a/extern/shiny/Platforms/Ogre/OgreMaterial.hpp b/extern/shiny/Platforms/Ogre/OgreMaterial.hpp index bec23f0b6f..bfa6e2c6f8 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterial.hpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterial.hpp @@ -18,6 +18,9 @@ namespace sh virtual boost::shared_ptr createPass (const std::string& configuration, unsigned short lodIndex); virtual bool createConfiguration (const std::string& name, unsigned short lodIndex); + virtual bool isUnreferenced(); + virtual void ensureLoaded(); + virtual void removeAll (); Ogre::MaterialPtr getOgreMaterial(); @@ -30,6 +33,7 @@ namespace sh private: Ogre::MaterialPtr mMaterial; + std::string mName; std::string mShadowCasterMaterial; }; diff --git a/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp b/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp index 4ec43fcaeb..f45e641557 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp @@ -2,6 +2,8 @@ #include +#include + namespace sh { void OgreMaterialSerializer::reset() @@ -44,6 +46,13 @@ namespace sh bool OgreMaterialSerializer::setTextureUnitProperty (const std::string& param, std::string value, Ogre::TextureUnitState* t) { + // quick access to automip setting, without having to use 'texture' which doesn't like spaces in filenames + if (param == "num_mipmaps") + { + t->setNumMipmaps(Ogre::StringConverter::parseInt(value)); + return true; + } + reset(); mScriptContext.section = Ogre::MSS_TEXTUREUNIT; diff --git a/extern/shiny/Platforms/Ogre/OgrePass.cpp b/extern/shiny/Platforms/Ogre/OgrePass.cpp index 3ed48b96f8..3a25c5c740 100644 --- a/extern/shiny/Platforms/Ogre/OgrePass.cpp +++ b/extern/shiny/Platforms/Ogre/OgrePass.cpp @@ -20,9 +20,9 @@ namespace sh mPass = t->createPass(); } - boost::shared_ptr OgrePass::createTextureUnitState () + boost::shared_ptr OgrePass::createTextureUnitState (const std::string& name) { - return boost::shared_ptr (new OgreTextureUnitState (this)); + return boost::shared_ptr (new OgreTextureUnitState (this, name)); } void OgrePass::assignProgram (GpuProgramType type, const std::string& name) @@ -105,7 +105,17 @@ namespace sh else if (type == GPT_Fragment) params = mPass->getFragmentProgramParameters(); - params->addSharedParameters (name); + try + { + params->addSharedParameters (name); + } + catch (Ogre::Exception& e) + { + std::stringstream msg; + msg << "Could not create a shared parameter instance for '" + << name << "'. Make sure this shared parameter has a value set (via Factory::setSharedParameter)!"; + throw std::runtime_error(msg.str()); + } } void OgrePass::setTextureUnitIndex (int programType, const std::string& name, int index) diff --git a/extern/shiny/Platforms/Ogre/OgrePass.hpp b/extern/shiny/Platforms/Ogre/OgrePass.hpp index da67a1a2a5..e7cfd4e337 100644 --- a/extern/shiny/Platforms/Ogre/OgrePass.hpp +++ b/extern/shiny/Platforms/Ogre/OgrePass.hpp @@ -14,7 +14,7 @@ namespace sh public: OgrePass (OgreMaterial* parent, const std::string& configuration, unsigned short lodIndex); - virtual boost::shared_ptr createTextureUnitState (); + virtual boost::shared_ptr createTextureUnitState (const std::string& name); virtual void assignProgram (GpuProgramType type, const std::string& name); Ogre::Pass* getOgrePass(); diff --git a/extern/shiny/Platforms/Ogre/OgrePlatform.cpp b/extern/shiny/Platforms/Ogre/OgrePlatform.cpp index 357949402f..3725d5f354 100644 --- a/extern/shiny/Platforms/Ogre/OgrePlatform.cpp +++ b/extern/shiny/Platforms/Ogre/OgrePlatform.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "OgreMaterial.hpp" @@ -23,7 +24,9 @@ namespace return "hlsl"; else if (lang == sh::Language_GLSL) return "glsl"; - throw std::runtime_error ("invalid language, valid are: cg, hlsl, glsl"); + else if (lang == sh::Language_GLSLES) + return "glsles"; + throw std::runtime_error ("invalid language, valid are: cg, hlsl, glsl, glsles"); } } @@ -76,6 +79,11 @@ namespace sh return boost::shared_ptr (material); } + void OgrePlatform::destroyGpuProgram(const std::string &name) + { + Ogre::HighLevelGpuProgramManager::getSingleton().remove(name); + } + boost::shared_ptr OgrePlatform::createGpuProgram ( GpuProgramType type, const std::string& compileArguments, @@ -122,6 +130,7 @@ namespace sh if (mSharedParameters.find(name) == mSharedParameters.end()) { params = Ogre::GpuProgramManager::getSingleton().createSharedParameters(name); + Ogre::GpuConstantType type; if (typeid(*value) == typeid(Vector4)) type = Ogre::GCT_FLOAT4; diff --git a/extern/shiny/Platforms/Ogre/OgrePlatform.hpp b/extern/shiny/Platforms/Ogre/OgrePlatform.hpp index d115c46bb4..d3a1a83608 100644 --- a/extern/shiny/Platforms/Ogre/OgrePlatform.hpp +++ b/extern/shiny/Platforms/Ogre/OgrePlatform.hpp @@ -47,6 +47,8 @@ namespace sh const std::string& name, const std::string& profile, const std::string& source, Language lang); + virtual void destroyGpuProgram (const std::string& name); + virtual void setSharedParameter (const std::string& name, PropertyValuePtr value); friend class ShaderInstance; diff --git a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp index 0938cf667d..54dda3523f 100644 --- a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp +++ b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp @@ -6,10 +6,11 @@ namespace sh { - OgreTextureUnitState::OgreTextureUnitState (OgrePass* parent) + OgreTextureUnitState::OgreTextureUnitState (OgrePass* parent, const std::string& name) : TextureUnitState() { mTextureUnitState = parent->getOgrePass()->createTextureUnitState(""); + mTextureUnitState->setName(name); } bool OgreTextureUnitState::setPropertyOverride (const std::string &name, PropertyValuePtr& value, PropertySetGet* context) diff --git a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.hpp b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.hpp index d36f4b945a..0f1914f62e 100644 --- a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.hpp +++ b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.hpp @@ -12,7 +12,7 @@ namespace sh class OgreTextureUnitState : public TextureUnitState { public: - OgreTextureUnitState (OgrePass* parent); + OgreTextureUnitState (OgrePass* parent, const std::string& name); virtual void setTextureName (const std::string& textureName); diff --git a/extern/shiny/Preprocessor/aq.cpp b/extern/shiny/Preprocessor/aq.cpp deleted file mode 100644 index b81d5b3324..0000000000 --- a/extern/shiny/Preprocessor/aq.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001 Daniel C. Nuffer. - Copyright (c) 2001-2011 Hartmut Kaiser. - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include - -#include -#include - -#include // configuration data -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -namespace boost { -namespace wave { -namespace cpplexer { -namespace re2clex { - -int aq_grow(aq_queue q) -{ - using namespace std; // some systems have memcpy/realloc in std - std::size_t new_size = q->max_size << 1; - aq_stdelement* new_queue = (aq_stdelement*)realloc(q->queue, - new_size * sizeof(aq_stdelement)); - - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->max_size < 100000); - BOOST_ASSERT(q->size <= q->max_size); - -#define ASSERT_SIZE BOOST_ASSERT( \ - ((q->tail + q->max_size + 1) - q->head) % q->max_size == \ - q->size % q->max_size) - - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - if (!new_queue) - { - BOOST_ASSERT(0); - return 0; - } - - q->queue = new_queue; - if (q->tail <= q->head) /* tail has wrapped around */ - { - /* move the tail from the beginning to the end */ - memcpy(q->queue + q->max_size, q->queue, - (q->tail + 1) * sizeof(aq_stdelement)); - q->tail += q->max_size; - } - q->max_size = new_size; - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return 1; -} - -int aq_enqueue(aq_queue q, aq_stdelement e) -{ - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - - if (AQ_FULL(q)) - if (!aq_grow(q)) - return 0; - - ++q->tail; - if (q->tail == q->max_size) - q->tail = 0; - - q->queue[q->tail] = e; - ++q->size; - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return 1; -} - -int aq_enqueue_front(aq_queue q, aq_stdelement e) -{ - BOOST_ASSERT(NULL != q); - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - - if (AQ_FULL(q)) - if (!aq_grow(q)) - return 0; - - if (q->head == 0) - q->head = q->max_size - 1; - else - --q->head; - - q->queue[q->head] = e; - ++q->size; - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return 1; -} - -int aq_serve(aq_queue q, aq_stdelement *e) -{ - - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - - if (AQ_EMPTY(q)) - return 0; - - *e = q->queue[q->head]; - return aq_pop(q); -} - -int aq_pop(aq_queue q) -{ - - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - - if (AQ_EMPTY(q)) - return 0; - - ++q->head; - if (q->head == q->max_size) - q->head = 0; - --q->size; - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return 1; -} - -aq_queue aq_create(void) -{ - aq_queue q; - - using namespace std; // some systems have malloc in std - q = (aq_queue)malloc(sizeof(aq_queuetype)); - if (!q) - { - return 0; - } - - q->max_size = 8; /* initial size */ - q->queue = (aq_stdelement*)malloc( - sizeof(aq_stdelement) * q->max_size); - if (!q->queue) - { - free(q); - return 0; - } - - q->head = 0; - q->tail = q->max_size - 1; - q->size = 0; - - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return q; -} - -void aq_terminate(aq_queue q) -{ - using namespace std; // some systems have free in std - - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - free(q->queue); - free(q); -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace re2clex -} // namespace cpplexer -} // namespace wave -} // namespace boost - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - diff --git a/extern/shiny/Preprocessor/cpp_re.cpp b/extern/shiny/Preprocessor/cpp_re.cpp deleted file mode 100644 index 69d77c3726..0000000000 --- a/extern/shiny/Preprocessor/cpp_re.cpp +++ /dev/null @@ -1,442 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - - Copyright (c) 2001 Daniel C. Nuffer - Copyright (c) 2001-2011 Hartmut Kaiser. - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - TODO: - It also may be necessary to add $ to identifiers, for asm. - handle errors better. - have some easier way to parse strings instead of files (done) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include - -#include -#include -#include -#include -#include -#include -#include - -#include // configuration data - -#if defined(BOOST_HAS_UNISTD_H) -#include -#else -#include -#endif - -#include -#include - -#include -#include -#include -#include -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -#if defined(BOOST_MSVC) -#pragma warning (disable: 4101) // 'foo' : unreferenced local variable -#pragma warning (disable: 4102) // 'foo' : unreferenced label -#endif - -/////////////////////////////////////////////////////////////////////////////// -#define BOOST_WAVE_BSIZE 196608 - -#define YYCTYPE uchar -#define YYCURSOR cursor -#define YYLIMIT limit -#define YYMARKER marker -#define YYFILL(n) \ - { \ - cursor = uchar_wrapper(fill(s, cursor), cursor.column); \ - limit = uchar_wrapper (s->lim); \ - } \ - /**/ - -#include - -/////////////////////////////////////////////////////////////////////////////// -#define BOOST_WAVE_UPDATE_CURSOR() \ - { \ - s->line += count_backslash_newlines(s, cursor); \ - s->curr_column = cursor.column; \ - s->cur = cursor; \ - s->lim = limit; \ - s->ptr = marker; \ - } \ - /**/ - -/////////////////////////////////////////////////////////////////////////////// -#define BOOST_WAVE_RET(i) \ - { \ - BOOST_WAVE_UPDATE_CURSOR() \ - if (s->cur > s->lim) \ - return T_EOF; /* may happen for empty files */ \ - return (i); \ - } \ - /**/ - -/////////////////////////////////////////////////////////////////////////////// -namespace boost { -namespace wave { -namespace cpplexer { -namespace re2clex { - -#define RE2C_ASSERT BOOST_ASSERT - -int get_one_char(Scanner *s) -{ - if (0 != s->act) { - RE2C_ASSERT(s->first != 0 && s->last != 0); - RE2C_ASSERT(s->first <= s->act && s->act <= s->last); - if (s->act < s->last) - return *(s->act)++; - } - return -1; -} - -std::ptrdiff_t rewind_stream (Scanner *s, int cnt) -{ - if (0 != s->act) { - RE2C_ASSERT(s->first != 0 && s->last != 0); - s->act += cnt; - RE2C_ASSERT(s->first <= s->act && s->act <= s->last); - return s->act - s->first; - } - return 0; -} - -std::size_t get_first_eol_offset(Scanner* s) -{ - if (!AQ_EMPTY(s->eol_offsets)) - { - return s->eol_offsets->queue[s->eol_offsets->head]; - } - else - { - return (unsigned int)-1; - } -} - -void adjust_eol_offsets(Scanner* s, std::size_t adjustment) -{ - aq_queue q; - std::size_t i; - - if (!s->eol_offsets) - s->eol_offsets = aq_create(); - - q = s->eol_offsets; - - if (AQ_EMPTY(q)) - return; - - i = q->head; - while (i != q->tail) - { - if (adjustment > q->queue[i]) - q->queue[i] = 0; - else - q->queue[i] -= adjustment; - ++i; - if (i == q->max_size) - i = 0; - } - if (adjustment > q->queue[i]) - q->queue[i] = 0; - else - q->queue[i] -= adjustment; -} - -int count_backslash_newlines(Scanner *s, uchar *cursor) -{ - std::size_t diff, offset; - int skipped = 0; - - /* figure out how many backslash-newlines skipped over unknowingly. */ - diff = cursor - s->bot; - offset = get_first_eol_offset(s); - while (offset <= diff && offset != (unsigned int)-1) - { - skipped++; - aq_pop(s->eol_offsets); - offset = get_first_eol_offset(s); - } - return skipped; -} - -bool is_backslash(uchar *p, uchar *end, int &len) -{ - if (*p == '\\') { - len = 1; - return true; - } - else if (*p == '?' && *(p+1) == '?' && (p+2 < end && *(p+2) == '/')) { - len = 3; - return true; - } - return false; -} - -uchar *fill(Scanner *s, uchar *cursor) -{ - using namespace std; // some systems have memcpy etc. in namespace std - if(!s->eof) - { - uchar* p; - std::ptrdiff_t cnt = s->tok - s->bot; - if(cnt) - { - if (NULL == s->lim) - s->lim = s->top; - memmove(s->bot, s->tok, s->lim - s->tok); - s->tok = s->cur = s->bot; - s->ptr -= cnt; - cursor -= cnt; - s->lim -= cnt; - adjust_eol_offsets(s, cnt); - } - - if((s->top - s->lim) < BOOST_WAVE_BSIZE) - { - uchar *buf = (uchar*) malloc(((s->lim - s->bot) + BOOST_WAVE_BSIZE)*sizeof(uchar)); - if (buf == 0) - { - using namespace std; // some systems have printf in std - if (0 != s->error_proc) { - (*s->error_proc)(s, lexing_exception::unexpected_error, - "Out of memory!"); - } - else - printf("Out of memory!\n"); - - /* get the scanner to stop */ - *cursor = 0; - return cursor; - } - - memmove(buf, s->tok, s->lim - s->tok); - s->tok = s->cur = buf; - s->ptr = &buf[s->ptr - s->bot]; - cursor = &buf[cursor - s->bot]; - s->lim = &buf[s->lim - s->bot]; - s->top = &s->lim[BOOST_WAVE_BSIZE]; - free(s->bot); - s->bot = buf; - } - - if (s->act != 0) { - cnt = s->last - s->act; - if (cnt > BOOST_WAVE_BSIZE) - cnt = BOOST_WAVE_BSIZE; - memmove(s->lim, s->act, cnt); - s->act += cnt; - if (cnt != BOOST_WAVE_BSIZE) - { - s->eof = &s->lim[cnt]; *(s->eof)++ = '\0'; - } - } - - /* backslash-newline erasing time */ - - /* first scan for backslash-newline and erase them */ - for (p = s->lim; p < s->lim + cnt - 2; ++p) - { - int len = 0; - if (is_backslash(p, s->lim + cnt, len)) - { - if (*(p+len) == '\n') - { - int offset = len + 1; - memmove(p, p + offset, s->lim + cnt - p - offset); - cnt -= offset; - --p; - aq_enqueue(s->eol_offsets, p - s->bot + 1); - } - else if (*(p+len) == '\r') - { - if (*(p+len+1) == '\n') - { - int offset = len + 2; - memmove(p, p + offset, s->lim + cnt - p - offset); - cnt -= offset; - --p; - } - else - { - int offset = len + 1; - memmove(p, p + offset, s->lim + cnt - p - offset); - cnt -= offset; - --p; - } - aq_enqueue(s->eol_offsets, p - s->bot + 1); - } - } - } - - /* FIXME: the following code should be fixed to recognize correctly the - trigraph backslash token */ - - /* check to see if what we just read ends in a backslash */ - if (cnt >= 2) - { - uchar last = s->lim[cnt-1]; - uchar last2 = s->lim[cnt-2]; - /* check \ EOB */ - if (last == '\\') - { - int next = get_one_char(s); - /* check for \ \n or \ \r or \ \r \n straddling the border */ - if (next == '\n') - { - --cnt; /* chop the final \, we've already read the \n. */ - aq_enqueue(s->eol_offsets, cnt + (s->lim - s->bot)); - } - else if (next == '\r') - { - int next2 = get_one_char(s); - if (next2 == '\n') - { - --cnt; /* skip the backslash */ - } - else - { - /* rewind one, and skip one char */ - rewind_stream(s, -1); - --cnt; - } - aq_enqueue(s->eol_offsets, cnt + (s->lim - s->bot)); - } - else if (next != -1) /* -1 means end of file */ - { - /* next was something else, so rewind the stream */ - rewind_stream(s, -1); - } - } - /* check \ \r EOB */ - else if (last == '\r' && last2 == '\\') - { - int next = get_one_char(s); - if (next == '\n') - { - cnt -= 2; /* skip the \ \r */ - } - else - { - /* rewind one, and skip two chars */ - rewind_stream(s, -1); - cnt -= 2; - } - aq_enqueue(s->eol_offsets, cnt + (s->lim - s->bot)); - } - /* check \ \n EOB */ - else if (last == '\n' && last2 == '\\') - { - cnt -= 2; - aq_enqueue(s->eol_offsets, cnt + (s->lim - s->bot)); - } - } - - s->lim += cnt; - if (s->eof) /* eof needs adjusting if we erased backslash-newlines */ - { - s->eof = s->lim; - *(s->eof)++ = '\0'; - } - } - return cursor; -} - -/////////////////////////////////////////////////////////////////////////////// -// Special wrapper class holding the current cursor position -struct uchar_wrapper -{ - uchar_wrapper (uchar *base_cursor, unsigned int column = 1) - : base_cursor(base_cursor), column(column) - {} - - uchar_wrapper& operator++() - { - ++base_cursor; - ++column; - return *this; - } - - uchar_wrapper& operator--() - { - --base_cursor; - --column; - return *this; - } - - uchar operator* () const - { - return *base_cursor; - } - - operator uchar *() const - { - return base_cursor; - } - - friend std::ptrdiff_t - operator- (uchar_wrapper const& lhs, uchar_wrapper const& rhs) - { - return lhs.base_cursor - rhs.base_cursor; - } - - uchar *base_cursor; - unsigned int column; -}; - -/////////////////////////////////////////////////////////////////////////////// -boost::wave::token_id scan(Scanner *s) -{ - BOOST_ASSERT(0 != s->error_proc); // error handler must be given - - uchar_wrapper cursor (s->tok = s->cur, s->column = s->curr_column); - uchar_wrapper marker (s->ptr); - uchar_wrapper limit (s->lim); - -// include the correct Re2C token definition rules -#if BOOST_WAVE_USE_STRICT_LEXER != 0 -#include "strict_cpp_re.inc" -#else -#include "cpp_re.inc" -#endif - -} /* end of scan */ - -/////////////////////////////////////////////////////////////////////////////// -} // namespace re2clex -} // namespace cpplexer -} // namespace wave -} // namespace boost - -#undef BOOST_WAVE_RET -#undef BOOST_WAVE_BSIZE -#undef YYCTYPE -#undef YYCURSOR -#undef YYLIMIT -#undef YYMARKER -#undef YYFILL - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - diff --git a/extern/shiny/Preprocessor/cpp_re.inc b/extern/shiny/Preprocessor/cpp_re.inc deleted file mode 100644 index afb7fc189e..0000000000 --- a/extern/shiny/Preprocessor/cpp_re.inc +++ /dev/null @@ -1,9044 +0,0 @@ -/* Generated by re2c 0.13.5 on Sun Jan 09 15:38:23 2011 */ -#line 1 "cpp.re" -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - - Copyright (c) 2001 Daniel C. Nuffer - Copyright (c) 2001-2011 Hartmut Kaiser. - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - This is a lexer conforming to the Standard with a few exceptions. - So it does allow the '$' to be part of identifiers. If you need strict - Standards conforming behaviour, please include the lexer definition - provided in the file strict_cpp.re. - - TODO: - handle errors better. -=============================================================================*/ - -#line 40 "cpp.re" - - - -#line 25 "cpp_re.inc" -{ - YYCTYPE yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - /* table 1 .. 8: 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 60, 32, 60, 60, 64, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 60, 60, 52, 60, 60, 60, 60, 56, - 60, 60, 156, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 44, 57, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 58, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - /* table 9 .. 12: 256 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 80, 0, 80, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 80, 64, 0, 64, 96, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 64, 64, 64, 64, 64, 0, - 64, 224, 224, 224, 224, 224, 224, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 0, 64, 64, 96, - 64, 224, 224, 224, 224, 224, 224, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - }; - - if ((YYLIMIT - YYCURSOR) < 17) YYFILL(17); - yych = *YYCURSOR; - switch (yych) { - case 0x00: goto yy90; - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x0E: - case 0x0F: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1A: - case 0x1B: - case 0x1C: - case 0x1D: - case 0x1E: - case 0x1F: goto yy93; - case '\t': - case '\v': - case '\f': goto yy84; - case '\n': goto yy87; - case '\r': goto yy89; - case ' ': goto yy86; - case '!': goto yy68; - case '"': goto yy79; - case '#': goto yy45; - case '$': - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - case 'G': - case 'H': - case 'I': - case 'J': - case 'K': - case 'M': - case 'N': - case 'O': - case 'P': - case 'Q': - case 'S': - case 'T': - case 'V': - case 'W': - case 'X': - case 'Y': - case 'Z': - case 'h': - case 'j': - case 'k': - case 'q': - case 'y': - case 'z': goto yy82; - case '%': goto yy37; - case '&': goto yy62; - case '\'': goto yy77; - case '(': goto yy47; - case ')': goto yy49; - case '*': goto yy57; - case '+': goto yy53; - case ',': goto yy74; - case '-': goto yy55; - case '.': goto yy4; - case '/': goto yy2; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': goto yy6; - case ':': goto yy43; - case ';': goto yy51; - case '<': goto yy33; - case '=': goto yy70; - case '>': goto yy72; - case '?': goto yy31; - case 'L': goto yy76; - case 'R': goto yy80; - case 'U': goto yy81; - case '[': goto yy39; - case '\\': goto yy83; - case ']': goto yy41; - case '^': goto yy59; - case '_': goto yy28; - case 'a': goto yy8; - case 'b': goto yy10; - case 'c': goto yy11; - case 'd': goto yy12; - case 'e': goto yy13; - case 'f': goto yy14; - case 'g': goto yy15; - case 'i': goto yy16; - case 'l': goto yy17; - case 'm': goto yy18; - case 'n': goto yy19; - case 'o': goto yy20; - case 'p': goto yy21; - case 'r': goto yy22; - case 's': goto yy23; - case 't': goto yy24; - case 'u': goto yy25; - case 'v': goto yy26; - case 'w': goto yy27; - case 'x': goto yy61; - case '{': goto yy29; - case '|': goto yy64; - case '}': goto yy35; - case '~': goto yy66; - default: goto yy92; - } -yy2: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '.') { - if (yych == '*') goto yy998; - } else { - if (yych <= '/') goto yy996; - if (yych == '=') goto yy994; - } -#line 188 "cpp.re" - { BOOST_WAVE_RET(T_DIVIDE); } -#line 238 "cpp_re.inc" -yy4: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '-') { - if (yych == '*') goto yy988; - } else { - if (yych <= '.') goto yy990; - if (yych <= '/') goto yy5; - if (yych <= '9') goto yy991; - } -yy5: -#line 174 "cpp.re" - { BOOST_WAVE_RET(T_DOT); } -#line 252 "cpp_re.inc" -yy6: - ++YYCURSOR; -yy7: -#line 45 "cpp.re" - { goto pp_number; } -#line 258 "cpp_re.inc" -yy8: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 'l': goto yy964; - case 'n': goto yy965; - case 's': goto yy966; - case 'u': goto yy967; - default: goto yy109; - } -yy9: -#line 290 "cpp.re" - { BOOST_WAVE_RET(T_IDENTIFIER); } -#line 272 "cpp_re.inc" -yy10: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'n') { - if (yych == 'i') goto yy946; - goto yy109; - } else { - if (yych <= 'o') goto yy947; - if (yych == 'r') goto yy948; - goto yy109; - } -yy11: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 'a': goto yy893; - case 'h': goto yy894; - case 'l': goto yy895; - case 'o': goto yy896; - default: goto yy109; - } -yy12: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'n') { - if (yych == 'e') goto yy855; - goto yy109; - } else { - if (yych <= 'o') goto yy856; - if (yych == 'y') goto yy858; - goto yy109; - } -yy13: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'm') { - if (yych == 'l') goto yy830; - goto yy109; - } else { - if (yych <= 'n') goto yy831; - if (yych == 'x') goto yy832; - goto yy109; - } -yy14: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 'a': goto yy811; - case 'l': goto yy812; - case 'o': goto yy813; - case 'r': goto yy814; - default: goto yy109; - } -yy15: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy807; - goto yy109; -yy16: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'l') { - if (yych == 'f') goto yy791; - goto yy109; - } else { - if (yych <= 'm') goto yy793; - if (yych <= 'n') goto yy794; - goto yy109; - } -yy17: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy787; - goto yy109; -yy18: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'u') goto yy780; - goto yy109; -yy19: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'e') { - if (yych == 'a') goto yy747; - if (yych <= 'd') goto yy109; - goto yy748; - } else { - if (yych <= 'o') { - if (yych <= 'n') goto yy109; - goto yy749; - } else { - if (yych == 'u') goto yy750; - goto yy109; - } - } -yy20: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'p') goto yy733; - if (yych == 'r') goto yy734; - goto yy109; -yy21: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'r') goto yy712; - if (yych == 'u') goto yy713; - goto yy109; -yy22: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy684; - goto yy109; -yy23: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 's') { - if (yych <= 'g') goto yy109; - if (yych <= 'h') goto yy638; - if (yych <= 'i') goto yy639; - goto yy109; - } else { - if (yych <= 't') goto yy640; - if (yych == 'w') goto yy641; - goto yy109; - } -yy24: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'h') { - if (yych == 'e') goto yy591; - if (yych <= 'g') goto yy109; - goto yy592; - } else { - if (yych <= 'r') { - if (yych <= 'q') goto yy109; - goto yy593; - } else { - if (yych == 'y') goto yy594; - goto yy109; - } - } -yy25: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '8') { - if (yych <= '&') { - if (yych == '"') goto yy129; - goto yy109; - } else { - if (yych <= '\'') goto yy131; - if (yych <= '7') goto yy109; - goto yy573; - } - } else { - if (yych <= 'm') { - if (yych == 'R') goto yy128; - goto yy109; - } else { - if (yych <= 'n') goto yy574; - if (yych == 's') goto yy575; - goto yy109; - } - } -yy26: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy555; - if (yych == 'o') goto yy556; - goto yy109; -yy27: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'c') goto yy543; - if (yych == 'h') goto yy544; - goto yy109; -yy28: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case '_': goto yy454; - case 'a': goto yy455; - case 'b': goto yy456; - case 'c': goto yy457; - case 'd': goto yy458; - case 'f': goto yy459; - case 'i': goto yy460; - case 's': goto yy461; - default: goto yy109; - } -yy29: - ++YYCURSOR; -#line 138 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACE); } -#line 466 "cpp_re.inc" -yy31: - yyaccept = 2; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '?') goto yy419; -yy32: -#line 163 "cpp.re" - { BOOST_WAVE_RET(T_QUESTION_MARK); } -#line 474 "cpp_re.inc" -yy33: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= ':') { - if (yych == '%') goto yy415; - if (yych >= ':') goto yy413; - } else { - if (yych <= ';') goto yy34; - if (yych <= '<') goto yy411; - if (yych <= '=') goto yy409; - } -yy34: -#line 204 "cpp.re" - { BOOST_WAVE_RET(T_LESS); } -#line 488 "cpp_re.inc" -yy35: - ++YYCURSOR; -#line 141 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACE); } -#line 493 "cpp_re.inc" -yy37: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '<') { - if (yych == ':') goto yy400; - } else { - if (yych <= '=') goto yy402; - if (yych <= '>') goto yy404; - } -#line 189 "cpp.re" - { BOOST_WAVE_RET(T_PERCENT); } -#line 504 "cpp_re.inc" -yy39: - ++YYCURSOR; -#line 144 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACKET); } -#line 509 "cpp_re.inc" -yy41: - ++YYCURSOR; -#line 147 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACKET); } -#line 514 "cpp_re.inc" -yy43: - ++YYCURSOR; - if ((yych = *YYCURSOR) == ':') goto yy396; - if (yych == '>') goto yy398; -#line 161 "cpp.re" - { BOOST_WAVE_RET(T_COLON); } -#line 521 "cpp_re.inc" -yy45: - yyaccept = 3; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'c') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy273; - } else { - if (yych <= '\f') goto yy273; - if (yych >= ' ') goto yy273; - } - } else { - if (yych <= '.') { - if (yych == '#') goto yy284; - } else { - if (yych <= '/') goto yy273; - if (yych == '?') goto yy283; - } - } - } else { - if (yych <= 'p') { - if (yych <= 'i') { - if (yych <= 'e') goto yy273; - if (yych >= 'i') goto yy273; - } else { - if (yych == 'l') goto yy273; - if (yych >= 'p') goto yy273; - } - } else { - if (yych <= 't') { - if (yych == 'r') goto yy273; - } else { - if (yych == 'v') goto yy46; - if (yych <= 'w') goto yy273; - } - } - } -yy46: -#line 150 "cpp.re" - { BOOST_WAVE_RET(T_POUND); } -#line 562 "cpp_re.inc" -yy47: - ++YYCURSOR; -#line 158 "cpp.re" - { BOOST_WAVE_RET(T_LEFTPAREN); } -#line 567 "cpp_re.inc" -yy49: - ++YYCURSOR; -#line 159 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTPAREN); } -#line 572 "cpp_re.inc" -yy51: - ++YYCURSOR; -#line 160 "cpp.re" - { BOOST_WAVE_RET(T_SEMICOLON); } -#line 577 "cpp_re.inc" -yy53: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '+') goto yy268; - if (yych == '=') goto yy270; -#line 185 "cpp.re" - { BOOST_WAVE_RET(T_PLUS); } -#line 584 "cpp_re.inc" -yy55: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '<') { - if (yych == '-') goto yy262; - } else { - if (yych <= '=') goto yy264; - if (yych <= '>') goto yy260; - } -#line 186 "cpp.re" - { BOOST_WAVE_RET(T_MINUS); } -#line 595 "cpp_re.inc" -yy57: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy258; -#line 187 "cpp.re" - { BOOST_WAVE_RET(T_STAR); } -#line 601 "cpp_re.inc" -yy59: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy256; -#line 190 "cpp.re" - { BOOST_WAVE_RET(T_XOR); } -#line 607 "cpp_re.inc" -yy61: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy249; - goto yy109; -yy62: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '&') goto yy245; - if (yych == '=') goto yy247; -#line 193 "cpp.re" - { BOOST_WAVE_RET(T_AND); } -#line 619 "cpp_re.inc" -yy64: - yyaccept = 4; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '>') { - if (yych == '=') goto yy240; - } else { - if (yych <= '?') goto yy237; - if (yych == '|') goto yy238; - } -yy65: -#line 195 "cpp.re" - { BOOST_WAVE_RET(T_OR); } -#line 632 "cpp_re.inc" -yy66: - ++YYCURSOR; -#line 198 "cpp.re" - { BOOST_WAVE_RET(T_COMPL); } -#line 637 "cpp_re.inc" -yy68: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy235; -#line 201 "cpp.re" - { BOOST_WAVE_RET(T_NOT); } -#line 643 "cpp_re.inc" -yy70: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy233; -#line 203 "cpp.re" - { BOOST_WAVE_RET(T_ASSIGN); } -#line 649 "cpp_re.inc" -yy72: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '<') goto yy73; - if (yych <= '=') goto yy227; - if (yych <= '>') goto yy229; -yy73: -#line 205 "cpp.re" - { BOOST_WAVE_RET(T_GREATER); } -#line 658 "cpp_re.inc" -yy74: - ++YYCURSOR; -#line 237 "cpp.re" - { BOOST_WAVE_RET(T_COMMA); } -#line 663 "cpp_re.inc" -yy76: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '?') { - if (yych <= '&') { - if (yych <= '"') { - if (yych <= '!') goto yy9; - goto yy137; - } else { - if (yych == '$') goto yy108; - goto yy9; - } - } else { - if (yych <= '/') { - if (yych <= '\'') goto yy226; - goto yy9; - } else { - if (yych <= '9') goto yy108; - if (yych <= '>') goto yy9; - goto yy111; - } - } - } else { - if (yych <= '[') { - if (yych <= 'Q') { - if (yych <= '@') goto yy9; - goto yy108; - } else { - if (yych <= 'R') goto yy225; - if (yych <= 'Z') goto yy108; - goto yy9; - } - } else { - if (yych <= '_') { - if (yych <= '\\') goto yy110; - if (yych <= '^') goto yy9; - goto yy108; - } else { - if (yych <= '`') goto yy9; - if (yych <= 'z') goto yy108; - goto yy9; - } - } - } -yy77: - yyaccept = 5; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '\f') { - if (yych == '\t') goto yy182; - if (yych >= '\v') goto yy182; - } else { - if (yych <= 0x1F) goto yy78; - if (yych != '\'') goto yy182; - } -yy78: -#line 339 "cpp.re" - { BOOST_WAVE_RET(TOKEN_FROM_ID(*s->tok, UnknownTokenType)); } -#line 721 "cpp_re.inc" -yy79: - yyaccept = 5; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '\n') { - if (yych == '\t') goto yy138; - goto yy78; - } else { - if (yych <= '\f') goto yy138; - if (yych <= 0x1F) goto yy78; - goto yy138; - } -yy80: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '"') goto yy135; - goto yy109; -yy81: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '&') { - if (yych == '"') goto yy129; - goto yy109; - } else { - if (yych <= '\'') goto yy131; - if (yych == 'R') goto yy128; - goto yy109; - } -yy82: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - goto yy109; -yy83: - yyaccept = 5; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'U') goto yy100; - if (yych == 'u') goto yy98; - goto yy78; -yy84: - ++YYCURSOR; - yych = *YYCURSOR; - goto yy97; -yy85: -#line 319 "cpp.re" - { BOOST_WAVE_RET(T_SPACE); } -#line 766 "cpp_re.inc" -yy86: - yych = *++YYCURSOR; - goto yy97; -yy87: - ++YYCURSOR; -yy88: -#line 322 "cpp.re" - { - s->line++; - cursor.column = 1; - BOOST_WAVE_RET(T_NEWLINE); - } -#line 779 "cpp_re.inc" -yy89: - yych = *++YYCURSOR; - if (yych == '\n') goto yy95; - goto yy88; -yy90: - ++YYCURSOR; -#line 329 "cpp.re" - { - if (s->eof && cursor != s->eof) - { - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\000' in input stream"); - } - BOOST_WAVE_RET(T_EOF); - } -#line 796 "cpp_re.inc" -yy92: - yych = *++YYCURSOR; - goto yy78; -yy93: - ++YYCURSOR; -#line 342 "cpp.re" - { - // flag the error - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\%03o' in input stream", *--YYCURSOR); - } -#line 809 "cpp_re.inc" -yy95: - yych = *++YYCURSOR; - goto yy88; -yy96: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy97: - if (yybm[256+yych] & 16) { - goto yy96; - } - goto yy85; -yy98: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy125; - } else { - if (yych <= 'F') goto yy125; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy125; - } -yy99: - YYCURSOR = YYMARKER; - if (yyaccept <= 56) { - if (yyaccept <= 28) { - if (yyaccept <= 14) { - if (yyaccept <= 7) { - if (yyaccept <= 3) { - if (yyaccept <= 1) { - if (yyaccept <= 0) { - goto yy5; - } else { - goto yy9; - } - } else { - if (yyaccept <= 2) { - goto yy32; - } else { - goto yy46; - } - } - } else { - if (yyaccept <= 5) { - if (yyaccept <= 4) { - goto yy65; - } else { - goto yy78; - } - } else { - if (yyaccept <= 6) { - goto yy142; - } else { - goto yy192; - } - } - } - } else { - if (yyaccept <= 11) { - if (yyaccept <= 9) { - if (yyaccept <= 8) { - goto yy251; - } else { - goto yy255; - } - } else { - if (yyaccept <= 10) { - goto yy291; - } else { - goto yy306; - } - } - } else { - if (yyaccept <= 13) { - if (yyaccept <= 12) { - goto yy401; - } else { - goto yy429; - } - } else { - goto yy433; - } - } - } - } else { - if (yyaccept <= 21) { - if (yyaccept <= 18) { - if (yyaccept <= 16) { - if (yyaccept <= 15) { - goto yy437; - } else { - goto yy468; - } - } else { - if (yyaccept <= 17) { - goto yy474; - } else { - goto yy482; - } - } - } else { - if (yyaccept <= 20) { - if (yyaccept <= 19) { - goto yy490; - } else { - goto yy495; - } - } else { - goto yy500; - } - } - } else { - if (yyaccept <= 25) { - if (yyaccept <= 23) { - if (yyaccept <= 22) { - goto yy503; - } else { - goto yy513; - } - } else { - if (yyaccept <= 24) { - goto yy519; - } else { - goto yy522; - } - } - } else { - if (yyaccept <= 27) { - if (yyaccept <= 26) { - goto yy529; - } else { - goto yy536; - } - } else { - goto yy538; - } - } - } - } - } else { - if (yyaccept <= 42) { - if (yyaccept <= 35) { - if (yyaccept <= 32) { - if (yyaccept <= 30) { - if (yyaccept <= 29) { - goto yy540; - } else { - goto yy542; - } - } else { - if (yyaccept <= 31) { - goto yy548; - } else { - goto yy554; - } - } - } else { - if (yyaccept <= 34) { - if (yyaccept <= 33) { - goto yy564; - } else { - goto yy566; - } - } else { - goto yy572; - } - } - } else { - if (yyaccept <= 39) { - if (yyaccept <= 37) { - if (yyaccept <= 36) { - goto yy579; - } else { - goto yy587; - } - } else { - if (yyaccept <= 38) { - goto yy590; - } else { - goto yy603; - } - } - } else { - if (yyaccept <= 41) { - if (yyaccept <= 40) { - goto yy605; - } else { - goto yy608; - } - } else { - goto yy611; - } - } - } - } else { - if (yyaccept <= 49) { - if (yyaccept <= 46) { - if (yyaccept <= 44) { - if (yyaccept <= 43) { - goto yy613; - } else { - goto yy619; - } - } else { - if (yyaccept <= 45) { - goto yy628; - } else { - goto yy630; - } - } - } else { - if (yyaccept <= 48) { - if (yyaccept <= 47) { - goto yy637; - } else { - goto yy646; - } - } else { - goto yy652; - } - } - } else { - if (yyaccept <= 53) { - if (yyaccept <= 51) { - if (yyaccept <= 50) { - goto yy656; - } else { - goto yy663; - } - } else { - if (yyaccept <= 52) { - goto yy669; - } else { - goto yy675; - } - } - } else { - if (yyaccept <= 55) { - if (yyaccept <= 54) { - goto yy679; - } else { - goto yy683; - } - } else { - goto yy691; - } - } - } - } - } - } else { - if (yyaccept <= 85) { - if (yyaccept <= 71) { - if (yyaccept <= 64) { - if (yyaccept <= 60) { - if (yyaccept <= 58) { - if (yyaccept <= 57) { - goto yy705; - } else { - goto yy711; - } - } else { - if (yyaccept <= 59) { - goto yy718; - } else { - goto yy727; - } - } - } else { - if (yyaccept <= 62) { - if (yyaccept <= 61) { - goto yy732; - } else { - goto yy735; - } - } else { - if (yyaccept <= 63) { - goto yy739; - } else { - goto yy746; - } - } - } - } else { - if (yyaccept <= 68) { - if (yyaccept <= 66) { - if (yyaccept <= 65) { - goto yy756; - } else { - goto yy759; - } - } else { - if (yyaccept <= 67) { - goto yy763; - } else { - goto yy769; - } - } - } else { - if (yyaccept <= 70) { - if (yyaccept <= 69) { - goto yy771; - } else { - goto yy779; - } - } else { - goto yy786; - } - } - } - } else { - if (yyaccept <= 78) { - if (yyaccept <= 75) { - if (yyaccept <= 73) { - if (yyaccept <= 72) { - goto yy790; - } else { - goto yy792; - } - } else { - if (yyaccept <= 74) { - goto yy797; - } else { - goto yy801; - } - } - } else { - if (yyaccept <= 77) { - if (yyaccept <= 76) { - goto yy806; - } else { - goto yy810; - } - } else { - goto yy819; - } - } - } else { - if (yyaccept <= 82) { - if (yyaccept <= 80) { - if (yyaccept <= 79) { - goto yy821; - } else { - goto yy825; - } - } else { - if (yyaccept <= 81) { - goto yy829; - } else { - goto yy838; - } - } - } else { - if (yyaccept <= 84) { - if (yyaccept <= 83) { - goto yy843; - } else { - goto yy848; - } - } else { - goto yy851; - } - } - } - } - } else { - if (yyaccept <= 99) { - if (yyaccept <= 92) { - if (yyaccept <= 89) { - if (yyaccept <= 87) { - if (yyaccept <= 86) { - goto yy854; - } else { - goto yy857; - } - } else { - if (yyaccept <= 88) { - goto yy869; - } else { - goto yy874; - } - } - } else { - if (yyaccept <= 91) { - if (yyaccept <= 90) { - goto yy881; - } else { - goto yy886; - } - } else { - goto yy892; - } - } - } else { - if (yyaccept <= 96) { - if (yyaccept <= 94) { - if (yyaccept <= 93) { - goto yy901; - } else { - goto yy908; - } - } else { - if (yyaccept <= 95) { - goto yy910; - } else { - goto yy916; - } - } - } else { - if (yyaccept <= 98) { - if (yyaccept <= 97) { - goto yy921; - } else { - goto yy925; - } - } else { - goto yy928; - } - } - } - } else { - if (yyaccept <= 106) { - if (yyaccept <= 103) { - if (yyaccept <= 101) { - if (yyaccept <= 100) { - goto yy934; - } else { - goto yy938; - } - } else { - if (yyaccept <= 102) { - goto yy943; - } else { - goto yy945; - } - } - } else { - if (yyaccept <= 105) { - if (yyaccept <= 104) { - goto yy952; - } else { - goto yy955; - } - } else { - goto yy960; - } - } - } else { - if (yyaccept <= 110) { - if (yyaccept <= 108) { - if (yyaccept <= 107) { - goto yy963; - } else { - goto yy970; - } - } else { - if (yyaccept <= 109) { - goto yy972; - } else { - goto yy974; - } - } - } else { - if (yyaccept <= 112) { - if (yyaccept <= 111) { - goto yy978; - } else { - goto yy985; - } - } else { - goto yy987; - } - } - } - } - } - } -yy100: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy101; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy101: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy102; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy102: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy103; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy103: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy104; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy104: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy105; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy105: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy106; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy106: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy107; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy107: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy108; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy108: - yyaccept = 1; - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy109: - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych != '\\') goto yy9; -yy110: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == 'U') goto yy114; - if (yych == 'u') goto yy113; - goto yy99; -yy111: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych != '?') goto yy99; - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == '/') goto yy110; - goto yy99; -yy113: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy122; - goto yy99; - } else { - if (yych <= 'F') goto yy122; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy122; - goto yy99; - } -yy114: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy115; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy115: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy116; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy116: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy117; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy117: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy118; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy118: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy119; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy119: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy120; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy120: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy121; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy121: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy108; - goto yy99; - } else { - if (yych <= 'F') goto yy108; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy108; - goto yy99; - } -yy122: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy123; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy123: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy124; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy124: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy108; - goto yy99; - } else { - if (yych <= 'F') goto yy108; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy108; - goto yy99; - } -yy125: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy126; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy126: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy127; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy127: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy108; - goto yy99; - } else { - if (yych <= 'F') goto yy108; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy108; - goto yy99; - } -yy128: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '"') goto yy133; - goto yy109; -yy129: - ++YYCURSOR; -#line 274 "cpp.re" - { - if (s->act_in_cpp0x_mode) - goto extstringlit; - --YYCURSOR; - BOOST_WAVE_RET(T_IDENTIFIER); - } -#line 1591 "cpp_re.inc" -yy131: - ++YYCURSOR; -#line 266 "cpp.re" - { - if (s->act_in_cpp0x_mode) - goto extcharlit; - --YYCURSOR; - BOOST_WAVE_RET(T_IDENTIFIER); - } -#line 1601 "cpp_re.inc" -yy133: - ++YYCURSOR; -#line 282 "cpp.re" - { - if (s->act_in_cpp0x_mode) - goto extrawstringlit; - --YYCURSOR; - BOOST_WAVE_RET(T_IDENTIFIER); - } -#line 1611 "cpp_re.inc" -yy135: - ++YYCURSOR; -#line 258 "cpp.re" - { - if (s->act_in_cpp0x_mode) - goto extrawstringlit; - --YYCURSOR; - BOOST_WAVE_RET(T_IDENTIFIER); - } -#line 1621 "cpp_re.inc" -yy137: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy138: - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych >= '\\') goto yy140; -yy139: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy152; -yy140: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy137; - goto yy99; - } else { - if (yych <= '\'') goto yy137; - if (yych <= '/') goto yy99; - goto yy147; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy145; - goto yy99; - } else { - if (yych <= 'U') goto yy144; - if (yych == '\\') goto yy137; - goto yy99; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy137; - if (yych <= 'e') goto yy99; - goto yy137; - } else { - if (yych == 'n') goto yy137; - if (yych <= 'q') goto yy99; - goto yy137; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy99; - if (yych <= 't') goto yy137; - goto yy143; - } else { - if (yych <= 'v') goto yy137; - if (yych == 'x') goto yy146; - goto yy99; - } - } - } -yy141: - ++YYCURSOR; -yy142: -#line 255 "cpp.re" - { BOOST_WAVE_RET(T_STRINGLIT); } -#line 1695 "cpp_re.inc" -yy143: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy178; - goto yy99; - } else { - if (yych <= 'F') goto yy178; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy178; - goto yy99; - } -yy144: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy171; - goto yy99; - } else { - if (yych <= 'F') goto yy171; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy171; - goto yy99; - } -yy145: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy151; - goto yy140; -yy146: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 128) { - goto yy149; - } - goto yy99; -yy147: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '"') { - if (yych <= '\n') { - if (yych == '\t') goto yy137; - goto yy99; - } else { - if (yych <= '\f') goto yy137; - if (yych <= 0x1F) goto yy99; - if (yych <= '!') goto yy137; - goto yy141; - } - } else { - if (yych <= '>') { - if (yych <= '/') goto yy137; - if (yych >= '8') goto yy137; - } else { - if (yych <= '?') goto yy139; - if (yych == '\\') goto yy140; - goto yy137; - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy139; - goto yy140; -yy149: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 128) { - goto yy149; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy137; - goto yy99; - } else { - if (yych <= '\f') goto yy137; - if (yych <= 0x1F) goto yy99; - goto yy137; - } - } else { - if (yych <= '?') { - if (yych <= '"') goto yy141; - if (yych <= '>') goto yy137; - goto yy139; - } else { - if (yych == '\\') goto yy140; - goto yy137; - } - } -yy151: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych >= '\\') goto yy140; -yy152: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 1) { - goto yy152; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy137; - goto yy99; - } else { - if (yych <= '\f') goto yy137; - if (yych <= 0x1F) goto yy99; - goto yy137; - } - } else { - if (yych <= '/') { - if (yych <= '"') goto yy141; - if (yych <= '.') goto yy137; - } else { - if (yych == '\\') goto yy140; - goto yy137; - } - } -yy154: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 2) { - goto yy154; - } - if (yych <= '7') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy158; - if (yych <= '/') goto yy137; - goto yy147; - } - } - } else { - if (yych <= 'U') { - if (yych == '?') goto yy159; - if (yych <= 'T') goto yy137; - goto yy157; - } else { - if (yych <= 'u') { - if (yych <= 't') goto yy137; - } else { - if (yych == 'x') goto yy149; - goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - goto yy168; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - goto yy168; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych <= 'f') goto yy168; - goto yy137; - } - } - } -yy157: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - goto yy161; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - goto yy161; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych <= 'f') goto yy161; - goto yy137; - } - } - } -yy158: - yyaccept = 6; - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy142; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy139; - goto yy140; -yy159: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych >= '\\') goto yy140; - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 1) { - goto yy152; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy137; - goto yy99; - } else { - if (yych <= '\f') goto yy137; - if (yych <= 0x1F) goto yy99; - goto yy137; - } - } else { - if (yych <= '/') { - if (yych <= '"') goto yy141; - if (yych <= '.') goto yy137; - goto yy154; - } else { - if (yych == '\\') goto yy140; - goto yy137; - } - } -yy161: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy139; - goto yy140; -yy168: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy139; - goto yy140; -yy171: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy172; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy172: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy173; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy173: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy174; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy174: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy175; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy175: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy176; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy176: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy177; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy177: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy137; - goto yy99; - } else { - if (yych <= 'F') goto yy137; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy137; - goto yy99; - } -yy178: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy179; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy179: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy180; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy180: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy137; - goto yy99; - } else { - if (yych <= 'F') goto yy137; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy137; - goto yy99; - } -yy181: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy182: - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych >= '\\') goto yy184; -yy183: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy196; -yy184: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy181; - goto yy99; - } else { - if (yych <= '\'') goto yy181; - if (yych <= '/') goto yy99; - goto yy189; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy187; - goto yy99; - } else { - if (yych <= 'U') goto yy186; - if (yych == '\\') goto yy181; - goto yy99; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy181; - if (yych <= 'e') goto yy99; - goto yy181; - } else { - if (yych == 'n') goto yy181; - if (yych <= 'q') goto yy99; - goto yy181; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy99; - if (yych <= 't') goto yy181; - } else { - if (yych <= 'v') goto yy181; - if (yych == 'x') goto yy188; - goto yy99; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy222; - goto yy99; - } else { - if (yych <= 'F') goto yy222; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy222; - goto yy99; - } -yy186: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy215; - goto yy99; - } else { - if (yych <= 'F') goto yy215; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy215; - goto yy99; - } -yy187: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy195; - goto yy184; -yy188: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy193; - goto yy99; - } else { - if (yych <= 'F') goto yy193; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy193; - goto yy99; - } -yy189: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\'') { - if (yych <= '\n') { - if (yych == '\t') goto yy181; - goto yy99; - } else { - if (yych <= '\f') goto yy181; - if (yych <= 0x1F) goto yy99; - if (yych <= '&') goto yy181; - goto yy191; - } - } else { - if (yych <= '>') { - if (yych <= '/') goto yy181; - if (yych >= '8') goto yy181; - } else { - if (yych <= '?') goto yy183; - if (yych == '\\') goto yy184; - goto yy181; - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy183; - goto yy184; -yy191: - ++YYCURSOR; -yy192: -#line 252 "cpp.re" - { BOOST_WAVE_RET(T_CHARLIT); } -#line 2542 "cpp_re.inc" -yy193: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - goto yy193; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - goto yy193; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych <= 'f') goto yy193; - goto yy181; - } - } - } -yy195: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych >= '\\') goto yy184; -yy196: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\'') { - if (yych <= '\n') { - if (yych == '\t') goto yy181; - goto yy99; - } else { - if (yych <= '\f') goto yy181; - if (yych <= 0x1F) goto yy99; - if (yych <= '&') goto yy181; - goto yy191; - } - } else { - if (yych <= '>') { - if (yych != '/') goto yy181; - } else { - if (yych <= '?') goto yy196; - if (yych == '\\') goto yy184; - goto yy181; - } - } -yy198: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '>') { - if (yych <= 0x1F) { - if (yych <= '\t') { - if (yych <= 0x08) goto yy99; - goto yy181; - } else { - if (yych <= '\n') goto yy99; - if (yych <= '\f') goto yy181; - goto yy99; - } - } else { - if (yych <= '\'') { - if (yych <= '&') goto yy181; - goto yy202; - } else { - if (yych <= '/') goto yy181; - if (yych <= '7') goto yy189; - goto yy181; - } - } - } else { - if (yych <= '\\') { - if (yych <= 'T') { - if (yych <= '?') goto yy203; - goto yy181; - } else { - if (yych <= 'U') goto yy201; - if (yych <= '[') goto yy181; - goto yy198; - } - } else { - if (yych <= 'u') { - if (yych <= 't') goto yy181; - } else { - if (yych == 'x') goto yy193; - goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - goto yy212; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - goto yy212; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych <= 'f') goto yy212; - goto yy181; - } - } - } -yy201: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - goto yy205; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - goto yy205; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych <= 'f') goto yy205; - goto yy181; - } - } - } -yy202: - yyaccept = 7; - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy192; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy183; - goto yy184; -yy203: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych >= '\\') goto yy184; - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\'') { - if (yych <= '\n') { - if (yych == '\t') goto yy181; - goto yy99; - } else { - if (yych <= '\f') goto yy181; - if (yych <= 0x1F) goto yy99; - if (yych <= '&') goto yy181; - goto yy191; - } - } else { - if (yych <= '>') { - if (yych == '/') goto yy198; - goto yy181; - } else { - if (yych <= '?') goto yy196; - if (yych == '\\') goto yy184; - goto yy181; - } - } -yy205: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy183; - goto yy184; -yy212: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy183; - goto yy184; -yy215: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy216; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy216: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy217; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy217: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy218; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy218: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy219; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy219: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy220; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy220: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy221; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy221: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy181; - goto yy99; - } else { - if (yych <= 'F') goto yy181; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy181; - goto yy99; - } -yy222: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy223; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy223: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy224; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy224: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy181; - goto yy99; - } else { - if (yych <= 'F') goto yy181; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy181; - goto yy99; - } -yy225: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '"') goto yy135; - goto yy109; -yy226: - yych = *++YYCURSOR; - if (yych == '\'') goto yy99; - goto yy182; -yy227: - ++YYCURSOR; -#line 227 "cpp.re" - { BOOST_WAVE_RET(T_GREATEREQUAL); } -#line 3175 "cpp_re.inc" -yy229: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy231; -#line 220 "cpp.re" - { BOOST_WAVE_RET(T_SHIFTRIGHT); } -#line 3181 "cpp_re.inc" -yy231: - ++YYCURSOR; -#line 221 "cpp.re" - { BOOST_WAVE_RET(T_SHIFTRIGHTASSIGN); } -#line 3186 "cpp_re.inc" -yy233: - ++YYCURSOR; -#line 223 "cpp.re" - { BOOST_WAVE_RET(T_EQUAL); } -#line 3191 "cpp_re.inc" -yy235: - ++YYCURSOR; -#line 224 "cpp.re" - { BOOST_WAVE_RET(T_NOTEQUAL); } -#line 3196 "cpp_re.inc" -yy237: - yych = *++YYCURSOR; - if (yych == '?') goto yy242; - goto yy99; -yy238: - ++YYCURSOR; -#line 230 "cpp.re" - { BOOST_WAVE_RET(T_OROR); } -#line 3205 "cpp_re.inc" -yy240: - ++YYCURSOR; -#line 216 "cpp.re" - { BOOST_WAVE_RET(T_ORASSIGN); } -#line 3210 "cpp_re.inc" -yy242: - yych = *++YYCURSOR; - if (yych != '!') goto yy99; - ++YYCURSOR; -#line 232 "cpp.re" - { BOOST_WAVE_RET(T_OROR_TRIGRAPH); } -#line 3217 "cpp_re.inc" -yy245: - ++YYCURSOR; -#line 228 "cpp.re" - { BOOST_WAVE_RET(T_ANDAND); } -#line 3222 "cpp_re.inc" -yy247: - ++YYCURSOR; -#line 214 "cpp.re" - { BOOST_WAVE_RET(T_ANDASSIGN); } -#line 3227 "cpp_re.inc" -yy249: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 8; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy252; - if (yych <= '`') goto yy251; - if (yych <= 'z') goto yy108; - } - } -yy251: -#line 192 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_XOR_ALT); } -#line 3254 "cpp_re.inc" -yy252: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'q') goto yy109; - yyaccept = 9; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy255: -#line 212 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_XORASSIGN_ALT); } -#line 3272 "cpp_re.inc" -yy256: - ++YYCURSOR; -#line 211 "cpp.re" - { BOOST_WAVE_RET(T_XORASSIGN); } -#line 3277 "cpp_re.inc" -yy258: - ++YYCURSOR; -#line 208 "cpp.re" - { BOOST_WAVE_RET(T_STARASSIGN); } -#line 3282 "cpp_re.inc" -yy260: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '*') goto yy266; -#line 248 "cpp.re" - { BOOST_WAVE_RET(T_ARROW); } -#line 3288 "cpp_re.inc" -yy262: - ++YYCURSOR; -#line 236 "cpp.re" - { BOOST_WAVE_RET(T_MINUSMINUS); } -#line 3293 "cpp_re.inc" -yy264: - ++YYCURSOR; -#line 207 "cpp.re" - { BOOST_WAVE_RET(T_MINUSASSIGN); } -#line 3298 "cpp_re.inc" -yy266: - ++YYCURSOR; -#line 239 "cpp.re" - { - if (s->act_in_c99_mode) { - --YYCURSOR; - BOOST_WAVE_RET(T_ARROW); - } - else { - BOOST_WAVE_RET(T_ARROWSTAR); - } - } -#line 3311 "cpp_re.inc" -yy268: - ++YYCURSOR; -#line 235 "cpp.re" - { BOOST_WAVE_RET(T_PLUSPLUS); } -#line 3316 "cpp_re.inc" -yy270: - ++YYCURSOR; -#line 206 "cpp.re" - { BOOST_WAVE_RET(T_PLUSASSIGN); } -#line 3321 "cpp_re.inc" -yy272: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12); - yych = *YYCURSOR; -yy273: - if (yych <= 'h') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy272; - goto yy99; - } else { - if (yych <= '\f') goto yy272; - if (yych <= 0x1F) goto yy99; - goto yy272; - } - } else { - if (yych <= 'c') { - if (yych != '/') goto yy99; - } else { - if (yych <= 'd') goto yy281; - if (yych <= 'e') goto yy275; - goto yy99; - } - } - } else { - if (yych <= 'q') { - if (yych <= 'l') { - if (yych <= 'i') goto yy282; - if (yych <= 'k') goto yy99; - goto yy279; - } else { - if (yych == 'p') goto yy278; - goto yy99; - } - } else { - if (yych <= 'u') { - if (yych <= 'r') goto yy276; - if (yych <= 't') goto yy99; - goto yy280; - } else { - if (yych == 'w') goto yy277; - goto yy99; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == '*') goto yy389; - goto yy99; -yy275: - yych = *++YYCURSOR; - if (yych <= 'm') { - if (yych == 'l') goto yy365; - goto yy99; - } else { - if (yych <= 'n') goto yy366; - if (yych == 'r') goto yy367; - goto yy99; - } -yy276: - yych = *++YYCURSOR; - if (yych == 'e') goto yy359; - goto yy99; -yy277: - yych = *++YYCURSOR; - if (yych == 'a') goto yy352; - goto yy99; -yy278: - yych = *++YYCURSOR; - if (yych == 'r') goto yy346; - goto yy99; -yy279: - yych = *++YYCURSOR; - if (yych == 'i') goto yy342; - goto yy99; -yy280: - yych = *++YYCURSOR; - if (yych == 'n') goto yy337; - goto yy99; -yy281: - yych = *++YYCURSOR; - if (yych == 'e') goto yy331; - goto yy99; -yy282: - yych = *++YYCURSOR; - if (yych == 'f') goto yy290; - if (yych == 'n') goto yy289; - goto yy99; -yy283: - yych = *++YYCURSOR; - if (yych == '?') goto yy286; - goto yy99; -yy284: - ++YYCURSOR; -#line 153 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND); } -#line 3419 "cpp_re.inc" -yy286: - yych = *++YYCURSOR; - if (yych != '=') goto yy99; - ++YYCURSOR; -#line 154 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND_TRIGRAPH); } -#line 3426 "cpp_re.inc" -yy289: - yych = *++YYCURSOR; - if (yych == 'c') goto yy301; - goto yy99; -yy290: - yyaccept = 10; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy293; - if (yych == 'n') goto yy292; -yy291: -#line 301 "cpp.re" - { BOOST_WAVE_RET(T_PP_IF); } -#line 3439 "cpp_re.inc" -yy292: - yych = *++YYCURSOR; - if (yych == 'd') goto yy297; - goto yy99; -yy293: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'f') goto yy99; - ++YYCURSOR; -#line 302 "cpp.re" - { BOOST_WAVE_RET(T_PP_IFDEF); } -#line 3452 "cpp_re.inc" -yy297: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'f') goto yy99; - ++YYCURSOR; -#line 303 "cpp.re" - { BOOST_WAVE_RET(T_PP_IFNDEF); } -#line 3461 "cpp_re.inc" -yy301: - yych = *++YYCURSOR; - if (yych != 'l') goto yy99; - yych = *++YYCURSOR; - if (yych != 'u') goto yy99; - yych = *++YYCURSOR; - if (yych != 'd') goto yy99; - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yyaccept = 11; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '_') goto yy307; - goto yy309; -yy306: -#line 299 "cpp.re" - { BOOST_WAVE_RET(T_PP_INCLUDE); } -#line 3478 "cpp_re.inc" -yy307: - yych = *++YYCURSOR; - if (yych == 'n') goto yy328; - goto yy99; -yy308: - yyaccept = 11; - YYMARKER = ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; -yy309: - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy308; - goto yy306; - } else { - if (yych <= '\f') goto yy308; - if (yych <= 0x1F) goto yy306; - goto yy308; - } - } else { - if (yych <= '.') { - if (yych == '"') goto yy312; - goto yy306; - } else { - if (yych <= '/') goto yy310; - if (yych == '<') goto yy311; - goto yy306; - } - } -yy310: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == '*') goto yy321; - goto yy99; -yy311: - yych = *++YYCURSOR; - if (yych == '>') goto yy99; - goto yy318; -yy312: - yych = *++YYCURSOR; - if (yych == '"') goto yy99; - goto yy314; -yy313: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy314: - if (yybm[0+yych] & 8) { - goto yy313; - } - if (yych <= '!') goto yy99; - ++YYCURSOR; -#line 296 "cpp.re" - { BOOST_WAVE_RET(T_PP_QHEADER); } -#line 3534 "cpp_re.inc" -yy317: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy318: - if (yybm[0+yych] & 16) { - goto yy317; - } - if (yych <= '=') goto yy99; - ++YYCURSOR; -#line 293 "cpp.re" - { BOOST_WAVE_RET(T_PP_HHEADER); } -#line 3547 "cpp_re.inc" -yy321: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy321; - } - if (yych == '\r') goto yy323; - if (yych <= ')') goto yy99; - goto yy325; -yy323: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy321; - } - if (yych == '\r') goto yy323; - if (yych <= ')') goto yy99; -yy325: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy325; - } - if (yych <= '\r') { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy321; - } else { - if (yych <= 0x1F) goto yy99; - if (yych == '/') goto yy308; - goto yy321; - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy321; - } - if (yych == '\r') goto yy323; - if (yych <= ')') goto yy99; - goto yy325; -yy328: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'x') goto yy99; - yych = *++YYCURSOR; - if (yych == 't') goto yy308; - goto yy99; -yy331: - yych = *++YYCURSOR; - if (yych != 'f') goto yy99; - yych = *++YYCURSOR; - if (yych != 'i') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - ++YYCURSOR; -#line 307 "cpp.re" - { BOOST_WAVE_RET(T_PP_DEFINE); } -#line 3611 "cpp_re.inc" -yy337: - yych = *++YYCURSOR; - if (yych != 'd') goto yy99; - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'f') goto yy99; - ++YYCURSOR; -#line 308 "cpp.re" - { BOOST_WAVE_RET(T_PP_UNDEF); } -#line 3622 "cpp_re.inc" -yy342: - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - ++YYCURSOR; -#line 309 "cpp.re" - { BOOST_WAVE_RET(T_PP_LINE); } -#line 3631 "cpp_re.inc" -yy346: - yych = *++YYCURSOR; - if (yych != 'a') goto yy99; - yych = *++YYCURSOR; - if (yych != 'g') goto yy99; - yych = *++YYCURSOR; - if (yych != 'm') goto yy99; - yych = *++YYCURSOR; - if (yych != 'a') goto yy99; - ++YYCURSOR; -#line 311 "cpp.re" - { BOOST_WAVE_RET(T_PP_PRAGMA); } -#line 3644 "cpp_re.inc" -yy352: - yych = *++YYCURSOR; - if (yych != 'r') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - yych = *++YYCURSOR; - if (yych != 'i') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - yych = *++YYCURSOR; - if (yych != 'g') goto yy99; - ++YYCURSOR; -#line 313 "cpp.re" - { BOOST_WAVE_RET(T_PP_WARNING); } -#line 3659 "cpp_re.inc" -yy359: - yych = *++YYCURSOR; - if (yych != 'g') goto yy99; - yych = *++YYCURSOR; - if (yych != 'i') goto yy99; - yych = *++YYCURSOR; - if (yych != 'o') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - ++YYCURSOR; -#line 315 "cpp.re" - { BOOST_WAVE_RET(T_MSEXT_PP_REGION); } -#line 3672 "cpp_re.inc" -yy365: - yych = *++YYCURSOR; - if (yych == 'i') goto yy383; - if (yych == 's') goto yy384; - goto yy99; -yy366: - yych = *++YYCURSOR; - if (yych == 'd') goto yy372; - goto yy99; -yy367: - yych = *++YYCURSOR; - if (yych != 'r') goto yy99; - yych = *++YYCURSOR; - if (yych != 'o') goto yy99; - yych = *++YYCURSOR; - if (yych != 'r') goto yy99; - ++YYCURSOR; -#line 310 "cpp.re" - { BOOST_WAVE_RET(T_PP_ERROR); } -#line 3692 "cpp_re.inc" -yy372: - yych = *++YYCURSOR; - if (yych == 'i') goto yy373; - if (yych == 'r') goto yy374; - goto yy99; -yy373: - yych = *++YYCURSOR; - if (yych == 'f') goto yy381; - goto yy99; -yy374: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'g') goto yy99; - yych = *++YYCURSOR; - if (yych != 'i') goto yy99; - yych = *++YYCURSOR; - if (yych != 'o') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - ++YYCURSOR; -#line 316 "cpp.re" - { BOOST_WAVE_RET(T_MSEXT_PP_ENDREGION); } -#line 3716 "cpp_re.inc" -yy381: - ++YYCURSOR; -#line 306 "cpp.re" - { BOOST_WAVE_RET(T_PP_ENDIF); } -#line 3721 "cpp_re.inc" -yy383: - yych = *++YYCURSOR; - if (yych == 'f') goto yy387; - goto yy99; -yy384: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - ++YYCURSOR; -#line 304 "cpp.re" - { BOOST_WAVE_RET(T_PP_ELSE); } -#line 3732 "cpp_re.inc" -yy387: - ++YYCURSOR; -#line 305 "cpp.re" - { BOOST_WAVE_RET(T_PP_ELIF); } -#line 3737 "cpp_re.inc" -yy389: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\r') { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy389; - } else { - if (yych <= 0x1F) goto yy99; - if (yych == '*') goto yy393; - goto yy389; - } -yy391: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\r') { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy389; - goto yy391; - } else { - if (yych <= 0x1F) goto yy99; - if (yych != '*') goto yy389; - } -yy393: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= 0x1F) { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy389; - if (yych >= 0x0E) goto yy99; - } else { - if (yych <= '*') { - if (yych <= ')') goto yy389; - goto yy393; - } else { - if (yych == '/') goto yy272; - goto yy389; - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\r') { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy389; - goto yy391; - } else { - if (yych <= 0x1F) goto yy99; - if (yych == '*') goto yy393; - goto yy389; - } -yy396: - ++YYCURSOR; -#line 165 "cpp.re" - { - if (s->act_in_c99_mode) { - --YYCURSOR; - BOOST_WAVE_RET(T_COLON); - } - else { - BOOST_WAVE_RET(T_COLON_COLON); - } - } -#line 3803 "cpp_re.inc" -yy398: - ++YYCURSOR; -#line 149 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACKET_ALT); } -#line 3808 "cpp_re.inc" -yy400: - yyaccept = 12; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'e') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy273; - } else { - if (yych <= '\f') goto yy273; - if (yych >= ' ') goto yy273; - } - } else { - if (yych <= '.') { - if (yych == '%') goto yy406; - } else { - if (yych <= '/') goto yy273; - if (yych >= 'd') goto yy273; - } - } - } else { - if (yych <= 'p') { - if (yych <= 'k') { - if (yych == 'i') goto yy273; - } else { - if (yych <= 'l') goto yy273; - if (yych >= 'p') goto yy273; - } - } else { - if (yych <= 't') { - if (yych == 'r') goto yy273; - } else { - if (yych == 'v') goto yy401; - if (yych <= 'w') goto yy273; - } - } - } -yy401: -#line 151 "cpp.re" - { BOOST_WAVE_RET(T_POUND_ALT); } -#line 3848 "cpp_re.inc" -yy402: - ++YYCURSOR; -#line 210 "cpp.re" - { BOOST_WAVE_RET(T_PERCENTASSIGN); } -#line 3853 "cpp_re.inc" -yy404: - ++YYCURSOR; -#line 143 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACE_ALT); } -#line 3858 "cpp_re.inc" -yy406: - yych = *++YYCURSOR; - if (yych != ':') goto yy99; - ++YYCURSOR; -#line 157 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND_ALT); } -#line 3865 "cpp_re.inc" -yy409: - ++YYCURSOR; -#line 226 "cpp.re" - { BOOST_WAVE_RET(T_LESSEQUAL); } -#line 3870 "cpp_re.inc" -yy411: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy417; -#line 219 "cpp.re" - { BOOST_WAVE_RET(T_SHIFTLEFT); } -#line 3876 "cpp_re.inc" -yy413: - ++YYCURSOR; -#line 146 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACKET_ALT); } -#line 3881 "cpp_re.inc" -yy415: - ++YYCURSOR; -#line 140 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACE_ALT); } -#line 3886 "cpp_re.inc" -yy417: - ++YYCURSOR; -#line 222 "cpp.re" - { BOOST_WAVE_RET(T_SHIFTLEFTASSIGN); } -#line 3891 "cpp_re.inc" -yy419: - yych = *++YYCURSOR; - switch (yych) { - case '!': goto yy432; - case '\'': goto yy430; - case '(': goto yy424; - case ')': goto yy426; - case '-': goto yy434; - case '/': goto yy436; - case '<': goto yy420; - case '=': goto yy428; - case '>': goto yy422; - default: goto yy99; - } -yy420: - ++YYCURSOR; -#line 139 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACE_TRIGRAPH); } -#line 3910 "cpp_re.inc" -yy422: - ++YYCURSOR; -#line 142 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACE_TRIGRAPH); } -#line 3915 "cpp_re.inc" -yy424: - ++YYCURSOR; -#line 145 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACKET_TRIGRAPH); } -#line 3920 "cpp_re.inc" -yy426: - ++YYCURSOR; -#line 148 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACKET_TRIGRAPH); } -#line 3925 "cpp_re.inc" -yy428: - yyaccept = 13; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'c') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy273; - } else { - if (yych <= '\f') goto yy273; - if (yych >= ' ') goto yy273; - } - } else { - if (yych <= '.') { - if (yych == '#') goto yy449; - } else { - if (yych <= '/') goto yy273; - if (yych == '?') goto yy448; - } - } - } else { - if (yych <= 'p') { - if (yych <= 'i') { - if (yych <= 'e') goto yy273; - if (yych >= 'i') goto yy273; - } else { - if (yych == 'l') goto yy273; - if (yych >= 'p') goto yy273; - } - } else { - if (yych <= 't') { - if (yych == 'r') goto yy273; - } else { - if (yych == 'v') goto yy429; - if (yych <= 'w') goto yy273; - } - } - } -yy429: -#line 152 "cpp.re" - { BOOST_WAVE_RET(T_POUND_TRIGRAPH); } -#line 3966 "cpp_re.inc" -yy430: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy446; -#line 191 "cpp.re" - { BOOST_WAVE_RET(T_XOR_TRIGRAPH); } -#line 3972 "cpp_re.inc" -yy432: - yyaccept = 14; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '>') { - if (yych == '=') goto yy441; - } else { - if (yych <= '?') goto yy438; - if (yych == '|') goto yy439; - } -yy433: -#line 197 "cpp.re" - { BOOST_WAVE_RET(T_OR_TRIGRAPH); } -#line 3985 "cpp_re.inc" -yy434: - ++YYCURSOR; -#line 199 "cpp.re" - { BOOST_WAVE_RET(T_COMPL_TRIGRAPH); } -#line 3990 "cpp_re.inc" -yy436: - yyaccept = 15; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'U') goto yy100; - if (yych == 'u') goto yy98; -yy437: -#line 249 "cpp.re" - { BOOST_WAVE_RET(T_ANY_TRIGRAPH); } -#line 3999 "cpp_re.inc" -yy438: - yych = *++YYCURSOR; - if (yych == '?') goto yy443; - goto yy99; -yy439: - ++YYCURSOR; -#line 231 "cpp.re" - { BOOST_WAVE_RET(T_OROR_TRIGRAPH); } -#line 4008 "cpp_re.inc" -yy441: - ++YYCURSOR; -#line 218 "cpp.re" - { BOOST_WAVE_RET(T_ORASSIGN_TRIGRAPH); } -#line 4013 "cpp_re.inc" -yy443: - yych = *++YYCURSOR; - if (yych != '!') goto yy99; - ++YYCURSOR; -#line 234 "cpp.re" - { BOOST_WAVE_RET(T_OROR_TRIGRAPH); } -#line 4020 "cpp_re.inc" -yy446: - ++YYCURSOR; -#line 213 "cpp.re" - { BOOST_WAVE_RET(T_XORASSIGN_TRIGRAPH); } -#line 4025 "cpp_re.inc" -yy448: - yych = *++YYCURSOR; - if (yych == '?') goto yy451; - goto yy99; -yy449: - ++YYCURSOR; -#line 155 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND_TRIGRAPH); } -#line 4034 "cpp_re.inc" -yy451: - yych = *++YYCURSOR; - if (yych != '=') goto yy99; - ++YYCURSOR; -#line 156 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND_TRIGRAPH); } -#line 4041 "cpp_re.inc" -yy454: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 'a': goto yy455; - case 'b': goto yy456; - case 'c': goto yy457; - case 'd': goto yy458; - case 'e': goto yy507; - case 'f': goto yy505; - case 'i': goto yy504; - case 'l': goto yy508; - case 's': goto yy461; - case 't': goto yy506; - default: goto yy109; - } -yy455: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy501; - goto yy109; -yy456: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy496; - goto yy109; -yy457: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy491; - goto yy109; -yy458: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy483; - goto yy109; -yy459: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy475; - goto yy109; -yy460: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy469; - goto yy109; -yy461: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 16; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy468: -#line 130 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_STDCALL : T_IDENTIFIER); } -#line 4117 "cpp_re.inc" -yy469: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; -yy470: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 17; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy474: -#line 135 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INLINE : T_IDENTIFIER); } -#line 4142 "cpp_re.inc" -yy475: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 18; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy482: -#line 129 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_FASTCALL : T_IDENTIFIER); } -#line 4172 "cpp_re.inc" -yy483: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 19; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy490: -#line 127 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_DECLSPEC : T_IDENTIFIER); } -#line 4202 "cpp_re.inc" -yy491: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 20; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy495: -#line 128 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_CDECL : T_IDENTIFIER); } -#line 4223 "cpp_re.inc" -yy496: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 21; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy500: -#line 126 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_BASED : T_IDENTIFIER); } -#line 4244 "cpp_re.inc" -yy501: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'm') goto yy109; - yyaccept = 22; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy503: -#line 136 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_ASM : T_IDENTIFIER); } -#line 4259 "cpp_re.inc" -yy504: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy530; - goto yy109; -yy505: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy475; - if (yych == 'i') goto yy523; - goto yy109; -yy506: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'r') goto yy520; - goto yy109; -yy507: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'x') goto yy514; - goto yy109; -yy508: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'v') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 23; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy513: -#line 134 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_LEAVE : T_IDENTIFIER); } -#line 4304 "cpp_re.inc" -yy514: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 24; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy519: -#line 132 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_EXCEPT : T_IDENTIFIER); } -#line 4328 "cpp_re.inc" -yy520: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'y') goto yy109; - yyaccept = 25; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy522: -#line 131 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_TRY : T_IDENTIFIER); } -#line 4343 "cpp_re.inc" -yy523: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'y') goto yy109; - yyaccept = 26; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy529: -#line 133 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_FINALLY : T_IDENTIFIER); } -#line 4370 "cpp_re.inc" -yy530: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy470; - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case '1': goto yy532; - case '3': goto yy533; - case '6': goto yy534; - case '8': goto yy535; - default: goto yy109; - } -yy532: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '6') goto yy541; - goto yy109; -yy533: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '2') goto yy539; - goto yy109; -yy534: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '4') goto yy537; - goto yy109; -yy535: - yyaccept = 27; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy536: -#line 122 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INT8 : T_IDENTIFIER); } -#line 4411 "cpp_re.inc" -yy537: - yyaccept = 28; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy538: -#line 125 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INT64 : T_IDENTIFIER); } -#line 4423 "cpp_re.inc" -yy539: - yyaccept = 29; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy540: -#line 124 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INT32 : T_IDENTIFIER); } -#line 4435 "cpp_re.inc" -yy541: - yyaccept = 30; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy542: -#line 123 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INT16 : T_IDENTIFIER); } -#line 4447 "cpp_re.inc" -yy543: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'h') goto yy549; - goto yy109; -yy544: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 31; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy548: -#line 120 "cpp.re" - { BOOST_WAVE_RET(T_WHILE); } -#line 4473 "cpp_re.inc" -yy549: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 32; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy554: -#line 119 "cpp.re" - { BOOST_WAVE_RET(T_WCHART); } -#line 4497 "cpp_re.inc" -yy555: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'r') goto yy567; - goto yy109; -yy556: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy557; - if (yych == 'l') goto yy558; - goto yy109; -yy557: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy565; - goto yy109; -yy558: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 33; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy564: -#line 118 "cpp.re" - { BOOST_WAVE_RET(T_VOLATILE); } -#line 4540 "cpp_re.inc" -yy565: - yyaccept = 34; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy566: -#line 117 "cpp.re" - { BOOST_WAVE_RET(T_VOID); } -#line 4552 "cpp_re.inc" -yy567: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 35; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy572: -#line 116 "cpp.re" - { BOOST_WAVE_RET(T_VIRTUAL); } -#line 4576 "cpp_re.inc" -yy573: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '"') goto yy129; - if (yych == 'R') goto yy128; - goto yy109; -yy574: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy580; - if (yych == 's') goto yy581; - goto yy109; -yy575: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'g') goto yy109; - yyaccept = 36; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy579: -#line 115 "cpp.re" - { BOOST_WAVE_RET(T_USING); } -#line 4609 "cpp_re.inc" -yy580: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy588; - goto yy109; -yy581: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'g') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 37; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy587: -#line 114 "cpp.re" - { BOOST_WAVE_RET(T_UNSIGNED); } -#line 4641 "cpp_re.inc" -yy588: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 38; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy590: -#line 113 "cpp.re" - { BOOST_WAVE_RET(T_UNION); } -#line 4656 "cpp_re.inc" -yy591: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'm') goto yy631; - goto yy109; -yy592: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy614; - if (yych == 'r') goto yy615; - goto yy109; -yy593: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'u') goto yy609; - if (yych == 'y') goto yy610; - goto yy109; -yy594: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'h') { - if (yych != 'd') goto yy109; - } else { - if (yych <= 'i') goto yy598; - if (yych == 'n') goto yy599; - goto yy109; - } - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy606; - goto yy109; -yy598: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy604; - goto yy109; -yy599: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'm') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 39; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy603: -#line 112 "cpp.re" - { BOOST_WAVE_RET(T_TYPENAME); } -#line 4719 "cpp_re.inc" -yy604: - yyaccept = 40; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy605: -#line 111 "cpp.re" - { BOOST_WAVE_RET(T_TYPEID); } -#line 4731 "cpp_re.inc" -yy606: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'f') goto yy109; - yyaccept = 41; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy608: -#line 110 "cpp.re" - { BOOST_WAVE_RET(T_TYPEDEF); } -#line 4746 "cpp_re.inc" -yy609: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy612; - goto yy109; -yy610: - yyaccept = 42; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy611: -#line 109 "cpp.re" - { BOOST_WAVE_RET(T_TRY); } -#line 4763 "cpp_re.inc" -yy612: - yyaccept = 43; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy613: -#line 108 "cpp.re" - { BOOST_WAVE_RET(T_TRUE); } -#line 4775 "cpp_re.inc" -yy614: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy629; - goto yy109; -yy615: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy616; - if (yych == 'o') goto yy617; - goto yy109; -yy616: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy620; - goto yy109; -yy617: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'w') goto yy109; - yyaccept = 44; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy619: -#line 107 "cpp.re" - { BOOST_WAVE_RET(T_THROW); } -#line 4806 "cpp_re.inc" -yy620: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 45; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy628: -#line 106 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_THREADLOCAL : T_IDENTIFIER); } -#line 4839 "cpp_re.inc" -yy629: - yyaccept = 46; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy630: -#line 105 "cpp.re" - { BOOST_WAVE_RET(T_THIS); } -#line 4851 "cpp_re.inc" -yy631: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 47; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy637: -#line 104 "cpp.re" - { BOOST_WAVE_RET(T_TEMPLATE); } -#line 4878 "cpp_re.inc" -yy638: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy680; - goto yy109; -yy639: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'g') goto yy670; - if (yych == 'z') goto yy671; - goto yy109; -yy640: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy647; - if (yych == 'r') goto yy648; - goto yy109; -yy641: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'h') goto yy109; - yyaccept = 48; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy646: -#line 103 "cpp.re" - { BOOST_WAVE_RET(T_SWITCH); } -#line 4919 "cpp_re.inc" -yy647: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 't') goto yy653; - goto yy109; -yy648: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 49; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy652: -#line 102 "cpp.re" - { BOOST_WAVE_RET(T_STRUCT); } -#line 4945 "cpp_re.inc" -yy653: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 50; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy657; - if (yych <= '`') goto yy656; - if (yych <= 'z') goto yy108; - } - } -yy656: -#line 99 "cpp.re" - { BOOST_WAVE_RET(T_STATIC); } -#line 4975 "cpp_re.inc" -yy657: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy658; - if (yych == 'c') goto yy659; - goto yy109; -yy658: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy664; - goto yy109; -yy659: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 51; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy663: -#line 100 "cpp.re" - { BOOST_WAVE_RET(T_STATICCAST); } -#line 5007 "cpp_re.inc" -yy664: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 52; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy669: -#line 101 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_STATICASSERT : T_IDENTIFIER); } -#line 5031 "cpp_re.inc" -yy670: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy676; - goto yy109; -yy671: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'f') goto yy109; - yyaccept = 53; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy675: -#line 98 "cpp.re" - { BOOST_WAVE_RET(T_SIZEOF); } -#line 5057 "cpp_re.inc" -yy676: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 54; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy679: -#line 97 "cpp.re" - { BOOST_WAVE_RET(T_SIGNED); } -#line 5075 "cpp_re.inc" -yy680: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 55; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy683: -#line 96 "cpp.re" - { BOOST_WAVE_RET(T_SHORT); } -#line 5093 "cpp_re.inc" -yy684: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'h') { - if (yych != 'g') goto yy109; - } else { - if (yych <= 'i') goto yy686; - if (yych == 't') goto yy687; - goto yy109; - } - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy706; - goto yy109; -yy686: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy692; - goto yy109; -yy687: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 56; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy691: -#line 95 "cpp.re" - { BOOST_WAVE_RET(T_RETURN); } -#line 5133 "cpp_re.inc" -yy692: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 57; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy705: -#line 94 "cpp.re" - { BOOST_WAVE_RET(T_REINTERPRETCAST); } -#line 5181 "cpp_re.inc" -yy706: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 58; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy711: -#line 93 "cpp.re" - { BOOST_WAVE_RET(T_REGISTER); } -#line 5205 "cpp_re.inc" -yy712: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy719; - if (yych == 'o') goto yy720; - goto yy109; -yy713: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'b') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 59; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy718: -#line 92 "cpp.re" - { BOOST_WAVE_RET(T_PUBLIC); } -#line 5235 "cpp_re.inc" -yy719: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'v') goto yy728; - goto yy109; -yy720: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 60; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy727: -#line 91 "cpp.re" - { BOOST_WAVE_RET(T_PROTECTED); } -#line 5270 "cpp_re.inc" -yy728: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 61; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy732: -#line 90 "cpp.re" - { BOOST_WAVE_RET(T_PRIVATE); } -#line 5291 "cpp_re.inc" -yy733: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy740; - goto yy109; -yy734: - yyaccept = 62; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy736; - if (yych <= '`') goto yy735; - if (yych <= 'z') goto yy108; - } - } -yy735: -#line 233 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_OROR_ALT); } -#line 5320 "cpp_re.inc" -yy736: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'q') goto yy109; - yyaccept = 63; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy739: -#line 217 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_ORASSIGN_ALT); } -#line 5338 "cpp_re.inc" -yy740: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 64; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy746: -#line 89 "cpp.re" - { BOOST_WAVE_RET(T_OPERATOR); } -#line 5365 "cpp_re.inc" -yy747: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'm') goto yy772; - goto yy109; -yy748: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'w') goto yy770; - goto yy109; -yy749: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy757; - if (yych == 't') goto yy758; - goto yy109; -yy750: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 65; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy756: -#line 88 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_NULLPTR : T_IDENTIFIER); } -#line 5408 "cpp_re.inc" -yy757: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'x') goto yy764; - goto yy109; -yy758: - yyaccept = 66; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy760; - if (yych <= '`') goto yy759; - if (yych <= 'z') goto yy108; - } - } -yy759: -#line 202 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_NOT_ALT); } -#line 5437 "cpp_re.inc" -yy760: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'q') goto yy109; - yyaccept = 67; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy763: -#line 225 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_NOTEQUAL_ALT); } -#line 5455 "cpp_re.inc" -yy764: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 68; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy769: -#line 87 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_NOEXCEPT : T_IDENTIFIER); } -#line 5479 "cpp_re.inc" -yy770: - yyaccept = 69; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy771: -#line 86 "cpp.re" - { BOOST_WAVE_RET(T_NEW); } -#line 5491 "cpp_re.inc" -yy772: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 70; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy779: -#line 85 "cpp.re" - { BOOST_WAVE_RET(T_NAMESPACE); } -#line 5521 "cpp_re.inc" -yy780: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'b') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 71; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy786: -#line 84 "cpp.re" - { BOOST_WAVE_RET(T_MUTABLE); } -#line 5548 "cpp_re.inc" -yy787: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'g') goto yy109; - yyaccept = 72; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy790: -#line 83 "cpp.re" - { BOOST_WAVE_RET(T_LONG); } -#line 5566 "cpp_re.inc" -yy791: - yyaccept = 73; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy792: -#line 79 "cpp.re" - { BOOST_WAVE_RET(T_IF); } -#line 5578 "cpp_re.inc" -yy793: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'p') goto yy802; - goto yy109; -yy794: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy795; - if (yych == 't') goto yy796; - goto yy109; -yy795: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy798; - goto yy109; -yy796: - yyaccept = 74; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy797: -#line 82 "cpp.re" - { BOOST_WAVE_RET(T_INT); } -#line 5606 "cpp_re.inc" -yy798: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 75; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy801: -#line 81 "cpp.re" - { BOOST_WAVE_RET(T_INLINE); } -#line 5624 "cpp_re.inc" -yy802: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 76; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy806: -#line 80 "cpp.re" - { BOOST_WAVE_RET(s->enable_import_keyword ? T_IMPORT : T_IDENTIFIER); } -#line 5645 "cpp_re.inc" -yy807: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 77; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy810: -#line 78 "cpp.re" - { BOOST_WAVE_RET(T_GOTO); } -#line 5663 "cpp_re.inc" -yy811: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy826; - goto yy109; -yy812: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy822; - goto yy109; -yy813: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'r') goto yy820; - goto yy109; -yy814: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 78; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy819: -#line 77 "cpp.re" - { BOOST_WAVE_RET(T_FRIEND); } -#line 5702 "cpp_re.inc" -yy820: - yyaccept = 79; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy821: -#line 76 "cpp.re" - { BOOST_WAVE_RET(T_FOR); } -#line 5714 "cpp_re.inc" -yy822: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 80; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy825: -#line 75 "cpp.re" - { BOOST_WAVE_RET(T_FLOAT); } -#line 5732 "cpp_re.inc" -yy826: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 81; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy829: -#line 74 "cpp.re" - { BOOST_WAVE_RET(T_FALSE); } -#line 5750 "cpp_re.inc" -yy830: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy852; - goto yy109; -yy831: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'u') goto yy849; - goto yy109; -yy832: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'p') goto yy833; - if (yych == 't') goto yy834; - goto yy109; -yy833: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy839; - if (yych == 'o') goto yy840; - goto yy109; -yy834: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 82; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy838: -#line 73 "cpp.re" - { BOOST_WAVE_RET(T_EXTERN); } -#line 5793 "cpp_re.inc" -yy839: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy844; - goto yy109; -yy840: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 83; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy843: -#line 72 "cpp.re" - { BOOST_WAVE_RET(T_EXPORT); } -#line 5816 "cpp_re.inc" -yy844: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 84; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy848: -#line 71 "cpp.re" - { BOOST_WAVE_RET(T_EXPLICIT); } -#line 5837 "cpp_re.inc" -yy849: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'm') goto yy109; - yyaccept = 85; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy851: -#line 70 "cpp.re" - { BOOST_WAVE_RET(T_ENUM); } -#line 5852 "cpp_re.inc" -yy852: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 86; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy854: -#line 69 "cpp.re" - { BOOST_WAVE_RET(T_ELSE); } -#line 5867 "cpp_re.inc" -yy855: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'e') { - if (yych == 'c') goto yy875; - goto yy109; - } else { - if (yych <= 'f') goto yy876; - if (yych == 'l') goto yy877; - goto yy109; - } -yy856: - yyaccept = 87; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'Z') { - if (yych <= '9') { - if (yych == '$') goto yy108; - if (yych >= '0') goto yy108; - } else { - if (yych == '?') goto yy111; - if (yych >= 'A') goto yy108; - } - } else { - if (yych <= '_') { - if (yych == '\\') goto yy110; - if (yych >= '_') goto yy108; - } else { - if (yych <= 't') { - if (yych >= 'a') goto yy108; - } else { - if (yych <= 'u') goto yy870; - if (yych <= 'z') goto yy108; - } - } - } -yy857: -#line 66 "cpp.re" - { BOOST_WAVE_RET(T_DO); } -#line 5906 "cpp_re.inc" -yy858: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'm') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 88; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy869: -#line 68 "cpp.re" - { BOOST_WAVE_RET(T_DYNAMICCAST); } -#line 5948 "cpp_re.inc" -yy870: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'b') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 89; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy874: -#line 67 "cpp.re" - { BOOST_WAVE_RET(T_DOUBLE); } -#line 5969 "cpp_re.inc" -yy875: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy887; - goto yy109; -yy876: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy882; - goto yy109; -yy877: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 90; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy881: -#line 65 "cpp.re" - { BOOST_WAVE_RET(T_DELETE); } -#line 6000 "cpp_re.inc" -yy882: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 91; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy886: -#line 64 "cpp.re" - { BOOST_WAVE_RET(T_DEFAULT); } -#line 6021 "cpp_re.inc" -yy887: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'y') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 92; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy892: -#line 63 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_DECLTYPE : T_IDENTIFIER); } -#line 6045 "cpp_re.inc" -yy893: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'r') goto yy109; - if (yych <= 's') goto yy939; - if (yych <= 't') goto yy940; - goto yy109; -yy894: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy926; - goto yy109; -yy895: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy922; - goto yy109; -yy896: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'l') goto yy109; - if (yych <= 'm') goto yy898; - if (yych >= 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'r') goto yy109; - if (yych <= 's') goto yy902; - if (yych <= 't') goto yy903; - goto yy109; -yy898: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 93; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy901: -#line 200 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_COMPL_ALT); } -#line 6092 "cpp_re.inc" -yy902: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 't') goto yy909; - goto yy109; -yy903: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 94; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy908: -#line 62 "cpp.re" - { BOOST_WAVE_RET(T_CONTINUE); } -#line 6121 "cpp_re.inc" -yy909: - yyaccept = 95; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'Z') { - if (yych <= '9') { - if (yych == '$') goto yy108; - if (yych >= '0') goto yy108; - } else { - if (yych == '?') goto yy111; - if (yych >= 'A') goto yy108; - } - } else { - if (yych <= '_') { - if (yych == '\\') goto yy110; - if (yych >= '_') goto yy911; - } else { - if (yych <= 'd') { - if (yych >= 'a') goto yy108; - } else { - if (yych <= 'e') goto yy912; - if (yych <= 'z') goto yy108; - } - } - } -yy910: -#line 59 "cpp.re" - { BOOST_WAVE_RET(T_CONST); } -#line 6149 "cpp_re.inc" -yy911: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'c') goto yy917; - goto yy109; -yy912: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'x') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 96; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy916: -#line 60 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_CONSTEXPR : T_IDENTIFIER); } -#line 6175 "cpp_re.inc" -yy917: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 97; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy921: -#line 61 "cpp.re" - { BOOST_WAVE_RET(T_CONSTCAST); } -#line 6196 "cpp_re.inc" -yy922: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 98; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy925: -#line 58 "cpp.re" - { BOOST_WAVE_RET(T_CLASS); } -#line 6214 "cpp_re.inc" -yy926: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 99; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '>') { - if (yych <= '0') { - if (yych == '$') goto yy108; - if (yych >= '0') goto yy108; - } else { - if (yych <= '2') { - if (yych <= '1') goto yy929; - goto yy108; - } else { - if (yych <= '3') goto yy930; - if (yych <= '9') goto yy108; - } - } - } else { - if (yych <= '\\') { - if (yych <= '@') { - if (yych <= '?') goto yy111; - } else { - if (yych <= 'Z') goto yy108; - if (yych >= '\\') goto yy110; - } - } else { - if (yych <= '_') { - if (yych >= '_') goto yy108; - } else { - if (yych <= '`') goto yy928; - if (yych <= 'z') goto yy108; - } - } - } -yy928: -#line 55 "cpp.re" - { BOOST_WAVE_RET(T_CHAR); } -#line 6254 "cpp_re.inc" -yy929: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '6') goto yy935; - goto yy109; -yy930: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '2') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 100; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy934: -#line 57 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_CHAR32_T : T_IDENTIFIER); } -#line 6280 "cpp_re.inc" -yy935: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 101; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy938: -#line 56 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_CHAR16_T : T_IDENTIFIER); } -#line 6298 "cpp_re.inc" -yy939: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy944; - goto yy109; -yy940: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'h') goto yy109; - yyaccept = 102; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy943: -#line 54 "cpp.re" - { BOOST_WAVE_RET(T_CATCH); } -#line 6321 "cpp_re.inc" -yy944: - yyaccept = 103; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy945: -#line 53 "cpp.re" - { BOOST_WAVE_RET(T_CASE); } -#line 6333 "cpp_re.inc" -yy946: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 't') goto yy956; - goto yy109; -yy947: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy953; - goto yy109; -yy948: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'k') goto yy109; - yyaccept = 104; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy952: -#line 52 "cpp.re" - { BOOST_WAVE_RET(T_BREAK); } -#line 6364 "cpp_re.inc" -yy953: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 105; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy955: -#line 51 "cpp.re" - { BOOST_WAVE_RET(T_BOOL); } -#line 6379 "cpp_re.inc" -yy956: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy957; - if (yych == 'o') goto yy958; - goto yy109; -yy957: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy961; - goto yy109; -yy958: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 106; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy960: -#line 196 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_OR_ALT); } -#line 6405 "cpp_re.inc" -yy961: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 107; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy963: -#line 194 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_AND_ALT); } -#line 6420 "cpp_re.inc" -yy964: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy979; - goto yy109; -yy965: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy973; - goto yy109; -yy966: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'm') goto yy971; - goto yy109; -yy967: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 108; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy970: -#line 50 "cpp.re" - { BOOST_WAVE_RET(T_AUTO); } -#line 6453 "cpp_re.inc" -yy971: - yyaccept = 109; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy972: -#line 49 "cpp.re" - { BOOST_WAVE_RET(T_ASM); } -#line 6465 "cpp_re.inc" -yy973: - yyaccept = 110; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy975; - if (yych <= '`') goto yy974; - if (yych <= 'z') goto yy108; - } - } -yy974: -#line 229 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_ANDAND_ALT); } -#line 6489 "cpp_re.inc" -yy975: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'q') goto yy109; - yyaccept = 111; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy978: -#line 215 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_ANDASSIGN_ALT); } -#line 6507 "cpp_re.inc" -yy979: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'g') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy982; - if (yych == 'o') goto yy983; - goto yy109; -yy982: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy986; - goto yy109; -yy983: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'f') goto yy109; - yyaccept = 112; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy985: -#line 48 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_ALIGNOF : T_IDENTIFIER); } -#line 6539 "cpp_re.inc" -yy986: - yyaccept = 113; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy987: -#line 47 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_ALIGNAS : T_IDENTIFIER); } -#line 6551 "cpp_re.inc" -yy988: - ++YYCURSOR; -#line 176 "cpp.re" - { - if (s->act_in_c99_mode) { - --YYCURSOR; - BOOST_WAVE_RET(T_DOT); - } - else { - BOOST_WAVE_RET(T_DOTSTAR); - } - } -#line 6564 "cpp_re.inc" -yy990: - yych = *++YYCURSOR; - if (yych == '.') goto yy992; - goto yy99; -yy991: - yych = *++YYCURSOR; - goto yy7; -yy992: - ++YYCURSOR; -#line 162 "cpp.re" - { BOOST_WAVE_RET(T_ELLIPSIS); } -#line 6576 "cpp_re.inc" -yy994: - ++YYCURSOR; -#line 209 "cpp.re" - { BOOST_WAVE_RET(T_DIVIDEASSIGN); } -#line 6581 "cpp_re.inc" -yy996: - ++YYCURSOR; -#line 44 "cpp.re" - { goto cppcomment; } -#line 6586 "cpp_re.inc" -yy998: - ++YYCURSOR; -#line 43 "cpp.re" - { goto ccomment; } -#line 6591 "cpp_re.inc" -} -#line 348 "cpp.re" - - -ccomment: - -#line 6598 "cpp_re.inc" -{ - YYCTYPE yych; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= '\f') { - if (yych <= 0x08) { - if (yych <= 0x00) goto yy1009; - goto yy1011; - } else { - if (yych == '\n') goto yy1004; - goto yy1007; - } - } else { - if (yych <= 0x1F) { - if (yych <= '\r') goto yy1006; - goto yy1011; - } else { - if (yych != '*') goto yy1008; - } - } - ++YYCURSOR; - if ((yych = *YYCURSOR) == '/') goto yy1014; -yy1003: -#line 363 "cpp.re" - { goto ccomment; } -#line 6624 "cpp_re.inc" -yy1004: - ++YYCURSOR; -yy1005: -#line 355 "cpp.re" - { - /*if(cursor == s->eof) BOOST_WAVE_RET(T_EOF);*/ - /*s->tok = cursor; */ - s->line += count_backslash_newlines(s, cursor) +1; - cursor.column = 1; - goto ccomment; - } -#line 6636 "cpp_re.inc" -yy1006: - yych = *++YYCURSOR; - if (yych == '\n') goto yy1013; - goto yy1005; -yy1007: - yych = *++YYCURSOR; - goto yy1003; -yy1008: - yych = *++YYCURSOR; - goto yy1003; -yy1009: - ++YYCURSOR; -#line 366 "cpp.re" - { - if(cursor == s->eof) - { - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_warning, - "Unterminated 'C' style comment"); - } - else - { - --YYCURSOR; // next call returns T_EOF - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character: '\\000' in input stream"); - } - } -#line 6665 "cpp_re.inc" -yy1011: - ++YYCURSOR; -#line 383 "cpp.re" - { - // flag the error - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\%03o' in input stream", *--YYCURSOR); - } -#line 6675 "cpp_re.inc" -yy1013: - yych = *++YYCURSOR; - goto yy1005; -yy1014: - ++YYCURSOR; -#line 352 "cpp.re" - { BOOST_WAVE_RET(T_CCOMMENT); } -#line 6683 "cpp_re.inc" -} -#line 389 "cpp.re" - - -cppcomment: - -#line 6690 "cpp_re.inc" -{ - YYCTYPE yych; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= '\n') { - if (yych <= 0x00) goto yy1024; - if (yych <= 0x08) goto yy1026; - if (yych <= '\t') goto yy1021; - } else { - if (yych <= '\f') goto yy1021; - if (yych <= '\r') goto yy1020; - if (yych <= 0x1F) goto yy1026; - goto yy1023; - } - ++YYCURSOR; -yy1019: -#line 394 "cpp.re" - { - /*if(cursor == s->eof) BOOST_WAVE_RET(T_EOF); */ - /*s->tok = cursor; */ - s->line++; - cursor.column = 1; - BOOST_WAVE_RET(T_CPPCOMMENT); - } -#line 6715 "cpp_re.inc" -yy1020: - yych = *++YYCURSOR; - if (yych == '\n') goto yy1028; - goto yy1019; -yy1021: - ++YYCURSOR; -yy1022: -#line 402 "cpp.re" - { goto cppcomment; } -#line 6725 "cpp_re.inc" -yy1023: - yych = *++YYCURSOR; - goto yy1022; -yy1024: - ++YYCURSOR; -#line 405 "cpp.re" - { - if (s->eof && cursor != s->eof) - { - --YYCURSOR; // next call returns T_EOF - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\000' in input stream"); - } - - --YYCURSOR; // next call returns T_EOF - if (!s->single_line_only) - { - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_warning, - "Unterminated 'C++' style comment"); - } - BOOST_WAVE_RET(T_CPPCOMMENT); - } -#line 6750 "cpp_re.inc" -yy1026: - ++YYCURSOR; -#line 425 "cpp.re" - { - // flag the error - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\%03o' in input stream", *--YYCURSOR); - } -#line 6760 "cpp_re.inc" -yy1028: - ++YYCURSOR; - yych = *YYCURSOR; - goto yy1019; -} -#line 431 "cpp.re" - - -/* this subscanner is called whenever a pp_number has been started */ -pp_number: -{ - cursor = uchar_wrapper(s->tok = s->cur, s->column = s->curr_column); - marker = uchar_wrapper(s->ptr); - limit = uchar_wrapper(s->lim); - - if (s->detect_pp_numbers) { - -#line 6778 "cpp_re.inc" -{ - YYCTYPE yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 64, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 64, 0, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 0, 0, 0, 0, 0, 0, - 0, 64, 64, 64, 64, 128, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 0, 0, 0, 0, 64, - 0, 64, 64, 64, 64, 128, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych == '.') goto yy1032; - if (yych <= '/') goto yy1031; - if (yych <= '9') goto yy1033; -yy1031: - YYCURSOR = YYMARKER; - goto yy1035; -yy1032: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; -yy1033: - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 64) { - goto yy1033; - } - if (yych <= 'Z') { - if (yych == '?') goto yy1039; - if (yych >= 'A') goto yy1036; - } else { - if (yych <= '\\') { - if (yych >= '\\') goto yy1038; - } else { - if (yych == 'e') goto yy1036; - } - } -yy1035: -#line 443 "cpp.re" - { BOOST_WAVE_RET(T_PP_NUMBER); } -#line 6847 "cpp_re.inc" -yy1036: - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1036; - } - if (yych <= '>') { - if (yych <= '+') { - if (yych == '$') goto yy1033; - if (yych <= '*') goto yy1035; - goto yy1033; - } else { - if (yych <= '.') { - if (yych <= ',') goto yy1035; - goto yy1033; - } else { - if (yych <= '/') goto yy1035; - if (yych <= '9') goto yy1033; - goto yy1035; - } - } - } else { - if (yych <= '\\') { - if (yych <= '@') { - if (yych <= '?') goto yy1039; - goto yy1035; - } else { - if (yych <= 'Z') goto yy1033; - if (yych <= '[') goto yy1035; - } - } else { - if (yych <= '_') { - if (yych <= '^') goto yy1035; - goto yy1033; - } else { - if (yych <= '`') goto yy1035; - if (yych <= 'z') goto yy1033; - goto yy1035; - } - } - } -yy1038: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == 'U') goto yy1042; - if (yych == 'u') goto yy1041; - goto yy1031; -yy1039: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych != '?') goto yy1031; - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == '/') goto yy1038; - goto yy1031; -yy1041: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych <= '9') goto yy1050; - goto yy1031; - } else { - if (yych <= 'F') goto yy1050; - if (yych <= '`') goto yy1031; - if (yych <= 'f') goto yy1050; - goto yy1031; - } -yy1042: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1043; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1043: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1044; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1044: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1045; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1045: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1046; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1046: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1047; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1047: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1048; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1048: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1049; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1049: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych <= '9') goto yy1033; - goto yy1031; - } else { - if (yych <= 'F') goto yy1033; - if (yych <= '`') goto yy1031; - if (yych <= 'f') goto yy1033; - goto yy1031; - } -yy1050: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1051; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1051: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1052; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1052: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych <= '9') goto yy1033; - goto yy1031; - } else { - if (yych <= 'F') goto yy1033; - if (yych <= '`') goto yy1031; - if (yych <= 'f') goto yy1033; - goto yy1031; - } -} -#line 444 "cpp.re" - - } - else { - -#line 7063 "cpp_re.inc" -{ - YYCTYPE yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 224, 224, 224, 224, 224, 224, 224, 224, - 160, 160, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4); - yych = *YYCURSOR; - if (yych <= '/') { - if (yych == '.') goto yy1060; - } else { - if (yych <= '0') goto yy1056; - if (yych <= '9') goto yy1058; - } -yy1055: - YYCURSOR = YYMARKER; - if (yyaccept <= 0) { - goto yy1057; - } else { - goto yy1063; - } -yy1056: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[0+yych] & 64) { - goto yy1075; - } - if (yych <= 'E') { - if (yych <= '/') { - if (yych == '.') goto yy1061; - } else { - if (yych <= '9') goto yy1078; - if (yych >= 'E') goto yy1071; - } - } else { - if (yych <= 'd') { - if (yych == 'X') goto yy1077; - } else { - if (yych <= 'e') goto yy1071; - if (yych == 'x') goto yy1077; - } - } -yy1057: -#line 451 "cpp.re" - { goto integer_suffix; } -#line 7140 "cpp_re.inc" -yy1058: - yyaccept = 0; - YYMARKER = ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy1058; - } - if (yych <= 'D') { - if (yych == '.') goto yy1061; - goto yy1057; - } else { - if (yych <= 'E') goto yy1071; - if (yych == 'e') goto yy1071; - goto yy1057; - } -yy1060: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1055; - if (yych >= ':') goto yy1055; -yy1061: - yyaccept = 1; - YYMARKER = ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yych <= 'K') { - if (yych <= 'D') { - if (yych <= '/') goto yy1063; - if (yych <= '9') goto yy1061; - } else { - if (yych <= 'E') goto yy1064; - if (yych <= 'F') goto yy1065; - } - } else { - if (yych <= 'e') { - if (yych <= 'L') goto yy1066; - if (yych >= 'e') goto yy1064; - } else { - if (yych <= 'f') goto yy1065; - if (yych == 'l') goto yy1066; - } - } -yy1063: -#line 449 "cpp.re" - { BOOST_WAVE_RET(T_FLOATLIT); } -#line 7186 "cpp_re.inc" -yy1064: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych == '+') goto yy1068; - goto yy1055; - } else { - if (yych <= '-') goto yy1068; - if (yych <= '/') goto yy1055; - if (yych <= '9') goto yy1069; - goto yy1055; - } -yy1065: - yych = *++YYCURSOR; - if (yych == 'L') goto yy1067; - if (yych == 'l') goto yy1067; - goto yy1063; -yy1066: - yych = *++YYCURSOR; - if (yych == 'F') goto yy1067; - if (yych != 'f') goto yy1063; -yy1067: - yych = *++YYCURSOR; - goto yy1063; -yy1068: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1055; - if (yych >= ':') goto yy1055; -yy1069: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= 'K') { - if (yych <= '9') { - if (yych <= '/') goto yy1063; - goto yy1069; - } else { - if (yych == 'F') goto yy1065; - goto yy1063; - } - } else { - if (yych <= 'f') { - if (yych <= 'L') goto yy1066; - if (yych <= 'e') goto yy1063; - goto yy1065; - } else { - if (yych == 'l') goto yy1066; - goto yy1063; - } - } -yy1071: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych != '+') goto yy1055; - } else { - if (yych <= '-') goto yy1072; - if (yych <= '/') goto yy1055; - if (yych <= '9') goto yy1073; - goto yy1055; - } -yy1072: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1055; - if (yych >= ':') goto yy1055; -yy1073: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= 'K') { - if (yych <= '9') { - if (yych <= '/') goto yy1063; - goto yy1073; - } else { - if (yych == 'F') goto yy1065; - goto yy1063; - } - } else { - if (yych <= 'f') { - if (yych <= 'L') goto yy1066; - if (yych <= 'e') goto yy1063; - goto yy1065; - } else { - if (yych == 'l') goto yy1066; - goto yy1063; - } - } -yy1075: - yyaccept = 0; - YYMARKER = ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yybm[0+yych] & 64) { - goto yy1075; - } - if (yych <= '9') { - if (yych == '.') goto yy1061; - if (yych <= '/') goto yy1057; - goto yy1078; - } else { - if (yych <= 'E') { - if (yych <= 'D') goto yy1057; - goto yy1071; - } else { - if (yych == 'e') goto yy1071; - goto yy1057; - } - } -yy1077: - yych = *++YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1080; - } - goto yy1055; -yy1078: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych == '.') goto yy1061; - if (yych <= '/') goto yy1055; - goto yy1078; - } else { - if (yych <= 'E') { - if (yych <= 'D') goto yy1055; - goto yy1071; - } else { - if (yych == 'e') goto yy1071; - goto yy1055; - } - } -yy1080: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1080; - } - goto yy1057; -} -#line 452 "cpp.re" - - } -} - -/* this subscanner is called, whenever an Integer was recognized */ -integer_suffix: -{ - if (s->enable_ms_extensions) { - -#line 7335 "cpp_re.inc" -{ - YYCTYPE yych; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *(YYMARKER = YYCURSOR); - if (yych <= 'h') { - if (yych <= 'L') { - if (yych >= 'L') goto yy1086; - } else { - if (yych == 'U') goto yy1085; - } - } else { - if (yych <= 'l') { - if (yych <= 'i') goto yy1087; - if (yych >= 'l') goto yy1086; - } else { - if (yych == 'u') goto yy1085; - } - } -yy1084: -#line 465 "cpp.re" - { BOOST_WAVE_RET(T_INTLIT); } -#line 7357 "cpp_re.inc" -yy1085: - yych = *++YYCURSOR; - if (yych == 'L') goto yy1094; - if (yych == 'l') goto yy1094; - goto yy1084; -yy1086: - yych = *++YYCURSOR; - if (yych <= 'U') { - if (yych == 'L') goto yy1093; - if (yych <= 'T') goto yy1084; - goto yy1092; - } else { - if (yych <= 'l') { - if (yych <= 'k') goto yy1084; - goto yy1093; - } else { - if (yych == 'u') goto yy1092; - goto yy1084; - } - } -yy1087: - yych = *++YYCURSOR; - if (yych == '6') goto yy1089; -yy1088: - YYCURSOR = YYMARKER; - goto yy1084; -yy1089: - yych = *++YYCURSOR; - if (yych != '4') goto yy1088; -yy1090: - ++YYCURSOR; -yy1091: -#line 462 "cpp.re" - { BOOST_WAVE_RET(T_LONGINTLIT); } -#line 7392 "cpp_re.inc" -yy1092: - yych = *++YYCURSOR; - goto yy1084; -yy1093: - yych = *++YYCURSOR; - if (yych == 'U') goto yy1090; - if (yych == 'u') goto yy1090; - goto yy1091; -yy1094: - ++YYCURSOR; - if ((yych = *YYCURSOR) == 'L') goto yy1090; - if (yych == 'l') goto yy1090; - goto yy1084; -} -#line 466 "cpp.re" - - } - else { - -#line 7412 "cpp_re.inc" -{ - YYCTYPE yych; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yych <= 'U') { - if (yych == 'L') goto yy1099; - if (yych >= 'U') goto yy1098; - } else { - if (yych <= 'l') { - if (yych >= 'l') goto yy1099; - } else { - if (yych == 'u') goto yy1098; - } - } -yy1097: -#line 474 "cpp.re" - { BOOST_WAVE_RET(T_INTLIT); } -#line 7430 "cpp_re.inc" -yy1098: - yych = *++YYCURSOR; - if (yych == 'L') goto yy1104; - if (yych == 'l') goto yy1104; - goto yy1097; -yy1099: - yych = *++YYCURSOR; - if (yych <= 'U') { - if (yych == 'L') goto yy1101; - if (yych <= 'T') goto yy1097; - } else { - if (yych <= 'l') { - if (yych <= 'k') goto yy1097; - goto yy1101; - } else { - if (yych != 'u') goto yy1097; - } - } - yych = *++YYCURSOR; - goto yy1097; -yy1101: - ++YYCURSOR; - if ((yych = *YYCURSOR) == 'U') goto yy1103; - if (yych == 'u') goto yy1103; -yy1102: -#line 471 "cpp.re" - { BOOST_WAVE_RET(T_LONGINTLIT); } -#line 7458 "cpp_re.inc" -yy1103: - yych = *++YYCURSOR; - goto yy1102; -yy1104: - ++YYCURSOR; - if ((yych = *YYCURSOR) == 'L') goto yy1103; - if (yych == 'l') goto yy1103; - goto yy1097; -} -#line 475 "cpp.re" - - } -} - -/* this subscanner is invoked for C++0x extended character literals */ -extcharlit: -{ - -#line 7477 "cpp_re.inc" -{ - YYCTYPE yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((YYLIMIT - YYCURSOR) < 13) YYFILL(13); - yych = *YYCURSOR; - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= 0x08) goto yy1107; - if (yych <= '\t') goto yy1108; - goto yy1112; - } else { - if (yych <= '\f') goto yy1108; - if (yych <= '\r') goto yy1112; - } - } else { - if (yych <= '>') { - if (yych == '\'') goto yy1112; - goto yy1108; - } else { - if (yych <= '?') goto yy1110; - if (yych == '\\') goto yy1111; - goto yy1108; - } - } -yy1107: - YYCURSOR = YYMARKER; - goto yy1109; -yy1108: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '\'') goto yy1120; -yy1109: -#line 487 "cpp.re" - { BOOST_WAVE_RET(TOKEN_FROM_ID(*s->tok, UnknownTokenType)); } -#line 7544 "cpp_re.inc" -yy1110: - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '\'') goto yy1120; - if (yych == '?') goto yy1135; - goto yy1109; -yy1111: - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1115; - goto yy1109; - } else { - if (yych <= '\'') goto yy1115; - if (yych <= '/') goto yy1109; - goto yy1118; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1116; - goto yy1109; - } else { - if (yych <= 'U') goto yy1114; - if (yych == '\\') goto yy1115; - goto yy1109; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1115; - if (yych <= 'e') goto yy1109; - goto yy1115; - } else { - if (yych == 'n') goto yy1115; - if (yych <= 'q') goto yy1109; - goto yy1115; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1109; - if (yych <= 't') goto yy1115; - goto yy1113; - } else { - if (yych <= 'v') goto yy1115; - if (yych == 'x') goto yy1117; - goto yy1109; - } - } - } -yy1112: - yych = *++YYCURSOR; - goto yy1109; -yy1113: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych <= '9') goto yy1132; - goto yy1107; - } else { - if (yych <= 'F') goto yy1132; - if (yych <= '`') goto yy1107; - if (yych <= 'f') goto yy1132; - goto yy1107; - } -yy1114: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych <= '9') goto yy1125; - goto yy1107; - } else { - if (yych <= 'F') goto yy1125; - if (yych <= '`') goto yy1107; - if (yych <= 'f') goto yy1125; - goto yy1107; - } -yy1115: - yych = *++YYCURSOR; - if (yych == '\'') goto yy1120; - goto yy1107; -yy1116: - yych = *++YYCURSOR; - if (yych == '\'') goto yy1120; - if (yych == '?') goto yy1124; - goto yy1107; -yy1117: - yych = *++YYCURSOR; - if (yych == '\'') goto yy1107; - goto yy1123; -yy1118: - yych = *++YYCURSOR; - if (yych == '\'') goto yy1120; - if (yych <= '/') goto yy1107; - if (yych >= '8') goto yy1107; - yych = *++YYCURSOR; - if (yych == '\'') goto yy1120; - if (yych <= '/') goto yy1107; - if (yych <= '7') goto yy1115; - goto yy1107; -yy1120: - ++YYCURSOR; -#line 484 "cpp.re" - { BOOST_WAVE_RET(T_CHARLIT); } -#line 7649 "cpp_re.inc" -yy1122: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy1123: - if (yybm[0+yych] & 128) { - goto yy1122; - } - if (yych == '\'') goto yy1120; - goto yy1107; -yy1124: - yych = *++YYCURSOR; - if (yych == '/') goto yy1115; - goto yy1107; -yy1125: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1126; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1126: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1127; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1127: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1128; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1128: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1129; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1129: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1130; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1130: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1131; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1131: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych <= '9') goto yy1115; - goto yy1107; - } else { - if (yych <= 'F') goto yy1115; - if (yych <= '`') goto yy1107; - if (yych <= 'f') goto yy1115; - goto yy1107; - } -yy1132: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1133; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1133: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1134; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1134: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych <= '9') goto yy1115; - goto yy1107; - } else { - if (yych <= 'F') goto yy1115; - if (yych <= '`') goto yy1107; - if (yych <= 'f') goto yy1115; - goto yy1107; - } -yy1135: - yych = *++YYCURSOR; - if (yych != '/') goto yy1107; - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1115; - goto yy1107; - } else { - if (yych <= '\'') goto yy1115; - if (yych <= '/') goto yy1107; - goto yy1118; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1116; - goto yy1107; - } else { - if (yych <= 'U') goto yy1114; - if (yych == '\\') goto yy1115; - goto yy1107; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1115; - if (yych <= 'e') goto yy1107; - goto yy1115; - } else { - if (yych == 'n') goto yy1115; - if (yych <= 'q') goto yy1107; - goto yy1115; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1107; - if (yych <= 't') goto yy1115; - goto yy1113; - } else { - if (yych <= 'v') goto yy1115; - if (yych == 'x') goto yy1117; - goto yy1107; - } - } - } -} -#line 488 "cpp.re" - -} - -/* this subscanner is invoked for C++0x extended character string literals */ -extstringlit: -{ - -#line 7824 "cpp_re.inc" -{ - YYCTYPE yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 0, 16, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 16, 16, 0, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 16, 16, 16, 16, 16, 32, - 16, 144, 144, 144, 144, 144, 144, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 64, 16, 16, 16, - 16, 144, 144, 144, 144, 144, 144, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - }; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= 0x08) goto yy1139; - if (yych <= '\t') goto yy1140; - goto yy1146; - } else { - if (yych <= '\f') goto yy1140; - if (yych <= '\r') goto yy1146; - } - } else { - if (yych <= '>') { - if (yych == '"') goto yy1144; - goto yy1140; - } else { - if (yych <= '?') goto yy1142; - if (yych == '\\') goto yy1143; - goto yy1140; - } - } -yy1139: - YYCURSOR = YYMARKER; - if (yyaccept <= 0) { - goto yy1141; - } else { - goto yy1145; - } -yy1140: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '\n') { - if (yych == '\t') goto yy1150; - } else { - if (yych <= '\f') goto yy1150; - if (yych >= ' ') goto yy1150; - } -yy1141: -#line 499 "cpp.re" - { BOOST_WAVE_RET(TOKEN_FROM_ID(*s->tok, UnknownTokenType)); } -#line 7902 "cpp_re.inc" -yy1142: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[0+yych] & 32) { - goto yy1158; - } - if (yych <= '\n') { - if (yych == '\t') goto yy1150; - goto yy1141; - } else { - if (yych <= '\f') goto yy1150; - if (yych <= 0x1F) goto yy1141; - goto yy1150; - } -yy1143: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1149; - goto yy1141; - } else { - if (yych <= '\'') goto yy1149; - if (yych <= '/') goto yy1141; - goto yy1153; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1151; - goto yy1141; - } else { - if (yych <= 'U') goto yy1148; - if (yych == '\\') goto yy1149; - goto yy1141; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1149; - if (yych <= 'e') goto yy1141; - goto yy1149; - } else { - if (yych == 'n') goto yy1149; - if (yych <= 'q') goto yy1141; - goto yy1149; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1141; - if (yych <= 't') goto yy1149; - goto yy1147; - } else { - if (yych <= 'v') goto yy1149; - if (yych == 'x') goto yy1152; - goto yy1141; - } - } - } -yy1144: - ++YYCURSOR; -yy1145: -#line 496 "cpp.re" - { BOOST_WAVE_RET(T_STRINGLIT); } -#line 7968 "cpp_re.inc" -yy1146: - yych = *++YYCURSOR; - goto yy1141; -yy1147: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych <= '9') goto yy1187; - goto yy1139; - } else { - if (yych <= 'F') goto yy1187; - if (yych <= '`') goto yy1139; - if (yych <= 'f') goto yy1187; - goto yy1139; - } -yy1148: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych <= '9') goto yy1180; - goto yy1139; - } else { - if (yych <= 'F') goto yy1180; - if (yych <= '`') goto yy1139; - if (yych <= 'f') goto yy1180; - goto yy1139; - } -yy1149: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy1150: - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1151: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1179; - goto yy1157; -yy1152: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1166; - } - goto yy1139; -yy1153: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '"') { - if (yych <= '\n') { - if (yych == '\t') goto yy1149; - goto yy1139; - } else { - if (yych <= '\f') goto yy1149; - if (yych <= 0x1F) goto yy1139; - if (yych <= '!') goto yy1149; - goto yy1155; - } - } else { - if (yych <= '>') { - if (yych <= '/') goto yy1149; - if (yych >= '8') goto yy1149; - } else { - if (yych <= '?') goto yy1156; - if (yych == '\\') goto yy1157; - goto yy1149; - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1155: - yych = *++YYCURSOR; - goto yy1145; -yy1156: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1158; -yy1157: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1149; - goto yy1139; - } else { - if (yych <= '\'') goto yy1149; - if (yych <= '/') goto yy1139; - goto yy1153; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1151; - goto yy1139; - } else { - if (yych <= 'U') goto yy1148; - if (yych == '\\') goto yy1149; - goto yy1139; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1149; - if (yych <= 'e') goto yy1139; - goto yy1149; - } else { - if (yych == 'n') goto yy1149; - if (yych <= 'q') goto yy1139; - goto yy1149; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1139; - if (yych <= 't') goto yy1149; - goto yy1147; - } else { - if (yych <= 'v') goto yy1149; - if (yych == 'x') goto yy1152; - goto yy1139; - } - } - } -yy1158: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy1158; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy1149; - goto yy1139; - } else { - if (yych <= '\f') goto yy1149; - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } - } else { - if (yych <= '/') { - if (yych <= '"') goto yy1155; - if (yych <= '.') goto yy1149; - } else { - if (yych == '\\') goto yy1157; - goto yy1149; - } - } -yy1160: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 64) { - goto yy1160; - } - if (yych <= '7') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1164; - if (yych <= '/') goto yy1149; - goto yy1153; - } - } - } else { - if (yych <= 'U') { - if (yych == '?') goto yy1165; - if (yych <= 'T') goto yy1149; - goto yy1163; - } else { - if (yych <= 'u') { - if (yych <= 't') goto yy1149; - } else { - if (yych == 'x') goto yy1166; - goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - goto yy1176; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - goto yy1176; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych <= 'f') goto yy1176; - goto yy1149; - } - } - } -yy1163: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - goto yy1169; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - goto yy1169; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych <= 'f') goto yy1169; - goto yy1149; - } - } - } -yy1164: - yyaccept = 1; - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1145; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1165: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1168; - goto yy1157; -yy1166: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1166; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy1149; - goto yy1139; - } else { - if (yych <= '\f') goto yy1149; - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } - } else { - if (yych <= '?') { - if (yych <= '"') goto yy1155; - if (yych <= '>') goto yy1149; - goto yy1156; - } else { - if (yych == '\\') goto yy1157; - goto yy1149; - } - } -yy1168: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy1158; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy1149; - goto yy1139; - } else { - if (yych <= '\f') goto yy1149; - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } - } else { - if (yych <= '/') { - if (yych <= '"') goto yy1155; - if (yych <= '.') goto yy1149; - goto yy1160; - } else { - if (yych == '\\') goto yy1157; - goto yy1149; - } - } -yy1169: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1176: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1179: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1158; - goto yy1157; -yy1180: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1181; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1181: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1182; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1182: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1183; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1183: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1184; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1184: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1185; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1185: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1186; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1186: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych <= '9') goto yy1149; - goto yy1139; - } else { - if (yych <= 'F') goto yy1149; - if (yych <= '`') goto yy1139; - if (yych <= 'f') goto yy1149; - goto yy1139; - } -yy1187: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1188; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1188: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1189; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1189: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych <= '9') goto yy1149; - goto yy1139; - } else { - if (yych <= 'F') goto yy1149; - if (yych <= '`') goto yy1139; - if (yych <= 'f') goto yy1149; - goto yy1139; - } -} -#line 500 "cpp.re" - -} - -extrawstringlit: -{ - -#line 8743 "cpp_re.inc" -{ - YYCTYPE yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12); - yych = *YYCURSOR; - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= 0x08) goto yy1192; - if (yych <= '\t') goto yy1193; - goto yy1197; - } else { - if (yych <= '\f') goto yy1193; - if (yych <= '\r') goto yy1199; - } - } else { - if (yych <= '>') { - if (yych == '"') goto yy1200; - goto yy1193; - } else { - if (yych <= '?') goto yy1195; - if (yych == '\\') goto yy1196; - goto yy1193; - } - } -yy1192: - YYCURSOR = YYMARKER; - goto yy1194; -yy1193: - ++YYCURSOR; -yy1194: -#line 507 "cpp.re" - { - goto extrawstringlit; - } -#line 8811 "cpp_re.inc" -yy1195: - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '?') goto yy1221; - goto yy1194; -yy1196: - yych = *++YYCURSOR; - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1193; - goto yy1192; - } else { - if (yych <= '\'') goto yy1193; - if (yych <= '/') goto yy1192; - goto yy1206; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1204; - goto yy1192; - } else { - if (yych <= 'U') goto yy1203; - if (yych == '\\') goto yy1193; - goto yy1192; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1193; - if (yych <= 'e') goto yy1192; - goto yy1193; - } else { - if (yych == 'n') goto yy1193; - if (yych <= 'q') goto yy1192; - goto yy1193; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1192; - if (yych <= 't') goto yy1193; - goto yy1202; - } else { - if (yych <= 'v') goto yy1193; - if (yych == 'x') goto yy1205; - goto yy1192; - } - } - } -yy1197: - ++YYCURSOR; -yy1198: -#line 512 "cpp.re" - { - s->line += count_backslash_newlines(s, cursor) +1; - cursor.column = 1; - goto extrawstringlit; - } -#line 8870 "cpp_re.inc" -yy1199: - yych = *++YYCURSOR; - if (yych == '\n') goto yy1197; - goto yy1198; -yy1200: - ++YYCURSOR; -#line 518 "cpp.re" - { BOOST_WAVE_RET(T_RAWSTRINGLIT); } -#line 8879 "cpp_re.inc" -yy1202: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych <= '9') goto yy1218; - goto yy1192; - } else { - if (yych <= 'F') goto yy1218; - if (yych <= '`') goto yy1192; - if (yych <= 'f') goto yy1218; - goto yy1192; - } -yy1203: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych <= '9') goto yy1211; - goto yy1192; - } else { - if (yych <= 'F') goto yy1211; - if (yych <= '`') goto yy1192; - if (yych <= 'f') goto yy1211; - goto yy1192; - } -yy1204: - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '?') goto yy1210; - goto yy1194; -yy1205: - yych = *++YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1208; - } - goto yy1192; -yy1206: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1194; - if (yych >= '8') goto yy1194; - yych = *++YYCURSOR; - if (yych <= '/') goto yy1194; - if (yych <= '7') goto yy1193; - goto yy1194; -yy1208: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1208; - } - goto yy1194; -yy1210: - yych = *++YYCURSOR; - if (yych == '/') goto yy1193; - goto yy1192; -yy1211: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1212; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1212: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1213; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1213: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1214; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1214: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1215; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1215: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1216; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1216: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1217; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1217: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych <= '9') goto yy1193; - goto yy1192; - } else { - if (yych <= 'F') goto yy1193; - if (yych <= '`') goto yy1192; - if (yych <= 'f') goto yy1193; - goto yy1192; - } -yy1218: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1219; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1219: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1220; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1220: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych <= '9') goto yy1193; - goto yy1192; - } else { - if (yych <= 'F') goto yy1193; - if (yych <= '`') goto yy1192; - if (yych <= 'f') goto yy1193; - goto yy1192; - } -yy1221: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '/') goto yy1196; - goto yy1192; -} -#line 519 "cpp.re" - -} diff --git a/extern/shiny/Preprocessor/instantiate_cpp_exprgrammar.cpp b/extern/shiny/Preprocessor/instantiate_cpp_exprgrammar.cpp deleted file mode 100644 index 7318c29fa4..0000000000 --- a/extern/shiny/Preprocessor/instantiate_cpp_exprgrammar.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost - Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include -#include - -#include -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the expression_grammar_gen template with the -// correct lexer iterator type. This instantiates the corresponding parse -// function, which in turn instantiates the expression_grammar object (see -// wave/grammars/cpp_expression_grammar.hpp) -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use your own token type the following line must be adjusted -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -template struct boost::wave::grammars::expression_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_cpp_grammar.cpp b/extern/shiny/Preprocessor/instantiate_cpp_grammar.cpp deleted file mode 100644 index 89cc3d7f36..0000000000 --- a/extern/shiny/Preprocessor/instantiate_cpp_grammar.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost - Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include -#include - -#include -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the cpp_grammar_gen template with the correct -// token type. This instantiates the corresponding pt_parse function, which -// in turn instantiates the cpp_grammar object -// (see wave/grammars/cpp_grammar.hpp) -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use your own token type the following line must be adjusted -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -typedef boost::wave::cpplexer::lex_iterator lexer_type; -typedef std::list > - token_sequence_type; - -template struct boost::wave::grammars::cpp_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_cpp_literalgrs.cpp b/extern/shiny/Preprocessor/instantiate_cpp_literalgrs.cpp deleted file mode 100644 index 4fbfb87f22..0000000000 --- a/extern/shiny/Preprocessor/instantiate_cpp_literalgrs.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost - Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include - -#include -#include - -#include -#include -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the intlit_grammar_gen and chlit_grammar_gen -// templates with the correct token type. This instantiates the corresponding -// parse function, which in turn instantiates the corresponding parser object. -// -/////////////////////////////////////////////////////////////////////////////// - -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -template struct boost::wave::grammars::intlit_grammar_gen; -#if BOOST_WAVE_WCHAR_T_SIGNEDNESS == BOOST_WAVE_WCHAR_T_AUTOSELECT || \ - BOOST_WAVE_WCHAR_T_SIGNEDNESS == BOOST_WAVE_WCHAR_T_FORCE_SIGNED -template struct boost::wave::grammars::chlit_grammar_gen; -#endif -template struct boost::wave::grammars::chlit_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_defined_grammar.cpp b/extern/shiny/Preprocessor/instantiate_defined_grammar.cpp deleted file mode 100644 index b7afe3f1ef..0000000000 --- a/extern/shiny/Preprocessor/instantiate_defined_grammar.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost - Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include - -#include -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the defined_grammar_gen template -// with the correct token type. This instantiates the corresponding parse -// function, which in turn instantiates the defined_grammar -// object (see wave/grammars/cpp_defined_grammar.hpp) -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use your own token type the following line must be adjusted -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -typedef boost::wave::cpplexer::lex_iterator lexer_type; -template struct boost::wave::grammars::defined_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_predef_macros.cpp b/extern/shiny/Preprocessor/instantiate_predef_macros.cpp deleted file mode 100644 index 758ad9734a..0000000000 --- a/extern/shiny/Preprocessor/instantiate_predef_macros.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost - Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include - -#include -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the predefined_macros_grammar_gen template -// with the correct token type. This instantiates the corresponding pt_parse -// function, which in turn instantiates the cpp_predefined_macros_grammar -// object (see wave/grammars/cpp_predef_macros_grammar.hpp) -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use your own token type the following line must be adjusted -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -typedef boost::wave::cpplexer::lex_iterator lexer_type; -template struct boost::wave::grammars::predefined_macros_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_re2c_lexer.cpp b/extern/shiny/Preprocessor/instantiate_re2c_lexer.cpp deleted file mode 100644 index cd1b8898ff..0000000000 --- a/extern/shiny/Preprocessor/instantiate_re2c_lexer.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - Explicit instantiation of the lex_functor generation function - - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost - Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include // configuration data - -#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION != 0 - -#include - -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////// -// The following file needs to be included only once throughout the whole -// program. -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// This instantiates the correct 'new_lexer' function, which generates the -// C++ lexer used in this sample. You will have to instantiate the -// new_lexer_gen<> template with the same iterator type, as you have used for -// instantiating the boost::wave::context<> object. -// -// This is moved into a separate compilation unit to decouple the compilation -// of the C++ lexer from the compilation of the other modules, which helps to -// reduce compilation time. -// -// The template parameter(s) supplied should be identical to the first -// parameter supplied while instantiating the boost::wave::context<> template -// (see the file cpp.cpp). -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use another iterator type for the underlying input stream -// a corresponding explicit template instantiation needs to be added below -template struct boost::wave::cpplexer::new_lexer_gen< - BOOST_WAVE_STRINGTYPE::iterator>; -template struct boost::wave::cpplexer::new_lexer_gen< - BOOST_WAVE_STRINGTYPE::const_iterator>; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION != 0 diff --git a/extern/shiny/Preprocessor/instantiate_re2c_lexer_str.cpp b/extern/shiny/Preprocessor/instantiate_re2c_lexer_str.cpp deleted file mode 100644 index 138ed6c3bb..0000000000 --- a/extern/shiny/Preprocessor/instantiate_re2c_lexer_str.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - Explicit instantiation of the lex_functor generation function - - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost - Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include // configuration data - -#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION != 0 - -#include - -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////// -// The following file needs to be included only once throughout the whole -// program. -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// If you've used another iterator type as std::string::iterator, you have to -// instantiate the new_lexer_gen<> template for this iterator type too. -// The reason is, that the library internally uses the new_lexer_gen<> -// template with a std::string::iterator. (You just have to undefine the -// following line.) -// -// This is moved into a separate compilation unit to decouple the compilation -// of the C++ lexer from the compilation of the other modules, which helps to -// reduce compilation time. -// -// The template parameter(s) supplied should be identical to the first -// parameter supplied while instantiating the boost::wave::context<> template -// (see the file cpp.cpp). -// -/////////////////////////////////////////////////////////////////////////////// - -#if !defined(BOOST_WAVE_STRINGTYPE_USE_STDSTRING) -template struct boost::wave::cpplexer::new_lexer_gen; -template struct boost::wave::cpplexer::new_lexer_gen; -#endif - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION != 0 diff --git a/extern/shiny/Preprocessor/token_ids.cpp b/extern/shiny/Preprocessor/token_ids.cpp deleted file mode 100644 index 35e7725b43..0000000000 --- a/extern/shiny/Preprocessor/token_ids.cpp +++ /dev/null @@ -1,447 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - The definition of a default set of token identifiers and related - functions. - - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost - Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include - -#include -#include -#include - -#include -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -namespace boost { -namespace wave { - -/////////////////////////////////////////////////////////////////////////////// -// return a token name -BOOST_WAVE_STRINGTYPE -get_token_name(token_id tokid) -{ -// Table of token names -// -// Please note that the sequence of token names must match the sequence of -// token id's defined in then enum token_id above. -static char const *tok_names[] = { - /* 256 */ "AND", - /* 257 */ "ANDAND", - /* 258 */ "ASSIGN", - /* 259 */ "ANDASSIGN", - /* 260 */ "OR", - /* 261 */ "ORASSIGN", - /* 262 */ "XOR", - /* 263 */ "XORASSIGN", - /* 264 */ "COMMA", - /* 265 */ "COLON", - /* 266 */ "DIVIDE", - /* 267 */ "DIVIDEASSIGN", - /* 268 */ "DOT", - /* 269 */ "DOTSTAR", - /* 270 */ "ELLIPSIS", - /* 271 */ "EQUAL", - /* 272 */ "GREATER", - /* 273 */ "GREATEREQUAL", - /* 274 */ "LEFTBRACE", - /* 275 */ "LESS", - /* 276 */ "LESSEQUAL", - /* 277 */ "LEFTPAREN", - /* 278 */ "LEFTBRACKET", - /* 279 */ "MINUS", - /* 280 */ "MINUSASSIGN", - /* 281 */ "MINUSMINUS", - /* 282 */ "PERCENT", - /* 283 */ "PERCENTASSIGN", - /* 284 */ "NOT", - /* 285 */ "NOTEQUAL", - /* 286 */ "OROR", - /* 287 */ "PLUS", - /* 288 */ "PLUSASSIGN", - /* 289 */ "PLUSPLUS", - /* 290 */ "ARROW", - /* 291 */ "ARROWSTAR", - /* 292 */ "QUESTION_MARK", - /* 293 */ "RIGHTBRACE", - /* 294 */ "RIGHTPAREN", - /* 295 */ "RIGHTBRACKET", - /* 296 */ "COLON_COLON", - /* 297 */ "SEMICOLON", - /* 298 */ "SHIFTLEFT", - /* 299 */ "SHIFTLEFTASSIGN", - /* 300 */ "SHIFTRIGHT", - /* 301 */ "SHIFTRIGHTASSIGN", - /* 302 */ "STAR", - /* 303 */ "COMPL", - /* 304 */ "STARASSIGN", - /* 305 */ "ASM", - /* 306 */ "AUTO", - /* 307 */ "BOOL", - /* 308 */ "FALSE", - /* 309 */ "TRUE", - /* 310 */ "BREAK", - /* 311 */ "CASE", - /* 312 */ "CATCH", - /* 313 */ "CHAR", - /* 314 */ "CLASS", - /* 315 */ "CONST", - /* 316 */ "CONSTCAST", - /* 317 */ "CONTINUE", - /* 318 */ "DEFAULT", - /* 319 */ "DELETE", - /* 320 */ "DO", - /* 321 */ "DOUBLE", - /* 322 */ "DYNAMICCAST", - /* 323 */ "ELSE", - /* 324 */ "ENUM", - /* 325 */ "EXPLICIT", - /* 326 */ "EXPORT", - /* 327 */ "EXTERN", - /* 328 */ "FLOAT", - /* 329 */ "FOR", - /* 330 */ "FRIEND", - /* 331 */ "GOTO", - /* 332 */ "IF", - /* 333 */ "INLINE", - /* 334 */ "INT", - /* 335 */ "LONG", - /* 336 */ "MUTABLE", - /* 337 */ "NAMESPACE", - /* 338 */ "NEW", - /* 339 */ "OPERATOR", - /* 340 */ "PRIVATE", - /* 341 */ "PROTECTED", - /* 342 */ "PUBLIC", - /* 343 */ "REGISTER", - /* 344 */ "REINTERPRETCAST", - /* 345 */ "RETURN", - /* 346 */ "SHORT", - /* 347 */ "SIGNED", - /* 348 */ "SIZEOF", - /* 349 */ "STATIC", - /* 350 */ "STATICCAST", - /* 351 */ "STRUCT", - /* 352 */ "SWITCH", - /* 353 */ "TEMPLATE", - /* 354 */ "THIS", - /* 355 */ "THROW", - /* 356 */ "TRY", - /* 357 */ "TYPEDEF", - /* 358 */ "TYPEID", - /* 359 */ "TYPENAME", - /* 360 */ "UNION", - /* 361 */ "UNSIGNED", - /* 362 */ "USING", - /* 363 */ "VIRTUAL", - /* 364 */ "VOID", - /* 365 */ "VOLATILE", - /* 366 */ "WCHART", - /* 367 */ "WHILE", - /* 368 */ "PP_DEFINE", - /* 369 */ "PP_IF", - /* 370 */ "PP_IFDEF", - /* 371 */ "PP_IFNDEF", - /* 372 */ "PP_ELSE", - /* 373 */ "PP_ELIF", - /* 374 */ "PP_ENDIF", - /* 375 */ "PP_ERROR", - /* 376 */ "PP_LINE", - /* 377 */ "PP_PRAGMA", - /* 378 */ "PP_UNDEF", - /* 379 */ "PP_WARNING", - /* 380 */ "IDENTIFIER", - /* 381 */ "OCTALINT", - /* 382 */ "DECIMALINT", - /* 383 */ "HEXAINT", - /* 384 */ "INTLIT", - /* 385 */ "LONGINTLIT", - /* 386 */ "FLOATLIT", - /* 387 */ "CCOMMENT", - /* 388 */ "CPPCOMMENT", - /* 389 */ "CHARLIT", - /* 390 */ "STRINGLIT", - /* 391 */ "CONTLINE", - /* 392 */ "SPACE", - /* 393 */ "SPACE2", - /* 394 */ "NEWLINE", - /* 395 */ "POUND_POUND", - /* 396 */ "POUND", - /* 397 */ "ANY", - /* 398 */ "PP_INCLUDE", - /* 399 */ "PP_QHEADER", - /* 400 */ "PP_HHEADER", - /* 401 */ "EOF", - /* 402 */ "EOI", - /* 403 */ "PP_NUMBER", - - // MS extensions - /* 404 */ "MSEXT_INT8", - /* 405 */ "MSEXT_INT16", - /* 406 */ "MSEXT_INT32", - /* 407 */ "MSEXT_INT64", - /* 408 */ "MSEXT_BASED", - /* 409 */ "MSEXT_DECLSPEC", - /* 410 */ "MSEXT_CDECL", - /* 411 */ "MSEXT_FASTCALL", - /* 412 */ "MSEXT_STDCALL", - /* 413 */ "MSEXT_TRY", - /* 414 */ "MSEXT_EXCEPT", - /* 415 */ "MSEXT_FINALLY", - /* 416 */ "MSEXT_LEAVE", - /* 417 */ "MSEXT_INLINE", - /* 418 */ "MSEXT_ASM", - /* 419 */ "MSEXT_REGION", - /* 420 */ "MSEXT_ENDREGION", - - /* 421 */ "IMPORT", - - /* 422 */ "ALIGNAS", - /* 423 */ "ALIGNOF", - /* 424 */ "CHAR16_T", - /* 425 */ "CHAR32_T", - /* 426 */ "CONSTEXPR", - /* 427 */ "DECLTYPE", - /* 428 */ "NOEXCEPT", - /* 429 */ "NULLPTR", - /* 430 */ "STATIC_ASSERT", - /* 431 */ "THREADLOCAL", - /* 432 */ "RAWSTRINGLIT", - }; - - // make sure, I have not forgotten any commas (as I did more than once) - BOOST_STATIC_ASSERT( - sizeof(tok_names)/sizeof(tok_names[0]) == T_LAST_TOKEN-T_FIRST_TOKEN - ); - - unsigned int id = BASEID_FROM_TOKEN(tokid)-T_FIRST_TOKEN; - return (id < T_LAST_TOKEN-T_FIRST_TOKEN) ? tok_names[id] : ""; -} - -/////////////////////////////////////////////////////////////////////////////// -// return a token name -char const * -get_token_value(token_id tokid) -{ -// Table of token values -// -// Please note that the sequence of token names must match the sequence of -// token id's defined in then enum token_id above. -static char const *tok_values[] = { - /* 256 */ "&", - /* 257 */ "&&", - /* 258 */ "=", - /* 259 */ "&=", - /* 260 */ "|", - /* 261 */ "|=", - /* 262 */ "^", - /* 263 */ "^=", - /* 264 */ ",", - /* 265 */ ":", - /* 266 */ "/", - /* 267 */ "/=", - /* 268 */ ".", - /* 269 */ ".*", - /* 270 */ "...", - /* 271 */ "==", - /* 272 */ ">", - /* 273 */ ">=", - /* 274 */ "{", - /* 275 */ "<", - /* 276 */ "<=", - /* 277 */ "(", - /* 278 */ "[", - /* 279 */ "-", - /* 280 */ "-=", - /* 281 */ "--", - /* 282 */ "%", - /* 283 */ "%=", - /* 284 */ "!", - /* 285 */ "!=", - /* 286 */ "||", - /* 287 */ "+", - /* 288 */ "+=", - /* 289 */ "++", - /* 290 */ "->", - /* 291 */ "->*", - /* 292 */ "?", - /* 293 */ "}", - /* 294 */ ")", - /* 295 */ "]", - /* 296 */ "::", - /* 297 */ ";", - /* 298 */ "<<", - /* 299 */ "<<=", - /* 300 */ ">>", - /* 301 */ ">>=", - /* 302 */ "*", - /* 303 */ "~", - /* 304 */ "*=", - /* 305 */ "asm", - /* 306 */ "auto", - /* 307 */ "bool", - /* 308 */ "false", - /* 309 */ "true", - /* 310 */ "break", - /* 311 */ "case", - /* 312 */ "catch", - /* 313 */ "char", - /* 314 */ "class", - /* 315 */ "const", - /* 316 */ "const_cast", - /* 317 */ "continue", - /* 318 */ "default", - /* 319 */ "delete", - /* 320 */ "do", - /* 321 */ "double", - /* 322 */ "dynamic_cast", - /* 323 */ "else", - /* 324 */ "enum", - /* 325 */ "explicit", - /* 326 */ "export", - /* 327 */ "extern", - /* 328 */ "float", - /* 329 */ "for", - /* 330 */ "friend", - /* 331 */ "goto", - /* 332 */ "if", - /* 333 */ "inline", - /* 334 */ "int", - /* 335 */ "long", - /* 336 */ "mutable", - /* 337 */ "namespace", - /* 338 */ "new", - /* 339 */ "operator", - /* 340 */ "private", - /* 341 */ "protected", - /* 342 */ "public", - /* 343 */ "register", - /* 344 */ "reinterpret_cast", - /* 345 */ "return", - /* 346 */ "short", - /* 347 */ "signed", - /* 348 */ "sizeof", - /* 349 */ "static", - /* 350 */ "static_cast", - /* 351 */ "struct", - /* 352 */ "switch", - /* 353 */ "template", - /* 354 */ "this", - /* 355 */ "throw", - /* 356 */ "try", - /* 357 */ "typedef", - /* 358 */ "typeid", - /* 359 */ "typename", - /* 360 */ "union", - /* 361 */ "unsigned", - /* 362 */ "using", - /* 363 */ "virtual", - /* 364 */ "void", - /* 365 */ "volatile", - /* 366 */ "wchar_t", - /* 367 */ "while", - /* 368 */ "#define", - /* 369 */ "#if", - /* 370 */ "#ifdef", - /* 371 */ "#ifndef", - /* 372 */ "#else", - /* 373 */ "#elif", - /* 374 */ "#endif", - /* 375 */ "#error", - /* 376 */ "#line", - /* 377 */ "#pragma", - /* 378 */ "#undef", - /* 379 */ "#warning", - /* 380 */ "", // identifier - /* 381 */ "", // octalint - /* 382 */ "", // decimalint - /* 383 */ "", // hexlit - /* 384 */ "", // intlit - /* 385 */ "", // longintlit - /* 386 */ "", // floatlit - /* 387 */ "", // ccomment - /* 388 */ "", // cppcomment - /* 389 */ "", // charlit - /* 390 */ "", // stringlit - /* 391 */ "", // contline - /* 392 */ "", // space - /* 393 */ "", // space2 - /* 394 */ "\n", - /* 395 */ "##", - /* 396 */ "#", - /* 397 */ "", // any - /* 398 */ "#include", - /* 399 */ "#include", - /* 400 */ "#include", - /* 401 */ "", // eof - /* 402 */ "", // eoi - /* 403 */ "", // pp-number - - // MS extensions - /* 404 */ "__int8", - /* 405 */ "__int16", - /* 406 */ "__int32", - /* 407 */ "__int64", - /* 408 */ "__based", - /* 409 */ "__declspec", - /* 410 */ "__cdecl", - /* 411 */ "__fastcall", - /* 412 */ "__stdcall", - /* 413 */ "__try", - /* 414 */ "__except", - /* 415 */ "__finally", - /* 416 */ "__leave", - /* 417 */ "__inline", - /* 418 */ "__asm", - /* 419 */ "#region", - /* 420 */ "#endregion", - - /* 421 */ "import", - - /* 422 */ "alignas", - /* 423 */ "alignof", - /* 424 */ "char16_t", - /* 425 */ "char32_t", - /* 426 */ "constexpr", - /* 427 */ "decltype", - /* 428 */ "noexcept", - /* 429 */ "nullptr", - /* 430 */ "static_assert", - /* 431 */ "threadlocal", - /* 432 */ "", // extrawstringlit - }; - - // make sure, I have not forgotten any commas (as I did more than once) - BOOST_STATIC_ASSERT( - sizeof(tok_values)/sizeof(tok_values[0]) == T_LAST_TOKEN-T_FIRST_TOKEN - ); - - unsigned int id = BASEID_FROM_TOKEN(tokid)-T_FIRST_TOKEN; - return (id < T_LAST_TOKEN-T_FIRST_TOKEN) ? tok_values[id] : ""; -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace wave -} // namespace boost - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - - 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/atmosphere.shader b/files/materials/atmosphere.shader index eb05c3e18b..f02c5766fe 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -3,13 +3,24 @@ #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) + shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) shOutput(float, alphaFade) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); alphaFade = shInputPosition.z < 150.0 ? 0.0 : 1.0; } diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index e60026d3bd..b76f2ded72 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -3,15 +3,26 @@ #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) +shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) shOutput(float, alphaFade) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); + UV = uv0; alphaFade = (shInputPosition.z <= 200.f) ? ((shInputPosition.z <= 100.f) ? 0.0 : 0.25) : 1.0; } diff --git a/files/materials/moon.shader b/files/materials/moon.shader index 231f60ba06..a7d183d108 100644 --- a/files/materials/moon.shader +++ b/files/materials/moon.shader @@ -3,14 +3,26 @@ #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, world) @shAutoConstant(world, world_matrix) + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) +shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shMatrixMult(world, shInputPosition))); + UV = uv0; } #else 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..8f8734d629 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,38 @@ 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 + { + direct_texture $normalMap + // force automips here for now + num_mipmaps 4 + } + + 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 46e3abeb3c..0c869d2cb8 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,12 +75,17 @@ 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_object_space_array, @shGlobalSettingString(num_lights)) + 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 @@ -92,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 @@ -131,23 +173,26 @@ #if VERTEX_LIGHTING + float3 viewPos = shMatrixMult(worldView, shInputPosition).xyz; + float3 viewNormal = normalize(shMatrixMult(worldView, float4(normal.xyz, 0)).xyz); + float3 lightDir; float d; lightResult = float4(0,0,0,1); @shForeach(@shGlobalSettingString(num_lights)) - lightDir = lightPosition[@shIterator].xyz - (shInputPosition.xyz * lightPosition[@shIterator].w); + lightDir = lightPosition[@shIterator].xyz - (viewPos * lightPosition[@shIterator].w); d = length(lightDir); lightDir = normalize(lightDir); #if VERTEXCOLOR_MODE == 2 lightResult.xyz += colour.xyz * lightDiffuse[@shIterator].xyz - * (1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normalize(normal.xyz), normalize(lightDir)), 0); + * 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 - * (1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normalize(normal.xyz), normalize(lightDir)), 0); + * 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 @@ -184,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) @@ -218,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); @@ -263,7 +415,7 @@ float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); #endif -#if SHADOWS +#if SHADOWS || SHADOWS_PSSM shOutputColour(0) *= (lightResult - float4(directionalResult * (1.0-shadow),0)); #else shOutputColour(0) *= lightResult; @@ -271,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)); @@ -279,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/sky.mat b/files/materials/sky.mat index e50aa51d8f..ccf2a8053c 100644 --- a/files/materials/sky.mat +++ b/files/materials/sky.mat @@ -1,3 +1,34 @@ +material QueryTotalPixels +{ + allow_fixed_function false + pass + { + vertex_program sun_vertex + fragment_program sun_fragment + cull_hardware none + polygon_mode_overrideable off + depth_check off + depth_write off + colour_write off + } +} + +material QueryVisiblePixels +{ + allow_fixed_function false + pass + { + vertex_program sun_vertex + fragment_program sun_fragment + cull_hardware none + cull_software none + polygon_mode_overrideable off + depth_check on + depth_write off + colour_write off + } +} + material openmw_moon { allow_fixed_function false @@ -5,7 +36,8 @@ material openmw_moon { vertex_program moon_vertex fragment_program moon_fragment - + cull_hardware none + polygon_mode_overrideable off depth_write off depth_check off @@ -92,6 +124,7 @@ material openmw_sun { vertex_program sun_vertex fragment_program sun_fragment + cull_hardware none polygon_mode_overrideable off diff --git a/files/materials/stars.shader b/files/materials/stars.shader index fea007424b..33f22bd14b 100644 --- a/files/materials/stars.shader +++ b/files/materials/stars.shader @@ -3,15 +3,26 @@ #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) +shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) + shVertexInput(float2, uv0) shOutput(float2, UV) shOutput(float, fade) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); UV = uv0; fade = (shInputPosition.z > 50) ? 1 : 0; diff --git a/files/materials/sun.shader b/files/materials/sun.shader index 7954f417ce..abe4c97f15 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -3,14 +3,26 @@ #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, world) @shAutoConstant(world, world_matrix) + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) +shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shMatrixMult(world, shInputPosition))); + UV = uv0; } #else diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index c73b582f8d..58146118e9 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,12 @@ float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); #endif - + +#if !IS_FIRST_PASS +// Opacity the previous passes should have, i.e. 1 - (opacity of this pass) +float previousAlpha = 1.f; +#endif + // Layer calculations @shForeach(@shPropertyString(num_blendmaps)) float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); @@ -232,12 +240,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 + previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator); #endif @shEndForeach @@ -325,6 +341,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 = 1.f-previousAlpha; +#endif } #endif diff --git a/files/materials/water.mat b/files/materials/water.mat index 0ec71d2df7..1e5f8c8e04 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -54,5 +54,24 @@ material Water scale 0.1 0.1 alpha_op_ex source1 src_manual src_current 0.7 } + + texture_unit shadowMap0 + { + content_type shadow + tex_address_mode clamp + filtering none + } + texture_unit shadowMap1 + { + content_type shadow + tex_address_mode clamp + filtering none + } + texture_unit shadowMap2 + { + content_type shadow + tex_address_mode clamp + filtering none + } } } diff --git a/files/materials/water.shader b/files/materials/water.shader index 793cdc95e3..a6d0a47e6e 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -50,6 +50,13 @@ // Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html ) +#define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm) +#define SHADOWS @shGlobalSettingBool(shadows) + +#if SHADOWS || SHADOWS_PSSM + #include "shadows.h" +#endif + #define RIPPLES 1 #define REFRACTION @shGlobalSettingBool(refraction) @@ -64,6 +71,23 @@ shOutput(float4, position) shOutput(float, depthPassthrough) + +#if SHADOWS + shOutput(float4, lightSpacePos0) + shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) +#endif + +#if SHADOWS_PSSM + @shForeach(3) + shOutput(float4, lightSpacePos@shIterator) + shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) + @shEndForeach +#endif + +#if SHADOWS || SHADOWS_PSSM + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) +#endif + SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); @@ -88,6 +112,17 @@ position = shInputPosition; depthPassthrough = shOutputPosition.z; + + +#if SHADOWS + lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); +#endif +#if SHADOWS_PSSM + float4 wPos = shMatrixMult(worldMatrix, shInputPosition); + @shForeach(3) + lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos); + @shEndForeach +#endif } #else @@ -186,10 +221,47 @@ shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space) + + +#if SHADOWS + shInput(float4, lightSpacePos0) + shSampler2D(shadowMap0) + shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1) +#endif +#if SHADOWS_PSSM + @shForeach(3) + shInput(float4, lightSpacePos@shIterator) + shSampler2D(shadowMap@shIterator) + shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1)) + @shEndForeach + shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints) +#endif + +#if SHADOWS || SHADOWS_PSSM + shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) +#endif SH_START_PROGRAM { +#if SHADOWS + float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); +#endif +#if SHADOWS_PSSM + float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints); +#endif + +#if SHADOWS || SHADOWS_PSSM + float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; + float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange); + shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); +#endif + +#if !SHADOWS && !SHADOWS_PSSM + float shadow = 1.0; +#endif + + float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; screenCoords.y = (1-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y; @@ -244,7 +316,7 @@ float3 llR = reflect(lVec, pNormal); float s = shSaturate(dot(lR, vVec)*2.0-1.2); - float lightScatter = shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); + float lightScatter = shadow * shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); // fresnel @@ -267,7 +339,7 @@ #endif // specular - float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS); + float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow; #if REFRACTION shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz; diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index beace5b81e..af695ac6c5 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -78,6 +78,9 @@ set(MYGUI_FILES openmw_trainingwindow.layout openmw_travel_window.layout openmw_persuasion_dialog.layout + openmw_merchantrepair.layout + openmw_repair.layout + openmw_companion_window.layout smallbars.png DejaVuLGCSansMono.ttf markers.png diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index 8471f69df0..076a9293a1 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -59,11 +59,7 @@ - - - - - + diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 96d1153f0e..d9ac2f43ae 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -8,25 +8,27 @@ - - - - - + + + + + + - - - - - + + + + + + + - - + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index 4ef8da0f3f..c569abb863 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -22,7 +22,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -46,7 +46,7 @@ - + diff --git a/files/mygui/openmw_companion_window.layout b/files/mygui/openmw_companion_window.layout new file mode 100644 index 0000000000..87aa01fd47 --- /dev/null +++ b/files/mygui/openmw_companion_window.layout @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 452196aaea..5c9327b1d3 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -1,14 +1,10 @@ - + - - - - - + diff --git a/files/mygui/openmw_dialogue_window.layout b/files/mygui/openmw_dialogue_window.layout index 9a9da72d4c..46090d0008 100644 --- a/files/mygui/openmw_dialogue_window.layout +++ b/files/mygui/openmw_dialogue_window.layout @@ -5,14 +5,13 @@ - - - - - - - - + + + + + + + diff --git a/files/mygui/openmw_enchanting_dialog.layout b/files/mygui/openmw_enchanting_dialog.layout index a19c569256..f64d21deaa 100644 --- a/files/mygui/openmw_enchanting_dialog.layout +++ b/files/mygui/openmw_enchanting_dialog.layout @@ -26,14 +26,18 @@ - + + + - + + + @@ -66,18 +70,18 @@ - + - + - + - - + + @@ -85,18 +89,23 @@ - + + + + + - + - + + diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index b1446fae14..ef43003264 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -9,6 +9,7 @@ + @@ -22,6 +23,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index 382bc6dc98..67d0632f84 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -104,24 +104,24 @@ - - + - + - + - + - + - + diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 88cc5638d3..0f315b2398 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -6,8 +6,7 @@ - - + @@ -17,7 +16,6 @@ - @@ -27,11 +25,7 @@ - - - - - + diff --git a/files/mygui/openmw_itemselection_dialog.layout b/files/mygui/openmw_itemselection_dialog.layout index 39c0483030..003eb1c6ac 100644 --- a/files/mygui/openmw_itemselection_dialog.layout +++ b/files/mygui/openmw_itemselection_dialog.layout @@ -4,11 +4,7 @@ - - - - - + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index fdf82e4dee..58d2c2690a 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -2,24 +2,108 @@ - + + + + + - - - - - - - - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_journal_skin.xml b/files/mygui/openmw_journal_skin.xml index 9f82e907ff..44448225c0 100644 --- a/files/mygui/openmw_journal_skin.xml +++ b/files/mygui/openmw_journal_skin.xml @@ -10,8 +10,6 @@ - - - + diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 33a3ca40c7..84ec6f7c50 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -7,7 +7,7 @@ - + diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout index 86e65e99a9..765bf88a83 100644 --- a/files/mygui/openmw_levelup_dialog.layout +++ b/files/mygui/openmw_levelup_dialog.layout @@ -1,17 +1,36 @@ - + + + + + + - + - + + + + + + + + + + + + + + + @@ -127,12 +146,17 @@ - - - - + + + + + + + + + diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 6631424ccf..a5065c7ca9 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -126,6 +126,15 @@ + + + + + + + + + diff --git a/files/mygui/openmw_merchantrepair.layout b/files/mygui/openmw_merchantrepair.layout new file mode 100644 index 0000000000..360f5f4f09 --- /dev/null +++ b/files/mygui/openmw_merchantrepair.layout @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_repair.layout b/files/mygui/openmw_repair.layout new file mode 100644 index 0000000000..2881a5853a --- /dev/null +++ b/files/mygui/openmw_repair.layout @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_scroll.layout b/files/mygui/openmw_scroll.layout index 6315c02416..194700f36e 100644 --- a/files/mygui/openmw_scroll.layout +++ b/files/mygui/openmw_scroll.layout @@ -13,7 +13,7 @@ - + diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 693c5a9cb5..77d14d0f6d 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -16,7 +16,7 @@ - + @@ -31,7 +31,7 @@ - + @@ -65,35 +65,35 @@ - + - + - + - + - + @@ -118,7 +118,7 @@ - + @@ -134,7 +134,7 @@ - + @@ -199,7 +199,7 @@ - + @@ -224,7 +224,7 @@ - + @@ -232,7 +232,7 @@ - + diff --git a/files/mygui/openmw_spell_buying_window.layout b/files/mygui/openmw_spell_buying_window.layout index 1e18fda236..c55cd0e22d 100644 --- a/files/mygui/openmw_spell_buying_window.layout +++ b/files/mygui/openmw_spell_buying_window.layout @@ -5,13 +5,13 @@ - - + + - - - + + + diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index 13b22ff93d..b2a15b503b 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -154,8 +154,13 @@ - - + + + + + + + diff --git a/files/mygui/openmw_tooltips.layout b/files/mygui/openmw_tooltips.layout index 514d1a25b3..7291f2ca21 100644 --- a/files/mygui/openmw_tooltips.layout +++ b/files/mygui/openmw_tooltips.layout @@ -5,124 +5,136 @@ - - - - + + + + - + + + + + + + + + + + - - - - + + + - + - - + - - - - + + + - + - - + - - - + - - - + + + - + + + + + + + - + - - - - + + + - + + + - - - + + + - + - - - - + + + - + + + - - - + + + + + + + + + + + - - - - - - + - - - - + @@ -135,37 +147,38 @@ - - + + + - + + - - - + + + + + + + + + + + + + - - - - - - + - - - - - - @@ -181,43 +194,49 @@ - - + + + - + - - - - + + + - + + + + + + + + + + + + + + + + + - - - - - - - - - + - - diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index ecc794c928..a540c387e9 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -24,11 +24,7 @@ - - - - - + diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp index 8744c4e4d6..431d2b91b4 100644 --- a/libs/openengine/bullet/BulletShapeLoader.cpp +++ b/libs/openengine/bullet/BulletShapeLoader.cpp @@ -19,8 +19,8 @@ Ogre::Resource(creator, name, handle, group, isManual, loader) */ mCollisionShape = NULL; mRaycastingShape = NULL; + mHasCollisionNode = false; mCollide = true; - mIgnore = false; createParamDictionary("BulletShape"); } diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index fa03b02766..a6591010ad 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -35,12 +35,14 @@ public: btCollisionShape* mCollisionShape; btCollisionShape* mRaycastingShape; + // Whether or not a NiRootCollisionNode was present in the .nif. If there is none, the collision behaviour + // depends on object type, so we need to expose this variable. + bool mHasCollisionNode; + Ogre::Vector3 mBoxTranslation; Ogre::Quaternion mBoxRotation; //this flag indicate if the shape is used for collision or if it's for raycasting only. bool mCollide; - - bool mIgnore; }; /** diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index c4947af1e1..bbe6338474 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -19,7 +19,7 @@ namespace Physic PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale) : mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0) - , mBody(0), onGround(false), collisionMode(true), mBoxRotation(0,0,0,0), verticalForce(0.0f) + , mBody(0), mRaycastingBody(0), onGround(false), collisionMode(true), mBoxRotation(0,0,0,0), verticalForce(0.0f) { // FIXME: Force player to start in no-collision mode for now, until he spawns at a proper door marker. if(name == "player") @@ -28,7 +28,7 @@ namespace Physic mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true); Ogre::Quaternion inverse = mBoxRotation.Inverse(); mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); - mEngine->addRigidBody(mBody, false, mRaycastingBody); //Add rigid body to dynamics world, but do not add to object map + mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map } PhysicActor::~PhysicActor() @@ -47,6 +47,7 @@ namespace Physic void PhysicActor::enableCollisions(bool collision) { + assert(mBody); if(collision && !collisionMode) mBody->translate(btVector3(0,0,-1000)); if(!collision && collisionMode) mBody->translate(btVector3(0,0,1000)); collisionMode = collision; @@ -55,6 +56,7 @@ namespace Physic void PhysicActor::setPosition(const Ogre::Vector3 &pos) { + assert(mBody); if(pos != getPosition()) { mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); @@ -64,6 +66,7 @@ namespace Physic void PhysicActor::setRotation(const Ogre::Quaternion &quat) { + assert(mBody); if(!quat.equals(getRotation(), Ogre::Radian(0))){ mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); mEngine->adjustRigidBody(mRaycastingBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); @@ -74,6 +77,7 @@ namespace Physic Ogre::Vector3 PhysicActor::getPosition() { + assert(mBody); btVector3 vec = mBody->getWorldTransform().getOrigin(); Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(), mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ()); @@ -84,23 +88,28 @@ namespace Physic Ogre::Quaternion PhysicActor::getRotation() { + assert(mBody); btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse; return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); } void PhysicActor::setScale(float scale){ - Ogre::Vector3 position = getPosition(); - Ogre::Quaternion rotation = getRotation(); //We only need to change the scaled box translation, box rotations remain the same. + assert(mBody); mBoxScaledTranslation = mBoxScaledTranslation / mBody->getCollisionShape()->getLocalScaling().getX(); mBoxScaledTranslation *= scale; + Ogre::Vector3 pos = getPosition(); + Ogre::Quaternion rot = getRotation(); if(mBody){ mEngine->dynamicsWorld->removeRigidBody(mBody); + mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody); delete mBody; + delete mRaycastingBody; } //Create the newly scaled rigid body - mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation); - mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map + mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot); + mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true); + mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map } Ogre::Vector3 PhysicActor::getHalfExtents() const @@ -144,6 +153,7 @@ namespace Physic RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name) : btRigidBody(CI) , mName(name) + , mPlaceable(false) { } @@ -257,8 +267,8 @@ namespace Physic } } - PhysicActorContainer::iterator pa_it = PhysicActorMap.begin(); - for (; pa_it != PhysicActorMap.end(); ++pa_it) + PhysicActorContainer::iterator pa_it = mActorMap.begin(); + for (; pa_it != mActorMap.end(); ++pa_it) { if (pa_it->second != NULL) { @@ -321,7 +331,8 @@ namespace Physic mHeightFieldMap [name] = hf; - dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_ActorInternal|CollisionType_ActorExternal); + dynamicsWorld->addRigidBody(body,CollisionType_HeightMap|CollisionType_Raycasting, + CollisionType_World|CollisionType_Actor|CollisionType_Raycasting); } void PhysicEngine::removeHeightField(int x, int y) @@ -368,7 +379,7 @@ namespace Physic RigidBody* PhysicEngine::createAndAdjustRigidBody(const std::string &mesh, const std::string &name, float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, - Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation, bool raycasting) + Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation, bool raycasting, bool placeable) { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; @@ -378,6 +389,9 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); + if (placeable && !raycasting && shape->mCollisionShape && !shape->mHasCollisionNode) + return NULL; + if (!shape->mCollisionShape && !raycasting) return NULL; if (!shape->mRaycastingShape && raycasting) @@ -395,6 +409,7 @@ namespace Physic btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo (0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); RigidBody* body = new RigidBody(CI,name); + body->mPlaceable = placeable; if(scaledBoxTranslation != 0) *scaledBoxTranslation = shape->mBoxTranslation * scale; @@ -407,15 +422,17 @@ namespace Physic } - void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody) + void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody,bool actor) { if(!body && !raycastingBody) return; // nothing to do const std::string& name = (body ? body->mName : raycastingBody->mName); - if (body) - dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_ActorInternal|CollisionType_ActorExternal); + if (body){ + if(actor) dynamicsWorld->addRigidBody(body,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap); + else dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); + } if (raycastingBody) dynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_World); @@ -494,10 +511,47 @@ namespace Physic } } + class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback + { + public: + std::vector mResult; + + // added in bullet 2.81 + // this is just a quick hack, as there does not seem to be a BULLET_VERSION macro? +#if defined(BT_COLLISION_OBJECT_WRAPPER_H) + virtual btScalar addSingleResult(btManifoldPoint& cp, + const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0, + const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1) + { + const RigidBody* body = dynamic_cast(colObj0Wrap->m_collisionObject); + if (body) + mResult.push_back(body->mName); + return 0.f; + } +#else + virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0, + const btCollisionObject* col1, int partId1, int index1) + { + const RigidBody* body = dynamic_cast(col0); + if (body) + mResult.push_back(body->mName); + return 0.f; + } +#endif + }; + + std::vector PhysicEngine::getCollisions(const std::string& name) + { + RigidBody* body = getRigidBody(name); + ContactTestResultCallback callback; + dynamicsWorld->contactTest(body, callback); + return callback.mResult; + } + void PhysicEngine::stepSimulation(double deltaT) { - // This isn't needed as there are no dynamic objects at this point - //dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); + // This seems to be needed for character controller objects + dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); if(isDebugCreated) { mDebugDrawer->step(); @@ -515,13 +569,13 @@ namespace Physic //dynamicsWorld->addAction( newActor->mCharacter ); - PhysicActorMap[name] = newActor; + mActorMap[name] = newActor; } void PhysicEngine::removeCharacter(const std::string &name) { - PhysicActorContainer::iterator it = PhysicActorMap.find(name); - if (it != PhysicActorMap.end() ) + PhysicActorContainer::iterator it = mActorMap.find(name); + if (it != mActorMap.end() ) { PhysicActor* act = it->second; if(act != NULL) @@ -529,16 +583,16 @@ namespace Physic delete act; } - PhysicActorMap.erase(it); + mActorMap.erase(it); } } PhysicActor* PhysicEngine::getCharacter(const std::string &name) { - PhysicActorContainer::iterator it = PhysicActorMap.find(name); - if (it != PhysicActorMap.end() ) + PhysicActorContainer::iterator it = mActorMap.find(name); + if (it != mActorMap.end() ) { - PhysicActor* act = PhysicActorMap[name]; + PhysicActor* act = mActorMap[name]; return act; } else @@ -551,14 +605,20 @@ namespace Physic { } - std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to) + std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly,bool ignoreHeightMap) { std::string name = ""; float d = -1; float d1 = 10000.; btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); - resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; + if(raycastingObjectOnly) + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; + else + resultCallback1.m_collisionFilterMask = CollisionType_World; + + if(!ignoreHeightMap) + resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap; dynamicsWorld->rayTest(from, to, resultCallback1); if (resultCallback1.hasHit()) { @@ -570,6 +630,41 @@ namespace Physic return std::pair(name,d); } + // callback that ignores player in results + struct OurClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback + { + public: + OurClosestConvexResultCallback(const btVector3& convexFromWorld,const btVector3& convexToWorld) + : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld) {} + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + if (const RigidBody* body = dynamic_cast(convexResult.m_hitCollisionObject)) + if (body->mName == "player") + return 0; + return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); + } + }; + + std::pair PhysicEngine::sphereCast (float radius, btVector3& from, btVector3& to) + { + OurClosestConvexResultCallback callback(from, to); + callback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap; + + btSphereShape shape(radius); + const btQuaternion btrot(0.0f, 0.0f, 0.0f); + + btTransform from_ (btrot, from); + btTransform to_ (btrot, to); + + dynamicsWorld->convexSweepTest(&shape, from_, to_, callback); + + if (callback.hasHit()) + return std::make_pair(true, callback.m_closestHitFraction); + else + return std::make_pair(false, 1); + } + std::vector< std::pair > PhysicEngine::rayTest2(btVector3& from, btVector3& to) { MyRayResultCallback resultCallback1; @@ -603,6 +698,32 @@ namespace Physic btTransform trans; trans.setIdentity(); - shape->mCollisionShape->getAabb(trans, min, max); + if (shape->mRaycastingShape) + shape->mRaycastingShape->getAabb(trans, min, max); + else if (shape->mCollisionShape) + shape->mCollisionShape->getAabb(trans, min, max); + else + { + min = btVector3(0,0,0); + max = btVector3(0,0,0); + } } -}} + + bool PhysicEngine::isAnyActorStandingOn (const std::string& objectName) + { + for (PhysicActorContainer::iterator it = mActorMap.begin(); it != mActorMap.end(); ++it) + { + if (!it->second->getOnGround()) + continue; + Ogre::Vector3 pos = it->second->getPosition(); + btVector3 from (pos.x, pos.y, pos.z); + btVector3 to = from - btVector3(0,0,5); + std::pair result = rayTest(from, to); + if (result.first == objectName) + return true; + } + return false; + } + +} +} diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 367db261fd..80c681fe5b 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -46,8 +46,8 @@ namespace Physic enum CollisionType { CollisionType_Nothing = 0, // rayTest(btVector3& from,btVector3& to); + std::pair rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly = true,bool ignoreHeightMap = false); /** * Return all objects hit by a ray. */ std::vector< std::pair > rayTest2(btVector3& from, btVector3& to); + std::pair sphereCast (float radius, btVector3& from, btVector3& to); + ///< @return (hit, relative distance) + + std::vector getCollisions(const std::string& name); + //event list of non player object std::list NPEventList; @@ -323,7 +331,7 @@ namespace Physic RigidBodyContainer mRaycastingObjectMap; typedef std::map PhysicActorContainer; - PhysicActorContainer PhysicActorMap; + PhysicActorContainer mActorMap; Ogre::SceneManager* mSceneMgr; diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index cc443f2679..d246417c7a 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -15,19 +15,19 @@ enum traceWorldType bothWorldTrace = collisionWorldTrace | pickWorldTrace }; -void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine *enginePass) //Traceobj was a Aedra Object +void newtrace(traceResults *results, const Ogre::Quaternion& orient, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine *enginePass) //Traceobj was a Aedra Object { const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); - const btQuaternion btrot(0.0f, 0.0f, 0.0f); //y, x, z + const btQuaternion btorient (orient.x, orient.y, orient.z, orient.w); const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); //const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2); - const btTransform from(btrot, btstart); - const btTransform to(btrot, btend); + const btTransform from(btorient, btstart); + const btTransform to(btorient, btend); btCollisionWorld::ClosestConvexResultCallback newTraceCallback(btstart, btend); - newTraceCallback.m_collisionFilterMask = OEngine::Physic::CollisionType_World; + newTraceCallback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap|OEngine::Physic::CollisionType_Actor; enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback); diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index f484497d97..cd2547f8c0 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -21,6 +21,6 @@ struct traceResults float fraction; }; -void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); +void newtrace(traceResults *results, const Ogre::Quaternion& orient, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); #endif diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index c9b5614008..a0a4ab0aea 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,12 +27,540 @@ 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); assert(mgr); mSceneMgr = mgr; + mShaderRenderManager = NULL; + mRenderManager = NULL; using namespace MyGUI; @@ -41,15 +576,21 @@ 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(); + if (!Ogre::Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_FIXED_FUNCTION)) + mShaderRenderManager = new MyGUI::ShaderBasedRenderManager(); + else + mRenderManager = new MyGUI::OgreRenderManager(); + mDataManager = new MyGUI::FixedOgreDataManager(); LogManager::getInstance().setSTDOutputEnabled(logging); if (!theLogFile.empty()) LogManager::getInstance().createDefaultSource(theLogFile); - mRenderManager->initialise(wnd, mgr); + if (mShaderRenderManager) + mShaderRenderManager->initialise(wnd, mgr); + else + mRenderManager->initialise(wnd, mgr); mDataManager->initialise("General"); // Create GUI @@ -59,8 +600,16 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd) { - mRenderManager->setRenderWindow (wnd); - mRenderManager->setActiveViewport(0); + if (mShaderRenderManager) + { + mShaderRenderManager->setRenderWindow (wnd); + mShaderRenderManager->setActiveViewport(0); + } + else + { + mRenderManager->setRenderWindow (wnd); + mRenderManager->setActiveViewport(0); + } } void MyGUIManager::shutdown() @@ -73,6 +622,12 @@ void MyGUIManager::shutdown() delete mRenderManager; mRenderManager = NULL; } + if(mShaderRenderManager) + { + mShaderRenderManager->shutdown(); + delete mShaderRenderManager; + mShaderRenderManager = NULL; + } if(mDataManager) { mDataManager->shutdown(); diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index 16673ef980..9535f2a24e 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -9,6 +9,7 @@ namespace MyGUI class LogManager; class OgreDataManager; class OgreRenderManager; + class ShaderBasedRenderManager; } namespace Ogre @@ -26,11 +27,11 @@ namespace GUI MyGUI::LogManager* mLogManager; MyGUI::OgreDataManager* mDataManager; MyGUI::OgreRenderManager* mRenderManager; + MyGUI::ShaderBasedRenderManager* mShaderRenderManager; Ogre::SceneManager* mSceneMgr; public: - MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {} MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) { setup(wnd,mgr,logging, logDir); diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 9390d06647..923b0b7e38 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -19,6 +19,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr) , mTargetAlpha(0.f) , mCurrentAlpha(0.f) , mStartAlpha(0.f) + , mFactor(1.f) { // Create the fading material MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); @@ -62,19 +63,20 @@ void Fader::update(float dt) mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha); if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha; } - - applyAlpha(); - + mRemainingTime -= dt; } - if (mCurrentAlpha == 0.f) mRectangle->setVisible(false); + if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f) + mRectangle->setVisible(false); + else + applyAlpha(); } void Fader::applyAlpha() { mRectangle->setVisible(true); - mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha); + mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 1.f-((1.f-mCurrentAlpha) * mFactor)); } void Fader::fadeIn(float time) diff --git a/libs/openengine/ogre/fader.hpp b/libs/openengine/ogre/fader.hpp index bddf5dc919..53124e2f65 100644 --- a/libs/openengine/ogre/fader.hpp +++ b/libs/openengine/ogre/fader.hpp @@ -29,6 +29,8 @@ namespace Render void fadeOut(const float time); void fadeTo(const int percent, const float time); + void setFactor (float factor) { mFactor = factor; } + private: enum FadingMode { @@ -49,6 +51,8 @@ namespace Render float mCurrentAlpha; float mStartAlpha; + float mFactor; + Ogre::SceneManager* mSceneMgr; }; }} diff --git a/libs/openengine/ogre/particles.cpp b/libs/openengine/ogre/particles.cpp new file mode 100644 index 0000000000..707bd75e08 --- /dev/null +++ b/libs/openengine/ogre/particles.cpp @@ -0,0 +1,693 @@ +#include "particles.hpp" + +#include +#include +#include +#include +#include + +/* FIXME: "Nif" isn't really an appropriate emitter name. */ +class NifEmitter : public Ogre::ParticleEmitter +{ +public: + /** Command object for the emitter width (see Ogre::ParamCommand).*/ + class CmdWidth : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getWidth()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setWidth(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter height (see Ogre::ParamCommand).*/ + class CmdHeight : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getHeight()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setHeight(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter depth (see Ogre::ParamCommand).*/ + class CmdDepth : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getDepth()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setDepth(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter vertical_direction (see Ogre::ParamCommand).*/ + class CmdVerticalDir : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getVerticalDirection().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setVerticalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter vertical_angle (see Ogre::ParamCommand).*/ + class CmdVerticalAngle : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getVerticalAngle().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setVerticalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter horizontal_direction (see Ogre::ParamCommand).*/ + class CmdHorizontalDir : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getHorizontalDirection().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setHorizontalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter horizontal_angle (see Ogre::ParamCommand).*/ + class CmdHorizontalAngle : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getHorizontalAngle().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setHorizontalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + + NifEmitter(Ogre::ParticleSystem *psys) + : Ogre::ParticleEmitter(psys) + { + initDefaults("Nif"); + } + + /** See Ogre::ParticleEmitter. */ + unsigned short _getEmissionCount(Ogre::Real timeElapsed) + { + // Use basic constant emission + return genConstantEmissionCount(timeElapsed); + } + + /** See Ogre::ParticleEmitter. */ + void _initParticle(Ogre::Particle *particle) + { + Ogre::Vector3 xOff, yOff, zOff; + + // Call superclass + ParticleEmitter::_initParticle(particle); + + xOff = Ogre::Math::SymmetricRandom() * mXRange; + yOff = Ogre::Math::SymmetricRandom() * mYRange; + zOff = Ogre::Math::SymmetricRandom() * mZRange; + + particle->position = mPosition + xOff + yOff + zOff; + + // Generate complex data by reference + genEmissionColour(particle->colour); + + // NOTE: We do not use mDirection/mAngle for the initial direction. + Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom(); + Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom(); + particle->direction = (Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * + Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * + Ogre::Vector3::UNIT_Z; + + genEmissionVelocity(particle->direction); + + // Generate simpler data + particle->timeToLive = particle->totalTimeToLive = genEmissionTTL(); + } + + /** Overloaded to update the trans. matrix */ + void setDirection(const Ogre::Vector3 &dir) + { + ParticleEmitter::setDirection(dir); + genAreaAxes(); + } + + /** Sets the size of the area from which particles are emitted. + @param + size Vector describing the size of the area. The area extends + around the center point by half the x, y and z components of + this vector. The box is aligned such that it's local Z axis points + along it's direction (see setDirection) + */ + void setSize(const Ogre::Vector3 &size) + { + mSize = size; + genAreaAxes(); + } + + /** Sets the size of the area from which particles are emitted. + @param x,y,z + Individual axis lengths describing the size of the area. The area + extends around the center point by half the x, y and z components + of this vector. The box is aligned such that it's local Z axis + points along it's direction (see setDirection) + */ + void setSize(Ogre::Real x, Ogre::Real y, Ogre::Real z) + { + mSize.x = x; + mSize.y = y; + mSize.z = z; + genAreaAxes(); + } + + /** Sets the width (local x size) of the emitter. */ + void setWidth(Ogre::Real width) + { + mSize.x = width; + genAreaAxes(); + } + /** Gets the width (local x size) of the emitter. */ + Ogre::Real getWidth(void) const + { return mSize.x; } + /** Sets the height (local y size) of the emitter. */ + void setHeight(Ogre::Real height) + { + mSize.y = height; + genAreaAxes(); + } + /** Gets the height (local y size) of the emitter. */ + Ogre::Real getHeight(void) const + { return mSize.y; } + /** Sets the depth (local y size) of the emitter. */ + void setDepth(Ogre::Real depth) + { + mSize.z = depth; + genAreaAxes(); + } + /** Gets the depth (local y size) of the emitter. */ + Ogre::Real getDepth(void) const + { return mSize.z; } + + void setVerticalDirection(Ogre::Radian vdir) + { mVerticalDir = vdir; } + Ogre::Radian getVerticalDirection(void) const + { return mVerticalDir; } + + void setVerticalAngle(Ogre::Radian vangle) + { mVerticalAngle = vangle; } + Ogre::Radian getVerticalAngle(void) const + { return mVerticalAngle; } + + void setHorizontalDirection(Ogre::Radian hdir) + { mHorizontalDir = hdir; } + Ogre::Radian getHorizontalDirection(void) const + { return mHorizontalDir; } + + void setHorizontalAngle(Ogre::Radian hangle) + { mHorizontalAngle = hangle; } + Ogre::Radian getHorizontalAngle(void) const + { return mHorizontalAngle; } + + +protected: + /// Size of the area + Ogre::Vector3 mSize; + + Ogre::Radian mVerticalDir; + Ogre::Radian mVerticalAngle; + Ogre::Radian mHorizontalDir; + Ogre::Radian mHorizontalAngle; + + /// Local axes, not normalised, their magnitude reflects area size + Ogre::Vector3 mXRange, mYRange, mZRange; + + /// Internal method for generating the area axes + void genAreaAxes(void) + { + Ogre::Vector3 mLeft = mUp.crossProduct(mDirection); + mXRange = mLeft * (mSize.x * 0.5f); + mYRange = mUp * (mSize.y * 0.5f); + mZRange = mDirection * (mSize.z * 0.5f); + } + + /** Internal for initializing some defaults and parameters + @return True if custom parameters need initialising + */ + bool initDefaults(const Ogre::String &t) + { + // Defaults + mDirection = Ogre::Vector3::UNIT_Z; + mUp = Ogre::Vector3::UNIT_Y; + setSize(100.0f, 100.0f, 100.0f); + mType = t; + + // Set up parameters + if(createParamDictionary(mType + "Emitter")) + { + addBaseParameters(); + Ogre::ParamDictionary *dict = getParamDictionary(); + + // Custom params + dict->addParameter(Ogre::ParameterDef("width", + "Width of the shape in world coordinates.", + Ogre::PT_REAL), + &msWidthCmd); + dict->addParameter(Ogre::ParameterDef("height", + "Height of the shape in world coordinates.", + Ogre::PT_REAL), + &msHeightCmd); + dict->addParameter(Ogre::ParameterDef("depth", + "Depth of the shape in world coordinates.", + Ogre::PT_REAL), + &msDepthCmd); + + dict->addParameter(Ogre::ParameterDef("vertical_direction", + "Vertical direction of emitted particles (in degrees).", + Ogre::PT_REAL), + &msVerticalDirCmd); + dict->addParameter(Ogre::ParameterDef("vertical_angle", + "Vertical direction variance of emitted particles (in degrees).", + Ogre::PT_REAL), + &msVerticalAngleCmd); + dict->addParameter(Ogre::ParameterDef("horizontal_direction", + "Horizontal direction of emitted particles (in degrees).", + Ogre::PT_REAL), + &msHorizontalDirCmd); + dict->addParameter(Ogre::ParameterDef("horizontal_angle", + "Horizontal direction variance of emitted particles (in degrees).", + Ogre::PT_REAL), + &msHorizontalAngleCmd); + + return true; + } + return false; + } + + /// Command objects + static CmdWidth msWidthCmd; + static CmdHeight msHeightCmd; + static CmdDepth msDepthCmd; + static CmdVerticalDir msVerticalDirCmd; + static CmdVerticalAngle msVerticalAngleCmd; + static CmdHorizontalDir msHorizontalDirCmd; + static CmdHorizontalAngle msHorizontalAngleCmd; +}; +NifEmitter::CmdWidth NifEmitter::msWidthCmd; +NifEmitter::CmdHeight NifEmitter::msHeightCmd; +NifEmitter::CmdDepth NifEmitter::msDepthCmd; +NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd; +NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd; +NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd; +NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd; + +Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys) +{ + Ogre::ParticleEmitter *emit = OGRE_NEW NifEmitter(psys); + mEmitters.push_back(emit); + return emit; +} + + +class GrowFadeAffector : public Ogre::ParticleAffector +{ +public: + /** 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; +} + + +class GravityAffector : public Ogre::ParticleAffector +{ + enum ForceType { + Type_Wind, + Type_Point + }; + +public: + /** Command object for force (see Ogre::ParamCommand).*/ + class CmdForce : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getForce()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setForce(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for force_type (see Ogre::ParamCommand).*/ + class CmdForceType : public Ogre::ParamCommand + { + static ForceType getTypeFromString(const Ogre::String &type) + { + if(type == "wind") + return Type_Wind; + if(type == "point") + return Type_Point; + OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type string: "+type, + "CmdForceType::getTypeFromString"); + } + + static Ogre::String getStringFromType(ForceType type) + { + switch(type) + { + case Type_Wind: return "wind"; + case Type_Point: return "point"; + } + OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type enum: "+Ogre::StringConverter::toString(type), + "CmdForceType::getStringFromType"); + } + + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return getStringFromType(self->getForceType()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setForceType(getTypeFromString(val)); + } + }; + + /** Command object for direction (see Ogre::ParamCommand).*/ + class CmdDirection : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getDirection()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setDirection(Ogre::StringConverter::parseVector3(val)); + } + }; + + /** Command object for position (see Ogre::ParamCommand).*/ + class CmdPosition : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getPosition()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setPosition(Ogre::StringConverter::parseVector3(val)); + } + }; + + + /** Default constructor. */ + GravityAffector(Ogre::ParticleSystem *psys) + : ParticleAffector(psys) + , mForce(0.0f) + , mForceType(Type_Wind) + , mPosition(0.0f) + , mDirection(0.0f) + { + mType = "Gravity"; + + // Init parameters + if(createParamDictionary("GravityAffector")) + { + Ogre::ParamDictionary *dict = getParamDictionary(); + + Ogre::String force_title("force"); + Ogre::String force_descr("Amount of force applied to particles."); + Ogre::String force_type_title("force_type"); + Ogre::String force_type_descr("Type of force applied to particles (point or wind)."); + Ogre::String direction_title("direction"); + Ogre::String direction_descr("Direction of wind forces."); + Ogre::String position_title("position"); + Ogre::String position_descr("Position of point forces."); + + dict->addParameter(Ogre::ParameterDef(force_title, force_descr, Ogre::PT_REAL), &msForceCmd); + dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd); + dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd); + dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd); + } + } + + /** See Ogre::ParticleAffector. */ + void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + switch(mForceType) + { + case Type_Wind: + applyWindForce(psys, timeElapsed); + break; + case Type_Point: + applyPointForce(psys, timeElapsed); + break; + } + } + + void setForce(Ogre::Real force) + { mForce = force; } + Ogre::Real getForce() const + { return mForce; } + + void setForceType(ForceType type) + { mForceType = type; } + ForceType getForceType() const + { return mForceType; } + + void setDirection(const Ogre::Vector3 &dir) + { mDirection = dir; } + const Ogre::Vector3 &getDirection() const + { return mDirection; } + + void setPosition(const Ogre::Vector3 &pos) + { mPosition = pos; } + const Ogre::Vector3 &getPosition() const + { return mPosition; } + + static CmdForce msForceCmd; + static CmdForceType msForceTypeCmd; + static CmdDirection msDirectionCmd; + static CmdPosition msPositionCmd; + +protected: + void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + const Ogre::Vector3 vec = mDirection * mForce * timeElapsed; + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + p->direction += vec; + } + } + + void applyPointForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + const Ogre::Real force = mForce * timeElapsed; + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + const Ogre::Vector3 vec = (p->position - mPosition).normalisedCopy() * force; + p->direction += vec; + } + } + + + float mForce; + + ForceType mForceType; + + Ogre::Vector3 mPosition; + Ogre::Vector3 mDirection; +}; +GravityAffector::CmdForce GravityAffector::msForceCmd; +GravityAffector::CmdForceType GravityAffector::msForceTypeCmd; +GravityAffector::CmdDirection GravityAffector::msDirectionCmd; +GravityAffector::CmdPosition GravityAffector::msPositionCmd; + +Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys) +{ + Ogre::ParticleAffector *p = new GravityAffector(psys); + mAffectors.push_back(p); + return p; +} diff --git a/libs/openengine/ogre/particles.hpp b/libs/openengine/ogre/particles.hpp new file mode 100644 index 0000000000..e1f3fd282c --- /dev/null +++ b/libs/openengine/ogre/particles.hpp @@ -0,0 +1,41 @@ +#ifndef OENGINE_OGRE_PARTICLES_H +#define OENGINE_OGRE_PARTICLES_H + +#include +#include + +/** Factory class for NifEmitter. */ +class NifEmitterFactory : public Ogre::ParticleEmitterFactory +{ +public: + /** See ParticleEmitterFactory */ + Ogre::String getName() const + { return "Nif"; } + + /** See ParticleEmitterFactory */ + Ogre::ParticleEmitter* createEmitter(Ogre::ParticleSystem *psys); +}; + +/** Factory class for GrowFadeAffector. */ +class GrowFadeAffectorFactory : public Ogre::ParticleAffectorFactory +{ + /** See Ogre::ParticleAffectorFactory */ + Ogre::String getName() const + { return "GrowFade"; } + + /** See Ogre::ParticleAffectorFactory */ + Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys); +}; + +/** Factory class for GravityAffector. */ +class GravityAffectorFactory : public Ogre::ParticleAffectorFactory +{ + /** See Ogre::ParticleAffectorFactory */ + Ogre::String getName() const + { return "Gravity"; } + + /** See Ogre::ParticleAffectorFactory */ + Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys); +}; + +#endif /* OENGINE_OGRE_PARTICLES_H */ diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 309ba8a060..7be7137969 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,16 @@ void OgreRenderer::loadPlugins() void OgreRenderer::unloadPlugins() { + std::vector::iterator ei; + for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();ei++) + OGRE_DELETE (*ei); + mEmitterFactories.clear(); + + std::vector::iterator ai; + for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();ai++) + OGRE_DELETE (*ai); + mAffectorFactories.clear(); + #ifdef ENABLE_PLUGIN_GL delete mGLPlugin; mGLPlugin = NULL; @@ -195,6 +209,24 @@ 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::ParticleEmitterFactory *emitter; + emitter = OGRE_NEW NifEmitterFactory(); + Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter); + mEmitterFactories.push_back(emitter); + + + Ogre::ParticleAffectorFactory *affector; + affector = OGRE_NEW GrowFadeAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + + affector = OGRE_NEW GravityAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 251dc9c54d..962ae4f2ec 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -40,6 +40,8 @@ namespace Ogre class SceneManager; class Camera; class Viewport; + class ParticleEmitterFactory; + class ParticleAffectorFactory; } namespace OEngine @@ -94,6 +96,8 @@ namespace OEngine Ogre::D3D9Plugin* mD3D9Plugin; #endif Fader* mFader; + std::vector mEmitterFactories; + std::vector mAffectorFactories; bool logging; public: diff --git a/readme.txt b/readme.txt index 8edb0c4b31..f8361780c4 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.22.0 +Version: 0.23.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org @@ -31,7 +31,7 @@ Open DMG file, copy OpenMW folder anywhere, for example in /Applications BUILD FROM SOURCE -TODO add description here +https://wiki.openmw.org/index.php?title=Development_Environment_Setup THE DATA PATH @@ -94,6 +94,68 @@ Allowed options: CHANGELOG +0.23.0 + +Bug #522: Player collides with placeable items +Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open +Bug #561: Tooltip word wrapping delay +Bug #578: Bribing works incorrectly +Bug #601: PositionCell fails on negative coordinates +Bug #606: Some NPCs hairs not rendered with Better Heads addon +Bug #609: Bad rendering of bone boots +Bug #613: Messagebox causing assert to fail +Bug #631: Segfault on shutdown +Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard +Bug #635: Scale NPCs depending on race +Bug #643: Dialogue Race select function is inverted +Bug #646: Twohanded weapons don't work properly +Bug #654: Crash when dropping objects without a collision shape +Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell +Bug #660: "g" in "change" cut off in Race Menu +Bug #661: Arrille sells me the key to his upstairs room +Bug #662: Day counter starts at 2 instead of 1 +Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur +Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. +Bug #666: Looking up/down problem +Bug #667: Active effects border visible during loading +Bug #669: incorrect player position at new game start +Bug #670: race selection menu: sex, face and hair left button not totally clickable +Bug #671: new game: player is naked +Bug #674: buying or selling items doesn't change amount of gold +Bug #675: fatigue is not set to its maximum when starting a new game +Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly +Bug #680: different gold coins in Tel Mara +Bug #682: Race menu ignores playable flag for some hairs and faces +Bug #685: Script compiler does not accept ":" after a function name +Bug #688: dispose corpse makes cross-hair to disappear +Bug #691: Auto equipping ignores equipment conditions +Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder +Bug #696: Draugr incorrect head offset +Bug #697: Sail transparency issue +Bug #700: "On the rocks" mod does not load its UV coordinates correctly. +Bug #702: Some race mods don't work +Bug #711: Crash during character creation +Bug #715: Growing Tauryon +Bug #725: Auto calculate stats +Bug #728: Failure to open container and talk dialogue +Bug #731: Crash with Mush-Mere's "background" topic +Feature #55/657: Item Repairing +Feature #62/87: Enchanting +Feature #99: Pathfinding +Feature #104: AI Package: Travel +Feature #129: Levelled items +Feature #204: Texture animations +Feature #239: Fallback-Settings +Feature #535: Console object selection improvements +Feature #629: Add levelup description in levelup layout dialog +Feature #630: Optional format subrecord in (tes3) header +Feature #641: Armor rating +Feature #645: OnDeath script function +Feature #683: Companion item UI +Feature #698: Basic Particles +Task #648: Split up components/esm/loadlocks +Task #695: mwgui cleanup + 0.22.0 Bug #311: Potential infinite recursion in script compiler