diff -Nru openmw-0.37.0/apps/esmtool/esmtool.cpp openmw-0.38.0/apps/esmtool/esmtool.cpp --- openmw-0.37.0/apps/esmtool/esmtool.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/esmtool/esmtool.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,7 +27,8 @@ std::vector masters; std::deque mRecords; - std::map > mCellRefs; + // Value: (Reference, Deleted flag) + std::map > > mCellRefs; std::map mRecordStats; static const std::set sLabeledRec; @@ -255,7 +256,7 @@ while(cell.getNextRef(esm, ref, deleted)) { if (save) { - info.data.mCellRefs[&cell].push_back(ref); + info.data.mCellRefs[&cell].push_back(std::make_pair(ref, deleted)); } if(quiet) continue; @@ -352,61 +353,58 @@ uint32_t flags; esm.getRecHeader(flags); + EsmTool::RecordBase *record = EsmTool::RecordBase::create(n); + if (record == 0) + { + if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end()) + { + std::cout << "Skipping " << n.toString() << " records." << std::endl; + skipped.push_back(n.val); + } + + esm.skipRecord(); + if (quiet) break; + std::cout << " Skipping\n"; + + continue; + } + + record->setFlags(static_cast(flags)); + record->setPrintPlain(info.plain_given); + record->load(esm); + // Is the user interested in this record type? bool interested = true; if (!info.types.empty()) { std::vector::iterator match; - match = std::find(info.types.begin(), info.types.end(), - n.toString()); + match = std::find(info.types.begin(), info.types.end(), n.toString()); if (match == info.types.end()) interested = false; } - std::string id = esm.getHNOString("NAME"); - if (id.empty()) - id = esm.getHNOString("INAM"); - - if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id)) + if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, record->getId())) interested = false; if(!quiet && interested) - std::cout << "\nRecord: " << n.toString() - << " '" << id << "'\n"; - - EsmTool::RecordBase *record = EsmTool::RecordBase::create(n); - - if (record == 0) { - if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end()) - { - std::cout << "Skipping " << n.toString() << " records." << std::endl; - skipped.push_back(n.val); - } - - esm.skipRecord(); - if (quiet) break; - std::cout << " Skipping\n"; - } else { - if (record->getType().val == ESM::REC_GMST) { - // preset id for GameSetting record - record->cast()->get().mId = id; - } - record->setId(id); - record->setFlags((int) flags); - record->setPrintPlain(info.plain_given); - record->load(esm); - if (!quiet && interested) record->print(); + { + std::cout << "\nRecord: " << n.toString() << " '" << record->getId() << "'\n"; + record->print(); + } - if (record->getType().val == ESM::REC_CELL && loadCells && interested) { - loadCell(record->cast()->get(), esm, info); - } + if (record->getType().val == ESM::REC_CELL && loadCells && interested) + { + loadCell(record->cast()->get(), esm, info); + } - if (save) { - info.data.mRecords.push_back(record); - } else { - delete record; - } - ++info.data.mRecordStats[n.val]; + if (save) + { + info.data.mRecords.push_back(record); + } + else + { + delete record; } + ++info.data.mRecordStats[n.val]; } } catch(std::exception &e) { @@ -493,28 +491,19 @@ for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it) { EsmTool::RecordBase *record = *it; - name.val = record->getType().val; esm.startRecord(name.toString(), record->getFlags()); - // TODO wrap this with std::set - if (ESMData::sLabeledRec.count(name.val) > 0) { - esm.writeHNCString("NAME", record->getId()); - } else { - esm.writeHNOString("NAME", record->getId()); - } - record->save(esm); - if (name.val == ESM::REC_CELL) { ESM::Cell *ptr = &record->cast()->get(); if (!info.data.mCellRefs[ptr].empty()) { - typedef std::deque RefList; + typedef std::deque > RefList; RefList &refs = info.data.mCellRefs[ptr]; for (RefList::iterator refIt = refs.begin(); refIt != refs.end(); ++refIt) { - refIt->save(esm); + refIt->first.save(esm, refIt->second); } } } diff -Nru openmw-0.37.0/apps/esmtool/record.cpp openmw-0.38.0/apps/esmtool/record.cpp --- openmw-0.37.0/apps/esmtool/record.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/esmtool/record.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -405,6 +405,7 @@ std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -419,6 +420,7 @@ std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl; printEffectList(mData.mEffects); + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -447,6 +449,7 @@ if (pit->mFemale != "") std::cout << " Female Name: " << pit->mFemale << std::endl; } + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -461,6 +464,7 @@ std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -474,6 +478,7 @@ std::cout << " Part: " << meshPartLabel(mData.mData.mPart) << " (" << (int)mData.mData.mPart << ")" << std::endl; std::cout << " Vampire: " << (int)mData.mData.mVampire << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -502,6 +507,7 @@ { std::cout << " Text: [skipped]" << std::endl; } + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -513,6 +519,7 @@ std::vector::iterator pit; for (pit = mData.mPowers.mList.begin(); pit != mData.mPowers.mList.end(); ++pit) std::cout << " Power: " << *pit << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -541,6 +548,7 @@ std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl; std::cout << " Water Level Int: " << mData.mWaterInt << std::endl; std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } @@ -563,6 +571,7 @@ for (int i = 0; i != 5; i++) std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1]) << " (" << mData.mData.mSkills[i][1] << ")" << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -589,6 +598,7 @@ if (pit->mFemale != "") std::cout << " Female Name: " << pit->mFemale << std::endl; } + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -604,6 +614,7 @@ for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); ++cit) std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount << " Item: " << cit->mItem.toString() << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -670,6 +681,7 @@ std::vector::iterator pit; for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit) printAIPackage(*pit); + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -677,6 +689,7 @@ { std::cout << " Type: " << dialogTypeLabel(mData.mType) << " (" << (int)mData.mType << ")" << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; // Sadly, there are no DialInfos, because the loader dumps as it // loads, rather than loading and then dumping. :-( Anyone mind if // I change this? @@ -693,6 +706,7 @@ std::cout << " Script: " << mData.mScript << std::endl; std::cout << " OpenSound: " << mData.mOpenSound << std::endl; std::cout << " CloseSound: " << mData.mCloseSound << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -704,6 +718,7 @@ std::cout << " Charge: " << mData.mData.mCharge << std::endl; std::cout << " AutoCalc: " << mData.mData.mAutocalc << std::endl; printEffectList(mData.mEffects); + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -737,12 +752,14 @@ std::map::iterator rit; for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); ++rit) std::cout << " Reaction: " << rit->second << " = " << rit->first << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " " << mData.mValue << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -809,6 +826,7 @@ std::cout << " Result Script: [skipped]" << std::endl; } } + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -832,6 +850,7 @@ std::cout << " Attribute: " << attributeLabel(mData.mData.mAttributes[i]) << " (" << mData.mData.mAttributes[i] << ")" << std::endl; } + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -848,6 +867,8 @@ std::cout << " Unknown1: " << data->mUnk1 << std::endl; std::cout << " Unknown2: " << data->mUnk2 << std::endl; } + mData.unloadData(); + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -860,6 +881,7 @@ for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit) std::cout << " Creature: Level: " << iit->mLevel << " Creature: " << iit->mId << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -872,6 +894,7 @@ for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit) std::cout << " Inventory: Level: " << iit->mLevel << " Item: " << iit->mId << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -892,6 +915,7 @@ std::cout << " Duration: " << mData.mData.mTime << std::endl; std::cout << " Radius: " << mData.mData.mRadius << std::endl; std::cout << " Color: " << mData.mData.mColor << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -906,6 +930,7 @@ std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Uses: " << mData.mData.mUses << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -920,6 +945,7 @@ std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Uses: " << mData.mData.mUses << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -934,6 +960,7 @@ std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Uses: " << mData.mData.mUses << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -942,6 +969,7 @@ std::cout << " Id: " << mData.mId << std::endl; std::cout << " Index: " << mData.mIndex << std::endl; std::cout << " Texture: " << mData.mTexture << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -992,6 +1020,7 @@ std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Is Key: " << mData.mData.mIsKey << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -1077,6 +1106,8 @@ std::vector::iterator pit; for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit) printAIPackage(*pit); + + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -1111,6 +1142,8 @@ std::cout << " BAD POINT IN EDGE!" << std::endl; i++; } + + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -1151,6 +1184,8 @@ std::vector::iterator sit; for (sit = mData.mPowers.mList.begin(); sit != mData.mPowers.mList.end(); ++sit) std::cout << " Power: " << *sit << std::endl; + + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -1210,6 +1245,8 @@ { std::cout << " Script: [skipped]" << std::endl; } + + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -1233,6 +1270,7 @@ std::cout << " Sound: " << mData.mSound << std::endl; std::cout << " Type: " << soundTypeLabel(mData.mType) << " (" << mData.mType << ")" << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -1243,6 +1281,7 @@ if (mData.mData.mMinRange != 0 && mData.mData.mMaxRange != 0) std::cout << " Range: " << (int)mData.mData.mMinRange << " - " << (int)mData.mData.mMaxRange << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -1254,13 +1293,15 @@ std::cout << " Flags: " << spellFlags(mData.mData.mFlags) << std::endl; std::cout << " Cost: " << mData.mData.mCost << std::endl; printEffectList(mData.mEffects); + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { - std::cout << "Start Script: " << mData.mId << std::endl; - std::cout << "Start Data: " << mData.mData << std::endl; + std::cout << " Start Script: " << mData.mId << std::endl; + std::cout << " Start Data: " << mData.mData << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> @@ -1301,6 +1342,37 @@ if (mData.mData.mThrust[0] != 0 && mData.mData.mThrust[1] != 0) std::cout << " Thrust: " << (int)mData.mData.mThrust[0] << "-" << (int)mData.mData.mThrust[1] << std::endl; + std::cout << " Deleted: " << mIsDeleted << std::endl; +} + +template<> +std::string Record::getId() const +{ + return mData.mName; +} + +template<> +std::string Record::getId() const +{ + return ""; // No ID for Land record +} + +template<> +std::string Record::getId() const +{ + return ""; // No ID for MagicEffect record +} + +template<> +std::string Record::getId() const +{ + return ""; // No ID for Pathgrid record +} + +template<> +std::string Record::getId() const +{ + return ""; // No ID for Skill record } } // end namespace diff -Nru openmw-0.37.0/apps/esmtool/record.hpp openmw-0.38.0/apps/esmtool/record.hpp --- openmw-0.37.0/apps/esmtool/record.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/esmtool/record.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -32,13 +32,7 @@ virtual ~RecordBase() {} - const std::string &getId() const { - return mId; - } - - void setId(const std::string &id) { - mId = id; - } + virtual std::string getId() const = 0; uint32_t getFlags() const { return mFlags; @@ -73,22 +67,37 @@ class Record : public RecordBase { T mData; + bool mIsDeleted; public: + Record() + : mIsDeleted(false) + {} + + std::string getId() const { + return mData.mId; + } + T &get() { return mData; } void save(ESM::ESMWriter &esm) { - mData.save(esm); + mData.save(esm, mIsDeleted); } void load(ESM::ESMReader &esm) { - mData.load(esm); + mData.load(esm, mIsDeleted); } void print(); }; + + template<> std::string Record::getId() const; + template<> std::string Record::getId() const; + template<> std::string Record::getId() const; + template<> std::string Record::getId() const; + template<> std::string Record::getId() const; template<> void Record::print(); template<> void Record::print(); diff -Nru openmw-0.37.0/apps/essimporter/converter.cpp openmw-0.38.0/apps/essimporter/converter.cpp --- openmw-0.37.0/apps/essimporter/converter.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/essimporter/converter.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -158,9 +158,9 @@ void ConvertCell::read(ESM::ESMReader &esm) { ESM::Cell cell; - std::string id = esm.getHNString("NAME"); - cell.mName = id; - cell.load(esm, false); + bool isDeleted = false; + + cell.load(esm, isDeleted, false); // I wonder what 0x40 does? if (cell.isExterior() && cell.mData.mFlags & 0x20) @@ -169,7 +169,7 @@ } // note if the player is in a nameless exterior cell, we will assign the cellId later based on player position - if (id == mContext->mPlayerCellName) + if (cell.mName == mContext->mPlayerCellName) { mContext->mPlayer.mCellId = cell.getCellId(); } @@ -277,7 +277,7 @@ if (cell.isExterior()) mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell; else - mIntCells[id] = newcell; + mIntCells[cell.mName] = newcell; } void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm) diff -Nru openmw-0.37.0/apps/essimporter/converter.hpp openmw-0.38.0/apps/essimporter/converter.hpp --- openmw-0.37.0/apps/essimporter/converter.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/essimporter/converter.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -54,6 +54,8 @@ void setContext(Context& context) { mContext = &context; } + /// @note The load method of ESM records accept the deleted flag as a parameter. + /// I don't know can the DELE sub-record appear in saved games, so the deleted flag will be ignored. virtual void read(ESM::ESMReader& esm) { } @@ -78,10 +80,11 @@ virtual void read(ESM::ESMReader& esm) { - std::string id = esm.getHNString("NAME"); T record; - record.load(esm); - mRecords[id] = record; + bool isDeleted = false; + + record.load(esm, isDeleted); + mRecords[record.mId] = record; } virtual void write(ESM::ESMWriter& esm) @@ -89,7 +92,6 @@ for (typename std::map::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it) { esm.startRecord(T::sRecordId); - esm.writeHNString("NAME", it->first); it->second.save(esm); esm.endRecord(T::sRecordId); } @@ -105,14 +107,15 @@ virtual void read(ESM::ESMReader &esm) { ESM::NPC npc; - std::string id = esm.getHNString("NAME"); - npc.load(esm); - if (id != "player") + bool isDeleted = false; + + npc.load(esm, isDeleted); + if (npc.mId != "player") { // Handles changes to the NPC struct, but since there is no index here // it will apply to ALL instances of the class. seems to be the reason for the // "feature" in MW where changing AI settings of one guard will change it for all guards of that refID. - mContext->mNpcs[Misc::StringUtils::lowerCase(id)] = npc; + mContext->mNpcs[Misc::StringUtils::lowerCase(npc.mId)] = npc; } else { @@ -142,9 +145,10 @@ { // See comment in ConvertNPC ESM::Creature creature; - std::string id = esm.getHNString("NAME"); - creature.load(esm); - mContext->mCreatures[Misc::StringUtils::lowerCase(id)] = creature; + bool isDeleted = false; + + creature.load(esm, isDeleted); + mContext->mCreatures[Misc::StringUtils::lowerCase(creature.mId)] = creature; } }; @@ -157,18 +161,19 @@ public: virtual void read(ESM::ESMReader &esm) { - std::string id = esm.getHNString("NAME"); ESM::Global global; - global.load(esm); - if (Misc::StringUtils::ciEqual(id, "gamehour")) + bool isDeleted = false; + + global.load(esm, isDeleted); + if (Misc::StringUtils::ciEqual(global.mId, "gamehour")) mContext->mHour = global.mValue.getFloat(); - if (Misc::StringUtils::ciEqual(id, "day")) + if (Misc::StringUtils::ciEqual(global.mId, "day")) mContext->mDay = global.mValue.getInteger(); - if (Misc::StringUtils::ciEqual(id, "month")) + if (Misc::StringUtils::ciEqual(global.mId, "month")) mContext->mMonth = global.mValue.getInteger(); - if (Misc::StringUtils::ciEqual(id, "year")) + if (Misc::StringUtils::ciEqual(global.mId, "year")) mContext->mYear = global.mValue.getInteger(); - mRecords[id] = global; + mRecords[global.mId] = global; } }; @@ -177,14 +182,14 @@ public: virtual void read(ESM::ESMReader &esm) { - std::string id = esm.getHNString("NAME"); ESM::Class class_; - class_.load(esm); + bool isDeleted = false; - if (id == "NEWCLASSID_CHARGEN") + class_.load(esm, isDeleted); + if (class_.mId == "NEWCLASSID_CHARGEN") mContext->mCustomPlayerClassName = class_.mName; - mRecords[id] = class_; + mRecords[class_.mId] = class_; } }; @@ -193,13 +198,14 @@ public: virtual void read(ESM::ESMReader &esm) { - std::string id = esm.getHNString("NAME"); ESM::Book book; - book.load(esm); + bool isDeleted = false; + + book.load(esm, isDeleted); if (book.mData.mSkillID == -1) - mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(id)); + mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId)); - mRecords[id] = book; + mRecords[book.mId] = book; } }; @@ -371,11 +377,12 @@ public: virtual void read(ESM::ESMReader& esm) { - std::string id = esm.getHNString("NAME"); ESM::Faction faction; - faction.load(esm); + bool isDeleted = false; + + faction.load(esm, isDeleted); + std::string id = Misc::StringUtils::lowerCase(faction.mId); - Misc::StringUtils::toLower(id); for (std::map::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it) { std::string faction2 = Misc::StringUtils::lowerCase(it->first); @@ -391,7 +398,7 @@ virtual void read(ESM::ESMReader &esm) { std::string itemid = esm.getHNString("NAME"); - Misc::StringUtils::toLower(itemid); + Misc::StringUtils::lowerCaseInPlace(itemid); while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM")) { diff -Nru openmw-0.37.0/apps/essimporter/importacdt.cpp openmw-0.38.0/apps/essimporter/importacdt.cpp --- openmw-0.37.0/apps/essimporter/importacdt.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/essimporter/importacdt.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -32,7 +32,8 @@ if (esm.isNextSub("MNAM")) esm.skipHSub(); - ESM::CellRef::loadData(esm); + bool isDeleted = false; + ESM::CellRef::loadData(esm, isDeleted); mHasACDT = false; if (esm.isNextSub("ACDT")) diff -Nru openmw-0.37.0/apps/essimporter/importer.cpp openmw-0.38.0/apps/essimporter/importer.cpp --- openmw-0.37.0/apps/essimporter/importer.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/essimporter/importer.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -54,7 +54,7 @@ *(image->data(x,y)+2) = *it++; *(image->data(x,y)+1) = *it++; *image->data(x,y) = *it++; - it++; // skip alpha + ++it; // skip alpha } } @@ -394,7 +394,7 @@ } writer.startRecord(ESM::REC_NPC_); - writer.writeHNString("NAME", "player"); + context.mPlayerBase.mId = "player"; context.mPlayerBase.save(writer); writer.endRecord(ESM::REC_NPC_); diff -Nru openmw-0.37.0/apps/essimporter/importinventory.cpp openmw-0.38.0/apps/essimporter/importinventory.cpp --- openmw-0.37.0/apps/essimporter/importinventory.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/essimporter/importinventory.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -32,7 +32,8 @@ item.mSCRI.load(esm); // for XSOL and XCHG seen so far, but probably others too - item.ESM::CellRef::loadData(esm); + bool isDeleted = false; + item.ESM::CellRef::loadData(esm, isDeleted); int charge=-1; esm.getHNOT(charge, "XHLT"); diff -Nru openmw-0.37.0/apps/launcher/CMakeLists.txt openmw-0.38.0/apps/launcher/CMakeLists.txt --- openmw-0.37.0/apps/launcher/CMakeLists.txt 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/launcher/CMakeLists.txt 2016-01-12 16:11:28.000000000 +0000 @@ -7,8 +7,6 @@ textslotmsgbox.cpp settingspage.cpp - settings/graphicssettings.cpp - utils/profilescombobox.cpp utils/textinputdialog.cpp utils/lineedit.cpp @@ -24,8 +22,6 @@ textslotmsgbox.hpp settingspage.hpp - settings/graphicssettings.hpp - utils/profilescombobox.hpp utils/textinputdialog.hpp utils/lineedit.hpp diff -Nru openmw-0.37.0/apps/launcher/datafilespage.cpp openmw-0.38.0/apps/launcher/datafilespage.cpp --- openmw-0.37.0/apps/launcher/datafilespage.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/launcher/datafilespage.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -75,7 +75,7 @@ QStringList profiles = mLauncherSettings.getContentLists(); QString currentProfile = mLauncherSettings.getCurrentContentListName(); - qDebug() << "current profile is: " << currentProfile; + qDebug() << "The current profile is: " << currentProfile; foreach (const QString &item, profiles) addProfile (item, false); diff -Nru openmw-0.37.0/apps/launcher/graphicspage.cpp openmw-0.38.0/apps/launcher/graphicspage.cpp --- openmw-0.37.0/apps/launcher/graphicspage.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/launcher/graphicspage.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -18,7 +18,7 @@ #include -#include "settings/graphicssettings.hpp" +#include QString getAspect(int x, int y) { @@ -32,10 +32,10 @@ return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); } -Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent) +Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent) : QWidget(parent) , mCfgMgr(cfg) - , mGraphicsSettings(graphicsSetting) + , mEngineSettings(engineSettings) { setObjectName ("GraphicsPage"); setupUi(this); @@ -80,25 +80,26 @@ if (!setupSDL()) return false; - if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true")) + if (mEngineSettings.getBool("vsync", "Video")) vSyncCheckBox->setCheckState(Qt::Checked); - if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true")) + if (mEngineSettings.getBool("fullscreen", "Video")) fullScreenCheckBox->setCheckState(Qt::Checked); - if (mGraphicsSettings.value(QString("Video/window border")) == QLatin1String("true")) + if (mEngineSettings.getBool("window border", "Video")) windowBorderCheckBox->setCheckState(Qt::Checked); - int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing"))); + // aaValue is the actual value (0, 1, 2, 4, 8, 16) + int aaValue = mEngineSettings.getInt("antialiasing", "Video"); + // aaIndex is the index into the allowed values in the pull down. + int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue)); if (aaIndex != -1) antiAliasingComboBox->setCurrentIndex(aaIndex); - QString width = mGraphicsSettings.value(QString("Video/resolution x")); - QString height = mGraphicsSettings.value(QString("Video/resolution y")); - QString resolution = width + QString(" x ") + height; - QString screen = mGraphicsSettings.value(QString("Video/screen")); - - screenComboBox->setCurrentIndex(screen.toInt()); + int width = mEngineSettings.getInt("resolution x", "Video"); + int height = mEngineSettings.getInt("resolution y", "Video"); + QString resolution = QString::number(width) + QString(" x ") + QString::number(height); + screenComboBox->setCurrentIndex(mEngineSettings.getInt("screen", "Video")); int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith); @@ -107,9 +108,8 @@ resolutionComboBox->setCurrentIndex(resIndex); } else { customRadioButton->toggle(); - customWidthSpinBox->setValue(width.toInt()); - customHeightSpinBox->setValue(height.toInt()); - + customWidthSpinBox->setValue(width); + customHeightSpinBox->setValue(height); } return true; @@ -117,31 +117,46 @@ void Launcher::GraphicsPage::saveSettings() { - vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true")) - : mGraphicsSettings.setValue(QString("Video/vsync"), QString("false")); - - fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true")) - : mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false")); - - windowBorderCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/window border"), QString("true")) - : mGraphicsSettings.setValue(QString("Video/window border"), QString("false")); - - mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText()); - + // Ensure we only set the new settings if they changed. This is to avoid cluttering the + // user settings file (which by definition should only contain settings the user has touched) + bool cVSync = vSyncCheckBox->checkState(); + if (cVSync != mEngineSettings.getBool("vsync", "Video")) + mEngineSettings.setBool("vsync", "Video", cVSync); + + bool cFullScreen = fullScreenCheckBox->checkState(); + if (cFullScreen != mEngineSettings.getBool("fullscreen", "Video")) + mEngineSettings.setBool("fullscreen", "Video", cFullScreen); + + bool cWindowBorder = windowBorderCheckBox->checkState(); + if (cWindowBorder != mEngineSettings.getBool("window border", "Video")) + mEngineSettings.setBool("window border", "Video", cWindowBorder); + + int cAAValue = antiAliasingComboBox->currentText().toInt(); + if (cAAValue != mEngineSettings.getInt("antialiasing", "Video")) + mEngineSettings.setInt("antialiasing", "Video", cAAValue); + int cWidth = 0; + int cHeight = 0; if (standardRadioButton->isChecked()) { QRegExp resolutionRe(QString("(\\d+) x (\\d+).*")); - if (resolutionRe.exactMatch(resolutionComboBox->currentText().simplified())) { - mGraphicsSettings.setValue(QString("Video/resolution x"), resolutionRe.cap(1)); - mGraphicsSettings.setValue(QString("Video/resolution y"), resolutionRe.cap(2)); + cWidth = resolutionRe.cap(1).toInt(); + cHeight = resolutionRe.cap(2).toInt(); } } else { - mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value())); - mGraphicsSettings.setValue(QString("Video/resolution y"), QString::number(customHeightSpinBox->value())); + cWidth = customWidthSpinBox->value(); + cHeight = customHeightSpinBox->value(); } - mGraphicsSettings.setValue(QString("Video/screen"), QString::number(screenComboBox->currentIndex())); + if (cWidth != mEngineSettings.getInt("resolution x", "Video")) + mEngineSettings.setInt("resolution x", "Video", cWidth); + + if (cHeight != mEngineSettings.getInt("resolution y", "Video")) + mEngineSettings.setInt("resolution y", "Video", cHeight); + + int cScreen = screenComboBox->currentIndex(); + if (cScreen != mEngineSettings.getInt("screen", "Video")) + mEngineSettings.setInt("screen", "Video", cScreen); } QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) diff -Nru openmw-0.37.0/apps/launcher/graphicspage.hpp openmw-0.38.0/apps/launcher/graphicspage.hpp --- openmw-0.37.0/apps/launcher/graphicspage.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/launcher/graphicspage.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -5,6 +5,8 @@ #include "ui_graphicspage.h" +#include + namespace Files { struct ConfigurationManager; } namespace Launcher @@ -16,7 +18,7 @@ Q_OBJECT public: - GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); + GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent = 0); void saveSettings(); bool loadSettings(); @@ -30,7 +32,7 @@ private: Files::ConfigurationManager &mCfgMgr; - GraphicsSettings &mGraphicsSettings; + Settings::Manager &mEngineSettings; QStringList getAvailableResolutions(int screen); QRect getMaximumResolution(); diff -Nru openmw-0.37.0/apps/launcher/maindialog.cpp openmw-0.38.0/apps/launcher/maindialog.cpp --- openmw-0.37.0/apps/launcher/maindialog.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/launcher/maindialog.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -24,6 +24,15 @@ using namespace Process; +void cfgError(const QString& title, const QString& msg) { + QMessageBox msgBox; + msgBox.setWindowTitle(title); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(msg); + msgBox.exec(); +} + Launcher::MainDialog::MainDialog(QWidget *parent) : QMainWindow(parent), mGameSettings (mCfgMgr) { @@ -105,7 +114,7 @@ { mPlayPage = new PlayPage(this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); - mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this); + mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this); mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this); // Set the combobox of the play page to imitate the combobox on the datafilespage @@ -248,6 +257,8 @@ bool Launcher::MainDialog::setupLauncherSettings() { + mLauncherSettings.clear(); + mLauncherSettings.setMultiValueEnabled(true); QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); @@ -257,18 +268,14 @@ paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName)); foreach (const QString &path, paths) { - qDebug() << "Loading config file:" << qPrintable(path); + qDebug() << "Loading config file:" << path.toUtf8().constData(); QFile file(path); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open %0 for reading

\ - Please make sure you have the right permissions \ - and try again.
").arg(file.fileName())); - msgBox.exec(); + cfgError(tr("Error opening OpenMW configuration file"), + tr("
Could not open %0 for reading

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); return false; } QTextStream stream(&file); @@ -284,6 +291,8 @@ bool Launcher::MainDialog::setupGameSettings() { + mGameSettings.clear(); + QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); @@ -292,18 +301,14 @@ QString path = userPath + QLatin1String("openmw.cfg"); QFile file(path); - qDebug() << "Loading config file:" << qPrintable(path); + qDebug() << "Loading config file:" << path.toUtf8().constData(); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open %0 for reading

\ - Please make sure you have the right permissions \ - and try again.
").arg(file.fileName())); - msgBox.exec(); + cfgError(tr("Error opening OpenMW configuration file"), + tr("
Could not open %0 for reading

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); return false; } QTextStream stream(&file); @@ -319,19 +324,15 @@ paths.append(userPath + QString("openmw.cfg")); foreach (const QString &path, paths) { - qDebug() << "Loading config file:" << qPrintable(path); + qDebug() << "Loading config file:" << path.toUtf8().constData(); QFile file(path); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open %0 for reading

\ - Please make sure you have the right permissions \ - and try again.
").arg(file.fileName())); - msgBox.exec(); + cfgError(tr("Error opening OpenMW configuration file"), + tr("
Could not open %0 for reading

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); return false; } QTextStream stream(&file); @@ -383,53 +384,54 @@ bool Launcher::MainDialog::setupGraphicsSettings() { - mGraphicsSettings.setMultiValueEnabled(false); - - QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); - QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); - - QFile localDefault(QString("settings-default.cfg")); - QFile globalDefault(globalPath + QString("settings-default.cfg")); - - if (!localDefault.exists() && !globalDefault.exists()) { - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error reading OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not find settings-default.cfg

\ - The problem may be due to an incomplete installation of OpenMW.
\ - Reinstalling OpenMW may resolve the problem.")); - msgBox.exec(); + // This method is almost a copy of OMW::Engine::loadSettings(). They should definitely + // remain consistent, and possibly be merged into a shared component. At the very least + // the filenames should be in the CfgMgr component. + + // Ensure to clear previous settings in case we had already loaded settings. + mEngineSettings.clear(); + + // Create the settings manager and load default settings file + const std::string localDefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string(); + const std::string globalDefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string(); + std::string defaultPath; + + // Prefer the settings-default.cfg in the current directory. + if (boost::filesystem::exists(localDefault)) + defaultPath = localDefault; + else if (boost::filesystem::exists(globalDefault)) + defaultPath = globalDefault; + // Something's very wrong if we can't find the file at all. + else { + cfgError(tr("Error reading OpenMW configuration file"), + tr("
Could not find settings-default.cfg

\ + The problem may be due to an incomplete installation of OpenMW.
\ + Reinstalling OpenMW may resolve the problem.")); return false; } + // Load the default settings, report any parsing errors. + try { + mEngineSettings.loadDefault(defaultPath); + } + catch (std::exception& e) { + std::string msg = std::string("
Error reading settings-default.cfg

") + e.what(); + cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str())); + return false; + } - QStringList paths; - paths.append(globalPath + QString("settings-default.cfg")); - paths.append(QString("settings-default.cfg")); - paths.append(userPath + QString("settings.cfg")); - - foreach (const QString &path, paths) { - qDebug() << "Loading config file:" << qPrintable(path); - QFile file(path); - if (file.exists()) { - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open %0 for reading

\ - Please make sure you have the right permissions \ - and try again.
").arg(file.fileName())); - msgBox.exec(); - return false; - } - QTextStream stream(&file); - stream.setCodec(QTextCodec::codecForName("UTF-8")); - - mGraphicsSettings.readFile(stream); - } - file.close(); + // Load user settings if they exist + const std::string userPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string(); + // User settings are not required to exist, so if they don't we're done. + if (!boost::filesystem::exists(userPath)) return true; + + try { + mEngineSettings.loadUser(userPath); + } + catch (std::exception& e) { + std::string msg = std::string("
Error reading settings.cfg

") + e.what(); + cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str())); + return false; } return true; @@ -478,15 +480,11 @@ if (!dir.exists()) { if (!dir.mkpath(userPath)) { - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not create %0

\ - Please make sure you have the right permissions \ - and try again.
").arg(userPath)); - msgBox.exec(); - return false; + cfgError(tr("Error creating OpenMW configuration directory"), + tr("
Could not create %0

\ + Please make sure you have the right permissions \ + and try again.
").arg(userPath)); + return false; } } @@ -495,15 +493,11 @@ if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { // File cannot be opened or created - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open or create %0 for writing

\ - Please make sure you have the right permissions \ - and try again.
").arg(file.fileName())); - msgBox.exec(); - return false; + cfgError(tr("Error writing OpenMW configuration file"), + tr("
Could not open or create %0 for writing

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); + return false; } @@ -511,44 +505,30 @@ file.close(); // Graphics settings - file.setFileName(userPath + QString("settings.cfg")); - - if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { - // File cannot be opened or created - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open or create %0 for writing

\ - Please make sure you have the right permissions \ - and try again.
").arg(file.fileName())); - msgBox.exec(); - return false; + const std::string settingsPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string(); + try { + mEngineSettings.saveUser(settingsPath); + } + catch (std::exception& e) { + std::string msg = "
Error writing settings.cfg

" + + settingsPath + "

" + e.what(); + cfgError(tr("Error writing user settings file"), tr(msg.c_str())); + return false; } - QTextStream stream(&file); - stream.setDevice(&file); - stream.setCodec(QTextCodec::codecForName("UTF-8")); - - mGraphicsSettings.writeFile(stream); - file.close(); - // Launcher settings file.setFileName(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName)); if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { // File cannot be opened or created - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error writing Launcher configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open or create %0 for writing

\ - Please make sure you have the right permissions \ - and try again.
").arg(file.fileName())); - msgBox.exec(); - return false; + cfgError(tr("Error writing Launcher configuration file"), + tr("
Could not open or create %0 for writing

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); + return false; } + QTextStream stream(&file); stream.setDevice(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); diff -Nru openmw-0.37.0/apps/launcher/maindialog.hpp openmw-0.38.0/apps/launcher/maindialog.hpp --- openmw-0.37.0/apps/launcher/maindialog.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/launcher/maindialog.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -13,7 +13,7 @@ #include #include -#include "settings/graphicssettings.hpp" +#include #include "ui_mainwindow.h" @@ -93,7 +93,7 @@ Files::ConfigurationManager mCfgMgr; Config::GameSettings mGameSettings; - GraphicsSettings mGraphicsSettings; + Settings::Manager mEngineSettings; Config::LauncherSettings mLauncherSettings; }; diff -Nru openmw-0.37.0/apps/launcher/settings/graphicssettings.cpp openmw-0.38.0/apps/launcher/settings/graphicssettings.cpp --- openmw-0.37.0/apps/launcher/settings/graphicssettings.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/launcher/settings/graphicssettings.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -#include "graphicssettings.hpp" - -#include -#include -#include -#include - -Launcher::GraphicsSettings::GraphicsSettings() -{ -} - -Launcher::GraphicsSettings::~GraphicsSettings() -{ -} - -bool Launcher::GraphicsSettings::writeFile(QTextStream &stream) -{ - QString sectionPrefix; - QRegExp sectionRe("([^/]+)/(.+)$"); - QMap settings = SettingsBase::getSettings(); - - QMapIterator i(settings); - while (i.hasNext()) { - i.next(); - - QString prefix; - QString key; - - if (sectionRe.exactMatch(i.key())) { - prefix = sectionRe.cap(1); - key = sectionRe.cap(2); - } - - if (sectionPrefix != prefix) { - sectionPrefix = prefix; - stream << "\n[" << prefix << "]\n"; - } - - stream << key << " = " << i.value() << "\n"; - } - - return true; - -} diff -Nru openmw-0.37.0/apps/launcher/settings/graphicssettings.hpp openmw-0.38.0/apps/launcher/settings/graphicssettings.hpp --- openmw-0.37.0/apps/launcher/settings/graphicssettings.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/launcher/settings/graphicssettings.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -#ifndef GRAPHICSSETTINGS_HPP -#define GRAPHICSSETTINGS_HPP - -#include - -namespace Launcher -{ - class GraphicsSettings : public Config::SettingsBase > - { - public: - GraphicsSettings(); - ~GraphicsSettings(); - - bool writeFile(QTextStream &stream); - - }; -} -#endif // GRAPHICSSETTINGS_HPP diff -Nru openmw-0.37.0/apps/mwiniimporter/importer.cpp openmw-0.38.0/apps/mwiniimporter/importer.cpp --- openmw-0.37.0/apps/mwiniimporter/importer.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/mwiniimporter/importer.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,5 +1,6 @@ #include "importer.hpp" +#include #include #include #include @@ -638,6 +639,9 @@ "Blood:Texture Name 1", "Blood:Texture Name 2", + // werewolf (Bloodmoon) + "General:Werewolf FOV", + 0 }; @@ -846,7 +850,7 @@ for(std::vector::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { std::string filetype(entry->substr(entry->length()-3)); - Misc::StringUtils::toLower(filetype); + Misc::StringUtils::lowerCaseInPlace(filetype); if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { boost::filesystem::path filepath(gameFilesDir); @@ -894,8 +898,13 @@ boost::filesystem::path resolved = filename; #endif writeTime = boost::filesystem::last_write_time(resolved); - std::cout << "content file: " << resolved << " timestamp = (" << writeTime << - ") " << asctime(localtime(&writeTime)) << std::endl; + + // print timestamp + const int size=1024; + char timeStrBuffer[size]; + if (std::strftime(timeStrBuffer, size, "%x %X", localtime(&writeTime)) > 0) + std::cout << "content file: " << resolved << " timestamp = (" << writeTime << + ") " << timeStrBuffer << std::endl; } else { diff -Nru openmw-0.37.0/apps/opencs/CMakeLists.txt openmw-0.38.0/apps/opencs/CMakeLists.txt --- openmw-0.37.0/apps/opencs/CMakeLists.txt 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/CMakeLists.txt 2016-01-12 16:11:28.000000000 +0000 @@ -106,31 +106,16 @@ subviews ) -opencs_units (view/settings - settingwindow - dialog - page - view - booleanview - textview - listview - rangeview - resizeablestackedwidget - spinbox - ) - -opencs_units_noqt (view/settings - frame - ) - -opencs_units (model/settings - usersettings - setting - connector +opencs_units (view/prefs + dialogue pagebase page ) -opencs_hdrs_noqt (model/settings - support +opencs_units (model/prefs + state setting intsetting doublesetting boolsetting enumsetting coloursetting + ) + +opencs_units_noqt (model/prefs + category ) opencs_units_noqt (model/filter @@ -205,7 +190,13 @@ endif(APPLE) target_link_libraries(openmw-cs - ${OPENSCENEGRAPH_LIBRARIES} + ${OSG_LIBRARIES} + ${OPENTHREADS_LIBRARIES} + ${OSGUTIL_LIBRARIES} + ${OSGVIEWER_LIBRARIES} + ${OSGGA_LIBRARIES} + ${OSGFX_LIBRARIES} + ${OSGQT_LIBRARIES} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff -Nru openmw-0.37.0/apps/opencs/editor.cpp openmw-0.38.0/apps/opencs/editor.cpp --- openmw-0.37.0/apps/opencs/editor.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/editor.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -18,7 +18,7 @@ #endif CS::Editor::Editor () -: mUserSettings (mCfgMgr), mDocumentManager (mCfgMgr), +: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), mViewManager (mDocumentManager), mPid(""), mLock(), mMerge (mDocumentManager), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) @@ -27,9 +27,6 @@ setupDataFiles (config.first); - CSMSettings::UserSettings::instance().loadSettings ("opencs.ini"); - mSettings.setModel (CSMSettings::UserSettings::instance()); - NifOsg::Loader::setShowMarkers(true); mVFS.reset(new VFS::Manager(mFsStrict)); diff -Nru openmw-0.37.0/apps/opencs/editor.hpp openmw-0.38.0/apps/opencs/editor.hpp --- openmw-0.37.0/apps/opencs/editor.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/editor.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -17,15 +17,16 @@ #include -#include "model/settings/usersettings.hpp" #include "model/doc/documentmanager.hpp" +#include "model/prefs/state.hpp" + #include "view/doc/viewmanager.hpp" #include "view/doc/startup.hpp" #include "view/doc/filedialog.hpp" #include "view/doc/newgame.hpp" -#include "view/settings/dialog.hpp" +#include "view/prefs/dialogue.hpp" #include "view/tools/merge.hpp" @@ -49,12 +50,12 @@ std::auto_ptr mVFS; Files::ConfigurationManager mCfgMgr; - CSMSettings::UserSettings mUserSettings; + CSMPrefs::State mSettingsState; CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; CSVDoc::StartupDialogue mStartup; CSVDoc::NewGameDialogue mNewGame; - CSVSettings::Dialog mSettings; + CSVPrefs::Dialogue mSettings; CSVDoc::FileDialog mFileDialog; boost::filesystem::path mLocal; boost::filesystem::path mResources; diff -Nru openmw-0.37.0/apps/opencs/model/doc/loader.cpp openmw-0.38.0/apps/opencs/model/doc/loader.cpp --- openmw-0.37.0/apps/opencs/model/doc/loader.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/doc/loader.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -45,13 +45,12 @@ bool done = false; - const int batchingSize = 50; - try { if (iter->second.mRecordsLeft) { Messages messages (Message::Severity_Error); + const int batchingSize = 50; for (int i=0; igetData().continueLoading (messages)) { diff -Nru openmw-0.37.0/apps/opencs/model/doc/operation.cpp openmw-0.38.0/apps/opencs/model/doc/operation.cpp --- openmw-0.37.0/apps/opencs/model/doc/operation.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/doc/operation.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,7 +6,6 @@ #include #include "../world/universalid.hpp" -#include "../settings/usersettings.hpp" #include "state.hpp" #include "stage.hpp" @@ -23,9 +22,6 @@ { iter->second = iter->first->setup(); mTotalSteps += iter->second; - - for (std::map::const_iterator iter2 (mSettings.begin()); iter2!=mSettings.end(); ++iter2) - iter->first->updateUserSetting (iter2->first, iter2->second); } } @@ -47,7 +43,7 @@ void CSMDoc::Operation::run() { mTimer->stop(); - + if (!mConnected) { connect (mTimer, SIGNAL (timeout()), this, SLOT (executeStage())); @@ -64,14 +60,6 @@ mStages.push_back (std::make_pair (stage, 0)); } -void CSMDoc::Operation::configureSettings (const std::vector& settings) -{ - for (std::vector::const_iterator iter (settings.begin()); iter!=settings.end(); ++iter) - { - mSettings.insert (std::make_pair (*iter, CSMSettings::UserSettings::instance().definitions (*iter))); - } -} - void CSMDoc::Operation::setDefaultSeverity (Message::Severity severity) { mDefaultSeverity = severity; @@ -101,14 +89,6 @@ mCurrentStage = mStages.end(); } -void CSMDoc::Operation::updateUserSetting (const QString& name, const QStringList& value) -{ - std::map::iterator iter = mSettings.find (name); - - if (iter!=mSettings.end()) - iter->second = value; -} - void CSMDoc::Operation::executeStage() { if (!mPrepared) @@ -116,7 +96,7 @@ prepareStages(); mPrepared = true; } - + Messages messages (mDefaultSeverity); while (mCurrentStage!=mStages.end()) diff -Nru openmw-0.37.0/apps/opencs/model/doc/operationholder.cpp openmw-0.38.0/apps/opencs/model/doc/operationholder.cpp --- openmw-0.37.0/apps/opencs/model/doc/operationholder.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/doc/operationholder.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,7 +1,5 @@ #include "operationholder.hpp" -#include "../settings/usersettings.hpp" - #include "operation.hpp" CSMDoc::OperationHolder::OperationHolder (Operation *operation) : mRunning (false) @@ -30,9 +28,6 @@ connect (this, SIGNAL (abortSignal()), mOperation, SLOT (abort())); connect (&mThread, SIGNAL (started()), mOperation, SLOT (run())); - - connect (&CSMSettings::UserSettings::instance(), SIGNAL (userSettingUpdated (const QString&, const QStringList&)), - mOperation, SLOT (updateUserSetting (const QString&, const QStringList&))); } bool CSMDoc::OperationHolder::isRunning() const diff -Nru openmw-0.37.0/apps/opencs/model/doc/operation.hpp openmw-0.38.0/apps/opencs/model/doc/operation.hpp --- openmw-0.37.0/apps/opencs/model/doc/operation.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/doc/operation.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -34,7 +34,6 @@ bool mError; bool mConnected; QTimer *mTimer; - std::map mSettings; bool mPrepared; Message::Severity mDefaultSeverity; @@ -53,11 +52,6 @@ /// /// \attention Do no call this function while this Operation is running. - /// Specify settings to be passed on to stages. - /// - /// \attention Do no call this function while this Operation is running. - void configureSettings (const std::vector& settings); - /// \attention Do no call this function while this Operation is running. void setDefaultSeverity (Message::Severity severity); @@ -77,8 +71,6 @@ void run(); - void updateUserSetting (const QString& name, const QStringList& value); - private slots: void executeStage(); diff -Nru openmw-0.37.0/apps/opencs/model/doc/savingstages.cpp openmw-0.38.0/apps/opencs/model/doc/savingstages.cpp --- openmw-0.37.0/apps/opencs/model/doc/savingstages.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/doc/savingstages.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -99,95 +99,77 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& messages) { + ESM::ESMWriter& writer = mState.getWriter(); const CSMWorld::Record& topic = mTopics.getRecord (stage); - CSMWorld::RecordBase::State state = topic.mState; - - if (state==CSMWorld::RecordBase::State_Deleted) + if (topic.mState == CSMWorld::RecordBase::State_Deleted) { // if the topic is deleted, we do not need to bother with INFO records. - - /// \todo wrote record with delete flag - + ESM::Dialogue dialogue = topic.get(); + writer.startRecord(dialogue.sRecordId); + dialogue.save(writer, true); + writer.endRecord(dialogue.sRecordId); return; } // Test, if we need to save anything associated info records. bool infoModified = false; - CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId); for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) { - CSMWorld::RecordBase::State state = iter->mState; - - if (state==CSMWorld::RecordBase::State_Modified || - state==CSMWorld::RecordBase::State_ModifiedOnly || - state==CSMWorld::RecordBase::State_Deleted) + if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted) { infoModified = true; break; } } - if (state==CSMWorld::RecordBase::State_Modified || - state==CSMWorld::RecordBase::State_ModifiedOnly || - infoModified) + if (topic.isModified() || infoModified) { - if (infoModified && state != CSMWorld::RecordBase::State_Modified - && state != CSMWorld::RecordBase::State_ModifiedOnly) + if (infoModified && topic.mState != CSMWorld::RecordBase::State_Modified + && topic.mState != CSMWorld::RecordBase::State_ModifiedOnly) { mState.getWriter().startRecord (topic.mBase.sRecordId); - mState.getWriter().writeHNCString ("NAME", topic.mBase.mId); - topic.mBase.save (mState.getWriter()); + topic.mBase.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted); mState.getWriter().endRecord (topic.mBase.sRecordId); } else { mState.getWriter().startRecord (topic.mModified.sRecordId); - mState.getWriter().writeHNCString ("NAME", topic.mModified.mId); - topic.mModified.save (mState.getWriter()); + topic.mModified.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted); mState.getWriter().endRecord (topic.mModified.sRecordId); } // write modified selected info records - for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; - ++iter) + for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) { - CSMWorld::RecordBase::State infoState = iter->mState; - - if (infoState==CSMWorld::RecordBase::State_Deleted) - { - /// \todo wrote record with delete flag - } - else if (infoState==CSMWorld::RecordBase::State_Modified || - infoState==CSMWorld::RecordBase::State_ModifiedOnly) + if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted) { ESM::DialInfo info = iter->get(); info.mId = info.mId.substr (info.mId.find_last_of ('#')+1); + info.mPrev = ""; if (iter!=range.first) { CSMWorld::InfoCollection::RecordConstIterator prev = iter; --prev; - info.mPrev = - prev->mModified.mId.substr (prev->mModified.mId.find_last_of ('#')+1); + info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1); } CSMWorld::InfoCollection::RecordConstIterator next = iter; ++next; + info.mNext = ""; if (next!=range.second) { - info.mNext = - next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1); + info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1); } - mState.getWriter().startRecord (info.sRecordId); - mState.getWriter().writeHNCString ("INAM", info.mId); - info.save (mState.getWriter()); - mState.getWriter().endRecord (info.sRecordId); + writer.startRecord (info.sRecordId); + info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted); + writer.endRecord (info.sRecordId); } } } @@ -235,9 +217,7 @@ const CSMWorld::Record& record = mDocument.getData().getReferences().getRecord (i); - if (record.mState==CSMWorld::RecordBase::State_Deleted || - record.mState==CSMWorld::RecordBase::State_Modified || - record.mState==CSMWorld::RecordBase::State_ModifiedOnly) + if (record.isModified() || record.mState == CSMWorld::RecordBase::State_Deleted) { std::string cellId = record.get().mOriginalCell.empty() ? record.get().mCell : record.get().mOriginalCell; @@ -279,36 +259,34 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) { - const CSMWorld::Record& cell = - mDocument.getData().getCells().getRecord (stage); + ESM::ESMWriter& writer = mState.getWriter(); + const CSMWorld::Record& cell = mDocument.getData().getCells().getRecord (stage); std::map >::const_iterator references = mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId)); - if (cell.mState==CSMWorld::RecordBase::State_Modified || - cell.mState==CSMWorld::RecordBase::State_ModifiedOnly || + if (cell.isModified() || + cell.mState == CSMWorld::RecordBase::State_Deleted || references!=mState.getSubRecords().end()) { - bool interior = cell.get().mId.substr (0, 1)!="#"; + CSMWorld::Cell cellRecord = cell.get(); + bool interior = cellRecord.mId.substr (0, 1)!="#"; // write cell data - mState.getWriter().startRecord (cell.mModified.sRecordId); - - mState.getWriter().writeHNOCString ("NAME", cell.get().mName); - - ESM::Cell cell2 = cell.get(); + writer.startRecord (cellRecord.sRecordId); if (interior) - cell2.mData.mFlags |= ESM::Cell::Interior; + cellRecord.mData.mFlags |= ESM::Cell::Interior; else { - cell2.mData.mFlags &= ~ESM::Cell::Interior; + cellRecord.mData.mFlags &= ~ESM::Cell::Interior; - std::istringstream stream (cell.get().mId.c_str()); + std::istringstream stream (cellRecord.mId.c_str()); char ignore; - stream >> ignore >> cell2.mData.mX >> cell2.mData.mY; + stream >> ignore >> cellRecord.mData.mX >> cellRecord.mData.mY; } - cell2.save (mState.getWriter()); + + cellRecord.save (writer, cell.mState == CSMWorld::RecordBase::State_Deleted); // write references if (references!=mState.getSubRecords().end()) @@ -319,24 +297,25 @@ const CSMWorld::Record& ref = mDocument.getData().getReferences().getRecord (*iter); - if (ref.mState==CSMWorld::RecordBase::State_Modified || - ref.mState==CSMWorld::RecordBase::State_ModifiedOnly) + if (ref.isModified() || ref.mState == CSMWorld::RecordBase::State_Deleted) { + CSMWorld::CellRef refRecord = ref.get(); + // recalculate the ref's cell location std::ostringstream stream; if (!interior) { - std::pair index = ref.get().getCellIndex(); + std::pair index = refRecord.getCellIndex(); stream << "#" << index.first << " " << index.second; } // An empty mOriginalCell is meant to indicate that it is the same as // the current cell. It is possible that a moved ref is moved again. - if ((ref.get().mOriginalCell.empty() ? ref.get().mCell : ref.get().mOriginalCell) + if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) != stream.str() && !interior) { ESM::MovedCellRef moved; - moved.mRefNum = ref.get().mRefNum; + moved.mRefNum = refRecord.mRefNum; // Need to fill mTarget with the ref's new position. std::istringstream istream (stream.str().c_str()); @@ -344,24 +323,16 @@ char ignore; istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1]; - ref.get().mRefNum.save (mState.getWriter(), false, "MVRF"); - mState.getWriter().writeHNT ("CNDT", moved.mTarget, 8); + refRecord.mRefNum.save (writer, false, "MVRF"); + writer.writeHNT ("CNDT", moved.mTarget, 8); } - ref.get().save (mState.getWriter()); - } - else if (ref.mState==CSMWorld::RecordBase::State_Deleted) - { - /// \todo write record with delete flag + refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted); } } } - mState.getWriter().endRecord (cell.mModified.sRecordId); - } - else if (cell.mState==CSMWorld::RecordBase::State_Deleted) - { - /// \todo write record with delete flag + writer.endRecord (cellRecord.sRecordId); } } @@ -378,11 +349,11 @@ void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages) { - const CSMWorld::Record& pathgrid = + ESM::ESMWriter& writer = mState.getWriter(); + const CSMWorld::Record& pathgrid = mDocument.getData().getPathgrids().getRecord (stage); - if (pathgrid.mState==CSMWorld::RecordBase::State_Modified || - pathgrid.mState==CSMWorld::RecordBase::State_ModifiedOnly) + if (pathgrid.isModified() || pathgrid.mState == CSMWorld::RecordBase::State_Deleted) { CSMWorld::Pathgrid record = pathgrid.get(); @@ -395,15 +366,9 @@ else record.mCell = record.mId; - mState.getWriter().startRecord (record.sRecordId); - - record.save (mState.getWriter()); - - mState.getWriter().endRecord (record.sRecordId); - } - else if (pathgrid.mState==CSMWorld::RecordBase::State_Deleted) - { - /// \todo write record with delete flag + writer.startRecord (record.sRecordId); + record.save (writer, pathgrid.mState == CSMWorld::RecordBase::State_Deleted); + writer.endRecord (record.sRecordId); } } @@ -420,26 +385,20 @@ void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages) { - const CSMWorld::Record& land = + ESM::ESMWriter& writer = mState.getWriter(); + const CSMWorld::Record& land = mDocument.getData().getLand().getRecord (stage); - if (land.mState==CSMWorld::RecordBase::State_Modified || - land.mState==CSMWorld::RecordBase::State_ModifiedOnly) + if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted) { - const CSMWorld::Land& record = land.get(); - - mState.getWriter().startRecord (record.sRecordId); - - record.save (mState.getWriter()); + CSMWorld::Land record = land.get(); + writer.startRecord (record.sRecordId); + record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted); if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes)) data->save (mState.getWriter()); - mState.getWriter().endRecord (record.sRecordId); - } - else if (land.mState==CSMWorld::RecordBase::State_Deleted) - { - /// \todo write record with delete flag + writer.endRecord (record.sRecordId); } } @@ -456,25 +415,16 @@ void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages) { - const CSMWorld::Record& landTexture = + ESM::ESMWriter& writer = mState.getWriter(); + const CSMWorld::Record& landTexture = mDocument.getData().getLandTextures().getRecord (stage); - if (landTexture.mState==CSMWorld::RecordBase::State_Modified || - landTexture.mState==CSMWorld::RecordBase::State_ModifiedOnly) + if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted) { CSMWorld::LandTexture record = landTexture.get(); - - mState.getWriter().startRecord (record.sRecordId); - - mState.getWriter().writeHNString("NAME", record.mId); - - record.save (mState.getWriter()); - - mState.getWriter().endRecord (record.sRecordId); - } - else if (landTexture.mState==CSMWorld::RecordBase::State_Deleted) - { - /// \todo write record with delete flag + writer.startRecord (record.sRecordId); + record.save (writer, landTexture.mState == CSMWorld::RecordBase::State_Deleted); + writer.endRecord (record.sRecordId); } } diff -Nru openmw-0.37.0/apps/opencs/model/doc/savingstages.hpp openmw-0.38.0/apps/opencs/model/doc/savingstages.hpp --- openmw-0.37.0/apps/opencs/model/doc/savingstages.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/doc/savingstages.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -100,26 +100,17 @@ if (CSMWorld::getScopeFromId (mCollection.getRecord (stage).get().mId)!=mScope) return; + ESM::ESMWriter& writer = mState.getWriter(); CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState; + typename CollectionT::ESXRecord record = mCollection.getRecord (stage).get(); - if (state==CSMWorld::RecordBase::State_Modified || - state==CSMWorld::RecordBase::State_ModifiedOnly) + if (state == CSMWorld::RecordBase::State_Modified || + state == CSMWorld::RecordBase::State_ModifiedOnly || + state == CSMWorld::RecordBase::State_Deleted) { - // FIXME: A quick Workaround to support records which should not write - // NAME, including SKIL, MGEF and SCPT. If there are many more - // idcollection records that doesn't use NAME then a more generic - // solution may be required. - uint32_t name = mCollection.getRecord (stage).mModified.sRecordId; - mState.getWriter().startRecord (name); - - if(name != ESM::REC_SKIL && name != ESM::REC_MGEF && name != ESM::REC_SCPT) - mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage)); - mCollection.getRecord (stage).mModified.save (mState.getWriter()); - mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId); - } - else if (state==CSMWorld::RecordBase::State_Deleted) - { - /// \todo write record with delete flag + writer.startRecord (record.sRecordId); + record.save (writer, state == CSMWorld::RecordBase::State_Deleted); + writer.endRecord (record.sRecordId); } } diff -Nru openmw-0.37.0/apps/opencs/model/doc/stage.cpp openmw-0.38.0/apps/opencs/model/doc/stage.cpp --- openmw-0.37.0/apps/opencs/model/doc/stage.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/doc/stage.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,5 +1,3 @@ #include "stage.hpp" CSMDoc::Stage::~Stage() {} - -void CSMDoc::Stage::updateUserSetting (const QString& name, const QStringList& value) {} diff -Nru openmw-0.37.0/apps/opencs/model/doc/stage.hpp openmw-0.38.0/apps/opencs/model/doc/stage.hpp --- openmw-0.37.0/apps/opencs/model/doc/stage.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/doc/stage.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,11 +23,7 @@ virtual void perform (int stage, Messages& messages) = 0; ///< Messages resulting from this stage will be appended to \a messages. - - /// Default-implementation: ignore - virtual void updateUserSetting (const QString& name, const QStringList& value); }; } #endif - diff -Nru openmw-0.37.0/apps/opencs/model/prefs/boolsetting.cpp openmw-0.38.0/apps/opencs/model/prefs/boolsetting.cpp --- openmw-0.37.0/apps/opencs/model/prefs/boolsetting.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/boolsetting.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,47 @@ + +#include "boolsetting.hpp" + +#include +#include + +#include + +#include "category.hpp" +#include "state.hpp" + +CSMPrefs::BoolSetting::BoolSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, bool default_) +: Setting (parent, values, mutex, key, label), mDefault (default_) +{} + +CSMPrefs::BoolSetting& CSMPrefs::BoolSetting::setTooltip (const std::string& tooltip) +{ + mTooltip = tooltip; + return *this; +} + +std::pair CSMPrefs::BoolSetting::makeWidgets (QWidget *parent) +{ + QCheckBox *widget = new QCheckBox (QString::fromUtf8 (getLabel().c_str()), parent); + widget->setCheckState (mDefault ? Qt::Checked : Qt::Unchecked); + + if (!mTooltip.empty()) + { + QString tooltip = QString::fromUtf8 (mTooltip.c_str()); + widget->setToolTip (tooltip); + } + + connect (widget, SIGNAL (stateChanged (int)), this, SLOT (valueChanged (int))); + + return std::make_pair (static_cast (0), widget); +} + +void CSMPrefs::BoolSetting::valueChanged (int value) +{ + { + QMutexLocker lock (getMutex()); + getValues().setBool (getKey(), getParent()->getKey(), value); + } + + getParent()->getState()->update (*this); +} diff -Nru openmw-0.37.0/apps/opencs/model/prefs/boolsetting.hpp openmw-0.38.0/apps/opencs/model/prefs/boolsetting.hpp --- openmw-0.37.0/apps/opencs/model/prefs/boolsetting.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/boolsetting.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,31 @@ +#ifndef CSM_PREFS_BOOLSETTING_H +#define CSM_PREFS_BOOLSETTING_H + +#include "setting.hpp" + +namespace CSMPrefs +{ + class BoolSetting : public Setting + { + Q_OBJECT + + std::string mTooltip; + bool mDefault; + + public: + + BoolSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, bool default_); + + BoolSetting& setTooltip (const std::string& tooltip); + + /// Return label, input widget. + virtual std::pair makeWidgets (QWidget *parent); + + private slots: + + void valueChanged (int value); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/model/prefs/category.cpp openmw-0.38.0/apps/opencs/model/prefs/category.cpp --- openmw-0.37.0/apps/opencs/model/prefs/category.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/category.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,51 @@ + +#include "category.hpp" + +#include + +#include "setting.hpp" +#include "state.hpp" + +CSMPrefs::Category::Category (State *parent, const std::string& key) +: mParent (parent), mKey (key) +{} + +const std::string& CSMPrefs::Category::getKey() const +{ + return mKey; +} + +CSMPrefs::State *CSMPrefs::Category::getState() const +{ + return mParent; +} + +void CSMPrefs::Category::addSetting (Setting *setting) +{ + mSettings.push_back (setting); +} + +CSMPrefs::Category::Iterator CSMPrefs::Category::begin() +{ + return mSettings.begin(); +} + +CSMPrefs::Category::Iterator CSMPrefs::Category::end() +{ + return mSettings.end(); +} + +CSMPrefs::Setting& CSMPrefs::Category::operator[] (const std::string& key) +{ + for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter) + if ((*iter)->getKey()==key) + return **iter; + + throw std::logic_error ("Invalid user setting: " + key); +} + +void CSMPrefs::Category::update() +{ + for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter) + mParent->update (**iter); +} diff -Nru openmw-0.37.0/apps/opencs/model/prefs/category.hpp openmw-0.38.0/apps/opencs/model/prefs/category.hpp --- openmw-0.37.0/apps/opencs/model/prefs/category.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/category.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,45 @@ +#ifndef CSM_PREFS_CATEGORY_H +#define CSM_PREFS_CATEGORY_H + +#include +#include + +namespace CSMPrefs +{ + class State; + class Setting; + + class Category + { + public: + + typedef std::vector Container; + typedef Container::iterator Iterator; + + private: + + State *mParent; + std::string mKey; + Container mSettings; + + public: + + Category (State *parent, const std::string& key); + + const std::string& getKey() const; + + State *getState() const; + + void addSetting (Setting *setting); + + Iterator begin(); + + Iterator end(); + + Setting& operator[] (const std::string& key); + + void update(); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/model/prefs/coloursetting.cpp openmw-0.38.0/apps/opencs/model/prefs/coloursetting.cpp --- openmw-0.37.0/apps/opencs/model/prefs/coloursetting.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/coloursetting.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,52 @@ + +#include "coloursetting.hpp" + +#include +#include + +#include + +#include "../../view/widget/coloreditor.hpp" + +#include "category.hpp" +#include "state.hpp" + +CSMPrefs::ColourSetting::ColourSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, QColor default_) +: Setting (parent, values, mutex, key, label), mDefault (default_) +{} + +CSMPrefs::ColourSetting& CSMPrefs::ColourSetting::setTooltip (const std::string& tooltip) +{ + mTooltip = tooltip; + return *this; +} + +std::pair CSMPrefs::ColourSetting::makeWidgets (QWidget *parent) +{ + QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); + + CSVWidget::ColorEditor *widget = new CSVWidget::ColorEditor (mDefault, parent); + + if (!mTooltip.empty()) + { + QString tooltip = QString::fromUtf8 (mTooltip.c_str()); + label->setToolTip (tooltip); + widget->setToolTip (tooltip); + } + + connect (widget, SIGNAL (pickingFinished()), this, SLOT (valueChanged())); + + return std::make_pair (label, widget); +} + +void CSMPrefs::ColourSetting::valueChanged() +{ + CSVWidget::ColorEditor& widget = dynamic_cast (*sender()); + { + QMutexLocker lock (getMutex()); + getValues().setString (getKey(), getParent()->getKey(), widget.color().name().toUtf8().data()); + } + + getParent()->getState()->update (*this); +} diff -Nru openmw-0.37.0/apps/opencs/model/prefs/coloursetting.hpp openmw-0.38.0/apps/opencs/model/prefs/coloursetting.hpp --- openmw-0.37.0/apps/opencs/model/prefs/coloursetting.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/coloursetting.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,34 @@ +#ifndef CSM_PREFS_COLOURSETTING_H +#define CSM_PREFS_COLOURSETTING_H + +#include "setting.hpp" + +#include + +namespace CSMPrefs +{ + class ColourSetting : public Setting + { + Q_OBJECT + + std::string mTooltip; + QColor mDefault; + + public: + + ColourSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, + QColor default_); + + ColourSetting& setTooltip (const std::string& tooltip); + + /// Return label, input widget. + virtual std::pair makeWidgets (QWidget *parent); + + private slots: + + void valueChanged(); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/model/prefs/doublesetting.cpp openmw-0.38.0/apps/opencs/model/prefs/doublesetting.cpp --- openmw-0.37.0/apps/opencs/model/prefs/doublesetting.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/doublesetting.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,75 @@ + +#include "doublesetting.hpp" + +#include + +#include +#include +#include + +#include + +#include "category.hpp" +#include "state.hpp" + +CSMPrefs::DoubleSetting::DoubleSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, double default_) +: Setting (parent, values, mutex, key, label), + mMin (0), mMax (std::numeric_limits::max()), + mDefault (default_) +{} + +CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setRange (double min, double max) +{ + mMin = min; + mMax = max; + return *this; +} + +CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMin (double min) +{ + mMin = min; + return *this; +} + +CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMax (double max) +{ + mMax = max; + return *this; +} + +CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setTooltip (const std::string& tooltip) +{ + mTooltip = tooltip; + return *this; +} + +std::pair CSMPrefs::DoubleSetting::makeWidgets (QWidget *parent) +{ + QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); + + QDoubleSpinBox *widget = new QDoubleSpinBox (parent); + widget->setRange (mMin, mMax); + widget->setValue (mDefault); + + if (!mTooltip.empty()) + { + QString tooltip = QString::fromUtf8 (mTooltip.c_str()); + label->setToolTip (tooltip); + widget->setToolTip (tooltip); + } + + connect (widget, SIGNAL (valueChanged (double)), this, SLOT (valueChanged (double))); + + return std::make_pair (label, widget); +} + +void CSMPrefs::DoubleSetting::valueChanged (double value) +{ + { + QMutexLocker lock (getMutex()); + getValues().setFloat (getKey(), getParent()->getKey(), value); + } + + getParent()->getState()->update (*this); +} diff -Nru openmw-0.37.0/apps/opencs/model/prefs/doublesetting.hpp openmw-0.38.0/apps/opencs/model/prefs/doublesetting.hpp --- openmw-0.37.0/apps/opencs/model/prefs/doublesetting.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/doublesetting.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,41 @@ +#ifndef CSM_PREFS_DOUBLESETTING_H +#define CSM_PREFS_DOUBLESETTING_H + +#include "setting.hpp" + +namespace CSMPrefs +{ + class DoubleSetting : public Setting + { + Q_OBJECT + + double mMin; + double mMax; + std::string mTooltip; + double mDefault; + + public: + + DoubleSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, + double default_); + + // defaults to [0, std::numeric_limits::max()] + DoubleSetting& setRange (double min, double max); + + DoubleSetting& setMin (double min); + + DoubleSetting& setMax (double max); + + DoubleSetting& setTooltip (const std::string& tooltip); + + /// Return label, input widget. + virtual std::pair makeWidgets (QWidget *parent); + + private slots: + + void valueChanged (double value); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/model/prefs/enumsetting.cpp openmw-0.38.0/apps/opencs/model/prefs/enumsetting.cpp --- openmw-0.37.0/apps/opencs/model/prefs/enumsetting.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/enumsetting.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,112 @@ + +#include "enumsetting.hpp" + +#include +#include +#include + +#include + +#include "category.hpp" +#include "state.hpp" + + +CSMPrefs::EnumValue::EnumValue (const std::string& value, const std::string& tooltip) +: mValue (value), mTooltip (tooltip) +{} + +CSMPrefs::EnumValue::EnumValue (const char *value) +: mValue (value) +{} + + +CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValues& values) +{ + mValues.insert (mValues.end(), values.mValues.begin(), values.mValues.end()); + return *this; +} + +CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValue& value) +{ + mValues.push_back (value); + return *this; +} + +CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const std::string& value, const std::string& tooltip) +{ + mValues.push_back (EnumValue (value, tooltip)); + return *this; +} + + +CSMPrefs::EnumSetting::EnumSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, const EnumValue& default_) +: Setting (parent, values, mutex, key, label), mDefault (default_) +{} + +CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::setTooltip (const std::string& tooltip) +{ + mTooltip = tooltip; + return *this; +} + +CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValues (const EnumValues& values) +{ + mValues.add (values); + return *this; +} + +CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const EnumValue& value) +{ + mValues.add (value); + return *this; +} + +CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const std::string& value, const std::string& tooltip) +{ + mValues.add (value, tooltip); + return *this; +} + +std::pair CSMPrefs::EnumSetting::makeWidgets (QWidget *parent) +{ + QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); + + QComboBox *widget = new QComboBox (parent); + + int index = 0; + + for (int i=0; i (mValues.mValues.size()); ++i) + { + if (mDefault.mValue==mValues.mValues[i].mValue) + index = i; + + widget->addItem (QString::fromUtf8 (mValues.mValues[i].mValue.c_str())); + + if (!mValues.mValues[i].mTooltip.empty()) + widget->setItemData (i, QString::fromUtf8 (mValues.mValues[i].mTooltip.c_str()), + Qt::ToolTipRole); + } + + widget->setCurrentIndex (index); + + if (!mTooltip.empty()) + { + QString tooltip = QString::fromUtf8 (mTooltip.c_str()); + label->setToolTip (tooltip); + } + + connect (widget, SIGNAL (currentIndexChanged (int)), this, SLOT (valueChanged (int))); + + return std::make_pair (label, widget); +} + +void CSMPrefs::EnumSetting::valueChanged (int value) +{ + { + QMutexLocker lock (getMutex()); + getValues().setString (getKey(), getParent()->getKey(), mValues.mValues.at (value).mValue); + } + + getParent()->getState()->update (*this); +} diff -Nru openmw-0.37.0/apps/opencs/model/prefs/enumsetting.hpp openmw-0.38.0/apps/opencs/model/prefs/enumsetting.hpp --- openmw-0.37.0/apps/opencs/model/prefs/enumsetting.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/enumsetting.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,62 @@ +#ifndef CSM_PREFS_ENUMSETTING_H +#define CSM_PREFS_ENUMSETTING_H + +#include + +#include "setting.hpp" + +namespace CSMPrefs +{ + struct EnumValue + { + std::string mValue; + std::string mTooltip; + + EnumValue (const std::string& value, const std::string& tooltip = ""); + + EnumValue (const char *value); + }; + + struct EnumValues + { + std::vector mValues; + + EnumValues& add (const EnumValues& values); + + EnumValues& add (const EnumValue& value); + + EnumValues& add (const std::string& value, const std::string& tooltip); + }; + + class EnumSetting : public Setting + { + Q_OBJECT + + std::string mTooltip; + EnumValue mDefault; + EnumValues mValues; + + public: + + EnumSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, + const EnumValue& default_); + + EnumSetting& setTooltip (const std::string& tooltip); + + EnumSetting& addValues (const EnumValues& values); + + EnumSetting& addValue (const EnumValue& value); + + EnumSetting& addValue (const std::string& value, const std::string& tooltip); + + /// Return label, input widget. + virtual std::pair makeWidgets (QWidget *parent); + + private slots: + + void valueChanged (int value); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/model/prefs/intsetting.cpp openmw-0.38.0/apps/opencs/model/prefs/intsetting.cpp --- openmw-0.37.0/apps/opencs/model/prefs/intsetting.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/intsetting.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,74 @@ + +#include "intsetting.hpp" + +#include + +#include +#include +#include + +#include + +#include "category.hpp" +#include "state.hpp" + +CSMPrefs::IntSetting::IntSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, int default_) +: Setting (parent, values, mutex, key, label), mMin (0), mMax (std::numeric_limits::max()), + mDefault (default_) +{} + +CSMPrefs::IntSetting& CSMPrefs::IntSetting::setRange (int min, int max) +{ + mMin = min; + mMax = max; + return *this; +} + +CSMPrefs::IntSetting& CSMPrefs::IntSetting::setMin (int min) +{ + mMin = min; + return *this; +} + +CSMPrefs::IntSetting& CSMPrefs::IntSetting::setMax (int max) +{ + mMax = max; + return *this; +} + +CSMPrefs::IntSetting& CSMPrefs::IntSetting::setTooltip (const std::string& tooltip) +{ + mTooltip = tooltip; + return *this; +} + +std::pair CSMPrefs::IntSetting::makeWidgets (QWidget *parent) +{ + QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); + + QSpinBox *widget = new QSpinBox (parent); + widget->setRange (mMin, mMax); + widget->setValue (mDefault); + + if (!mTooltip.empty()) + { + QString tooltip = QString::fromUtf8 (mTooltip.c_str()); + label->setToolTip (tooltip); + widget->setToolTip (tooltip); + } + + connect (widget, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); + + return std::make_pair (label, widget); +} + +void CSMPrefs::IntSetting::valueChanged (int value) +{ + { + QMutexLocker lock (getMutex()); + getValues().setInt (getKey(), getParent()->getKey(), value); + } + + getParent()->getState()->update (*this); +} diff -Nru openmw-0.37.0/apps/opencs/model/prefs/intsetting.hpp openmw-0.38.0/apps/opencs/model/prefs/intsetting.hpp --- openmw-0.37.0/apps/opencs/model/prefs/intsetting.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/intsetting.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,40 @@ +#ifndef CSM_PREFS_INTSETTING_H +#define CSM_PREFS_INTSETTING_H + +#include "setting.hpp" + +namespace CSMPrefs +{ + class IntSetting : public Setting + { + Q_OBJECT + + int mMin; + int mMax; + std::string mTooltip; + int mDefault; + + public: + + IntSetting (Category *parent, Settings::Manager *values, + QMutex *mutex, const std::string& key, const std::string& label, int default_); + + // defaults to [0, std::numeric_limits::max()] + IntSetting& setRange (int min, int max); + + IntSetting& setMin (int min); + + IntSetting& setMax (int max); + + IntSetting& setTooltip (const std::string& tooltip); + + /// Return label, input widget. + virtual std::pair makeWidgets (QWidget *parent); + + private slots: + + void valueChanged (int value); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/model/prefs/setting.cpp openmw-0.38.0/apps/opencs/model/prefs/setting.cpp --- openmw-0.37.0/apps/opencs/model/prefs/setting.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/setting.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,97 @@ + +#include "setting.hpp" + +#include +#include + +#include "category.hpp" +#include "state.hpp" + +Settings::Manager& CSMPrefs::Setting::getValues() +{ + return *mValues; +} + +QMutex *CSMPrefs::Setting::getMutex() +{ + return mMutex; +} + +CSMPrefs::Setting::Setting (Category *parent, Settings::Manager *values, QMutex *mutex, + const std::string& key, const std::string& label) +: QObject (parent->getState()), mParent (parent), mValues (values), mMutex (mutex), mKey (key), + mLabel (label) +{} + +CSMPrefs::Setting:: ~Setting() {} + +std::pair CSMPrefs::Setting::makeWidgets (QWidget *parent) +{ + return std::pair (0, 0); +} + +const CSMPrefs::Category *CSMPrefs::Setting::getParent() const +{ + return mParent; +} + +const std::string& CSMPrefs::Setting::getKey() const +{ + return mKey; +} + +const std::string& CSMPrefs::Setting::getLabel() const +{ + return mLabel; +} + +int CSMPrefs::Setting::toInt() const +{ + QMutexLocker lock (mMutex); + return mValues->getInt (mKey, mParent->getKey()); +} + +double CSMPrefs::Setting::toDouble() const +{ + QMutexLocker lock (mMutex); + return mValues->getFloat (mKey, mParent->getKey()); +} + +std::string CSMPrefs::Setting::toString() const +{ + QMutexLocker lock (mMutex); + return mValues->getString (mKey, mParent->getKey()); +} + +bool CSMPrefs::Setting::isTrue() const +{ + QMutexLocker lock (mMutex); + return mValues->getBool (mKey, mParent->getKey()); +} + +QColor CSMPrefs::Setting::toColor() const +{ + // toString() handles lock + return QColor (QString::fromUtf8 (toString().c_str())); +} + +bool CSMPrefs::operator== (const Setting& setting, const std::string& key) +{ + std::string fullKey = setting.getParent()->getKey() + "/" + setting.getKey(); + return fullKey==key; +} + +bool CSMPrefs::operator== (const std::string& key, const Setting& setting) +{ + return setting==key; +} + +bool CSMPrefs::operator!= (const Setting& setting, const std::string& key) +{ + return !(setting==key); +} + +bool CSMPrefs::operator!= (const std::string& key, const Setting& setting) +{ + return !(key==setting); +} diff -Nru openmw-0.37.0/apps/opencs/model/prefs/setting.hpp openmw-0.38.0/apps/opencs/model/prefs/setting.hpp --- openmw-0.37.0/apps/opencs/model/prefs/setting.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/setting.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,74 @@ +#ifndef CSM_PREFS_SETTING_H +#define CSM_PREFS_SETTING_H + +#include +#include + +#include + +class QWidget; +class QColor; +class QMutex; + +namespace Settings +{ + class Manager; +} + +namespace CSMPrefs +{ + class Category; + + class Setting : public QObject + { + Q_OBJECT + + Category *mParent; + Settings::Manager *mValues; + QMutex *mMutex; + std::string mKey; + std::string mLabel; + + protected: + + Settings::Manager& getValues(); + + QMutex *getMutex(); + + public: + + Setting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label); + + virtual ~Setting(); + + /// Return label, input widget. + /// + /// \note first can be a 0-pointer, which means that the label is part of the input + /// widget. + virtual std::pair makeWidgets (QWidget *parent); + + const Category *getParent() const; + + const std::string& getKey() const; + + const std::string& getLabel() const; + + int toInt() const; + + double toDouble() const; + + std::string toString() const; + + bool isTrue() const; + + QColor toColor() const; + }; + + // note: fullKeys have the format categoryKey/settingKey + bool operator== (const Setting& setting, const std::string& fullKey); + bool operator== (const std::string& fullKey, const Setting& setting); + bool operator!= (const Setting& setting, const std::string& fullKey); + bool operator!= (const std::string& fullKey, const Setting& setting); +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/model/prefs/state.cpp openmw-0.38.0/apps/opencs/model/prefs/state.cpp --- openmw-0.37.0/apps/opencs/model/prefs/state.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/state.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,392 @@ + +#include "state.hpp" + +#include +#include +#include + +#include "intsetting.hpp" +#include "doublesetting.hpp" +#include "boolsetting.hpp" +#include "coloursetting.hpp" + +CSMPrefs::State *CSMPrefs::State::sThis = 0; + +void CSMPrefs::State::load() +{ + // default settings file + boost::filesystem::path local = mConfigurationManager.getLocalPath() / mConfigFile; + boost::filesystem::path global = mConfigurationManager.getGlobalPath() / mConfigFile; + + if (boost::filesystem::exists (local)) + mSettings.loadDefault (local.string()); + else if (boost::filesystem::exists (global)) + mSettings.loadDefault (global.string()); + else + throw std::runtime_error ("No default settings file found! Make sure the file \"openmw-cs.cfg\" was properly installed."); + + // user settings file + boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile; + + if (boost::filesystem::exists (user)) + mSettings.loadUser (user.string()); +} + +void CSMPrefs::State::declare() +{ + declareCategory ("Windows"); + declareInt ("default-width", "Default window width", 800). + setTooltip ("Newly opened top-level windows will open with this width."). + setMin (80); + declareInt ("default-height", "Default window height", 600). + setTooltip ("Newly opened top-level windows will open with this height."). + setMin (80); + declareBool ("show-statusbar", "Show Status Bar", true). + setTooltip ("If a newly open top level window is showing status bars or not. " + " Note that this does not affect existing windows."); + declareSeparator(); + declareBool ("reuse", "Reuse Subviews", true). + setTooltip ("When a new subview is requested and a matching subview already " + " exist, do not open a new subview and use the existing one instead."); + declareInt ("max-subviews", "Maximum number of subviews per top-level window", 256). + setTooltip ("If the maximum number is reached and a new subview is opened " + "it will be placed into a new top-level window."). + setRange (1, 256); + declareBool ("hide-subview", "Hide single subview", false). + setTooltip ("When a view contains only a single subview, hide the subview title " + "bar and if this subview is closed also close the view (unless it is the last " + "view for this document)"); + declareInt ("minimum-width", "Minimum subview width", 325). + setTooltip ("Minimum width of subviews."). + setRange (50, 10000); + declareSeparator(); + EnumValue scrollbarOnly ("Scrollbar Only", "Simple addition of scrollbars, the view window " + "does not grow automatically."); + declareEnum ("mainwindow-scrollbar", "Horizontal scrollbar mode for main window.", scrollbarOnly). + addValue (scrollbarOnly). + addValue ("Grow Only", "The view window grows as subviews are added. No scrollbars."). + addValue ("Grow then Scroll", "The view window grows. The scrollbar appears once it cannot grow any further."); + declareBool ("grow-limit", "Grow Limit Screen", false). + setTooltip ("When \"Grow then Scroll\" option is selected, the window size grows to" + " the width of the virtual desktop. \nIf this option is selected the the window growth" + "is limited to the current screen."); + + declareCategory ("Records"); + EnumValue iconAndText ("Icon and Text"); + EnumValues recordValues; + recordValues.add (iconAndText).add ("Icon Only").add ("Text Only"); + declareEnum ("status-format", "Modification status display format", iconAndText). + addValues (recordValues); + declareEnum ("type-format", "ID type display format", iconAndText). + addValues (recordValues); + + declareCategory ("ID Tables"); + EnumValue inPlaceEdit ("Edit in Place", "Edit the clicked cell"); + EnumValue editRecord ("Edit Record", "Open a dialogue subview for the clicked record"); + EnumValue view ("View", "Open a scene subview for the clicked record (not available everywhere)"); + EnumValue editRecordAndClose ("Edit Record and Close"); + EnumValues doubleClickValues; + doubleClickValues.add (inPlaceEdit).add (editRecord).add (view).add ("Revert"). + add ("Delete").add (editRecordAndClose). + add ("View and Close", "Open a scene subview for the clicked record and close the table subview"); + declareEnum ("double", "Double Click", inPlaceEdit).addValues (doubleClickValues); + declareEnum ("double-s", "Shift Double Click", editRecord).addValues (doubleClickValues); + declareEnum ("double-c", "Control Double Click", view).addValues (doubleClickValues); + declareEnum ("double-sc", "Shift Control Double Click", editRecordAndClose).addValues (doubleClickValues); + declareSeparator(); + EnumValue jumpAndSelect ("Jump and Select", "Scroll new record into view and make it the selection"); + declareEnum ("jump-to-added", "Action on adding or cloning a record", jumpAndSelect). + addValue (jumpAndSelect). + addValue ("Jump Only", "Scroll new record into view"). + addValue ("No Jump", "No special action"); + declareBool ("extended-config", + "Manually specify affected record types for an extended delete/revert", false). + setTooltip ("Delete and revert commands have an extended form that also affects " + "associated records.\n\n" + "If this option is enabled, types of affected records are selected " + "manually before a command execution.\nOtherwise, all associated " + "records are deleted/reverted immediately."); + + declareCategory ("ID Dialogues"); + declareBool ("toolbar", "Show toolbar", true); + + declareCategory ("Reports"); + EnumValue actionNone ("None"); + EnumValue actionEdit ("Edit", "Open a table or dialogue suitable for addressing the listed report"); + EnumValue actionRemove ("Remove", "Remove the report from the report table"); + EnumValue actionEditAndRemove ("Edit And Remove", "Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table"); + EnumValues reportValues; + reportValues.add (actionNone).add (actionEdit).add (actionRemove).add (actionEditAndRemove); + declareEnum ("double", "Double Click", actionEdit).addValues (reportValues); + declareEnum ("double-s", "Shift Double Click", actionRemove).addValues (reportValues); + declareEnum ("double-c", "Control Double Click", actionEditAndRemove).addValues (reportValues); + declareEnum ("double-sc", "Shift Control Double Click", actionNone).addValues (reportValues); + + declareCategory ("Search & Replace"); + declareInt ("char-before", "Characters before search string", 10). + setTooltip ("Maximum number of character to display in search result before the searched text"); + declareInt ("char-after", "Characters after search string", 10). + setTooltip ("Maximum number of character to display in search result after the searched text"); + declareBool ("auto-delete", "Delete row from result table after a successful replace", true); + + declareCategory ("Scripts"); + declareBool ("show-linenum", "Show Line Numbers", true). + setTooltip ("Show line numbers to the left of the script editor window." + "The current row and column numbers of the text cursor are shown at the bottom."); + declareBool ("mono-font", "Use monospace font", true); + EnumValue warningsNormal ("Normal", "Report warnings as warning"); + declareEnum ("warnings", "Warning Mode", warningsNormal). + addValue ("Ignore", "Do not report warning"). + addValue (warningsNormal). + addValue ("Strcit", "Promote warning to an error"); + declareBool ("toolbar", "Show toolbar", true); + declareInt ("compile-delay", "Delay between updating of source errors", 100). + setTooltip ("Delay in milliseconds"). + setRange (0, 10000); + declareInt ("error-height", "Initial height of the error panel", 100). + setRange (100, 10000); + declareSeparator(); + declareColour ("colour-int", "Highlight Colour: Integer Literals", QColor ("darkmagenta")); + declareColour ("colour-float", "Highlight Colour: Float Literals", QColor ("magenta")); + declareColour ("colour-name", "Highlight Colour: Names", QColor ("grey")); + declareColour ("colour-keyword", "Highlight Colour: Keywords", QColor ("red")); + declareColour ("colour-special", "Highlight Colour: Special Characters", QColor ("darkorange")); + declareColour ("colour-comment", "Highlight Colour: Comments", QColor ("green")); + declareColour ("colour-id", "Highlight Colour: IDs", QColor ("blue")); + + declareCategory ("General Input"); + declareBool ("cycle", "Cyclic next/previous", false). + setTooltip ("When using next/previous functions at the last/first item of a " + "list go to the first/last item"); + + declareCategory ("3D Scene Input"); + EnumValue left ("Left Mouse-Button"); + EnumValue cLeft ("Ctrl-Left Mouse-Button"); + EnumValue right ("Right Mouse-Button"); + EnumValue cRight ("Ctrl-Right Mouse-Button"); + EnumValue middle ("Middle Mouse-Button"); + EnumValue cMiddle ("Ctrl-Middle Mouse-Button"); + EnumValues inputButtons; + inputButtons.add (left).add (cLeft).add (right).add (cRight).add (middle).add (cMiddle); + declareEnum ("p-navi", "Primary Camera Navigation Button", left).addValues (inputButtons); + declareEnum ("s-navi", "Secondary Camera Navigation Button", cLeft).addValues (inputButtons); + declareEnum ("p-edit", "Primary Editing Button", right).addValues (inputButtons); + declareEnum ("s-edit", "Secondary Editing Button", cRight).addValues (inputButtons); + declareEnum ("p-select", "Primary Selection Button", middle).addValues (inputButtons); + declareEnum ("s-select", "Secondary Selection Button", cMiddle).addValues (inputButtons); + declareSeparator(); + declareBool ("context-select", "Context Sensitive Selection", false); + declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0). + setRange (0.001, 100.0); + declareDouble ("drag-wheel-factor", "Mouse wheel sensitivity during drag operations", 1.0). + setRange (0.001, 100.0); + declareDouble ("drag-shift-factor", + "Shift-acceleration factor during drag operations", 4.0). + setTooltip ("Acceleration factor during drag operations while holding down shift"). + setRange (0.001, 100.0); + + declareCategory ("Tooltips"); + declareBool ("scene", "Show Tooltips in 3D scenes", true); + declareBool ("scene-hide-basic", "Hide basic 3D scenes tooltips", false); + declareInt ("scene-delay", "Tooltip delay in milliseconds", 500). + setMin (1); +} + +void CSMPrefs::State::declareCategory (const std::string& key) +{ + std::map::iterator iter = mCategories.find (key); + + if (iter!=mCategories.end()) + { + mCurrentCategory = iter; + } + else + { + mCurrentCategory = + mCategories.insert (std::make_pair (key, Category (this, key))).first; + } +} + +CSMPrefs::IntSetting& CSMPrefs::State::declareInt (const std::string& key, + const std::string& label, int default_) +{ + if (mCurrentCategory==mCategories.end()) + throw std::logic_error ("no category for setting"); + + std::ostringstream stream; + stream << default_; + setDefault (key, stream.str()); + + default_ = mSettings.getInt (key, mCurrentCategory->second.getKey()); + + CSMPrefs::IntSetting *setting = + new CSMPrefs::IntSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, + default_); + + mCurrentCategory->second.addSetting (setting); + + return *setting; +} + +CSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble (const std::string& key, + const std::string& label, double default_) +{ + if (mCurrentCategory==mCategories.end()) + throw std::logic_error ("no category for setting"); + + std::ostringstream stream; + stream << default_; + setDefault (key, stream.str()); + + default_ = mSettings.getFloat (key, mCurrentCategory->second.getKey()); + + CSMPrefs::DoubleSetting *setting = + new CSMPrefs::DoubleSetting (&mCurrentCategory->second, &mSettings, &mMutex, + key, label, default_); + + mCurrentCategory->second.addSetting (setting); + + return *setting; +} + +CSMPrefs::BoolSetting& CSMPrefs::State::declareBool (const std::string& key, + const std::string& label, bool default_) +{ + if (mCurrentCategory==mCategories.end()) + throw std::logic_error ("no category for setting"); + + setDefault (key, default_ ? "true" : "false"); + + default_ = mSettings.getBool (key, mCurrentCategory->second.getKey()); + + CSMPrefs::BoolSetting *setting = + new CSMPrefs::BoolSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, + default_); + + mCurrentCategory->second.addSetting (setting); + + return *setting; +} + +CSMPrefs::EnumSetting& CSMPrefs::State::declareEnum (const std::string& key, + const std::string& label, EnumValue default_) +{ + if (mCurrentCategory==mCategories.end()) + throw std::logic_error ("no category for setting"); + + setDefault (key, default_.mValue); + + default_.mValue = mSettings.getString (key, mCurrentCategory->second.getKey()); + + CSMPrefs::EnumSetting *setting = + new CSMPrefs::EnumSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, + default_); + + mCurrentCategory->second.addSetting (setting); + + return *setting; +} + +CSMPrefs::ColourSetting& CSMPrefs::State::declareColour (const std::string& key, + const std::string& label, QColor default_) +{ + if (mCurrentCategory==mCategories.end()) + throw std::logic_error ("no category for setting"); + + setDefault (key, default_.name().toUtf8().data()); + + default_.setNamedColor (QString::fromUtf8 (mSettings.getString (key, mCurrentCategory->second.getKey()).c_str())); + + CSMPrefs::ColourSetting *setting = + new CSMPrefs::ColourSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, + default_); + + mCurrentCategory->second.addSetting (setting); + + return *setting; +} + +void CSMPrefs::State::declareSeparator() +{ + if (mCurrentCategory==mCategories.end()) + throw std::logic_error ("no category for setting"); + + CSMPrefs::Setting *setting = + new CSMPrefs::Setting (&mCurrentCategory->second, &mSettings, &mMutex, "", ""); + + mCurrentCategory->second.addSetting (setting); +} + +void CSMPrefs::State::setDefault (const std::string& key, const std::string& default_) +{ + Settings::CategorySetting fullKey (mCurrentCategory->second.getKey(), key); + + Settings::CategorySettingValueMap::iterator iter = + mSettings.mDefaultSettings.find (fullKey); + + if (iter==mSettings.mDefaultSettings.end()) + mSettings.mDefaultSettings.insert (std::make_pair (fullKey, default_)); +} + +CSMPrefs::State::State (const Files::ConfigurationManager& configurationManager) +: mConfigFile ("openmw-cs.cfg"), mConfigurationManager (configurationManager), + mCurrentCategory (mCategories.end()) +{ + if (sThis) + throw std::logic_error ("An instance of CSMPRefs::State already exists"); + + load(); + declare(); + + sThis = this; +} + +CSMPrefs::State::~State() +{ + sThis = 0; +} + +void CSMPrefs::State::save() +{ + boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile; + mSettings.saveUser (user.string()); +} + +CSMPrefs::State::Iterator CSMPrefs::State::begin() +{ + return mCategories.begin(); +} + +CSMPrefs::State::Iterator CSMPrefs::State::end() +{ + return mCategories.end(); +} + +CSMPrefs::Category& CSMPrefs::State::operator[] (const std::string& key) +{ + Iterator iter = mCategories.find (key); + + if (iter==mCategories.end()) + throw std::logic_error ("Invalid user settings category: " + key); + + return iter->second; +} + +void CSMPrefs::State::update (const Setting& setting) +{ + emit (settingChanged (&setting)); +} + +CSMPrefs::State& CSMPrefs::State::get() +{ + if (!sThis) + throw std::logic_error ("No instance of CSMPrefs::State"); + + return *sThis; +} + + +CSMPrefs::State& CSMPrefs::get() +{ + return State::get(); +} diff -Nru openmw-0.37.0/apps/opencs/model/prefs/state.hpp openmw-0.38.0/apps/opencs/model/prefs/state.hpp --- openmw-0.37.0/apps/opencs/model/prefs/state.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/prefs/state.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,105 @@ +#ifndef CSV_PREFS_STATE_H +#define CSM_PREFS_STATE_H + +#include +#include + +#include +#include + +#ifndef Q_MOC_RUN +#include +#endif + +#include + +#include "category.hpp" +#include "setting.hpp" +#include "enumsetting.hpp" + +class QColor; + +namespace CSMPrefs +{ + class IntSetting; + class DoubleSetting; + class BoolSetting; + class ColourSetting; + + /// \brief User settings state + /// + /// \note Access to the user settings is thread-safe once all declarations and loading has + /// been completed. + class State : public QObject + { + Q_OBJECT + + static State *sThis; + + public: + + typedef std::map Collection; + typedef Collection::iterator Iterator; + + private: + + const std::string mConfigFile; + const Files::ConfigurationManager& mConfigurationManager; + Settings::Manager mSettings; + Collection mCategories; + Iterator mCurrentCategory; + QMutex mMutex; + + // not implemented + State (const State&); + State& operator= (const State&); + + private: + + void load(); + + void declare(); + + void declareCategory (const std::string& key); + + IntSetting& declareInt (const std::string& key, const std::string& label, int default_); + DoubleSetting& declareDouble (const std::string& key, const std::string& label, double default_); + + BoolSetting& declareBool (const std::string& key, const std::string& label, bool default_); + + EnumSetting& declareEnum (const std::string& key, const std::string& label, EnumValue default_); + + ColourSetting& declareColour (const std::string& key, const std::string& label, QColor default_); + + void declareSeparator(); + + void setDefault (const std::string& key, const std::string& default_); + + public: + + State (const Files::ConfigurationManager& configurationManager); + + ~State(); + + void save(); + + Iterator begin(); + + Iterator end(); + + Category& operator[](const std::string& key); + + void update (const Setting& setting); + + static State& get(); + + signals: + + void settingChanged (const CSMPrefs::Setting *setting); + }; + + // convenience function + State& get(); +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/model/settings/connector.cpp openmw-0.38.0/apps/opencs/model/settings/connector.cpp --- openmw-0.37.0/apps/opencs/model/settings/connector.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/settings/connector.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ -#include "connector.hpp" -#include "../../view/settings/view.hpp" -#include "../../view/settings/page.hpp" - -CSMSettings::Connector::Connector(CSVSettings::View *master, - QObject *parent) - : QObject(parent), mMasterView (master) -{} - -void CSMSettings::Connector::addSlaveView (CSVSettings::View *view, - QList &masterProxyValues) -{ - mSlaveViews.append (view); - - mProxyListMap[view->viewKey()].append (masterProxyValues); -} - -QList CSMSettings::Connector::getSlaveViewValues() const -{ - QList list; - - foreach (const CSVSettings::View *view, mSlaveViews) - list.append (view->selectedValues()); - - return list; -} - -bool CSMSettings::Connector::proxyListsMatch ( - const QList &list1, - const QList &list2) const -{ - bool success = true; - - for (int i = 0; i < list1.size(); i++) - { - success = stringListsMatch (list1.at(i), list2.at(i)); - - if (!success) - break; - } - return success; -} - -void CSMSettings::Connector::slotUpdateMaster() const -{ - //list of the current values for each slave. - QList slaveValueList = getSlaveViewValues(); - - int masterColumn = -1; - - /* - * A row in the master view is one of the values in the - * master view's data model. This corresponds directly to the number of - * values in a proxy list contained in the ProxyListMap member. - * Thus, we iterate each "column" in the master proxy list - * (one for each vlaue in the master. Each column represents - * one master value's corresponding list of slave values. We examine - * each master value's list, comparing it to the current slave value list, - * stopping when we find a match using proxyListsMatch(). - * - * If no match is found, clear the master view's value - */ - - for (int i = 0; i < mMasterView->rowCount(); i++) - { - QList proxyValueList; - - foreach (const QString &settingKey, mProxyListMap.keys()) - { - // append the proxy value list stored in the i'th column - // for each setting key. A setting key is the id of the setting - // in page.name format. - proxyValueList.append (mProxyListMap.value(settingKey).at(i)); - } - - if (proxyListsMatch (slaveValueList, proxyValueList)) - { - masterColumn = i; - break; - } - } - - QString masterValue = mMasterView->value (masterColumn); - - mMasterView->setSelectedValue (masterValue); -} - -void CSMSettings::Connector::slotUpdateSlaves() const -{ - int row = mMasterView->currentIndex(); - - if (row == -1) - return; - - //iterate the proxy lists for the chosen master index - //and pass the list to each slave for updating - for (int i = 0; i < mSlaveViews.size(); i++) - { - QList proxyList = - mProxyListMap.value(mSlaveViews.at(i)->viewKey()); - - mSlaveViews.at(i)->setSelectedValues (proxyList.at(row)); - } -} - -bool CSMSettings::Connector::stringListsMatch ( - const QStringList &list1, - const QStringList &list2) const -{ - //returns a "sloppy" match, verifying that each list contains all the same - //items, though not necessarily in the same order. - - if (list1.size() != list2.size()) - return false; - - QStringList tempList(list2); - - //iterate each value in the list, removing one occurrence of the value in - //the other list. If no corresponding value is found, test fails - foreach (const QString &value, list1) - { - if (!tempList.contains(value)) - return false; - - tempList.removeOne(value); - } - return true; -} diff -Nru openmw-0.37.0/apps/opencs/model/settings/connector.hpp openmw-0.38.0/apps/opencs/model/settings/connector.hpp --- openmw-0.37.0/apps/opencs/model/settings/connector.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/settings/connector.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -#ifndef CSMSETTINGS_CONNECTOR_HPP -#define CSMSETTINGS_CONNECTOR_HPP - -#include -#include -#include -#include - -#include "support.hpp" - -namespace CSVSettings { - class View; -} - -namespace CSMSettings { - - class Connector : public QObject - { - Q_OBJECT - - CSVSettings::View *mMasterView; - - ///map using the view pointer as a key to it's index value - QList mSlaveViews; - - ///list of proxy values for each master value. - ///value list order is indexed to the master value index. - QMap < QString, QList > mProxyListMap; - - public: - explicit Connector(CSVSettings::View *master, - QObject *parent = 0); - - ///Set the view which acts as a proxy for other setting views - void setMasterView (CSVSettings::View *view); - - ///Add a view to be updated / update to the master - void addSlaveView (CSVSettings::View *view, - QList &masterProxyValues); - - private: - - ///loosely matches lists of proxy values across registered slaves - ///against a proxy value list for a given master value - bool proxyListsMatch (const QList &list1, - const QList &list2) const; - - ///loosely matches two string lists - bool stringListsMatch (const QStringList &list1, - const QStringList &list2) const; - - ///retrieves current values of registered slave views - QList getSlaveViewValues() const; - - public slots: - - ///updates slave views with proxy values associated with current - ///master value - void slotUpdateSlaves() const; - - ///updates master value associated with the currently selected - ///slave values, if applicable. - void slotUpdateMaster() const; - }; -} - -#endif // CSMSETTINGS_CONNECTOR_HPP diff -Nru openmw-0.37.0/apps/opencs/model/settings/setting.cpp openmw-0.38.0/apps/opencs/model/settings/setting.cpp --- openmw-0.37.0/apps/opencs/model/settings/setting.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/settings/setting.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,414 +0,0 @@ -#include "setting.hpp" -#include "support.hpp" - -CSMSettings::Setting::Setting(SettingType typ, const QString &settingName, - const QString &pageName, const QString& label) -: mIsEditorSetting (true) -{ - buildDefaultSetting(); - - int settingType = static_cast (typ); - - //even-numbered setting types are multi-valued - if ((settingType % 2) == 0) - setProperty (Property_IsMultiValue, QVariant(true).toString()); - - //view type is related to setting type by an order of magnitude - setProperty (Property_SettingType, QVariant (settingType).toString()); - setProperty (Property_Page, pageName); - setProperty (Property_Name, settingName); - setProperty (Property_Label, label.isEmpty() ? settingName : label); -} - -void CSMSettings::Setting::buildDefaultSetting() -{ - int arrLen = sizeof(sPropertyDefaults) / sizeof (*sPropertyDefaults); - - for (int i = 0; i < arrLen; i++) - { - QStringList propertyList; - - if (i list; - - foreach (const QString &val, vals) - list << (QStringList() << val); - - mProxies [setting->page() + '/' + setting->name()] = list; -} - -void CSMSettings::Setting::addProxy (const Setting *setting, - const QList &list) -{ - if (serializable()) - setProperty (Property_Serializable, false); - - mProxies [setting->page() + '/' + setting->name()] = list; -} - -void CSMSettings::Setting::setColumnSpan (int value) -{ - setProperty (Property_ColumnSpan, value); -} - -int CSMSettings::Setting::columnSpan() const -{ - return property (Property_ColumnSpan).at(0).toInt(); -} - -void CSMSettings::Setting::setDeclaredValues (QStringList list) -{ - setProperty (Property_DeclaredValues, list); -} - -QStringList CSMSettings::Setting::declaredValues() const -{ - return property (Property_DeclaredValues); -} - -QStringList CSMSettings::Setting::property (SettingProperty prop) const -{ - if (prop >= mProperties.size()) - return QStringList(); - - return mProperties.at(prop); -} - -void CSMSettings::Setting::setDefaultValue (int value) -{ - setDefaultValues (QStringList() << QVariant (value).toString()); -} - -void CSMSettings::Setting::setDefaultValue (double value) -{ - setDefaultValues (QStringList() << QVariant (value).toString()); -} - -void CSMSettings::Setting::setDefaultValue (const QString &value) -{ - setDefaultValues (QStringList() << value); -} - -void CSMSettings::Setting::setDefaultValues (const QStringList &values) -{ - setProperty (Property_DefaultValues, values); -} - -QStringList CSMSettings::Setting::defaultValues() const -{ - return property (Property_DefaultValues); -} - -void CSMSettings::Setting::setDelimiter (const QString &value) -{ - setProperty (Property_Delimiter, value); -} - -QString CSMSettings::Setting::delimiter() const -{ - return property (Property_Delimiter).at(0); -} - -void CSMSettings::Setting::setEditorSetting(bool state) -{ - mIsEditorSetting = true; -} - -bool CSMSettings::Setting::isEditorSetting() const -{ - return mIsEditorSetting; -} -void CSMSettings::Setting::setIsMultiLine (bool state) -{ - setProperty (Property_IsMultiLine, state); -} - -bool CSMSettings::Setting::isMultiLine() const -{ - return (property (Property_IsMultiLine).at(0) == "true"); -} - -void CSMSettings::Setting::setIsMultiValue (bool state) -{ - setProperty (Property_IsMultiValue, state); -} - -bool CSMSettings::Setting::isMultiValue() const -{ - return (property (Property_IsMultiValue).at(0) == "true"); -} - -const CSMSettings::ProxyValueMap &CSMSettings::Setting::proxyLists() const -{ - return mProxies; -} - -void CSMSettings::Setting::setSerializable (bool state) -{ - setProperty (Property_Serializable, state); -} - -bool CSMSettings::Setting::serializable() const -{ - return (property (Property_Serializable).at(0) == "true"); -} - -void CSMSettings::Setting::setSpecialValueText(const QString &text) -{ - setProperty (Property_SpecialValueText, text); -} - -QString CSMSettings::Setting::specialValueText() const -{ - return property (Property_SpecialValueText).at(0); -} - -void CSMSettings::Setting::setName (const QString &value) -{ - setProperty (Property_Name, value); -} - -QString CSMSettings::Setting::name() const -{ - return property (Property_Name).at(0); -} - -void CSMSettings::Setting::setPage (const QString &value) -{ - setProperty (Property_Page, value); -} - -QString CSMSettings::Setting::page() const -{ - return property (Property_Page).at(0); -} - -void CSMSettings::Setting::setStyleSheet (const QString &value) -{ - setProperty (Property_StyleSheet, value); -} - -QString CSMSettings::Setting::styleSheet() const -{ - return property (Property_StyleSheet).at(0); -} - -void CSMSettings::Setting::setPrefix (const QString &value) -{ - setProperty (Property_Prefix, value); -} - -QString CSMSettings::Setting::prefix() const -{ - return property (Property_Prefix).at(0); -} - -void CSMSettings::Setting::setRowSpan (const int value) -{ - setProperty (Property_RowSpan, value); -} - -int CSMSettings::Setting::rowSpan () const -{ - return property (Property_RowSpan).at(0).toInt(); -} - -void CSMSettings::Setting::setSingleStep (int value) -{ - setProperty (Property_SingleStep, value); -} - -void CSMSettings::Setting::setSingleStep (double value) -{ - setProperty (Property_SingleStep, value); -} - -QString CSMSettings::Setting::singleStep() const -{ - return property (Property_SingleStep).at(0); -} - -void CSMSettings::Setting::setSuffix (const QString &value) -{ - setProperty (Property_Suffix, value); -} - -QString CSMSettings::Setting::suffix() const -{ - return property (Property_Suffix).at(0); -} - -void CSMSettings::Setting::setTickInterval (int value) -{ - setProperty (Property_TickInterval, value); -} - -int CSMSettings::Setting::tickInterval () const -{ - return property (Property_TickInterval).at(0).toInt(); -} - -void CSMSettings::Setting::setTicksAbove (bool state) -{ - setProperty (Property_TicksAbove, state); -} - -bool CSMSettings::Setting::ticksAbove() const -{ - return (property (Property_TicksAbove).at(0) == "true"); -} - -void CSMSettings::Setting::setTicksBelow (bool state) -{ - setProperty (Property_TicksBelow, state); -} - -bool CSMSettings::Setting::ticksBelow() const -{ - return (property (Property_TicksBelow).at(0) == "true"); -} - -void CSMSettings::Setting::setType (int settingType) -{ - setProperty (Property_SettingType, settingType); -} - -CSMSettings::SettingType CSMSettings::Setting::type() const -{ - return static_cast ( property ( - Property_SettingType).at(0).toInt()); -} - -void CSMSettings::Setting::setRange (int min, int max) -{ - setProperty (Property_Minimum, min); - setProperty (Property_Maximum, max); -} - -void CSMSettings::Setting::setRange (double min, double max) -{ - setProperty (Property_Minimum, min); - setProperty (Property_Maximum, max); -} - -QString CSMSettings::Setting::maximum() const -{ - return property (Property_Maximum).at(0); -} - -QString CSMSettings::Setting::minimum() const -{ - return property (Property_Minimum).at(0); -} - -CSVSettings::ViewType CSMSettings::Setting::viewType() const -{ - return static_cast ( property ( - Property_SettingType).at(0).toInt() / 10); -} - -void CSMSettings::Setting::setViewColumn (int value) -{ - setProperty (Property_ViewColumn, value); -} - -int CSMSettings::Setting::viewColumn() const -{ - return property (Property_ViewColumn).at(0).toInt(); -} - -void CSMSettings::Setting::setViewLocation (int row, int column) -{ - setViewRow (row); - setViewColumn (column); -} - -void CSMSettings::Setting::setViewRow (int value) -{ - setProperty (Property_ViewRow, value); -} - -int CSMSettings::Setting::viewRow() const -{ - return property (Property_ViewRow).at(0).toInt(); -} - -void CSMSettings::Setting::setWidgetWidth (int value) -{ - setProperty (Property_WidgetWidth, value); -} - -int CSMSettings::Setting::widgetWidth() const -{ - return property (Property_WidgetWidth).at(0).toInt(); -} - -void CSMSettings::Setting::setWrapping (bool state) -{ - setProperty (Property_Wrapping, state); -} - -bool CSMSettings::Setting::wrapping() const -{ - return (property (Property_Wrapping).at(0) == "true"); -} - -void CSMSettings::Setting::setLabel (const QString& label) -{ - setProperty (Property_Label, label); -} - -QString CSMSettings::Setting::getLabel() const -{ - return property (Property_Label).at (0); -} - -void CSMSettings::Setting::setToolTip (const QString& toolTip) -{ - setProperty (Property_ToolTip, toolTip); -} - -QString CSMSettings::Setting::getToolTip() const -{ - return property (Property_ToolTip).at (0); -} - -void CSMSettings::Setting::setProperty (SettingProperty prop, bool value) -{ - setProperty (prop, QStringList() << QVariant (value).toString()); -} - -void CSMSettings::Setting::setProperty (SettingProperty prop, int value) -{ - setProperty (prop, QStringList() << QVariant (value).toString()); -} - -void CSMSettings::Setting::setProperty (SettingProperty prop, double value) -{ - setProperty (prop, QStringList() << QVariant (value).toString()); -} - -void CSMSettings::Setting::setProperty (SettingProperty prop, - const QString &value) -{ - setProperty (prop, QStringList() << value); -} - -void CSMSettings::Setting::setProperty (SettingProperty prop, - const QStringList &value) -{ - if (prop < mProperties.size()) - mProperties.replace (prop, value); -} diff -Nru openmw-0.37.0/apps/opencs/model/settings/setting.hpp openmw-0.38.0/apps/opencs/model/settings/setting.hpp --- openmw-0.37.0/apps/opencs/model/settings/setting.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/settings/setting.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -#ifndef CSMSETTINGS_SETTING_HPP -#define CSMSETTINGS_SETTING_HPP - -#include -#include -#include "support.hpp" - -namespace CSMSettings -{ - //QString is the setting id in the form of "page/name" - //QList is a list of stringlists of proxy values. - //Order is important! Proxy stringlists are matched against - //master values by their position in the QList. - typedef QMap > ProxyValueMap; - - ///Setting class is the interface for the User Settings. It contains - ///a great deal of boiler plate to provide the core API functions, as - ///well as the property() functions which use enumeration to be iterable. - ///This makes the Setting class capable of being manipulated by script. - ///See CSMSettings::support.hpp for enumerations / string values. - class Setting - { - QList mProperties; - QStringList mDefaults; - - bool mIsEditorSetting; - - ProxyValueMap mProxies; - - public: - - Setting(SettingType typ, const QString &settingName, - const QString &pageName, const QString& label = ""); - - void addProxy (const Setting *setting, const QStringList &vals); - void addProxy (const Setting *setting, const QList &list); - - const QList &properties() const { return mProperties; } - const ProxyValueMap &proxies() const { return mProxies; } - - void setColumnSpan (int value); - int columnSpan() const; - - void setDeclaredValues (QStringList list); - QStringList declaredValues() const; - - void setDefaultValue (int value); - void setDefaultValue (double value); - void setDefaultValue (const QString &value); - - void setDefaultValues (const QStringList &values); - QStringList defaultValues() const; - - void setDelimiter (const QString &value); - QString delimiter() const; - - void setEditorSetting (bool state); - bool isEditorSetting() const; - - void setIsMultiLine (bool state); - bool isMultiLine() const; - - void setIsMultiValue (bool state); - bool isMultiValue() const; - - void setMask (const QString &value); - QString mask() const; - - void setRange (int min, int max); - void setRange (double min, double max); - - QString maximum() const; - - QString minimum() const; - - void setName (const QString &value); - QString name() const; - - void setPage (const QString &value); - QString page() const; - - void setStyleSheet (const QString &value); - QString styleSheet() const; - - void setPrefix (const QString &value); - QString prefix() const; - - void setRowSpan (const int value); - int rowSpan() const; - - const ProxyValueMap &proxyLists() const; - - void setSerializable (bool state); - bool serializable() const; - - void setSpecialValueText (const QString &text); - QString specialValueText() const; - - void setSingleStep (int value); - void setSingleStep (double value); - QString singleStep() const; - - void setSuffix (const QString &value); - QString suffix() const; - - void setTickInterval (int value); - int tickInterval() const; - - void setTicksAbove (bool state); - bool ticksAbove() const; - - void setTicksBelow (bool state); - bool ticksBelow() const; - - void setViewColumn (int value); - int viewColumn() const; - - void setViewLocation (int row = -1, int column = -1); - - void setViewRow (int value); - int viewRow() const; - - void setType (int settingType); - CSMSettings::SettingType type() const; - - CSVSettings::ViewType viewType() const; - - void setWrapping (bool state); - bool wrapping() const; - - void setWidgetWidth (int value); - int widgetWidth() const; - - /// This is the text the user gets to see. - void setLabel (const QString& label); - QString getLabel() const; - - void setToolTip (const QString& toolTip); - QString getToolTip() const; - - ///returns the specified property value - QStringList property (SettingProperty prop) const; - - ///boilerplate code to convert setting values of common types - void setProperty (SettingProperty prop, bool value); - void setProperty (SettingProperty prop, int value); - void setProperty (SettingProperty prop, double value); - void setProperty (SettingProperty prop, const QString &value); - void setProperty (SettingProperty prop, const QStringList &value); - - void addProxy (Setting* setting, - QMap &proxyMap); - - protected: - void buildDefaultSetting(); - }; -} - -#endif // CSMSETTINGS_SETTING_HPP diff -Nru openmw-0.37.0/apps/opencs/model/settings/support.hpp openmw-0.38.0/apps/opencs/model/settings/support.hpp --- openmw-0.37.0/apps/opencs/model/settings/support.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/settings/support.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,149 +0,0 @@ -#ifndef SETTING_SUPPORT_HPP -#define SETTING_SUPPORT_HPP - -#include -#include -#include -#include -#include - -//Enums -namespace CSMSettings -{ - ///Enumerated properties for scripting - enum SettingProperty - { - Property_Name = 0, - Property_Page = 1, - Property_SettingType = 2, - Property_IsMultiValue = 3, - Property_IsMultiLine = 4, - Property_WidgetWidth = 5, - Property_ViewRow = 6, - Property_ViewColumn = 7, - Property_Delimiter = 8, - Property_Serializable = 9, - Property_ColumnSpan = 10, - Property_RowSpan = 11, - Property_Minimum = 12, - Property_Maximum = 13, - Property_SpecialValueText = 14, - Property_Prefix = 15, - Property_Suffix = 16, - Property_SingleStep = 17, - Property_Wrapping = 18, - Property_TickInterval = 19, - Property_TicksAbove = 20, - Property_TicksBelow = 21, - Property_StyleSheet = 22, - Property_Label = 23, - Property_ToolTip = 24, - - //Stringlists should always be the last items - Property_DefaultValues = 25, - Property_DeclaredValues = 26, - Property_DefinedValues = 27, - Property_Proxies = 28 - }; - - ///Basic setting widget types. - enum SettingType - { - /* - * 0 - 9 - Boolean widgets - * 10-19 - List widgets - * 21-29 - Range widgets - * 31-39 - Text widgets - * - * Each range corresponds to a View_Type enum by a factor of 10. - * - * Even-numbered values are single-value widgets - * Odd-numbered values are multi-valued widgets - */ - - Type_CheckBox = 0, - Type_RadioButton = 1, - Type_ListView = 10, - Type_ComboBox = 11, - Type_SpinBox = 21, - Type_DoubleSpinBox = 23, - Type_Slider = 25, - Type_Dial = 27, - Type_TextArea = 30, - Type_LineEdit = 31, - Type_Undefined = 40 - }; - -} - -namespace CSVSettings -{ - ///Categorical view types which encompass the setting widget types - enum ViewType - { - ViewType_Boolean = 0, - ViewType_List = 1, - ViewType_Range = 2, - ViewType_Text = 3, - ViewType_Undefined = 4 - }; -} - - -namespace CSMSettings -{ - ///used to construct default settings in the Setting class - struct PropertyDefaultValues - { - int id; - QString name; - QVariant value; - }; - - ///strings which correspond to setting values. These strings represent - ///the script language keywords which would be used to declare setting - ///views for 3rd party addons - const QString sPropertyNames[] = - { - "name", "page", "setting_type", "is_multi_value", - "is_multi_line", "widget_width", "view_row", "view_column", "delimiter", - "is_serializable","column_span", "row_span", "minimum", "maximum", - "special_value_text", "prefix", "suffix", "single_step", "wrapping", - "tick_interval", "ticks_above", "ticks_below", "stylesheet", - "defaults", "declarations", "definitions", "proxies" - }; - - ///Default values for a setting. Used in setting creation. - const QString sPropertyDefaults[] = - { - "", //name - "", //page - "40", //setting type - "false", //multivalue - "false", //multiline - "7", //widget width - "-1", //view row - "-1", //view column - ",", //delimiter - "true", //serialized - "1", //column span - "1", //row span - "0", //value range - "0", //value minimum - "0", //value maximum - "", //special text - "", //prefix - "", //suffix - "false", //wrapping - "1", //tick interval - "false", //ticks above - "true", //ticks below - "", //StyleSheet - "", //default values - "", //declared values - "", //defined values - "" //proxy values - }; -} - -#endif // VIEW_SUPPORT_HPP diff -Nru openmw-0.37.0/apps/opencs/model/settings/usersettings.cpp openmw-0.38.0/apps/opencs/model/settings/usersettings.cpp --- openmw-0.37.0/apps/opencs/model/settings/usersettings.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/settings/usersettings.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,805 +0,0 @@ -#include "usersettings.hpp" - -#include -#include - -#include -#include -#include - -#include "setting.hpp" -#include "support.hpp" -#include -#include - -/** - * Workaround for problems with whitespaces in paths in older versions of Boost library - */ -#if (BOOST_VERSION <= 104600) -namespace boost -{ - - template<> - inline boost::filesystem::path lexical_cast(const std::string& arg) - { - return boost::filesystem::path(arg); - } - -} /* namespace boost */ -#endif /* (BOOST_VERSION <= 104600) */ - -CSMSettings::UserSettings *CSMSettings::UserSettings::sUserSettingsInstance = 0; - - CSMSettings::UserSettings::UserSettings (const Files::ConfigurationManager& configurationManager) - : mCfgMgr (configurationManager) - , mSettingDefinitions(NULL) -{ - assert(!sUserSettingsInstance); - sUserSettingsInstance = this; - - buildSettingModelDefaults(); -} - -void CSMSettings::UserSettings::buildSettingModelDefaults() -{ - /* - declareSection ("3d-render", "3D Rendering"); - { - Setting *farClipDist = createSetting (Type_DoubleSpinBox, "far-clip-distance", "Far clipping distance"); - farClipDist->setDefaultValue (300000); - farClipDist->setRange (0, 1000000); - farClipDist->setToolTip ("The maximum distance objects are still rendered at."); - - QString defaultValue = "None"; - Setting *antialiasing = createSetting (Type_ComboBox, "antialiasing", "Antialiasing"); - antialiasing->setDeclaredValues (QStringList() - << defaultValue << "MSAA 2" << "MSAA 4" << "MSAA 8" << "MSAA 16"); - antialiasing->setDefaultValue (defaultValue); - } - */ - - /* - declareSection ("scene-input", "Scene Input"); - { - Setting *fastFactor = createSetting (Type_SpinBox, "fast-factor", - "Fast movement factor"); - fastFactor->setDefaultValue (4); - fastFactor->setRange (1, 100); - fastFactor->setToolTip ( - "Factor by which movement is speed up while the shift key is held down."); - } - */ - - declareSection ("window", "Window"); - { - Setting *preDefined = createSetting (Type_ComboBox, "pre-defined", - "Default window size"); - preDefined->setEditorSetting (false); - preDefined->setDeclaredValues ( - QStringList() << "640 x 480" << "800 x 600" << "1024 x 768" << "1440 x 900"); - preDefined->setViewLocation (1, 1); - preDefined->setColumnSpan (2); - preDefined->setToolTip ("Newly opened top-level windows will open with this size " - "(picked from a list of pre-defined values)"); - - Setting *width = createSetting (Type_LineEdit, "default-width", - "Default window width"); - width->setDefaultValues (QStringList() << "1024"); - width->setViewLocation (2, 1); - width->setColumnSpan (1); - width->setToolTip ("Newly opened top-level windows will open with this width."); - preDefined->addProxy (width, QStringList() << "640" << "800" << "1024" << "1440"); - - Setting *height = createSetting (Type_LineEdit, "default-height", - "Default window height"); - height->setDefaultValues (QStringList() << "768"); - height->setViewLocation (2, 2); - height->setColumnSpan (1); - height->setToolTip ("Newly opened top-level windows will open with this height."); - preDefined->addProxy (height, QStringList() << "480" << "600" << "768" << "900"); - - Setting *reuse = createSetting (Type_CheckBox, "reuse", "Reuse Subviews"); - reuse->setDefaultValue ("true"); - reuse->setToolTip ("When a new subview is requested and a matching subview already " - " exist, do not open a new subview and use the existing one instead."); - - Setting *statusBar = createSetting (Type_CheckBox, "show-statusbar", "Show Status Bar"); - statusBar->setDefaultValue ("true"); - statusBar->setToolTip ("If a newly open top level window is showing status bars or not. " - " Note that this does not affect existing windows."); - - Setting *maxSubView = createSetting (Type_SpinBox, "max-subviews", - "Maximum number of subviews per top-level window"); - maxSubView->setDefaultValue (256); - maxSubView->setRange (1, 256); - maxSubView->setToolTip ("If the maximum number is reached and a new subview is opened " - "it will be placed into a new top-level window."); - - Setting *hide = createSetting (Type_CheckBox, "hide-subview", "Hide single subview"); - hide->setDefaultValue ("false"); - hide->setToolTip ("When a view contains only a single subview, hide the subview title " - "bar and if this subview is closed also close the view (unless it is the last " - "view for this document)"); - - Setting *minWidth = createSetting (Type_SpinBox, "minimum-width", - "Minimum subview width"); - minWidth->setDefaultValue (325); - minWidth->setRange (50, 10000); - minWidth->setToolTip ("Minimum width of subviews."); - - QString defaultScroll = "Scrollbar Only"; - QStringList scrollValues = QStringList() << defaultScroll << "Grow Only" << "Grow then Scroll"; - - Setting *mainwinScroll = createSetting (Type_RadioButton, "mainwindow-scrollbar", - "Add a horizontal scrollbar to the main view window."); - mainwinScroll->setDefaultValue (defaultScroll); - mainwinScroll->setDeclaredValues (scrollValues); - mainwinScroll->setToolTip ("Scrollbar Only: Simple addition of scrollbars, the view window does not grow" - " automatically.\n" - "Grow Only: Original Editor behaviour. The view window grows as subviews are added. No scrollbars.\n" - "Grow then Scroll: The view window grows. The scrollbar appears once it cannot grow any further."); - - Setting *grow = createSetting (Type_CheckBox, "grow-limit", "Grow Limit Screen"); - grow->setDefaultValue ("false"); - grow->setToolTip ("When \"Grow then Scroll\" option is selected, the window size grows to" - " the width of the virtual desktop. \nIf this option is selected the the window growth" - "is limited to the current screen."); - } - - declareSection ("records", "Records"); - { - QString defaultValue = "Icon and Text"; - QStringList values = QStringList() << defaultValue << "Icon Only" << "Text Only"; - - Setting *rsd = createSetting (Type_RadioButton, "status-format", - "Modification status display format"); - rsd->setDefaultValue (defaultValue); - rsd->setDeclaredValues (values); - - Setting *ritd = createSetting (Type_RadioButton, "type-format", - "ID type display format"); - ritd->setDefaultValue (defaultValue); - ritd->setDeclaredValues (values); - } - - declareSection ("table-input", "ID Tables"); - { - QString inPlaceEdit ("Edit in Place"); - QString editRecord ("Edit Record"); - QString view ("View"); - QString editRecordAndClose ("Edit Record and Close"); - - QStringList values; - values - << "None" << inPlaceEdit << editRecord << view << "Revert" << "Delete" - << editRecordAndClose << "View and Close"; - - QString toolTip = "
    " - "
  • None
  • " - "
  • Edit in Place: Edit the clicked cell
  • " - "
  • Edit Record: Open a dialogue subview for the clicked record
  • " - "
  • View: Open a scene subview for the clicked record (not available everywhere)
  • " - "
  • Revert: Revert record
  • " - "
  • Delete: Delete recordy
  • " - "
  • Edit Record and Close: Open a dialogue subview for the clicked record and close the table subview
  • " - "
  • View And Close: Open a scene subview for the clicked record and close the table subview
  • " - "
"; - - Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click"); - doubleClick->setDeclaredValues (values); - doubleClick->setDefaultValue (inPlaceEdit); - doubleClick->setToolTip ("Action on double click in table:

" + toolTip); - - Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s", - "Shift Double Click"); - shiftDoubleClick->setDeclaredValues (values); - shiftDoubleClick->setDefaultValue (editRecord); - shiftDoubleClick->setToolTip ("Action on shift double click in table:

" + toolTip); - - Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c", - "Control Double Click"); - ctrlDoubleClick->setDeclaredValues (values); - ctrlDoubleClick->setDefaultValue (view); - ctrlDoubleClick->setToolTip ("Action on control double click in table:

" + toolTip); - - Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc", - "Shift Control Double Click"); - shiftCtrlDoubleClick->setDeclaredValues (values); - shiftCtrlDoubleClick->setDefaultValue (editRecordAndClose); - shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:

" + toolTip); - - QString defaultValue = "Jump and Select"; - QStringList jumpValues = QStringList() << defaultValue << "Jump Only" << "No Jump"; - - Setting *jumpToAdded = createSetting (Type_RadioButton, "jump-to-added", - "Jump to the added or cloned record."); - jumpToAdded->setDefaultValue (defaultValue); - jumpToAdded->setDeclaredValues (jumpValues); - - Setting *extendedConfig = createSetting (Type_CheckBox, "extended-config", - "Manually specify affected record types for an extended delete/revert"); - extendedConfig->setDefaultValue("false"); - extendedConfig->setToolTip("Delete and revert commands have an extended form that also affects " - "associated records.\n\n" - "If this option is enabled, types of affected records are selected " - "manually before a command execution.\nOtherwise, all associated " - "records are deleted/reverted immediately."); - } - - declareSection ("dialogues", "ID Dialogues"); - { - Setting *toolbar = createSetting (Type_CheckBox, "toolbar", "Show toolbar"); - toolbar->setDefaultValue ("true"); - } - - declareSection ("report-input", "Reports"); - { - QString none ("None"); - QString edit ("Edit"); - QString remove ("Remove"); - QString editAndRemove ("Edit And Remove"); - - QStringList values; - values << none << edit << remove << editAndRemove; - - QString toolTip = "

    " - "
  • None
  • " - "
  • Edit: Open a table or dialogue suitable for addressing the listed report
  • " - "
  • Remove: Remove the report from the report table
  • " - "
  • Edit and Remove: Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table
  • " - "
"; - - Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click"); - doubleClick->setDeclaredValues (values); - doubleClick->setDefaultValue (edit); - doubleClick->setToolTip ("Action on double click in report table:

" + toolTip); - - Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s", - "Shift Double Click"); - shiftDoubleClick->setDeclaredValues (values); - shiftDoubleClick->setDefaultValue (remove); - shiftDoubleClick->setToolTip ("Action on shift double click in report table:

" + toolTip); - - Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c", - "Control Double Click"); - ctrlDoubleClick->setDeclaredValues (values); - ctrlDoubleClick->setDefaultValue (editAndRemove); - ctrlDoubleClick->setToolTip ("Action on control double click in report table:

" + toolTip); - - Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc", - "Shift Control Double Click"); - shiftCtrlDoubleClick->setDeclaredValues (values); - shiftCtrlDoubleClick->setDefaultValue (none); - shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in report table:

" + toolTip); - } - - declareSection ("search", "Search & Replace"); - { - Setting *before = createSetting (Type_SpinBox, "char-before", - "Characters before search string"); - before->setDefaultValue (10); - before->setRange (0, 1000); - before->setToolTip ("Maximum number of character to display in search result before the searched text"); - - Setting *after = createSetting (Type_SpinBox, "char-after", - "Characters after search string"); - after->setDefaultValue (10); - after->setRange (0, 1000); - after->setToolTip ("Maximum number of character to display in search result after the searched text"); - - Setting *autoDelete = createSetting (Type_CheckBox, "auto-delete", "Delete row from result table after a successful replace"); - autoDelete->setDefaultValue ("true"); - } - - declareSection ("script-editor", "Scripts"); - { - Setting *lineNum = createSetting (Type_CheckBox, "show-linenum", "Show Line Numbers"); - lineNum->setDefaultValue ("true"); - lineNum->setToolTip ("Show line numbers to the left of the script editor window." - "The current row and column numbers of the text cursor are shown at the bottom."); - - Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font"); - monoFont->setDefaultValue ("true"); - monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview."); - - QString tooltip = - "\n#RGB (each of R, G, and B is a single hex digit)" - "\n#RRGGBB" - "\n#RRRGGGBBB" - "\n#RRRRGGGGBBBB" - "\nA name from the list of colors defined in the list of SVG color keyword names." - "\nX11 color names may also work."; - - QString modeNormal ("Normal"); - - QStringList modes; - modes << "Ignore" << modeNormal << "Strict"; - - Setting *warnings = createSetting (Type_ComboBox, "warnings", - "Warning Mode"); - warnings->setDeclaredValues (modes); - warnings->setDefaultValue (modeNormal); - warnings->setToolTip ("

    How to handle warning messages during compilation:

    " - "

  • Ignore: Do not report warning
  • " - "
  • Normal: Report warning as a warning
  • " - "
  • Strict: Promote warning to an error
  • " - "
"); - - Setting *toolbar = createSetting (Type_CheckBox, "toolbar", "Show toolbar"); - toolbar->setDefaultValue ("true"); - - Setting *delay = createSetting (Type_SpinBox, "compile-delay", - "Delay between updating of source errors"); - delay->setDefaultValue (100); - delay->setRange (0, 10000); - delay->setToolTip ("Delay in milliseconds"); - - Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int"); - formatInt->setDefaultValues (QStringList() << "Dark magenta"); - formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip); - - Setting *formatFloat = createSetting (Type_LineEdit, "colour-float", "Highlight Colour: Float"); - formatFloat->setDefaultValues (QStringList() << "Magenta"); - formatFloat->setToolTip ("(Default: Magenta) Use one of the following formats:" + tooltip); - - Setting *formatName = createSetting (Type_LineEdit, "colour-name", "Highlight Colour: Name"); - formatName->setDefaultValues (QStringList() << "Gray"); - formatName->setToolTip ("(Default: Gray) Use one of the following formats:" + tooltip); - - Setting *formatKeyword = createSetting (Type_LineEdit, "colour-keyword", "Highlight Colour: Keyword"); - formatKeyword->setDefaultValues (QStringList() << "Red"); - formatKeyword->setToolTip ("(Default: Red) Use one of the following formats:" + tooltip); - - Setting *formatSpecial = createSetting (Type_LineEdit, "colour-special", "Highlight Colour: Special"); - formatSpecial->setDefaultValues (QStringList() << "Dark yellow"); - formatSpecial->setToolTip ("(Default: Dark yellow) Use one of the following formats:" + tooltip); - - Setting *formatComment = createSetting (Type_LineEdit, "colour-comment", "Highlight Colour: Comment"); - formatComment->setDefaultValues (QStringList() << "Green"); - formatComment->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip); - - Setting *formatId = createSetting (Type_LineEdit, "colour-id", "Highlight Colour: Id"); - formatId->setDefaultValues (QStringList() << "Blue"); - formatId->setToolTip ("(Default: Blue) Use one of the following formats:" + tooltip); - } - - declareSection ("general-input", "General Input"); - { - Setting *cycle = createSetting (Type_CheckBox, "cycle", "Cyclic next/previous"); - cycle->setDefaultValue ("false"); - cycle->setToolTip ("When using next/previous functions at the last/first item of a " - "list go to the first/last item"); - } - - declareSection ("scene-input", "3D Scene Input"); - { - QString left ("Left Mouse-Button"); - QString cLeft ("Ctrl-Left Mouse-Button"); - QString right ("Right Mouse-Button"); - QString cRight ("Ctrl-Right Mouse-Button"); - QString middle ("Middle Mouse-Button"); - QString cMiddle ("Ctrl-Middle Mouse-Button"); - - QStringList values; - values << left << cLeft << right << cRight << middle << cMiddle; - - Setting *primaryNavigation = createSetting (Type_ComboBox, "p-navi", "Primary Camera Navigation Button"); - primaryNavigation->setDeclaredValues (values); - primaryNavigation->setDefaultValue (left); - - Setting *secondaryNavigation = createSetting (Type_ComboBox, "s-navi", "Secondary Camera Navigation Button"); - secondaryNavigation->setDeclaredValues (values); - secondaryNavigation->setDefaultValue (cLeft); - - Setting *primaryEditing = createSetting (Type_ComboBox, "p-edit", "Primary Editing Button"); - primaryEditing->setDeclaredValues (values); - primaryEditing->setDefaultValue (right); - - Setting *secondaryEditing = createSetting (Type_ComboBox, "s-edit", "Secondary Editing Button"); - secondaryEditing->setDeclaredValues (values); - secondaryEditing->setDefaultValue (cRight); - - Setting *primarySelection = createSetting (Type_ComboBox, "p-select", "Selection Button"); - primarySelection->setDeclaredValues (values); - primarySelection->setDefaultValue (middle); - - Setting *secondarySelection = createSetting (Type_ComboBox, "s-select", "Selection Button"); - secondarySelection->setDeclaredValues (values); - secondarySelection->setDefaultValue (cMiddle); - - Setting *contextSensitive = createSetting (Type_CheckBox, "context-select", "Context Sensitive Selection"); - contextSensitive->setDefaultValue ("false"); - - Setting *dragMouseSensitivity = createSetting (Type_DoubleSpinBox, "drag-factor", - "Mouse sensitivity during drag operations"); - dragMouseSensitivity->setDefaultValue (1.0); - dragMouseSensitivity->setRange (0.001, 100.0); - - Setting *dragWheelSensitivity = createSetting (Type_DoubleSpinBox, "drag-wheel-factor", - "Mouse wheel sensitivity during drag operations"); - dragWheelSensitivity->setDefaultValue (1.0); - dragWheelSensitivity->setRange (0.001, 100.0); - - Setting *dragShiftFactor = createSetting (Type_DoubleSpinBox, "drag-shift-factor", - "Acceleration factor during drag operations while holding down shift"); - dragShiftFactor->setDefaultValue (4.0); - dragShiftFactor->setRange (0.001, 100.0); - } - - { - /****************************************************************** - * There are three types of values: - * - * Declared values - * - * Pre-determined values, typically for - * combobox drop downs and boolean (radiobutton / checkbox) labels. - * These values represent the total possible list of values that - * may define a setting. No other values are allowed. - * - * Defined values - * - * Values which represent the actual, current value of - * a setting. For settings with declared values, this must be one - * or several declared values, as appropriate. - * - * Proxy values - * Values the proxy master updates the proxy slave when - * it's own definition is set / changed. These are definitions for - * proxy slave settings, but must match any declared values the - * proxy slave has, if any. - *******************************************************************/ -/* - //create setting objects, specifying the basic widget type, - //the page name, and the view name - - Setting *masterBoolean = createSetting (Type_RadioButton, section, - "Master Proxy"); - - Setting *slaveBoolean = createSetting (Type_CheckBox, section, - "Proxy Checkboxes"); - - Setting *slaveSingleText = createSetting (Type_LineEdit, section, - "Proxy TextBox 1"); - - Setting *slaveMultiText = createSetting (Type_LineEdit, section, - "ProxyTextBox 2"); - - Setting *slaveAlphaSpinbox = createSetting (Type_SpinBox, section, - "Alpha Spinbox"); - - Setting *slaveIntegerSpinbox = createSetting (Type_SpinBox, section, - "Int Spinbox"); - - Setting *slaveDoubleSpinbox = createSetting (Type_DoubleSpinBox, - section, "Double Spinbox"); - - Setting *slaveSlider = createSetting (Type_Slider, section, "Slider"); - - Setting *slaveDial = createSetting (Type_Dial, section, "Dial"); - - //set declared values for selected views - masterBoolean->setDeclaredValues (QStringList() - << "Profile One" << "Profile Two" - << "Profile Three" << "Profile Four"); - - slaveBoolean->setDeclaredValues (QStringList() - << "One" << "Two" << "Three" << "Four" << "Five"); - - slaveAlphaSpinbox->setDeclaredValues (QStringList() - << "One" << "Two" << "Three" << "Four"); - - - masterBoolean->addProxy (slaveBoolean, QList () - << (QStringList() << "One" << "Three") - << (QStringList() << "One" << "Three") - << (QStringList() << "One" << "Three" << "Five") - << (QStringList() << "Two" << "Four") - ); - - masterBoolean->addProxy (slaveSingleText, QList () - << (QStringList() << "Text A") - << (QStringList() << "Text B") - << (QStringList() << "Text A") - << (QStringList() << "Text C") - ); - - masterBoolean->addProxy (slaveMultiText, QList () - << (QStringList() << "One" << "Three") - << (QStringList() << "One" << "Three") - << (QStringList() << "One" << "Three" << "Five") - << (QStringList() << "Two" << "Four") - ); - - masterBoolean->addProxy (slaveAlphaSpinbox, QList () - << (QStringList() << "Four") - << (QStringList() << "Three") - << (QStringList() << "Two") - << (QStringList() << "One")); - - masterBoolean->addProxy (slaveIntegerSpinbox, QList () - << (QStringList() << "0") - << (QStringList() << "7") - << (QStringList() << "14") - << (QStringList() << "21")); - - masterBoolean->addProxy (slaveDoubleSpinbox, QList () - << (QStringList() << "0.17") - << (QStringList() << "0.34") - << (QStringList() << "0.51") - << (QStringList() << "0.68")); - - masterBoolean->addProxy (slaveSlider, QList () - << (QStringList() << "25") - << (QStringList() << "50") - << (QStringList() << "75") - << (QStringList() << "100") - ); - - masterBoolean->addProxy (slaveDial, QList () - << (QStringList() << "25") - << (QStringList() << "50") - << (QStringList() << "75") - << (QStringList() << "100") - ); - - //settings with proxies are not serialized by default - //other settings non-serialized for demo purposes - slaveBoolean->setSerializable (false); - slaveSingleText->setSerializable (false); - slaveMultiText->setSerializable (false); - slaveAlphaSpinbox->setSerializable (false); - slaveIntegerSpinbox->setSerializable (false); - slaveDoubleSpinbox->setSerializable (false); - slaveSlider->setSerializable (false); - slaveDial->setSerializable (false); - - slaveBoolean->setDefaultValues (QStringList() - << "One" << "Three" << "Five"); - - slaveSingleText->setDefaultValue ("Text A"); - - slaveMultiText->setDefaultValues (QStringList() - << "One" << "Three" << "Five"); - - slaveSingleText->setWidgetWidth (24); - slaveMultiText->setWidgetWidth (24); - - slaveAlphaSpinbox->setDefaultValue ("Two"); - slaveAlphaSpinbox->setWidgetWidth (20); - //slaveAlphaSpinbox->setPrefix ("No. "); - //slaveAlphaSpinbox->setSuffix ("!"); - slaveAlphaSpinbox->setWrapping (true); - - slaveIntegerSpinbox->setDefaultValue (14); - slaveIntegerSpinbox->setMinimum (0); - slaveIntegerSpinbox->setMaximum (58); - slaveIntegerSpinbox->setPrefix ("$"); - slaveIntegerSpinbox->setSuffix (".00"); - slaveIntegerSpinbox->setWidgetWidth (10); - slaveIntegerSpinbox->setSpecialValueText ("Nothing!"); - - slaveDoubleSpinbox->setDefaultValue (0.51); - slaveDoubleSpinbox->setSingleStep(0.17); - slaveDoubleSpinbox->setMaximum(4.0); - - slaveSlider->setMinimum (0); - slaveSlider->setMaximum (100); - slaveSlider->setDefaultValue (75); - slaveSlider->setWidgetWidth (100); - slaveSlider->setTicksAbove (true); - slaveSlider->setTickInterval (25); - - slaveDial->setMinimum (0); - slaveDial->setMaximum (100); - slaveDial->setSingleStep (5); - slaveDial->setDefaultValue (75); - slaveDial->setTickInterval (25); -*/ - } -} - -CSMSettings::UserSettings::~UserSettings() -{ - sUserSettingsInstance = 0; -} - -void CSMSettings::UserSettings::loadSettings (const QString &fileName) -{ - QString userFilePath = QString::fromUtf8 - (mCfgMgr.getUserConfigPath().string().c_str()); - - QString globalFilePath = QString::fromUtf8 - (mCfgMgr.getGlobalPath().string().c_str()); - - QString otherFilePath = globalFilePath; - - //test for local only if global fails (uninstalled copy) - if (!QFile (globalFilePath + fileName).exists()) - { - //if global is invalid, use the local path - otherFilePath = QString::fromUtf8 - (mCfgMgr.getLocalPath().string().c_str()); - } - - QSettings::setPath - (QSettings::IniFormat, QSettings::UserScope, userFilePath); - - QSettings::setPath - (QSettings::IniFormat, QSettings::SystemScope, otherFilePath); - - mSettingDefinitions = new QSettings - (QSettings::IniFormat, QSettings::UserScope, "opencs", QString(), this); -} - -// if the key is not found create one with a default value -QString CSMSettings::UserSettings::setting(const QString &viewKey, const QString &value) -{ - if(mSettingDefinitions->contains(viewKey)) - return settingValue(viewKey); - else if(value != QString()) - { - mSettingDefinitions->setValue (viewKey, QStringList() << value); - return value; - } - - return QString(); -} - -bool CSMSettings::UserSettings::hasSettingDefinitions (const QString &viewKey) const -{ - return (mSettingDefinitions->contains (viewKey)); -} - -void CSMSettings::UserSettings::setDefinitions - (const QString &key, const QStringList &list) -{ - mSettingDefinitions->setValue (key, list); -} - -void CSMSettings::UserSettings::saveDefinitions() const -{ - mSettingDefinitions->sync(); -} - -QString CSMSettings::UserSettings::settingValue (const QString &settingKey) -{ - QStringList defs; - - if (!mSettingDefinitions->contains (settingKey)) - return QString(); - - defs = mSettingDefinitions->value (settingKey).toStringList(); - - if (defs.isEmpty()) - return QString(); - - return defs.at(0); -} - -CSMSettings::UserSettings& CSMSettings::UserSettings::instance() -{ - assert(sUserSettingsInstance); - return *sUserSettingsInstance; -} - -void CSMSettings::UserSettings::updateUserSetting(const QString &settingKey, - const QStringList &list) -{ - mSettingDefinitions->setValue (settingKey ,list); - - emit userSettingUpdated (settingKey, list); -} - -CSMSettings::Setting *CSMSettings::UserSettings::findSetting - (const QString &pageName, const QString &settingName) -{ - foreach (Setting *setting, mSettings) - { - if (setting->name() == settingName) - { - if (setting->page() == pageName) - return setting; - } - } - return 0; -} - -void CSMSettings::UserSettings::removeSetting - (const QString &pageName, const QString &settingName) -{ - if (mSettings.isEmpty()) - return; - - QList ::iterator removeIterator = mSettings.begin(); - - while (removeIterator != mSettings.end()) - { - if ((*removeIterator)->name() == settingName) - { - if ((*removeIterator)->page() == pageName) - { - mSettings.erase (removeIterator); - break; - } - } - removeIterator++; - } -} - -CSMSettings::SettingPageMap CSMSettings::UserSettings::settingPageMap() const -{ - SettingPageMap pageMap; - - foreach (Setting *setting, mSettings) - { - SettingPageMap::iterator iter = pageMap.find (setting->page()); - - if (iter==pageMap.end()) - { - QPair > value; - - std::map::const_iterator iter2 = - mSectionLabels.find (setting->page()); - - value.first = iter2!=mSectionLabels.end() ? iter2->second : ""; - - iter = pageMap.insert (setting->page(), value); - } - - iter->second.append (setting); - } - - return pageMap; -} - -CSMSettings::Setting *CSMSettings::UserSettings::createSetting - (CSMSettings::SettingType type, const QString &name, const QString& label) -{ - Setting *setting = new Setting (type, name, mSection, label); - - // set useful defaults - int row = 1; - - if (!mSettings.empty()) - row = mSettings.back()->viewRow()+1; - - setting->setViewLocation (row, 1); - - setting->setColumnSpan (3); - - int width = 10; - - if (type==Type_CheckBox) - width = 40; - - setting->setWidgetWidth (width); - - if (type==Type_CheckBox) - setting->setStyleSheet ("QGroupBox { border: 0px; }"); - - if (type==Type_CheckBox) - setting->setDeclaredValues(QStringList() << "true" << "false"); - - if (type==Type_CheckBox) - setting->setSpecialValueText (setting->getLabel()); - - //add declaration to the model - mSettings.append (setting); - - return setting; -} - -void CSMSettings::UserSettings::declareSection (const QString& page, const QString& label) -{ - mSection = page; - mSectionLabels[page] = label; -} - -QStringList CSMSettings::UserSettings::definitions (const QString &viewKey) const -{ - if (mSettingDefinitions->contains (viewKey)) - return mSettingDefinitions->value (viewKey).toStringList(); - - return QStringList(); -} diff -Nru openmw-0.37.0/apps/opencs/model/settings/usersettings.hpp openmw-0.38.0/apps/opencs/model/settings/usersettings.hpp --- openmw-0.37.0/apps/opencs/model/settings/usersettings.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/settings/usersettings.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -#ifndef USERSETTINGS_HPP -#define USERSETTINGS_HPP - -#include - -#include -#include -#include -#include -#include - -#include -#include "support.hpp" - -#ifndef Q_MOC_RUN -#include -#endif - -namespace Files { typedef std::vector PathContainer; - struct ConfigurationManager;} - -class QFile; -class QSettings; - -namespace CSMSettings { - - class Setting; - typedef QMap > > SettingPageMap; - - class UserSettings: public QObject - { - - Q_OBJECT - - static UserSettings *sUserSettingsInstance; - const Files::ConfigurationManager& mCfgMgr; - - QSettings *mSettingDefinitions; - QList mSettings; - QString mSection; - std::map mSectionLabels; - - public: - - /// Singleton implementation - static UserSettings& instance(); - - UserSettings (const Files::ConfigurationManager& configurationManager); - ~UserSettings(); - - UserSettings (UserSettings const &); //not implemented - UserSettings& operator= (UserSettings const &); //not implemented - - /// Retrieves the settings file at all three levels (global, local and user). - void loadSettings (const QString &fileName); - - /// Updates QSettings and syncs with the ini file - void setDefinitions (const QString &key, const QStringList &defs); - - QString settingValue (const QString &settingKey); - - ///retrieve a setting object from a given page and setting name - Setting *findSetting - (const QString &pageName, const QString &settingName = QString()); - - ///remove a setting from the list - void removeSetting - (const QString &pageName, const QString &settingName); - - ///Retrieve a map of the settings, keyed by page name - SettingPageMap settingPageMap() const; - - ///Returns a string list of defined vlaues for the specified setting - ///in "page/name" format. - QStringList definitions (const QString &viewKey) const; - - ///Test to indicate whether or not a setting has any definitions - bool hasSettingDefinitions (const QString &viewKey) const; - - ///Save any unsaved changes in the QSettings object - void saveDefinitions() const; - - QString setting(const QString &viewKey, const QString &value = QString()); - - private: - - void buildSettingModelDefaults(); - - ///add a new setting to the model and return it - Setting *createSetting (CSMSettings::SettingType type, const QString &name, - const QString& label); - - /// Set the section for createSetting calls. - /// - /// Sections can be declared multiple times. - void declareSection (const QString& page, const QString& label); - - signals: - - void userSettingUpdated (const QString &, const QStringList &); - - public slots: - - void updateUserSetting (const QString &, const QStringList &); - }; -} -#endif // USERSETTINGS_HPP diff -Nru openmw-0.37.0/apps/opencs/model/tools/magiceffectcheck.cpp openmw-0.38.0/apps/opencs/model/tools/magiceffectcheck.cpp --- openmw-0.37.0/apps/opencs/model/tools/magiceffectcheck.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/tools/magiceffectcheck.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,7 +7,7 @@ namespace { - void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string text) + void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) { if (!text.empty()) { diff -Nru openmw-0.37.0/apps/opencs/model/tools/referenceablecheck.cpp openmw-0.38.0/apps/opencs/model/tools/referenceablecheck.cpp --- openmw-0.37.0/apps/opencs/model/tools/referenceablecheck.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/tools/referenceablecheck.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -397,6 +397,9 @@ //checking for name if (container.mName.empty()) messages.push_back (std::make_pair (id, container.mId + " has an empty name")); + + //checking contained items + inventoryListCheck(container.mInventory.mList, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(container, messages, id.toString()); @@ -471,6 +474,9 @@ if (creature.mScale == 0) messages.push_back (std::make_pair (id, creature.mId + " has zero scale value")); + // Check inventory + inventoryListCheck(creature.mInventory.mList, messages, id.toString()); + // Check that mentioned scripts exist scriptCheck(creature, messages, id.toString()); } @@ -742,6 +748,9 @@ //TODO: reputation, Disposition, rank, everything else + // Check inventory + inventoryListCheck(npc.mInventory.mList, messages, id.toString()); + // Check that mentioned scripts exist scriptCheck(npc, messages, id.toString()); } @@ -891,6 +900,45 @@ "There is no player record")); } +void CSMTools::ReferenceableCheckStage::inventoryListCheck( + const std::vector& itemList, + CSMDoc::Messages& messages, + const std::string& id) +{ + for (size_t i = 0; i < itemList.size(); ++i) + { + std::string itemName = itemList[i].mItem.toString(); + CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); + + if (localIndex.first == -1) + messages.push_back (std::make_pair (id, + id + " contains non-existing item (" + itemName + ")")); + else + { + // Needs to accomodate Containers, Creatures, and NPCs + switch (localIndex.second) + { + case CSMWorld::UniversalId::Type_Potion: + case CSMWorld::UniversalId::Type_Apparatus: + case CSMWorld::UniversalId::Type_Armor: + case CSMWorld::UniversalId::Type_Book: + case CSMWorld::UniversalId::Type_Clothing: + case CSMWorld::UniversalId::Type_Ingredient: + case CSMWorld::UniversalId::Type_Light: + case CSMWorld::UniversalId::Type_Lockpick: + case CSMWorld::UniversalId::Type_Miscellaneous: + case CSMWorld::UniversalId::Type_Probe: + case CSMWorld::UniversalId::Type_Repair: + case CSMWorld::UniversalId::Type_Weapon: + case CSMWorld::UniversalId::Type_ItemLevelledList: + break; + default: + messages.push_back (std::make_pair(id, + id + " contains item of invalid type (" + itemName + ")")); + } + } + } +} //Templates begins here diff -Nru openmw-0.37.0/apps/opencs/model/tools/referenceablecheck.hpp openmw-0.38.0/apps/opencs/model/tools/referenceablecheck.hpp --- openmw-0.37.0/apps/opencs/model/tools/referenceablecheck.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/tools/referenceablecheck.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -47,7 +47,9 @@ //FINAL CHECK void finalCheck (CSMDoc::Messages& messages); - //TEMPLATE CHECKS + //Convenience functions + void inventoryListCheck(const std::vector& itemList, CSMDoc::Messages& messages, const std::string& id); + template void inventoryItemCheck(const ITEM& someItem, CSMDoc::Messages& messages, const std::string& someID, diff -Nru openmw-0.37.0/apps/opencs/model/tools/scriptcheck.cpp openmw-0.38.0/apps/opencs/model/tools/scriptcheck.cpp --- openmw-0.37.0/apps/opencs/model/tools/scriptcheck.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/tools/scriptcheck.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,6 +10,8 @@ #include "../world/data.hpp" +#include "../prefs/state.hpp" + CSMDoc::Message::Severity CSMTools::ScriptCheckStage::getSeverity (Type type) { switch (type) @@ -46,7 +48,7 @@ std::ostringstream stream; stream << "script " << mFile << ": " << message; - + mMessages->add (id, stream.str(), "", getSeverity (type)); } @@ -62,6 +64,15 @@ int CSMTools::ScriptCheckStage::setup() { + std::string warnings = CSMPrefs::get()["Scripts"]["warnings"].toString(); + + if (warnings=="Ignore") + mWarningMode = Mode_Ignore; + else if (warnings=="Normal") + mWarningMode = Mode_Normal; + else if (warnings=="Strict") + mWarningMode = Mode_Strict; + mContext.clear(); mMessages = 0; mId.clear(); @@ -110,22 +121,9 @@ std::ostringstream stream; stream << "script " << mFile << ": " << error.what(); - + messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError); } mMessages = 0; } - -void CSMTools::ScriptCheckStage::updateUserSetting (const QString& name, const QStringList& value) -{ - if (name=="script-editor/warnings" && !value.isEmpty()) - { - if (value.at (0)=="Ignore") - mWarningMode = Mode_Ignore; - else if (value.at (0)=="Normal") - mWarningMode = Mode_Normal; - else if (value.at (0)=="Strict") - mWarningMode = Mode_Strict; - } -} diff -Nru openmw-0.37.0/apps/opencs/model/tools/scriptcheck.hpp openmw-0.38.0/apps/opencs/model/tools/scriptcheck.hpp --- openmw-0.37.0/apps/opencs/model/tools/scriptcheck.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/tools/scriptcheck.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -34,7 +34,7 @@ WarningMode mWarningMode; CSMDoc::Message::Severity getSeverity (Type type); - + virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); ///< Report error to the user. @@ -50,8 +50,6 @@ virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. - - virtual void updateUserSetting (const QString& name, const QStringList& value); }; } diff -Nru openmw-0.37.0/apps/opencs/model/tools/search.cpp openmw-0.38.0/apps/opencs/model/tools/search.cpp --- openmw-0.37.0/apps/opencs/model/tools/search.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/tools/search.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -120,24 +120,25 @@ return flat; } -CSMTools::Search::Search() : mType (Type_None), mPaddingBefore (10), mPaddingAfter (10) {} +CSMTools::Search::Search() : mType (Type_None), mValue (0), mIdColumn (0), mTypeColumn (0), + mPaddingBefore (10), mPaddingAfter (10) {} CSMTools::Search::Search (Type type, const std::string& value) -: mType (type), mText (value), mPaddingBefore (10), mPaddingAfter (10) +: mType (type), mText (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) { if (type!=Type_Text && type!=Type_Id) throw std::logic_error ("Invalid search parameter (string)"); } CSMTools::Search::Search (Type type, const QRegExp& value) -: mType (type), mRegExp (value), mPaddingBefore (10), mPaddingAfter (10) +: mType (type), mRegExp (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) { if (type!=Type_TextRegEx && type!=Type_IdRegEx) throw std::logic_error ("Invalid search parameter (RegExp)"); } CSMTools::Search::Search (Type type, int value) -: mType (type), mValue (value), mPaddingBefore (10), mPaddingAfter (10) +: mType (type), mValue (value), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) { if (type!=Type_RecordState) throw std::logic_error ("invalid search parameter (int)"); diff -Nru openmw-0.37.0/apps/opencs/model/tools/soundgencheck.cpp openmw-0.38.0/apps/opencs/model/tools/soundgencheck.cpp --- openmw-0.37.0/apps/opencs/model/tools/soundgencheck.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/tools/soundgencheck.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -26,7 +26,7 @@ return; } - const ESM::SoundGenerator soundGen = record.get(); + const ESM::SoundGenerator& soundGen = record.get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId); if (!soundGen.mCreature.empty()) diff -Nru openmw-0.37.0/apps/opencs/model/tools/tools.cpp openmw-0.38.0/apps/opencs/model/tools/tools.cpp --- openmw-0.37.0/apps/opencs/model/tools/tools.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/tools/tools.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -53,11 +53,6 @@ { mVerifierOperation = new CSMDoc::Operation (CSMDoc::State_Verifying, false); - std::vector settings; - settings.push_back ("script-editor/warnings"); - - mVerifierOperation->configureSettings (settings); - connect (&mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (&mVerifier, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool))); connect (&mVerifier, SIGNAL (reportMessage (const CSMDoc::Message&, int)), diff -Nru openmw-0.37.0/apps/opencs/model/world/cell.cpp openmw-0.38.0/apps/opencs/model/world/cell.cpp --- openmw-0.37.0/apps/opencs/model/world/cell.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/cell.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -2,18 +2,15 @@ #include -void CSMWorld::Cell::load (ESM::ESMReader &esm) +void CSMWorld::Cell::load (ESM::ESMReader &esm, bool &isDeleted) { - mName = mId; + ESM::Cell::load (esm, isDeleted, false); - ESM::Cell::load (esm, false); - - if (!(mData.mFlags & Interior)) + mId = mName; + if (isExterior()) { std::ostringstream stream; - stream << "#" << mData.mX << " " << mData.mY; - mId = stream.str(); } } diff -Nru openmw-0.37.0/apps/opencs/model/world/cell.hpp openmw-0.38.0/apps/opencs/model/world/cell.hpp --- openmw-0.37.0/apps/opencs/model/world/cell.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/cell.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -16,7 +16,7 @@ { std::string mId; - void load (ESM::ESMReader &esm); + void load (ESM::ESMReader &esm, bool &isDeleted); }; } diff -Nru openmw-0.37.0/apps/opencs/model/world/collection.hpp openmw-0.38.0/apps/opencs/model/world/collection.hpp --- openmw-0.37.0/apps/opencs/model/world/collection.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/collection.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -43,6 +43,12 @@ template > class Collection : public CollectionBase { + public: + + typedef ESXRecordT ESXRecord; + + private: + std::vector > mRecords; std::map mIndex; std::vector *> mColumns; diff -Nru openmw-0.37.0/apps/opencs/model/world/commanddispatcher.cpp openmw-0.38.0/apps/opencs/model/world/commanddispatcher.cpp --- openmw-0.37.0/apps/opencs/model/world/commanddispatcher.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/commanddispatcher.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -93,7 +93,7 @@ void CSMWorld::CommandDispatcher::setSelection (const std::vector& selection) { mSelection = selection; - std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::toLower); + std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::lowerCaseInPlace); std::sort (mSelection.begin(), mSelection.end()); } diff -Nru openmw-0.37.0/apps/opencs/model/world/data.cpp openmw-0.38.0/apps/opencs/model/world/data.cpp --- openmw-0.37.0/apps/opencs/model/world/data.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/data.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1010,41 +1010,43 @@ case ESM::REC_DIAL: { - std::string id = mReader->getHNOString ("NAME"); - ESM::Dialogue record; - record.mId = id; - record.load (*mReader); + bool isDeleted = false; - if (record.mType==ESM::Dialogue::Journal) - { - mJournals.load (record, mBase); - mDialogue = &mJournals.getRecord (id).get(); - } - else if (record.mType==ESM::Dialogue::Deleted) + record.load (*mReader, isDeleted); + + if (isDeleted) { - mDialogue = 0; // record vector can be shuffled around which would make pointer - // to record invalid + // record vector can be shuffled around which would make pointer to record invalid + mDialogue = 0; - if (mJournals.tryDelete (id)) + if (mJournals.tryDelete (record.mId)) { - /// \todo handle info records + mJournalInfos.removeDialogueInfos(record.mId); } - else if (mTopics.tryDelete (id)) + else if (mTopics.tryDelete (record.mId)) { - /// \todo handle info records + mTopicInfos.removeDialogueInfos(record.mId); } else { messages.add (UniversalId::Type_None, - "Trying to delete dialogue record " + id + " which does not exist", + "Trying to delete dialogue record " + record.mId + " which does not exist", "", CSMDoc::Message::Severity_Warning); } } else { - mTopics.load (record, mBase); - mDialogue = &mTopics.getRecord (id).get(); + if (record.mType == ESM::Dialogue::Journal) + { + mJournals.load (record, mBase); + mDialogue = &mJournals.getRecord (record.mId).get(); + } + else + { + mTopics.load (record, mBase); + mDialogue = &mTopics.getRecord (record.mId).get(); + } } break; diff -Nru openmw-0.37.0/apps/opencs/model/world/idcollection.hpp openmw-0.38.0/apps/opencs/model/world/idcollection.hpp --- openmw-0.37.0/apps/opencs/model/world/idcollection.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/idcollection.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,7 +11,7 @@ template > class IdCollection : public Collection { - virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader); + virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted); public: @@ -33,77 +33,46 @@ template void IdCollection::loadRecord (ESXRecordT& record, - ESM::ESMReader& reader) + ESM::ESMReader& reader, + bool& isDeleted) { - record.load (reader); + record.load (reader, isDeleted); } template int IdCollection::load (ESM::ESMReader& reader, bool base) { - std::string id = reader.getHNOString ("NAME"); + ESXRecordT record; + bool isDeleted = false; - if (reader.isNextSub ("DELE")) - { - int index = Collection::searchId (id); + loadRecord (record, reader, isDeleted); - reader.skipRecord(); + std::string id = IdAccessorT().getId (record); + int index = this->searchId (id); + if (isDeleted) + { if (index==-1) { // deleting a record that does not exist - // ignore it for now - /// \todo report the problem to the user - } - else if (base) - { - Collection::removeRows (index, 1); - } - else - { - Record record = Collection::getRecord (index); - record.mState = RecordBase::State_Deleted; - this->setRecord (index, record); + return -1; } - return -1; - } - else - { - ESXRecordT record; - - // Sometimes id (i.e. NAME of the cell) may be different to the id we stored - // earlier. e.g. NAME == "Vivec, Arena" but id == "#-4 11". Sometime NAME is - // missing altogether for scripts or cells. - // - // In such cases the returned index will be -1. We then try updating the - // IdAccessor's id manually (e.g. set mId of the record to "Vivec, Arena") - // and try getting the index once more after loading the record. The mId of the - // record would have changed to "#-4 11" after the load, and searchId() should find - // it (if this is a modify) - int index = this->searchId (id); - - if (index==-1) - IdAccessorT().getId (record) = id; - else + if (base) { - record = this->getRecord (index).get(); + this->removeRows (index, 1); + return -1; } - loadRecord (record, reader); - - if (index==-1) - { - std::string newId = IdAccessorT().getId(record); - int newIndex = this->searchId(newId); - if (newIndex != -1 && id != newId) - index = newIndex; - } - - return load (record, base, index); + Record baseRecord = this->getRecord (index); + baseRecord.mState = RecordBase::State_Deleted; + this->setRecord (index, baseRecord); + return index; } + + return load (record, base, index); } template diff -Nru openmw-0.37.0/apps/opencs/model/world/infocollection.cpp openmw-0.38.0/apps/opencs/model/world/infocollection.cpp --- openmw-0.37.0/apps/opencs/model/world/infocollection.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/infocollection.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -106,21 +106,20 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { - std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + - reader.getHNOString ("INAM"); + Info info; + bool isDeleted = false; - if (reader.isNextSub ("DELE")) + info.load (reader, isDeleted); + std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + info.mId; + + if (isDeleted) { int index = searchId (id); - 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) @@ -136,12 +135,9 @@ } else { - Info record; - record.mTopicId = dialogue.mId; - record.mId = id; - record.load (reader); - - load (record, base); + info.mTopicId = dialogue.mId; + info.mId = id; + load (info, base); } } @@ -193,3 +189,39 @@ return Range (begin, end); } + +void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId) +{ + std::string id = Misc::StringUtils::lowerCase(dialogueId); + std::vector erasedRecords; + + std::map::const_iterator current = getIdMap().lower_bound(id); + std::map::const_iterator end = getIdMap().end(); + for (; current != end; ++current) + { + Record record = getRecord(current->second); + + if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId)) + { + if (record.mState == RecordBase::State_ModifiedOnly) + { + erasedRecords.push_back(current->second); + } + else + { + record.mState = RecordBase::State_Deleted; + setRecord(current->second, record); + } + } + else + { + break; + } + } + + while (!erasedRecords.empty()) + { + removeRows(erasedRecords.back(), 1); + erasedRecords.pop_back(); + } +} diff -Nru openmw-0.37.0/apps/opencs/model/world/infocollection.hpp openmw-0.38.0/apps/opencs/model/world/infocollection.hpp --- openmw-0.37.0/apps/opencs/model/world/infocollection.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/infocollection.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -44,6 +44,8 @@ Range getTopicRange (const std::string& topic) const; ///< Return iterators that point to the beginning and past the end of the range for /// the given topic. + + void removeDialogueInfos(const std::string& dialogueId); }; } diff -Nru openmw-0.37.0/apps/opencs/model/world/land.cpp openmw-0.38.0/apps/opencs/model/world/land.cpp --- openmw-0.37.0/apps/opencs/model/world/land.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/land.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,13 +4,12 @@ namespace CSMWorld { - void Land::load(ESM::ESMReader &esm) + void Land::load(ESM::ESMReader &esm, bool &isDeleted) { - ESM::Land::load(esm); + ESM::Land::load(esm, isDeleted); std::ostringstream stream; stream << "#" << mX << " " << mY; - mId = stream.str(); } } diff -Nru openmw-0.37.0/apps/opencs/model/world/land.hpp openmw-0.38.0/apps/opencs/model/world/land.hpp --- openmw-0.37.0/apps/opencs/model/world/land.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/land.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,13 +10,12 @@ /// \brief Wrapper for Land record. Encodes X and Y cell index in the ID. /// /// \todo Add worldspace support to the Land record. - /// \todo Add a proper copy constructor (currently worked around using shared_ptr) struct Land : public ESM::Land { std::string mId; /// Loads the metadata and ID - void load (ESM::ESMReader &esm); + void load (ESM::ESMReader &esm, bool &isDeleted); }; } diff -Nru openmw-0.37.0/apps/opencs/model/world/landtexture.cpp openmw-0.38.0/apps/opencs/model/world/landtexture.cpp --- openmw-0.37.0/apps/opencs/model/world/landtexture.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/landtexture.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,10 +4,9 @@ namespace CSMWorld { - - void LandTexture::load(ESM::ESMReader &esm) + void LandTexture::load(ESM::ESMReader &esm, bool &isDeleted) { - ESM::LandTexture::load(esm); + ESM::LandTexture::load(esm, isDeleted); mPluginIndex = esm.getIndex(); } diff -Nru openmw-0.37.0/apps/opencs/model/world/landtexture.hpp openmw-0.38.0/apps/opencs/model/world/landtexture.hpp --- openmw-0.37.0/apps/opencs/model/world/landtexture.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/landtexture.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -12,7 +12,7 @@ { int mPluginIndex; - void load (ESM::ESMReader &esm); + void load (ESM::ESMReader &esm, bool &isDeleted); }; } diff -Nru openmw-0.37.0/apps/opencs/model/world/nestedcoladapterimp.cpp openmw-0.38.0/apps/opencs/model/world/nestedcoladapterimp.cpp --- openmw-0.37.0/apps/opencs/model/world/nestedcoladapterimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/nestedcoladapterimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -277,7 +277,7 @@ // WARNING: Assumed that the table view has the same order as std::map std::map::iterator iter = reactions.begin(); for(int i = 0; i < rowToRemove; ++i) - iter++; + ++iter; reactions.erase(iter); record.setModified (faction); @@ -314,7 +314,7 @@ // WARNING: Assumed that the table view has the same order as std::map std::map::const_iterator iter = reactions.begin(); for(int i = 0; i < subRowIndex; ++i) - iter++; + ++iter; switch (subColIndex) { case 0: return QString((*iter).first.c_str()); @@ -337,7 +337,7 @@ // WARNING: Assumed that the table view has the same order as std::map std::map::iterator iter = reactions.begin(); for(int i = 0; i < subRowIndex; ++i) - iter++; + ++iter; std::string factionId = (*iter).first; int reaction = (*iter).second; @@ -606,7 +606,7 @@ funcMap["09"] = "PC Fatigue"; funcMap["10"] = "PC Strength"; funcMap["11"] = "PC Block"; - funcMap["12"] = "PC Armoror"; + funcMap["12"] = "PC Armorer"; funcMap["13"] = "PC Medium Armor"; funcMap["14"] = "PC Heavy Armor"; funcMap["15"] = "PC Blunt Weapon"; diff -Nru openmw-0.37.0/apps/opencs/model/world/pathgrid.cpp openmw-0.38.0/apps/opencs/model/world/pathgrid.cpp --- openmw-0.37.0/apps/opencs/model/world/pathgrid.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/pathgrid.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,33 +4,28 @@ #include -void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, const IdCollection& cells) +void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection& cells) { - load (esm); + load (esm, isDeleted); // correct ID if (!mId.empty() && mId[0]!='#' && cells.searchId (mId)==-1) { std::ostringstream stream; - stream << "#" << mData.mX << " " << mData.mY; - mId = stream.str(); } } -void CSMWorld::Pathgrid::load (ESM::ESMReader &esm) +void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted) { - ESM::Pathgrid::load (esm); + ESM::Pathgrid::load (esm, isDeleted); + mId = mCell; if (mCell.empty()) { std::ostringstream stream; - stream << "#" << mData.mX << " " << mData.mY; - mId = stream.str(); } - else - mId = mCell; } diff -Nru openmw-0.37.0/apps/opencs/model/world/pathgrid.hpp openmw-0.38.0/apps/opencs/model/world/pathgrid.hpp --- openmw-0.37.0/apps/opencs/model/world/pathgrid.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/pathgrid.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,9 +20,8 @@ { std::string mId; - void load (ESM::ESMReader &esm, const IdCollection& cells); - - void load (ESM::ESMReader &esm); + void load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection& cells); + void load (ESM::ESMReader &esm, bool &isDeleted); }; } diff -Nru openmw-0.37.0/apps/opencs/model/world/refcollection.cpp openmw-0.38.0/apps/opencs/model/world/refcollection.cpp --- openmw-0.37.0/apps/opencs/model/world/refcollection.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/refcollection.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,12 +19,11 @@ Cell& cell2 = base ? cell.mBase : cell.mModified; CellRef ref; - - bool deleted = false; ESM::MovedCellRef mref; + bool isDeleted = false; // hack to initialise mindex - while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, deleted, true, &mref)) + while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref)) { // Keep mOriginalCell empty when in modified (as an indicator that the // original cell will always be equal the current cell). @@ -49,17 +48,6 @@ // https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30 ref.mOriginalCell = cell2.mId; - if (deleted) - { - // FIXME: how to mark the record deleted? - CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, - mCells.getId (cellIndex)); - - messages.add (id, "Moved reference "+ref.mRefID+" is in DELE state"); - - continue; - } - // It is not always possibe to ignore moved references sub-record and // calculate from coordinates. Some mods may place the ref in positions // outside normal bounds, resulting in non sensical cell id's. This often @@ -91,7 +79,7 @@ break; } - if (deleted) + if (isDeleted) { if (iter==cache.end()) { @@ -99,7 +87,6 @@ mCells.getId (cellIndex)); messages.add (id, "Attempt to delete a non-existing reference"); - continue; } @@ -107,7 +94,7 @@ Record record = getRecord (index); - if (record.mState==RecordBase::State_BaseOnly) + if (base) { removeRows (index, 1); cache.erase (iter); diff -Nru openmw-0.37.0/apps/opencs/model/world/refidadapterimp.cpp openmw-0.38.0/apps/opencs/model/world/refidadapterimp.cpp --- openmw-0.37.0/apps/opencs/model/world/refidadapterimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/refidadapterimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1153,7 +1153,7 @@ const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); - const ESM::Creature creature = record.get(); + const ESM::Creature& creature = record.get(); if (subColIndex == 0) return subRowIndex; @@ -1259,7 +1259,7 @@ const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); - const ESM::Creature creature = record.get(); + const ESM::Creature& creature = record.get(); if (subRowIndex < 0 || subRowIndex > 2 || subColIndex < 0 || subColIndex > 2) throw std::runtime_error ("index out of range"); @@ -1337,7 +1337,7 @@ const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); - const ESM::Creature creature = record.get(); + const ESM::Creature& creature = record.get(); switch (subColIndex) { diff -Nru openmw-0.37.0/apps/opencs/model/world/refidcollection.cpp openmw-0.38.0/apps/opencs/model/world/refidcollection.cpp --- openmw-0.37.0/apps/opencs/model/world/refidcollection.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/refidcollection.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -837,61 +837,7 @@ 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, base); - - 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; - } - } + mData.load(reader, base, type); } int CSMWorld::RefIdCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const diff -Nru openmw-0.37.0/apps/opencs/model/world/refiddata.cpp openmw-0.38.0/apps/opencs/model/world/refiddata.cpp --- openmw-0.37.0/apps/opencs/model/world/refiddata.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/refiddata.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,10 +3,20 @@ #include #include -#include - CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {} + +std::string CSMWorld::RefIdData::getRecordId(const CSMWorld::RefIdData::LocalIndex &index) const +{ + std::map::const_iterator found = + mRecordContainers.find (index.second); + + if (found == mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + return found->second->getId(index.first); +} + CSMWorld::RefIdData::RefIdData() { mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators)); @@ -161,15 +171,27 @@ return index; } -void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base) +void CSMWorld::RefIdData::load (ESM::ESMReader& reader, bool base, CSMWorld::UniversalId::Type type) { - std::map::iterator iter = - mRecordContainers.find (index.second); + std::map::iterator found = + mRecordContainers.find (type); - if (iter==mRecordContainers.end()) - throw std::logic_error ("invalid local index type"); + if (found == mRecordContainers.end()) + throw std::logic_error ("Invalid Referenceable ID type"); - iter->second->load (index.first, reader, base); + int index = found->second->load(reader, base); + if (index != -1) + { + LocalIndex localIndex = LocalIndex(index, type); + if (base && getRecord(localIndex).mState == RecordBase::State_Deleted) + { + erase(localIndex, 1); + } + else + { + mIndex[Misc::StringUtils::lowerCase(getRecordId(localIndex))] = localIndex; + } + } } void CSMWorld::RefIdData::erase (const LocalIndex& index, int count) diff -Nru openmw-0.37.0/apps/opencs/model/world/refiddata.hpp openmw-0.38.0/apps/opencs/model/world/refiddata.hpp --- openmw-0.37.0/apps/opencs/model/world/refiddata.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/refiddata.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -25,6 +25,8 @@ #include #include +#include + #include "record.hpp" #include "universalid.hpp" @@ -49,7 +51,8 @@ virtual void insertRecord (RecordBase& record) = 0; - virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; + virtual int load (ESM::ESMReader& reader, bool base) = 0; + ///< \return index of a loaded record or -1 if no record was loaded virtual void erase (int index, int count) = 0; @@ -73,7 +76,8 @@ virtual void insertRecord (RecordBase& record); - virtual void load (int index, ESM::ESMReader& reader, bool base); + virtual int load (ESM::ESMReader& reader, bool base); + ///< \return index of a loaded record or -1 if no record was loaded virtual void erase (int index, int count); @@ -122,9 +126,58 @@ } template - void RefIdDataContainer::load (int index, ESM::ESMReader& reader, bool base) + int RefIdDataContainer::load (ESM::ESMReader& reader, bool base) { - (base ? mContainer.at (index).mBase : mContainer.at (index).mModified).load (reader); + RecordT record; + bool isDeleted = false; + + record.load(reader, isDeleted); + + int index = 0; + int numRecords = static_cast(mContainer.size()); + for (; index < numRecords; ++index) + { + if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId)) + { + break; + } + } + + if (isDeleted) + { + if (index == numRecords) + { + // deleting a record that does not exist + // ignore it for now + /// \todo report the problem to the user + return -1; + } + + // Flag the record as Deleted even for a base content file. + // RefIdData is responsible for its erasure. + mContainer[index].mState = RecordBase::State_Deleted; + } + else + { + if (index == numRecords) + { + appendRecord(record.mId, base); + if (base) + { + mContainer.back().mBase = record; + } + else + { + mContainer.back().mModified = record; + } + } + else if (!base) + { + mContainer[index].setModified(record); + } + } + + return index; } template @@ -145,19 +198,14 @@ template void RefIdDataContainer::save (int index, ESM::ESMWriter& writer) const { - CSMWorld::RecordBase::State state = mContainer.at (index).mState; + Record record = mContainer.at(index); - if (state==CSMWorld::RecordBase::State_Modified || - state==CSMWorld::RecordBase::State_ModifiedOnly) + if (record.isModified() || record.mState == RecordBase::State_Deleted) { - writer.startRecord (mContainer.at (index).mModified.sRecordId); - writer.writeHNCString ("NAME", getId (index)); - mContainer.at (index).mModified.save (writer); - writer.endRecord (mContainer.at (index).mModified.sRecordId); - } - else if (state==CSMWorld::RecordBase::State_Deleted) - { - /// \todo write record with delete flag + RecordT esmRecord = record.get(); + writer.startRecord(esmRecord.sRecordId); + esmRecord.save(writer, record.mState == RecordBase::State_Deleted); + writer.endRecord(esmRecord.sRecordId); } } @@ -198,6 +246,8 @@ void erase (const LocalIndex& index, int count); ///< Must not spill over into another type. + std::string getRecordId(const LocalIndex &index) const; + public: RefIdData(); @@ -221,7 +271,7 @@ int getAppendIndex (UniversalId::Type type) const; - void load (const LocalIndex& index, ESM::ESMReader& reader, bool base); + void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); int getSize() const; diff -Nru openmw-0.37.0/apps/opencs/model/world/regionmap.cpp openmw-0.38.0/apps/opencs/model/world/regionmap.cpp --- openmw-0.37.0/apps/opencs/model/world/regionmap.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/regionmap.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -168,7 +168,7 @@ { std::vector regions2 (regions); - std::for_each (regions2.begin(), regions2.end(), &Misc::StringUtils::lowerCase); + std::for_each (regions2.begin(), regions2.end(), Misc::StringUtils::lowerCaseInPlace); std::sort (regions2.begin(), regions2.end()); for (std::map::const_iterator iter (mMap.begin()); diff -Nru openmw-0.37.0/apps/opencs/model/world/resourcesmanager.cpp openmw-0.38.0/apps/opencs/model/world/resourcesmanager.cpp --- openmw-0.37.0/apps/opencs/model/world/resourcesmanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/resourcesmanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,7 +19,9 @@ mVFS = vfs; mResources.clear(); - static const char * const sMeshTypes[] = { "nif", 0 }; + // maybe we could go over the osgDB::Registry to list all supported node formats + + static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 }; addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, sMeshTypes)); addResources (Resources (vfs, "icons", UniversalId::Type_Icon)); diff -Nru openmw-0.37.0/apps/opencs/model/world/scriptcontext.cpp openmw-0.38.0/apps/opencs/model/world/scriptcontext.cpp --- openmw-0.37.0/apps/opencs/model/world/scriptcontext.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/scriptcontext.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -93,7 +93,7 @@ { mIds = mData.getIds(); - std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::toLower); + std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCaseInPlace); std::sort (mIds.begin(), mIds.end()); mIdsUpdated = true; diff -Nru openmw-0.37.0/apps/opencs/model/world/subcellcollection.hpp openmw-0.38.0/apps/opencs/model/world/subcellcollection.hpp --- openmw-0.37.0/apps/opencs/model/world/subcellcollection.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/subcellcollection.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,7 +20,7 @@ { const IdCollection& mCells; - virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader); + virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted); public: @@ -29,9 +29,10 @@ template void SubCellCollection::loadRecord (ESXRecordT& record, - ESM::ESMReader& reader) + ESM::ESMReader& reader, + bool& isDeleted) { - record.load (reader, mCells); + record.load (reader, isDeleted, mCells); } template diff -Nru openmw-0.37.0/apps/opencs/model/world/tablemimedata.cpp openmw-0.38.0/apps/opencs/model/world/tablemimedata.cpp --- openmw-0.37.0/apps/opencs/model/world/tablemimedata.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/model/world/tablemimedata.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -179,7 +179,7 @@ } } - throw std::runtime_error ("TableMimeData object does not hold object of the seeked type"); + throw std::runtime_error ("TableMimeData object does not hold object of the sought type"); } CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const @@ -201,7 +201,7 @@ } } - throw std::runtime_error ("TableMimeData object does not hold object of the seeked type"); + throw std::runtime_error ("TableMimeData object does not hold object of the sought type"); } bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const diff -Nru openmw-0.37.0/apps/opencs/view/doc/filewidget.cpp openmw-0.38.0/apps/opencs/view/doc/filewidget.cpp --- openmw-0.37.0/apps/opencs/view/doc/filewidget.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/doc/filewidget.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -16,7 +16,6 @@ QHBoxLayout *layout = new QHBoxLayout (this); mInput = new QLineEdit (this); - mInput->setValidator (new QRegExpValidator(QRegExp("^[a-zA-Z0-9_-\\s]*$"))); layout->addWidget (mInput, 1); diff -Nru openmw-0.37.0/apps/opencs/view/doc/startup.cpp openmw-0.38.0/apps/opencs/view/doc/startup.cpp --- openmw-0.37.0/apps/opencs/view/doc/startup.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/doc/startup.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -104,14 +104,16 @@ layout->addWidget (createButtons()); layout->addWidget (createTools()); - /// \todo remove this label once loading and saving are fully implemented - QLabel *warning = new QLabel ("WARNING:

OpenCS is in alpha stage.
The code for loading and saving is incomplete.
This version of OpenCS is only a preview.
Do NOT use it for real editing!
You will lose records both on loading and on saving.

Please note:
If you lose data and come to the OpenMW forum to complain,
we will mock you.
"); + /// \todo remove this label once we are feature complete and convinced that this thing is + /// working properly. + QLabel *warning = new QLabel ("WARNING: OpenMW-CS is in alpha stage.

The editor is not feature complete and not sufficiently tested.
In theory your data should be safe. But we strongly advice to make backups regularly if you are working with live data.
"); QFont font; font.setPointSize (12); font.setBold (true); warning->setFont (font); + warning->setWordWrap (true); layout->addWidget (warning, 1); diff -Nru openmw-0.37.0/apps/opencs/view/doc/subview.cpp openmw-0.38.0/apps/opencs/view/doc/subview.cpp --- openmw-0.37.0/apps/opencs/view/doc/subview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/doc/subview.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -16,7 +16,7 @@ emit closeRequest(); return true; } - + return QDockWidget::event (event); } @@ -38,9 +38,6 @@ void CSVDoc::SubView::useHint (const std::string& hint) {} -void CSVDoc::SubView::updateUserSetting (const QString &, const QStringList &) -{} - void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id) { mUniversalId = id; diff -Nru openmw-0.37.0/apps/opencs/view/doc/subview.hpp openmw-0.38.0/apps/opencs/view/doc/subview.hpp --- openmw-0.37.0/apps/opencs/view/doc/subview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/doc/subview.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -52,8 +52,6 @@ virtual std::string getTitle() const; - virtual void updateUserSetting (const QString& name, const QStringList& value); - private: void closeEvent (QCloseEvent *event); diff -Nru openmw-0.37.0/apps/opencs/view/doc/view.cpp openmw-0.38.0/apps/opencs/view/doc/view.cpp --- openmw-0.37.0/apps/opencs/view/doc/view.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/doc/view.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -15,7 +15,7 @@ #include #include "../../model/doc/document.hpp" -#include "../../model/settings/usersettings.hpp" +#include "../../model/prefs/state.hpp" #include "../../model/world/idtable.hpp" @@ -121,10 +121,9 @@ mShowStatusBar = new QAction (tr ("Show Status Bar"), this); mShowStatusBar->setCheckable (true); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); - std::string showStatusBar = - CSMSettings::UserSettings::instance().settingValue("window/show-statusbar").toStdString(); - if(showStatusBar == "true") - mShowStatusBar->setChecked(true); + + mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); + view->addAction (mShowStatusBar); QAction *filters = new QAction (tr ("Filters"), this); @@ -333,9 +332,9 @@ if (mViewTotal>1) stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]"; - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; - bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" && + bool hideTitle = windows["hide-subview"].isTrue() && mSubViews.size()==1 && !mSubViews.at (0)->isFloating(); if (hideTitle) @@ -346,19 +345,18 @@ void CSVDoc::View::updateSubViewIndicies(SubView *view) { + CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; + if(view && mSubViews.contains(view)) { mSubViews.removeOne(view); // adjust (reduce) the scroll area (even floating), except when it is "Scrollbar Only" - CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance(); - if(settings.settingValue ("window/mainwindow-scrollbar") == "Grow then Scroll") + if (windows["mainwindow-scrollbar"].toString() == "Grow then Scroll") updateScrollbar(); } - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); - - bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" && + bool hideTitle = windows["hide-subview"].isTrue() && mSubViews.size()==1 && !mSubViews.at (0)->isFloating(); updateTitle(); @@ -406,21 +404,16 @@ : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews), mScroll(0), mScrollbarOnly(false) { - int width = CSMSettings::UserSettings::instance().settingValue - ("window/default-width").toInt(); - - int height = CSMSettings::UserSettings::instance().settingValue - ("window/default-height").toInt(); + CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; - width = std::max(width, 300); - height = std::max(height, 300); + int width = std::max (windows["default-width"].toInt(), 300); + int height = std::max (windows["default-height"].toInt(), 300); resize (width, height); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); - CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance(); - if(settings.settingValue ("window/mainwindow-scrollbar") == "Grow Only") + if (windows["mainwindow-scrollbar"].toString() == "Grow Only") { setCentralWidget (&mSubViewWindow); } @@ -449,6 +442,9 @@ mSubViewFactory.add (CSMWorld::UniversalId::Type_RunLog, new SubViewFactory); connect (mOperations, SIGNAL (abortOperation (int)), this, SLOT (abortOperation (int))); + + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); } CSVDoc::View::~View() @@ -503,14 +499,12 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::string& hint) { - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; bool isReferenceable = id.getClass() == CSMWorld::UniversalId::Class_RefRecord; // User setting to reuse sub views (on a per top level view basis) - bool reuse = - userSettings.setting ("window/reuse", QString("true")) == "true" ? true : false; - if(reuse) + if (windows["reuse"].isTrue()) { foreach(SubView *sb, mSubViews) { @@ -538,8 +532,7 @@ // // If the sub view limit setting is one, the sub view title bar is hidden and the // text in the main title bar is adjusted accordingly - int maxSubView = userSettings.setting("window/max-subviews", QString("256")).toInt(); - if(mSubViews.size() >= maxSubView) // create a new top level view + if(mSubViews.size() >= windows["max-subviews"].toInt()) // create a new top level view { mViewManager.addView(mDocument, id, hint); @@ -559,8 +552,8 @@ view->setParent(this); mSubViews.append(view); // only after assert - int minWidth = userSettings.setting ("window/minimum-width", QString("325")).toInt(); - view->setMinimumWidth(minWidth); + int minWidth = windows["minimum-width"].toInt(); + view->setMinimumWidth (minWidth); view->setStatusBar (mShowStatusBar->isChecked()); @@ -575,13 +568,11 @@ // should become visible) // - Move the scroll bar to the newly added subview // - CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance(); - QString mainwinScroll = settings.settingValue ("window/mainwindow-scrollbar"); - mScrollbarOnly = mainwinScroll.isEmpty() || mainwinScroll == "Scrollbar Only"; + mScrollbarOnly = windows["mainwindow-scrollbar"].toString() == "Scrollbar Only"; QDesktopWidget *dw = QApplication::desktop(); QRect rect; - if(settings.settingValue ("window/grow-limit") == "true") + if (windows["grow-limit"].isTrue()) rect = dw->screenGeometry(this); else rect = dw->screenGeometry(dw->screen(dw->screenNumber(this))); @@ -637,6 +628,45 @@ } } +void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) +{ + if (*setting=="Windows/hide-subview") + updateSubViewIndicies (0); + else if (*setting=="Windows/mainwindow-scrollbar") + { + if (setting->toString()!="Grow Only") + { + if (mScroll) + { + if (setting->toString()=="Scrollbar Only") + { + mScrollbarOnly = true; + mSubViewWindow.setMinimumWidth(0); + } + else if (mScrollbarOnly) + { + mScrollbarOnly = false; + updateScrollbar(); + } + } + else + { + mScroll = new QScrollArea(this); + mScroll->setWidgetResizable(true); + mScroll->setWidget(&mSubViewWindow); + setCentralWidget(mScroll); + } + } + else if (mScroll) + { + mScroll->takeWidget(); + setCentralWidget (&mSubViewWindow); + mScroll->deleteLater(); + mScroll = 0; + } + } +} + void CSVDoc::View::newView() { mViewManager.addView (mDocument); @@ -860,59 +890,6 @@ resize (geometry().width(), height); } -void CSVDoc::View::updateUserSetting (const QString &name, const QStringList &list) -{ - if (name=="window/hide-subview") - updateSubViewIndicies (0); - - foreach (SubView *subView, mSubViews) - { - subView->updateUserSetting (name, list); - } - - if (name=="window/mainwindow-scrollbar") - { - if(list.at(0) != "Grow Only") - { - if (mScroll) - { - if (list.at(0).isEmpty() || list.at(0) == "Scrollbar Only") - { - mScrollbarOnly = true; - mSubViewWindow.setMinimumWidth(0); - } - else - { - if(!mScrollbarOnly) - return; - - mScrollbarOnly = false; - updateScrollbar(); - } - } - else - { - mScroll = new QScrollArea(this); - mScroll->setWidgetResizable(true); - mScroll->setWidget(&mSubViewWindow); - setCentralWidget(mScroll); - } - } - else - { - if (mScroll) - { - mScroll->takeWidget(); - setCentralWidget (&mSubViewWindow); - mScroll->deleteLater(); - mScroll = 0; - } - else - return; - } - } -} - void CSVDoc::View::toggleShowStatusBar (bool show) { foreach (QObject *view, mSubViewWindow.children()) @@ -944,10 +921,9 @@ void CSVDoc::View::closeRequest (SubView *subView) { - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; - if (mSubViews.size()>1 || mViewTotal<=1 || - userSettings.setting ("window/hide-subview", QString ("false"))!="true") + if (mSubViews.size()>1 || mViewTotal<=1 || !windows["hide-subview"].isTrue()) { subView->deleteLater(); mSubViews.removeOne (subView); diff -Nru openmw-0.37.0/apps/opencs/view/doc/view.hpp openmw-0.38.0/apps/opencs/view/doc/view.hpp --- openmw-0.37.0/apps/opencs/view/doc/view.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/doc/view.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -22,6 +22,11 @@ class UniversalId; } +namespace CSMPrefs +{ + class Setting; +} + namespace CSVDoc { class ViewManager; @@ -83,8 +88,6 @@ void exitApplication(); - void loadUserSettings(); - /// User preference function void resizeViewWidth (int width); @@ -137,8 +140,6 @@ void abortOperation (int type); - void updateUserSetting (const QString &, const QStringList &); - void updateTitle(); // called when subviews are added or removed @@ -146,6 +147,8 @@ private slots: + void settingChanged (const CSMPrefs::Setting *setting); + void newView(); void save(); diff -Nru openmw-0.37.0/apps/opencs/view/doc/viewmanager.cpp openmw-0.38.0/apps/opencs/view/doc/viewmanager.cpp --- openmw-0.37.0/apps/opencs/view/doc/viewmanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/doc/viewmanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -14,6 +14,8 @@ #include "../../model/world/universalid.hpp" #include "../../model/world/idcompletionmanager.hpp" +#include "../../model/prefs/state.hpp" + #include "../world/util.hpp" #include "../world/enumdelegate.hpp" #include "../world/vartypedelegate.hpp" @@ -22,8 +24,6 @@ #include "../world/idcompletiondelegate.hpp" #include "../world/colordelegate.hpp" -#include "../../model/settings/usersettings.hpp" - #include "view.hpp" void CSVDoc::ViewManager::updateIndices() @@ -165,10 +165,7 @@ mViews.push_back (view); - std::string showStatusBar = - CSMSettings::UserSettings::instance().settingValue("window/show-statusbar").toStdString(); - - view->toggleStatusBar (showStatusBar == "true"); + view->toggleStatusBar (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); view->show(); connect (view, SIGNAL (newGameRequest ()), this, SIGNAL (newGameRequest())); @@ -177,11 +174,6 @@ connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest())); connect (view, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SIGNAL (mergeDocument (CSMDoc::Document *))); - connect (&CSMSettings::UserSettings::instance(), - SIGNAL (userSettingUpdated(const QString &, const QStringList &)), - view, - SLOT (updateUserSetting (const QString &, const QStringList &))); - updateIndices(); return view; diff -Nru openmw-0.37.0/apps/opencs/view/prefs/dialogue.cpp openmw-0.38.0/apps/opencs/view/prefs/dialogue.cpp --- openmw-0.37.0/apps/opencs/view/prefs/dialogue.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/prefs/dialogue.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,126 @@ + +#include "dialogue.hpp" + +#include +#include +#include +#include +#include +#include + +#include "../../model/prefs/state.hpp" + +#include "page.hpp" + +void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main) +{ + mList = new QListWidget (main); + mList->setMinimumWidth (50); + mList->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding); + + mList->setSelectionBehavior (QAbstractItemView::SelectItems); + + main->addWidget (mList); + + QFontMetrics metrics (QApplication::font()); + + int maxWidth = 1; + + for (CSMPrefs::State::Iterator iter = CSMPrefs::get().begin(); iter!=CSMPrefs::get().end(); + ++iter) + { + QString label = QString::fromUtf8 (iter->second.getKey().c_str()); + maxWidth = std::max (maxWidth, metrics.width (label)); + + mList->addItem (label); + } + + mList->setMaximumWidth (maxWidth + 10); + + connect (mList, SIGNAL (currentItemChanged (QListWidgetItem *, QListWidgetItem *)), + this, SLOT (selectionChanged (QListWidgetItem *, QListWidgetItem *))); +} + +void CSVPrefs::Dialogue::buildContentArea (QSplitter *main) +{ + mContent = new QStackedWidget (main); + mContent->setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Expanding); + + main->addWidget (mContent); +} + +CSVPrefs::PageBase *CSVPrefs::Dialogue::makePage (const std::string& key) +{ + // special case page code goes here + + return new Page (CSMPrefs::get()[key], mContent); +} + +CSVPrefs::Dialogue::Dialogue() +{ + setWindowTitle ("User Settings"); + + setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + + setMinimumSize (600, 400); + + QSplitter *main = new QSplitter (this); + + setCentralWidget (main); + buildCategorySelector (main); + buildContentArea (main); +} + +CSVPrefs::Dialogue::~Dialogue() +{ + if (isVisible()) + CSMPrefs::State::get().save(); +} + +void CSVPrefs::Dialogue::closeEvent (QCloseEvent *event) +{ + QMainWindow::closeEvent (event); + + CSMPrefs::State::get().save(); +} + +void CSVPrefs::Dialogue::show() +{ + if (QWidget *active = QApplication::activeWindow()) + { + // place at the centre of the window with focus + QSize size = active->size(); + move (active->geometry().x()+(size.width() - frameGeometry().width())/2, + active->geometry().y()+(size.height() - frameGeometry().height())/2); + } + else + { + // otherwise place at the centre of the screen + QPoint screenCenter = QApplication::desktop()->screenGeometry().center(); + move (screenCenter - QPoint(frameGeometry().width()/2, frameGeometry().height()/2)); + } + + QWidget::show(); +} + +void CSVPrefs::Dialogue::selectionChanged (QListWidgetItem *current, QListWidgetItem *previous) +{ + if (current) + { + std::string key = current->text().toUtf8().data(); + + for (int i=0; icount(); ++i) + { + PageBase& page = dynamic_cast (*mContent->widget (i)); + + if (page.getCategory().getKey()==key) + { + mContent->setCurrentIndex (i); + return; + } + } + + PageBase *page = makePage (key); + mContent->setCurrentIndex (mContent->addWidget (page)); + } +} diff -Nru openmw-0.37.0/apps/opencs/view/prefs/dialogue.hpp openmw-0.38.0/apps/opencs/view/prefs/dialogue.hpp --- openmw-0.37.0/apps/opencs/view/prefs/dialogue.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/prefs/dialogue.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,50 @@ +#ifndef CSV_PREFS_DIALOGUE_H +#define CSV_PREFS_DIALOGUE_H + +#include + +class QSplitter; +class QListWidget; +class QStackedWidget; +class QListWidgetItem; + +namespace CSVPrefs +{ + class PageBase; + + class Dialogue : public QMainWindow + { + Q_OBJECT + + QListWidget *mList; + QStackedWidget *mContent; + + private: + + void buildCategorySelector (QSplitter *main); + + void buildContentArea (QSplitter *main); + + PageBase *makePage (const std::string& key); + + public: + + Dialogue(); + + virtual ~Dialogue(); + + protected: + + void closeEvent (QCloseEvent *event); + + public slots: + + void show(); + + private slots: + + void selectionChanged (QListWidgetItem *current, QListWidgetItem *previous); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/view/prefs/pagebase.cpp openmw-0.38.0/apps/opencs/view/prefs/pagebase.cpp --- openmw-0.37.0/apps/opencs/view/prefs/pagebase.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/prefs/pagebase.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,13 @@ + +#include "pagebase.hpp" + +#include "../../model/prefs/category.hpp" + +CSVPrefs::PageBase::PageBase (CSMPrefs::Category& category, QWidget *parent) +: QScrollArea (parent), mCategory (category) +{} + +CSMPrefs::Category& CSVPrefs::PageBase::getCategory() +{ + return mCategory; +} diff -Nru openmw-0.37.0/apps/opencs/view/prefs/pagebase.hpp openmw-0.38.0/apps/opencs/view/prefs/pagebase.hpp --- openmw-0.37.0/apps/opencs/view/prefs/pagebase.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/prefs/pagebase.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,27 @@ +#ifndef CSV_PREFS_PAGEBASE_H +#define CSV_PREFS_PAGEBASE_H + +#include + +namespace CSMPrefs +{ + class Category; +} + +namespace CSVPrefs +{ + class PageBase : public QScrollArea + { + Q_OBJECT + + CSMPrefs::Category& mCategory; + + public: + + PageBase (CSMPrefs::Category& category, QWidget *parent); + + CSMPrefs::Category& getCategory(); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/view/prefs/page.cpp openmw-0.38.0/apps/opencs/view/prefs/page.cpp --- openmw-0.37.0/apps/opencs/view/prefs/page.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/prefs/page.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,40 @@ + +#include "page.hpp" + +#include + +#include "../../model/prefs/setting.hpp" +#include "../../model/prefs/category.hpp" + +CSVPrefs::Page::Page (CSMPrefs::Category& category, QWidget *parent) +: PageBase (category, parent) +{ + QWidget *widget = new QWidget (parent); + mGrid = new QGridLayout (widget); + + for (CSMPrefs::Category::Iterator iter = category.begin(); iter!=category.end(); ++iter) + addSetting (*iter); + + setWidget (widget); +} + +void CSVPrefs::Page::addSetting (CSMPrefs::Setting *setting) +{ + std::pair widgets = setting->makeWidgets (this); + + int next = mGrid->rowCount(); + + if (widgets.first) + { + mGrid->addWidget (widgets.first, next, 0); + mGrid->addWidget (widgets.second, next, 1); + } + else if (widgets.second) + { + mGrid->addWidget (widgets.second, next, 0, 1, 2); + } + else + { + mGrid->addWidget (new QWidget (this), next, 0); + } +} diff -Nru openmw-0.37.0/apps/opencs/view/prefs/page.hpp openmw-0.38.0/apps/opencs/view/prefs/page.hpp --- openmw-0.37.0/apps/opencs/view/prefs/page.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/prefs/page.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,29 @@ +#ifndef CSV_PREFS_PAGE_H +#define CSV_PREFS_PAGE_H + +#include "pagebase.hpp" + +class QGridLayout; + +namespace CSMPrefs +{ + class Setting; +} + +namespace CSVPrefs +{ + class Page : public PageBase + { + Q_OBJECT + + QGridLayout *mGrid; + + public: + + Page (CSMPrefs::Category& category, QWidget *parent); + + void addSetting (CSMPrefs::Setting *setting); + }; +} + +#endif diff -Nru openmw-0.37.0/apps/opencs/view/render/cellarrow.cpp openmw-0.38.0/apps/opencs/view/render/cellarrow.cpp --- openmw-0.37.0/apps/opencs/view/render/cellarrow.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/cellarrow.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -18,6 +18,33 @@ return mArrow; } +QString CSVRender::CellArrowTag::getToolTip (bool hideBasics) const +{ + QString text ("Direction: "); + + switch (mArrow->getDirection()) + { + case CellArrow::Direction_North: text += "North"; break; + case CellArrow::Direction_West: text += "West"; break; + case CellArrow::Direction_South: text += "South"; break; + case CellArrow::Direction_East: text += "East"; break; + } + + if (!hideBasics) + { + text += + "

" + "Modify which cells are shown" + "

  • Primary-Edit: Add cell in given direction
  • " + "
  • Secondary-Edit: Add cell and remove old cell
  • " + "
  • Shift Primary-Edit: Add cells in given direction
  • " + "
  • Shift Secondary-Edit: Add cells and remove old cells
  • " + "
"; + } + + return text; +} + void CSVRender::CellArrow::adjustTransform() { diff -Nru openmw-0.37.0/apps/opencs/view/render/cellarrow.hpp openmw-0.38.0/apps/opencs/view/render/cellarrow.hpp --- openmw-0.37.0/apps/opencs/view/render/cellarrow.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/cellarrow.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -26,6 +26,8 @@ CellArrowTag (CellArrow *arrow); CellArrow *getCellArrow() const; + + virtual QString getToolTip (bool hideBasics) const; }; diff -Nru openmw-0.37.0/apps/opencs/view/render/editmode.cpp openmw-0.38.0/apps/opencs/view/render/editmode.cpp --- openmw-0.37.0/apps/opencs/view/render/editmode.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/editmode.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -24,11 +24,6 @@ mWorldspaceWidget->clearSelection (~mMask); } -void CSVRender::EditMode::updateUserSetting (const QString& name, const QStringList& value) -{ - -} - void CSVRender::EditMode::setEditLock (bool locked) { @@ -69,3 +64,9 @@ void CSVRender::EditMode::dragAborted() {} void CSVRender::EditMode::dragWheel (int diff, double speedFactor) {} + +void CSVRender::EditMode::dragEnterEvent (QDragEnterEvent *event) {} + +void CSVRender::EditMode::dropEvent (QDropEvent* event) {} + +void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {} diff -Nru openmw-0.37.0/apps/opencs/view/render/editmode.hpp openmw-0.38.0/apps/opencs/view/render/editmode.hpp --- openmw-0.37.0/apps/opencs/view/render/editmode.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/editmode.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -5,6 +5,10 @@ #include "../widget/modebutton.hpp" +class QDragEnterEvent; +class QDropEvent; +class QDragMoveEvent; + namespace CSVRender { class WorldspaceWidget; @@ -30,9 +34,6 @@ virtual void activate (CSVWidget::SceneToolbar *toolbar); - /// Default-implementation: Do nothing. - virtual void updateUserSetting (const QString& name, const QStringList& value); - /// Default-implementation: Ignored. virtual void setEditLock (bool locked); @@ -82,6 +83,15 @@ /// Default-implementation: ignored virtual void dragWheel (int diff, double speedFactor); + + /// Default-implementation: ignored + virtual void dragEnterEvent (QDragEnterEvent *event); + + /// Default-implementation: ignored + virtual void dropEvent (QDropEvent* event); + + /// Default-implementation: ignored + virtual void dragMoveEvent (QDragMoveEvent *event); }; } diff -Nru openmw-0.37.0/apps/opencs/view/render/instancemode.cpp openmw-0.38.0/apps/opencs/view/render/instancemode.cpp --- openmw-0.37.0/apps/opencs/view/render/instancemode.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/instancemode.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,7 +1,7 @@ #include "instancemode.hpp" -#include "../../model/settings/usersettings.hpp" +#include "../../model/prefs/state.hpp" #include "elements.hpp" #include "object.hpp" @@ -9,33 +9,20 @@ CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":placeholder"), Element_Reference, "Instance editing", - parent), mContextSelect (false) + parent) { } -void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) -{ - EditMode::activate (toolbar); - - mContextSelect = CSMSettings::UserSettings::instance().setting ("scene-input/context-select")=="true"; -} - -void CSVRender::InstanceMode::updateUserSetting (const QString& name, const QStringList& value) -{ - if (name=="scene-input/context-select") - mContextSelect = value.at (0)=="true"; -} - void CSVRender::InstanceMode::primaryEditPressed (osg::ref_ptr tag) { - if (mContextSelect) + if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) primarySelectPressed (tag); } void CSVRender::InstanceMode::secondaryEditPressed (osg::ref_ptr tag) { - if (mContextSelect) + if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) secondarySelectPressed (tag); } diff -Nru openmw-0.37.0/apps/opencs/view/render/instancemode.hpp openmw-0.38.0/apps/opencs/view/render/instancemode.hpp --- openmw-0.37.0/apps/opencs/view/render/instancemode.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/instancemode.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -9,16 +9,10 @@ { Q_OBJECT - bool mContextSelect; - public: InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent = 0); - virtual void activate (CSVWidget::SceneToolbar *toolbar); - - virtual void updateUserSetting (const QString& name, const QStringList& value); - virtual void primaryEditPressed (osg::ref_ptr tag); virtual void secondaryEditPressed (osg::ref_ptr tag); diff -Nru openmw-0.37.0/apps/opencs/view/render/object.cpp openmw-0.38.0/apps/opencs/view/render/object.cpp --- openmw-0.37.0/apps/opencs/view/render/object.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/object.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -42,6 +42,11 @@ : TagBase (Element_Reference), mObject (object) {} +QString CSVRender::ObjectTag::getToolTip (bool hideBasics) const +{ + return QString::fromUtf8 (mObject->getReferenceableId().c_str()); +} + void CSVRender::Object::clear() { diff -Nru openmw-0.37.0/apps/opencs/view/render/object.hpp openmw-0.38.0/apps/opencs/view/render/object.hpp --- openmw-0.37.0/apps/opencs/view/render/object.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/object.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -46,6 +46,8 @@ ObjectTag (Object* object); Object* mObject; + + virtual QString getToolTip (bool hideBasics) const; }; diff -Nru openmw-0.37.0/apps/opencs/view/render/scenewidget.cpp openmw-0.38.0/apps/opencs/view/render/scenewidget.cpp --- openmw-0.37.0/apps/opencs/view/render/scenewidget.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/scenewidget.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -16,7 +16,6 @@ #include #include "../widget/scenetoolmode.hpp" -#include "../../model/settings/usersettings.hpp" #include "lighting.hpp" @@ -126,7 +125,7 @@ // Qt5 is currently crashing and reporting "Cannot make QOpenGLContext current in a different thread" when the viewer is run multi-threaded, this is regression from Qt4 osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::SingleThreaded; #else - osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::CullDrawThreadPerContext; + osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::DrawThreadPerContext; #endif setThreadingModel(threadingModel); diff -Nru openmw-0.37.0/apps/opencs/view/render/tagbase.cpp openmw-0.38.0/apps/opencs/view/render/tagbase.cpp --- openmw-0.37.0/apps/opencs/view/render/tagbase.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/tagbase.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,3 +7,8 @@ { return mElement; } + +QString CSVRender::TagBase::getToolTip (bool hideBasics) const +{ + return ""; +} diff -Nru openmw-0.37.0/apps/opencs/view/render/tagbase.hpp openmw-0.38.0/apps/opencs/view/render/tagbase.hpp --- openmw-0.37.0/apps/opencs/view/render/tagbase.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/tagbase.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,6 +3,8 @@ #include +#include + #include "elements.hpp" namespace CSVRender @@ -16,6 +18,9 @@ TagBase (Elements element); Elements getElement() const; + + virtual QString getToolTip (bool hideBasics) const; + }; } diff -Nru openmw-0.37.0/apps/opencs/view/render/terrainstorage.cpp openmw-0.38.0/apps/opencs/view/render/terrainstorage.cpp --- openmw-0.37.0/apps/opencs/view/render/terrainstorage.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/terrainstorage.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -37,9 +37,7 @@ return ltex; } - std::stringstream error; - error << "Can't find LandTexture " << index << " from plugin " << plugin; - throw std::runtime_error(error.str()); + return NULL; } void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) diff -Nru openmw-0.37.0/apps/opencs/view/render/worldspacewidget.cpp openmw-0.38.0/apps/opencs/view/render/worldspacewidget.cpp --- openmw-0.37.0/apps/opencs/view/render/worldspacewidget.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/worldspacewidget.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -19,7 +20,7 @@ #include "../../model/world/universalid.hpp" #include "../../model/world/idtable.hpp" -#include "../../model/settings/usersettings.hpp" +#include "../../model/prefs/state.hpp" #include "../widget/scenetoolmode.hpp" #include "../widget/scenetooltoggle2.hpp" @@ -30,20 +31,10 @@ #include "editmode.hpp" #include "instancemode.hpp" -namespace -{ - static const char * const sMappingSettings[] = - { - "p-navi", "s-navi", - "p-edit", "s-edit", - "p-select", "s-select", - 0 - }; -} - CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent) : SceneWidget (document.getData().getResourceSystem(), parent), mSceneElements(0), mRun(0), mDocument(document), - mInteractionMask (0), mEditMode (0), mLocked (false), mDragging (false) + mInteractionMask (0), mEditMode (0), mLocked (false), mDragging (false), + mToolTipPos (-1, -1) { setAcceptDrops(true); @@ -75,22 +66,36 @@ connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (debugProfileAboutToBeRemoved (const QModelIndex&, int, int))); - for (int i=0; sMappingSettings[i]; ++i) - { - QString key ("scene-input/"); - key += sMappingSettings[i]; - storeMappingSetting (key, CSMSettings::UserSettings::instance().settingValue (key)); - } + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); + CSMPrefs::get()["3D Scene Input"].update(); + CSMPrefs::get()["Tooltips"].update(); - mDragFactor = CSMSettings::UserSettings::instance().settingValue ("scene-input/drag-factor").toDouble(); - mDragWheelFactor = CSMSettings::UserSettings::instance().settingValue ("scene-input/drag-wheel-factor").toDouble(); - mDragShiftFactor = CSMSettings::UserSettings::instance().settingValue ("scene-input/drag-shift-factor").toDouble(); + mToolTipDelayTimer.setSingleShot (true); + connect (&mToolTipDelayTimer, SIGNAL (timeout()), this, SLOT (showToolTip())); } CSVRender::WorldspaceWidget::~WorldspaceWidget () { } +void CSVRender::WorldspaceWidget::settingChanged (const CSMPrefs::Setting *setting) +{ + if (storeMappingSetting (setting)) + return; + + if (*setting=="3D Scene Input/drag-factor") + mDragFactor = setting->toDouble(); + else if (*setting=="3D Scene Input/drag-wheel-factor") + mDragWheelFactor = setting->toDouble(); + else if (*setting=="3D Scene Input/drag-shift-factor") + mDragShiftFactor = setting->toDouble(); + else if (*setting=="Tooltips/scene-delay") + mToolTipDelay = setting->toInt(); + else if (*setting=="Tooltips/scene") + mShowToolTips = setting->isTrue(); +} + void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) { if (mode=="1st") @@ -283,21 +288,6 @@ return mInteractionMask & getVisibilityMask(); } -void CSVRender::WorldspaceWidget::updateUserSetting (const QString& name, const QStringList& value) -{ - if (!value.isEmpty() && storeMappingSetting (name, value.first())) - return; - - if (name=="scene-input/drag-factor") - mDragFactor = value.at (0).toDouble(); - else if (name=="scene-input/drag-wheel-factor") - mDragWheelFactor = value.at (0).toDouble(); - else if (name=="scene-input/drag-shift-factor") - mDragShiftFactor = value.at (0).toDouble(); - else - dynamic_cast (*mEditMode->getCurrent()).updateUserSetting (name, value); -} - void CSVRender::WorldspaceWidget::setEditLock (bool locked) { dynamic_cast (*mEditMode->getCurrent()).setEditLock (locked); @@ -327,52 +317,87 @@ void CSVRender::WorldspaceWidget::dragEnterEvent (QDragEnterEvent* event) { - event->accept(); + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped + return; + + if (mime->fromDocument (mDocument)) + { + if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) || + mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) || + mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile)) + { + // These drops are handled through the subview object. + event->accept(); + } + else + dynamic_cast (*mEditMode->getCurrent()).dragEnterEvent (event); + } } void CSVRender::WorldspaceWidget::dragMoveEvent(QDragMoveEvent *event) { - event->accept(); -} + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped + return; + if (mime->fromDocument (mDocument)) + { + if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) || + mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) || + mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile)) + { + // These drops are handled through the subview object. + event->accept(); + } + else + dynamic_cast (*mEditMode->getCurrent()).dragMoveEvent (event); + } +} -bool CSVRender::WorldspaceWidget::storeMappingSetting (const QString& key, const QString& value) +bool CSVRender::WorldspaceWidget::storeMappingSetting (const CSMPrefs::Setting *setting) { - const QString prefix = "scene-input/"; + if (setting->getParent()->getKey()!="3D Scene Input") + return false; - if (key.startsWith (prefix)) + static const char * const sMappingSettings[] = { - QString key2 (key.mid (prefix.length())); + "p-navi", "s-navi", + "p-edit", "s-edit", + "p-select", "s-select", + 0 + }; - for (int i=0; sMappingSettings[i]; ++i) - if (key2==sMappingSettings[i]) - { - Qt::MouseButton button = Qt::NoButton; + for (int i=0; sMappingSettings[i]; ++i) + if (setting->getKey()==sMappingSettings[i]) + { + QString value = QString::fromUtf8 (setting->toString().c_str()); - if (value.endsWith ("Left Mouse-Button")) - button = Qt::LeftButton; - else if (value.endsWith ("Right Mouse-Button")) - button = Qt::RightButton; - else if (value.endsWith ("Middle Mouse-Button")) - button = Qt::MiddleButton; - else - return false; + Qt::MouseButton button = Qt::NoButton; - bool ctrl = value.startsWith ("Ctrl-"); + if (value.endsWith ("Left Mouse-Button")) + button = Qt::LeftButton; + else if (value.endsWith ("Right Mouse-Button")) + button = Qt::RightButton; + else if (value.endsWith ("Middle Mouse-Button")) + button = Qt::MiddleButton; + else + return false; - mButtonMapping[std::make_pair (button, ctrl)] = sMappingSettings[i]; - return true; - } - } + bool ctrl = value.startsWith ("Ctrl-"); + + mButtonMapping[std::make_pair (button, ctrl)] = sMappingSettings[i]; + return true; + } return false; } -osg::ref_ptr CSVRender::WorldspaceWidget::mousePick (QMouseEvent *event) +osg::ref_ptr CSVRender::WorldspaceWidget::mousePick (const QPoint& localPos) { // (0,0) is considered the lower left corner of an OpenGL window - int x = event->x(); - int y = height() - event->y(); + int x = localPos.x(); + int y = height() - localPos.y(); osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y)); @@ -432,8 +457,15 @@ if (mime->fromDocument (mDocument)) { - emit dataDropped(mime->getData()); - } //not handling drops from different documents at the moment + if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) || + mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) || + mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile)) + { + emit dataDropped(mime->getData()); + } + else + dynamic_cast (*mEditMode->getCurrent()).dropEvent (event); + } } void CSVRender::WorldspaceWidget::runRequest (const std::string& profile) @@ -494,6 +526,20 @@ mDragging = false; } +void CSVRender::WorldspaceWidget::showToolTip() +{ + if (mShowToolTips) + { + QPoint pos = QCursor::pos(); + + if (osg::ref_ptr tag = mousePick (mapFromGlobal (pos))) + { + bool hideBasics = CSMPrefs::get()["Tooltips"]["scene-hide-basic"].isTrue(); + QToolTip::showText (pos, tag->getToolTip (hideBasics), this); + } + } +} + void CSVRender::WorldspaceWidget::elementSelectionChanged() { setVisibilityMask (getVisibilityMask()); @@ -509,13 +555,23 @@ { if (!mDragging) { - if (mDragMode=="p-navi" || mDragMode=="s-navi") + if (mDragMode.empty()) + { + if (event->globalPos()!=mToolTipPos) + { + mToolTipPos = event->globalPos(); + + if (mShowToolTips) + mToolTipDelayTimer.start (mToolTipDelay); + } + } + else if (mDragMode=="p-navi" || mDragMode=="s-navi") { } else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="p-select" || mDragMode=="s-select") { - osg::ref_ptr tag = mousePick (event); + osg::ref_ptr tag = mousePick (event->pos()); EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); @@ -595,7 +651,7 @@ else if (button=="p-edit" || button=="s-edit" || button=="p-select" || button=="s-select") { - osg::ref_ptr tag = mousePick (event); + osg::ref_ptr tag = mousePick (event->pos()); handleMouseClick (tag, button, event->modifiers() & Qt::ShiftModifier); } diff -Nru openmw-0.37.0/apps/opencs/view/render/worldspacewidget.hpp openmw-0.38.0/apps/opencs/view/render/worldspacewidget.hpp --- openmw-0.37.0/apps/opencs/view/render/worldspacewidget.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/render/worldspacewidget.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -5,12 +5,19 @@ #include +#include + #include "../../model/doc/document.hpp" #include "../../model/world/tablemimedata.hpp" #include "scenewidget.hpp" #include "elements.hpp" +namespace CSMPrefs +{ + class Setting; +} + namespace CSMWorld { class UniversalId; @@ -47,6 +54,10 @@ double mDragFactor; double mDragWheelFactor; double mDragShiftFactor; + QTimer mToolTipDelayTimer; + QPoint mToolTipPos; + bool mShowToolTips; + int mToolTipDelay; public: @@ -109,8 +120,6 @@ /// marked for interaction. unsigned int getInteractionMask() const; - virtual void updateUserSetting (const QString& name, const QStringList& value); - virtual void setEditLock (bool locked); CSMDoc::Document& getDocument(); @@ -145,9 +154,9 @@ void dragMoveEvent(QDragMoveEvent *event); /// \return Is \a key a button mapping setting? (ignored otherwise) - bool storeMappingSetting (const QString& key, const QString& value); + bool storeMappingSetting (const CSMPrefs::Setting *setting); - osg::ref_ptr mousePick (QMouseEvent *event); + osg::ref_ptr mousePick (const QPoint& localPos); std::string mapButton (QMouseEvent *event); @@ -155,6 +164,8 @@ private slots: + void settingChanged (const CSMPrefs::Setting *setting); + void selectNavigationMode (const std::string& mode); virtual void referenceableDataChanged (const QModelIndex& topLeft, @@ -179,6 +190,8 @@ void editModeChanged (const std::string& id); + void showToolTip(); + protected slots: void elementSelectionChanged(); diff -Nru openmw-0.37.0/apps/opencs/view/settings/booleanview.cpp openmw-0.38.0/apps/opencs/view/settings/booleanview.cpp --- openmw-0.37.0/apps/opencs/view/settings/booleanview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/booleanview.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -#include -#include - -#include -#include -#include - -#include - -#include "booleanview.hpp" -#include "../../model/settings/setting.hpp" - -CSVSettings::BooleanView::BooleanView (CSMSettings::Setting *setting, - Page *parent) - : View (setting, parent), mType(setting->type()) -{ - foreach (const QString &value, setting->declaredValues()) - { - QAbstractButton *button = 0; - - switch (mType) - { - case CSMSettings::Type_CheckBox: { - if(mButtons.empty()) // show only one for checkboxes - { - button = new QCheckBox (value, this); - button->setChecked (setting->defaultValues().at(0) == "true" ? true : false); - - // special visual treatment option for checkboxes - if(setting->specialValueText() != "") { - Frame::setTitle(""); - button->setText(setting->specialValueText()); - } - } - } - break; - - case CSMSettings::Type_RadioButton: - button = new QRadioButton (value, this); - break; - - default: - break; - } - - if(button && (mType != CSMSettings::Type_CheckBox || mButtons.empty())) - { - connect (button, SIGNAL (clicked (bool)), - this, SLOT (slotToggled (bool))); - - button->setObjectName (value); - - addWidget (button); - - mButtons[value] = button; - } - } -} - -void CSVSettings::BooleanView::slotToggled (bool state) -{ - //test only for true to avoid multiple selection updates with radiobuttons - if (!isMultiValue() && !state) - return; - - QStringList values; - - foreach (QString key, mButtons.keys()) - { - // checkbox values are true/false unlike radio buttons - if(mType == CSMSettings::Type_CheckBox) - values.append(mButtons.value(key)->isChecked() ? "true" : "false"); - else - { - if (mButtons.value(key)->isChecked()) - values.append (key); - } - } - setSelectedValues (values, false); - - View::updateView(); -} - -void CSVSettings::BooleanView::updateView (bool signalUpdate) const -{ - - QStringList values = selectedValues(); - - foreach (const QString &buttonName, mButtons.keys()) - { - QAbstractButton *button = mButtons[buttonName]; - - //if the value is not found in the list, the widget is checked false - bool buttonValue = values.contains(buttonName); - - //skip if the butotn value will not change - if (button->isChecked() == buttonValue) - continue; - - //disable autoexclusive if it's enabled and we're setting - //the button value to false - bool switchExclusive = (!buttonValue && button->autoExclusive()); - - if (switchExclusive) - button->setAutoExclusive (false); - - button->setChecked (buttonValue); - - if (switchExclusive) - button->setAutoExclusive(true); - } - View::updateView (signalUpdate); -} - -CSVSettings::BooleanView *CSVSettings::BooleanViewFactory::createView - (CSMSettings::Setting *setting, - Page *parent) -{ - return new BooleanView (setting, parent); -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/booleanview.hpp openmw-0.38.0/apps/opencs/view/settings/booleanview.hpp --- openmw-0.37.0/apps/opencs/view/settings/booleanview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/booleanview.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -#ifndef CSVSETTINGS_BOOLEANVIEW_HPP -#define CSVSETTINGS_BOOLEANVIEW_HPP - -#include -#include - -#include "view.hpp" -#include "../../model/settings/support.hpp" - -class QStringListModel; - -namespace CSVSettings -{ - class BooleanView : public View - { - Q_OBJECT - - QMap mButtons; - enum CSMSettings::SettingType mType; - - public: - explicit BooleanView (CSMSettings::Setting *setting, - Page *parent); - - protected: - void updateView (bool signalUpdate = true) const; - - private slots: - void slotToggled (bool state); - }; - - class BooleanViewFactory : public QObject, public IViewFactory - { - Q_OBJECT - - public: - explicit BooleanViewFactory (QWidget *parent = 0) - : QObject (parent) - {} - - BooleanView *createView (CSMSettings::Setting *setting, - Page *parent); - }; -} -#endif // CSVSETTINGS_BOOLEANVIEW_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/dialog.cpp openmw-0.38.0/apps/opencs/view/settings/dialog.cpp --- openmw-0.37.0/apps/opencs/view/settings/dialog.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/dialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,127 +0,0 @@ -#include "dialog.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "../../model/settings/usersettings.hpp" - -#include "page.hpp" - - -CSVSettings::Dialog::Dialog(QMainWindow *parent) - : SettingWindow (parent), mStackedWidget (0), mDebugMode (false) -{ - setWindowTitle(QString::fromUtf8 ("User Settings")); - - setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - - setMinimumSize (600, 400); - - setupDialog(); - - connect (mPageListWidget, - SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)), - this, - SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); -} - -void CSVSettings::Dialog::slotChangePage - (QListWidgetItem *cur, QListWidgetItem *prev) -{ - mStackedWidget->changePage - (mPageListWidget->row (cur), mPageListWidget->row (prev)); -} - -void CSVSettings::Dialog::setupDialog() -{ - QSplitter *centralWidget = new QSplitter (this); - centralWidget->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - - setCentralWidget (centralWidget); - - buildPageListWidget (centralWidget); - buildStackedWidget (centralWidget); -} - -void CSVSettings::Dialog::buildPages() -{ - SettingWindow::createPages (); - - QFontMetrics fm (QApplication::font()); - - int maxWidth = 1; - - foreach (Page *page, SettingWindow::pages()) - { - maxWidth = std::max (maxWidth, fm.width(page->getLabel())); - - new QListWidgetItem (page->getLabel(), mPageListWidget); - - mStackedWidget->addWidget (page); - } - - mPageListWidget->setMaximumWidth (maxWidth + 10); - - resize (mStackedWidget->sizeHint()); -} - -void CSVSettings::Dialog::buildPageListWidget (QSplitter *centralWidget) -{ - mPageListWidget = new QListWidget (centralWidget); - mPageListWidget->setMinimumWidth(50); - mPageListWidget->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding); - - mPageListWidget->setSelectionBehavior (QAbstractItemView::SelectItems); - - centralWidget->addWidget(mPageListWidget); -} - -void CSVSettings::Dialog::buildStackedWidget (QSplitter *centralWidget) -{ - mStackedWidget = new ResizeableStackedWidget (centralWidget); - mStackedWidget->setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Expanding); - - centralWidget->addWidget (mStackedWidget); -} - -void CSVSettings::Dialog::closeEvent (QCloseEvent *event) -{ - //SettingWindow::closeEvent() must be called first to ensure - //model is updated - SettingWindow::closeEvent (event); - - saveSettings(); -} - -void CSVSettings::Dialog::show() -{ - if (pages().isEmpty()) - { - buildPages(); - setViewValues(); - } - - QWidget *currView = QApplication::activeWindow(); - if(currView) - { - // place at the center of the window with focus - QSize size = currView->size(); - move(currView->geometry().x()+(size.width() - frameGeometry().width())/2, - currView->geometry().y()+(size.height() - frameGeometry().height())/2); - } - else - { - // something's gone wrong, place at the center of the screen - QPoint screenCenter = QApplication::desktop()->screenGeometry().center(); - move(screenCenter - QPoint(frameGeometry().width()/2, - frameGeometry().height()/2)); - } - QWidget::show(); -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/dialog.hpp openmw-0.38.0/apps/opencs/view/settings/dialog.hpp --- openmw-0.37.0/apps/opencs/view/settings/dialog.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/dialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -#ifndef CSVSETTINGS_DIALOG_H -#define CSVSETTINGS_DIALOG_H - -#include "settingwindow.hpp" -#include "resizeablestackedwidget.hpp" - -class QStackedWidget; -class QListWidget; -class QListWidgetItem; -class QSplitter; - -namespace CSVSettings { - - class Page; - - class Dialog : public SettingWindow - { - Q_OBJECT - - QListWidget *mPageListWidget; - ResizeableStackedWidget *mStackedWidget; - bool mDebugMode; - - public: - - explicit Dialog (QMainWindow *parent = 0); - - protected: - - /// Settings are written on close - void closeEvent (QCloseEvent *event); - - void setupDialog(); - - private: - - void buildPages(); - void buildPageListWidget (QSplitter *centralWidget); - void buildStackedWidget (QSplitter *centralWidget); - - public slots: - - void show(); - - private slots: - - void slotChangePage (QListWidgetItem *, QListWidgetItem *); - }; -} -#endif // CSVSETTINGS_DIALOG_H diff -Nru openmw-0.37.0/apps/opencs/view/settings/frame.cpp openmw-0.38.0/apps/opencs/view/settings/frame.cpp --- openmw-0.37.0/apps/opencs/view/settings/frame.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/frame.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -#include "frame.hpp" - -#include - -const QString CSVSettings::Frame::sInvisibleBoxStyle = - QString::fromUtf8("Frame { border:2px; padding: 2px; margin: 2px;}"); - -CSVSettings::Frame::Frame (bool isVisible, const QString &title, - QWidget *parent) - : QGroupBox (title, parent), mIsHorizontal (true), - mLayout (new SettingLayout()) -{ - setFlat (true); - mVisibleBoxStyle = styleSheet(); - - if (!isVisible) - { - // must be Page, not a View - setStyleSheet (sInvisibleBoxStyle); - } - - setLayout (mLayout); -} - -void CSVSettings::Frame::hideWidgets() -{ - for (int i = 0; i < children().size(); i++) - { - QObject *obj = children().at(i); - - Frame *widgFrame = dynamic_cast (obj); - - if (widgFrame) - { - widgFrame->hideWidgets(); - continue; - } - - QWidget *widg = static_cast (obj); - if (widg->property("sizePolicy").isValid()) - widg->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding); - } - - layout()->activate(); - setFixedSize(minimumSizeHint()); - -} - -void CSVSettings::Frame::showWidgets() -{ - for (int i = 0; i < children().size(); i++) - { - QObject *obj = children().at(i); - - Frame *widgFrame = dynamic_cast (obj); - - if (widgFrame) - { - widgFrame->showWidgets(); - continue; - } - - QWidget *widg = static_cast (obj); - - if (widg->property("sizePolicy").isValid()) - { - widg->setSizePolicy - (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - } - } - layout()->activate(); - setFixedSize(minimumSizeHint()); -} - -void CSVSettings::Frame::addWidget (QWidget *widget, int row, int column, - int rowSpan, int columnSpan) -{ - if (row == -1) - row = getNextRow(); - - if (column == -1) - column = getNextColumn(); - - mLayout->addWidget (widget, row, column, rowSpan, columnSpan); - //, Qt::AlignLeft | Qt::AlignTop); - - widget->setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored); -} - -int CSVSettings::Frame::getNextRow () const -{ - int row = mLayout->rowCount(); - - if (mIsHorizontal && row > 0) - row--; - - return row; -} - -int CSVSettings::Frame::getNextColumn () const -{ - int column = 0; - - if (mIsHorizontal) - column = mLayout->columnCount(); - - return column; -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/frame.hpp openmw-0.38.0/apps/opencs/view/settings/frame.hpp --- openmw-0.37.0/apps/opencs/view/settings/frame.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/frame.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -#ifndef CSVSETTINGS_FRAME_HPP -#define CSVSETTINGS_FRAME_HPP - -#include -#include -#include -#include "../../model/settings/support.hpp" - -namespace CSVSettings -{ - class SettingLayout : public QGridLayout - { - public: - explicit SettingLayout (QWidget *parent = 0) - : QGridLayout (parent) - { - setContentsMargins(0,0,0,0); - setAlignment(Qt::AlignLeft | Qt::AlignTop); - } - }; - - /// Custom implementation of QGroupBox to act as a base for view classes - class Frame : public QGroupBox - { - static const QString sInvisibleBoxStyle; - - QString mVisibleBoxStyle; - - bool mIsHorizontal; - - SettingLayout *mLayout; - - public: - explicit Frame (bool isVisible, const QString &title = "", - QWidget *parent = 0); - - ///Adds a widget to the grid layout, setting the position - ///relative to the last added widgets, or absolutely for positive - ///row / column values - void addWidget (QWidget *widget, int row = -1, int column = -1, - int rowSpan = 1, int columnSpan = 1); - - ///Force the grid to lay out in horizontal or vertical alignments - void setHLayout() { mIsHorizontal = true; } - void setVLayout() { mIsHorizontal = false; } - - ///show / hide widgets (when stacked widget page changes) - void showWidgets(); - void hideWidgets(); - - private: - - ///functions which return the index for the next layout row / column - int getNextColumn() const; - int getNextRow() const; - - }; -} - -#endif // CSVSETTINGS_FRAME_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/listview.cpp openmw-0.38.0/apps/opencs/view/settings/listview.cpp --- openmw-0.37.0/apps/opencs/view/settings/listview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/listview.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,106 +0,0 @@ -#include "listview.hpp" -#include "../../model/settings/setting.hpp" - -#include -#include -#include - -CSVSettings::ListView::ListView(CSMSettings::Setting *setting, - Page *parent) - : View(setting, parent), mAbstractItemView (0), mComboBox (0) -{ - QWidget *widget = - buildWidget(setting->isMultiLine(), setting->widgetWidth()); - - addWidget (widget, setting->viewRow(), setting->viewColumn()); - - if (mComboBox) - buildComboBoxModel(); - - else if (mAbstractItemView) - buildAbstractItemViewModel(); -} - -void CSVSettings::ListView::buildComboBoxModel() -{ - mComboBox->setModel (dataModel()); - mComboBox->setModelColumn (0); - mComboBox->view()->setSelectionModel (selectionModel()); - - int curIdx = -1; - - if (!selectionModel()->selection().isEmpty()) - curIdx = selectionModel()->selectedIndexes().at(0).row(); - - mComboBox->setCurrentIndex (curIdx); - - connect (mComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(emitItemViewUpdate(int))); -} - -void CSVSettings::ListView::buildAbstractItemViewModel() -{ - mAbstractItemView->setModel (dataModel()); - mAbstractItemView->setSelectionModel (selectionModel()); - - //connection needs to go here for list view update to signal to - //the outside -} - -void CSVSettings::ListView::emitItemViewUpdate (int idx) -{ - updateView(); -} - -QWidget *CSVSettings::ListView::buildWidget(bool isMultiLine, int width) -{ - QWidget *widget = 0; - - if (isMultiLine) - { - mAbstractItemView = new QListView (this); - widget = mAbstractItemView; - - if (width > 0) - widget->setFixedWidth (widgetWidth (width)); - } - else - { - mComboBox = new QComboBox (this); - widget = mComboBox; - - if (width > 0) - mComboBox->setMinimumContentsLength (width); - } - - return widget; -} - -void CSVSettings::ListView::showEvent ( QShowEvent * event ) -{ - View::showEvent (event); -} - -void CSVSettings::ListView::updateView (bool signalUpdate) const -{ - QStringList values = selectedValues(); - - if (mComboBox) - { - int idx = -1; - - if (values.size() > 0) - idx = (mComboBox->findText(values.at(0))); - - mComboBox->setCurrentIndex (idx); - } - - View::updateView (signalUpdate); -} - -CSVSettings::ListView *CSVSettings::ListViewFactory::createView - (CSMSettings::Setting *setting, - Page *parent) -{ - return new ListView(setting, parent); -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/listview.hpp openmw-0.38.0/apps/opencs/view/settings/listview.hpp --- openmw-0.37.0/apps/opencs/view/settings/listview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/listview.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -#ifndef CSVSETTINGS_LISTVIEW_HPP -#define CSVSETTINGS_LISTVIEW_HPP - -#include "view.hpp" - - -class QStringListModel; -class QComboBox; -class QAbstractItemView; - -namespace CSVSettings -{ - class ListView : public View - { - Q_OBJECT - - QAbstractItemView *mAbstractItemView; - QComboBox *mComboBox; - - public: - explicit ListView (CSMSettings::Setting *setting, - Page *parent); - - protected: - - void updateView (bool signalUpdate = true) const; - void showEvent ( QShowEvent * event ); - - ///Receives signal from widget and signals viwUpdated() - void slotTextEdited (QString value); - - private: - - ///Helper function to construct a model for an AbstractItemView - void buildAbstractItemViewModel(); - - ///Helper function to construct a model for a combobox - void buildComboBoxModel(); - - ///Helper function to build the view widget - QWidget *buildWidget (bool isMultiLine, int width); - - private slots: - - ///Receives updates from single-select widgets (like combobox) and - ///signals viewUpdated with the selected values. - void emitItemViewUpdate (int idx); - }; - - class ListViewFactory : public QObject, public IViewFactory - { - Q_OBJECT - - public: - explicit ListViewFactory (QWidget *parent = 0) - : QObject (parent) - {} - - ListView *createView (CSMSettings::Setting *setting, - Page *parent); - }; -} -#endif // CSVSETTINGS_LISTVIEW_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/page.cpp openmw-0.38.0/apps/opencs/view/settings/page.cpp --- openmw-0.37.0/apps/opencs/view/settings/page.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/page.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -#include "page.hpp" - -#include - -#include "view.hpp" -#include "booleanview.hpp" -#include "textview.hpp" -#include "listview.hpp" -#include "rangeview.hpp" - -#include "../../model/settings/usersettings.hpp" -#include "../../model/settings/connector.hpp" -#include "../../model/settings/support.hpp" - -#include "settingwindow.hpp" - -QMap - CSVSettings::Page::mViewFactories; - -CSVSettings::Page::Page (const QString &pageName, QList settingList, - SettingWindow *parent, const QString& label) -: Frame(false, "", parent), mParent(parent), mIsEditorPage (false), mLabel (label) -{ - setObjectName (pageName); - - if (mViewFactories.size() == 0) - buildFactories(); - - setVLayout(); - setupViews (settingList); -} - -void CSVSettings::Page::setupViews - (QList &settingList) -{ - foreach (CSMSettings::Setting *setting, settingList) - addView (setting); -} - -void CSVSettings::Page::addView (CSMSettings::Setting *setting) -{ - if (setting->viewType() == ViewType_Undefined) - { - if(setting->specialValueText() != "") - { - // hack to put a label - addWidget(new QLabel(setting->specialValueText()), - setting->viewRow(), setting->viewColumn(), - setting->rowSpan(), setting->columnSpan()); - return; - } - else - return; - } - - View *view = mViewFactories[setting->viewType()]->createView(setting, this); - - if (!view) - return; - - mViews.append (view); - - addWidget (view, setting->viewRow(), setting->viewColumn(), - setting->rowSpan(), setting->columnSpan() ); - - //if this page is an editor page, connect each of it's views up to the - //UserSettings singleton for signaling back to OpenCS - if (setting->isEditorSetting()) { - connect (view, SIGNAL (viewUpdated(const QString&, const QStringList&)), - &CSMSettings::UserSettings::instance(), - SLOT (updateUserSetting (const QString &, const QStringList &))); - } -} - -CSVSettings::View *CSVSettings::Page::findView (const QString &page, - const QString &setting) const -{ - - //if this is not the page we're looking for, - //appeal to the parent setting window to find the appropriate view - if (page != objectName()) - return mParent->findView (page, setting); - - //otherwise, return the matching view - for (int i = 0; i < mViews.size(); i++) - { - View *view = mViews.at(i); - - if (view->parentPage()->objectName() != page) - continue; - - if (view->objectName() == setting) - return view; - } - - return 0; -} - -void CSVSettings::Page::buildFactories() -{ - mViewFactories[ViewType_Boolean] = new BooleanViewFactory (this); - mViewFactories[ViewType_Text] = new TextViewFactory (this); - mViewFactories[ViewType_List] = new ListViewFactory (this); - mViewFactories[ViewType_Range] = new RangeViewFactory (this); -} - -QString CSVSettings::Page::getLabel() const -{ - return mLabel; -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/page.hpp openmw-0.38.0/apps/opencs/view/settings/page.hpp --- openmw-0.37.0/apps/opencs/view/settings/page.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/page.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -#ifndef CSVSETTINGS_PAGE_HPP -#define CSVSETTINGS_PAGE_HPP - -#include -#include -#include - -#include "frame.hpp" - -#include "../../model/settings/support.hpp" - -namespace CSMSettings { class Setting; } - -namespace CSVSettings -{ - class View; - class IViewFactory; - class SettingWindow; - - class Page : public Frame - { - Q_OBJECT - - QList mViews; - SettingWindow *mParent; - static QMap mViewFactories; - bool mIsEditorPage; - QString mLabel; - - public: - Page (const QString &pageName, QList settingList, - SettingWindow *parent, const QString& label); - - ///Creates a new view based on the passed setting and adds it to - ///the page. - void addView (CSMSettings::Setting *setting); - - ///Iterates the views created for this page based on the passed setting - ///and returns it. - View *findView (const QString &page, const QString &setting) const; - - ///returns the list of views associated with the page - const QList &views () const { return mViews; } - - QString getLabel() const; - - private: - - ///Creates views based on the passed setting list - void setupViews (QList &settingList); - - ///Creates factory objects for view construction - void buildFactories(); - }; -} -#endif // CSVSETTINGS_PAGE_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/rangeview.cpp openmw-0.38.0/apps/opencs/view/settings/rangeview.cpp --- openmw-0.37.0/apps/opencs/view/settings/rangeview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/rangeview.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,208 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "rangeview.hpp" -#include "spinbox.hpp" -#include "../../model/settings/setting.hpp" -#include "../../model/settings/support.hpp" - -CSVSettings::RangeView::RangeView (CSMSettings::Setting *setting, - Page *parent) - : View (setting, parent), mRangeWidget (0), mRangeType (setting->type()) -{ - - mRangeWidget = 0; - - if (isMultiValue()) - return; - - switch (mRangeType) - { - case CSMSettings::Type_SpinBox: - case CSMSettings::Type_DoubleSpinBox: - buildSpinBox (setting); - break; - - case CSMSettings::Type_Dial: - case CSMSettings::Type_Slider: - buildSlider (setting); - break; - - default: - break; - } - - if(mRangeWidget) - { - mRangeWidget->setFixedWidth (widgetWidth (setting->widgetWidth())); - mRangeWidget->setObjectName (setting->name()); - } - - addWidget (mRangeWidget); -} - -void CSVSettings::RangeView::buildSlider (CSMSettings::Setting *setting) -{ - switch (setting->type()) - { - case CSMSettings::Type_Slider: - mRangeWidget = new QSlider (Qt::Horizontal, this); - mRangeWidget->setProperty ("tickInterval", setting->tickInterval()); - - if (setting->ticksAbove()) - { - if (setting->ticksBelow()) - mRangeWidget->setProperty ("tickPosition", QSlider::TicksBothSides); - else - mRangeWidget->setProperty ("tickPosition", QSlider::TicksAbove); - } - else if (setting->ticksBelow()) - mRangeWidget->setProperty ("tickPosition", QSlider::TicksBelow); - else - mRangeWidget->setProperty ("tickPosition", QSlider::NoTicks); - - break; - - case CSMSettings::Type_Dial: - mRangeWidget = new QDial (this); - mRangeWidget->setProperty ("wrapping", setting->wrapping()); - mRangeWidget->setProperty ("notchesVisible", - (setting->ticksAbove() || setting->ticksBelow())); - break; - - default: - break; - } - - if(mRangeWidget) - { - mRangeWidget->setProperty ("minimum", setting->minimum()); - mRangeWidget->setProperty ("maximum", setting->maximum()); - mRangeWidget->setProperty ("tracking", false); - mRangeWidget->setProperty ("singleStep", setting->singleStep()); - - connect (mRangeWidget, SIGNAL (valueChanged (int)), - this, SLOT (slotUpdateView (int))); - } -} - -void CSVSettings::RangeView::buildSpinBox (CSMSettings::Setting *setting) -{ - SpinBox *sb = 0; - - switch (setting->type()) - { - case CSMSettings::Type_SpinBox: - - sb = new SpinBox (this); - - if (!setting->declaredValues().isEmpty()) - sb->setValueList (setting->declaredValues()); - - mRangeWidget = sb; - - connect (mRangeWidget, SIGNAL (valueChanged (int)), - this, SLOT (slotUpdateView (int))); - break; - - case CSMSettings::Type_DoubleSpinBox: - mRangeWidget = new QDoubleSpinBox (this); - - connect (mRangeWidget, SIGNAL (valueChanged (double)), - this, SLOT (slotUpdateView (double))); - break; - - default: - return; - } - - //min / max values are set automatically in AlphaSpinBox - if (setting->declaredValues().isEmpty()) - { - mRangeWidget->setProperty ("minimum", setting->minimum()); - mRangeWidget->setProperty ("maximum", setting->maximum()); - mRangeWidget->setProperty ("singleStep", setting->singleStep()); - } - - mRangeWidget->setProperty ("prefix", setting->prefix()); - mRangeWidget->setProperty ("suffix", setting->suffix()); - mRangeWidget->setProperty ("wrapping", setting->wrapping()); - dynamic_cast (mRangeWidget)->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); - - if(setting->type() == CSMSettings::Type_SpinBox && setting->declaredValues().isEmpty()) - dynamic_cast (mRangeWidget)->setValue (setting->defaultValues().at(0).toInt()); -} - -void CSVSettings::RangeView::slotUpdateView (int value) -{ - QString textValue = ""; - QStringList list; - - switch (mRangeType) - { - case CSMSettings::Type_SpinBox: - list = static_cast (mRangeWidget)->valueList(); - if (!list.isEmpty()) - textValue = list.at(value); - break; - - default: - break; - } - - if (textValue.isEmpty()) - textValue = QVariant (value).toString(); - - setSelectedValue (textValue, false); - - View::updateView(); -} - -void CSVSettings::RangeView::slotUpdateView (double value) -{ - setSelectedValue (QVariant(value).toString(), false); - - View::updateView(); -} - -void CSVSettings::RangeView::updateView (bool signalUpdate) const -{ - QString value; - - if (!selectedValues().isEmpty()) - value = selectedValues().at(0); - - switch (mRangeType) - { - case CSMSettings::Type_SpinBox: - static_cast (mRangeWidget)->setValue (value); - break; - - case CSMSettings::Type_DoubleSpinBox: - static_cast (mRangeWidget)->setValue (value.toDouble()); - break; - - case CSMSettings::Type_Slider: - case CSMSettings::Type_Dial: - mRangeWidget->setProperty ("value", value.toInt()); - mRangeWidget->setProperty ("sliderPosition", value.toInt()); - break; - - default: - break; - - } - - View::updateView (signalUpdate); -} - -CSVSettings::RangeView *CSVSettings::RangeViewFactory::createView - (CSMSettings::Setting *setting, - Page *parent) -{ - return new RangeView (setting, parent); -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/rangeview.hpp openmw-0.38.0/apps/opencs/view/settings/rangeview.hpp --- openmw-0.37.0/apps/opencs/view/settings/rangeview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/rangeview.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -#ifndef CSVSETTINGS_RANGEVIEW_HPP -#define CSVSETTINGS_RANGEVIEW_HPP - -#include "view.hpp" -#include "../../model/settings/support.hpp" - -class QStringListModel; -class QAbstractSpinBox; - -namespace CSVSettings -{ - class RangeView : public View - { - Q_OBJECT - - QWidget *mRangeWidget; - CSMSettings::SettingType mRangeType; - - public: - explicit RangeView (CSMSettings::Setting *setting, - Page *parent); - - protected: - - ///virtual function called through View - void updateView (bool signalUpdate = true) const; - - ///construct a slider-based view - void buildSlider (CSMSettings::Setting *setting); - - ///construct a spinbox-based view - void buildSpinBox (CSMSettings::Setting *setting); - - private slots: - - ///responds to valueChanged signals - void slotUpdateView (int value); - void slotUpdateView (double value); - - }; - - class RangeViewFactory : public QObject, public IViewFactory - { - Q_OBJECT - - public: - explicit RangeViewFactory (QWidget *parent = 0) - : QObject (parent) - {} - - RangeView *createView (CSMSettings::Setting *setting, - Page *parent); - }; -} -#endif // CSVSETTINGS_RANGEVIEW_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/resizeablestackedwidget.cpp openmw-0.38.0/apps/opencs/view/settings/resizeablestackedwidget.cpp --- openmw-0.37.0/apps/opencs/view/settings/resizeablestackedwidget.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/resizeablestackedwidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -#include "resizeablestackedwidget.hpp" -#include "page.hpp" - -#include - -CSVSettings::ResizeableStackedWidget::ResizeableStackedWidget(QWidget *parent) : - QStackedWidget(parent) -{} - -void CSVSettings::ResizeableStackedWidget::addWidget(QWidget* pWidget) -{ - QStackedWidget::addWidget(pWidget); -} - -void CSVSettings::ResizeableStackedWidget::changePage - (int current, int previous) -{ - if (current == previous) - return; - - Page *prevPage = 0; - Page *curPage = 0; - - if (previous > -1) - prevPage = static_cast (widget (previous)); - - if (current > -1) - curPage = static_cast (widget (current)); - - if (prevPage) - prevPage->hideWidgets(); - - if (curPage) - curPage->showWidgets(); - - layout()->activate(); - - setCurrentIndex (current); -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/resizeablestackedwidget.hpp openmw-0.38.0/apps/opencs/view/settings/resizeablestackedwidget.hpp --- openmw-0.37.0/apps/opencs/view/settings/resizeablestackedwidget.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/resizeablestackedwidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#ifndef CSVSETTINGS_RESIZEABLESTACKEDWIDGET_HPP -#define CSVSETTINGS_RESIZEABLESTACKEDWIDGET_HPP - -#include - -class QListWidgetItem; - -namespace CSVSettings -{ - class ResizeableStackedWidget : public QStackedWidget - { - Q_OBJECT - - public: - explicit ResizeableStackedWidget(QWidget *parent = 0); - - ///add a widget to the stacked widget - void addWidget(QWidget* pWidget); - - ///called whenever the stacked widget page is changed - void changePage (int, int); - }; -} - -#endif // CSVSETTINGS_RESIZEABLESTACKEDWIDGET_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/settingwindow.cpp openmw-0.38.0/apps/opencs/view/settings/settingwindow.cpp --- openmw-0.37.0/apps/opencs/view/settings/settingwindow.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/settingwindow.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,131 +0,0 @@ -#include -#include - -#include "../../model/settings/setting.hpp" -#include "../../model/settings/connector.hpp" -#include "../../model/settings/usersettings.hpp" -#include "settingwindow.hpp" -#include "page.hpp" -#include "view.hpp" - -CSVSettings::SettingWindow::SettingWindow(QWidget *parent) - : QMainWindow(parent), mModel(NULL) -{} - -void CSVSettings::SettingWindow::createPages() -{ - CSMSettings::SettingPageMap pageMap = mModel->settingPageMap(); - - QList connectedSettings; - - foreach (const QString &pageName, pageMap.keys()) - { - QList pageSettings = pageMap.value (pageName).second; - - mPages.append (new Page (pageName, pageSettings, this, pageMap.value (pageName).first)); - - for (int i = 0; i < pageSettings.size(); i++) - { - CSMSettings::Setting *setting = pageSettings.at(i); - - if (!setting->proxyLists().isEmpty()) - connectedSettings.append (setting); - } - } - - if (!connectedSettings.isEmpty()) - createConnections(connectedSettings); -} - -void CSVSettings::SettingWindow::createConnections - (const QList &list) -{ - foreach (const CSMSettings::Setting *setting, list) - { - View *masterView = findView (setting->page(), setting->name()); - - CSMSettings::Connector *connector = - new CSMSettings::Connector (masterView, this); - - connect (masterView, - SIGNAL (viewUpdated(const QString &, const QStringList &)), - connector, - SLOT (slotUpdateSlaves()) - ); - - const CSMSettings::ProxyValueMap &proxyMap = setting->proxyLists(); - - foreach (const QString &key, proxyMap.keys()) - { - QStringList keyPair = key.split('/'); - - if (keyPair.size() != 2) - continue; - - View *slaveView = findView (keyPair.at(0), keyPair.at(1)); - - if (!slaveView) - { - qWarning () << "Unable to create connection for view " - << key; - continue; - } - - QList proxyList = proxyMap.value (key); - connector->addSlaveView (slaveView, proxyList); - - connect (slaveView, - SIGNAL (viewUpdated(const QString &, const QStringList &)), - connector, - SLOT (slotUpdateMaster())); - } - } -} - -void CSVSettings::SettingWindow::setViewValues() -{ - //iterate each page and view, setting their definitions - //if they exist in the model - foreach (const Page *page, mPages) - { - foreach (const View *view, page->views()) - { - //testing beforehand prevents overwriting a proxy setting - if (!mModel->hasSettingDefinitions (view->viewKey())) - continue; - - QStringList defs = mModel->definitions (view->viewKey()); - - view->setSelectedValues(defs); - } - } -} - -CSVSettings::View *CSVSettings::SettingWindow::findView - (const QString &pageName, const QString &setting) -{ - foreach (const Page *page, mPages) - { - if (page->objectName() == pageName) - return page->findView (pageName, setting); - } - return 0; -} - -void CSVSettings::SettingWindow::saveSettings() -{ - //setting the definition in the model automatically syncs with the file - foreach (const Page *page, mPages) - { - foreach (const View *view, page->views()) - { - if (!view->serializable()) - continue; - - mModel->setDefinitions (view->viewKey(), view->selectedValues()); - } - } - - mModel->saveDefinitions(); -} - diff -Nru openmw-0.37.0/apps/opencs/view/settings/settingwindow.hpp openmw-0.38.0/apps/opencs/view/settings/settingwindow.hpp --- openmw-0.37.0/apps/opencs/view/settings/settingwindow.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/settingwindow.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -#ifndef CSVSETTINGS_SETTINGWINDOW_HPP -#define CSVSETTINGS_SETTINGWINDOW_HPP - -#include -#include - -#include "../../model/settings/support.hpp" - -namespace CSMSettings { - class Setting; - class UserSettings; -} - -namespace CSVSettings { - - class Page; - class View; - - typedef QList PageList; - - class SettingWindow : public QMainWindow - { - Q_OBJECT - - PageList mPages; - CSMSettings::UserSettings *mModel; - - public: - explicit SettingWindow(QWidget *parent = 0); - - ///retrieve a reference to a view based on it's page and setting name - View *findView (const QString &pageName, const QString &setting); - - ///set the model the view uses (instance of UserSettings) - void setModel (CSMSettings::UserSettings &model) { mModel = &model; } - - protected: - - ///construct the pages to be displayed in the dialog - void createPages(); - - ///return the list of constructed pages - const PageList &pages() const { return mPages; } - - ///save settings from the GUI to file - void saveSettings(); - - ///sets the defined values for the views that have been created - void setViewValues(); - - private: - - ///create connections between settings (used for proxy settings) - void createConnections (const QList &list); - }; -} - -#endif // CSVSETTINGS_SETTINGWINDOW_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/spinbox.cpp openmw-0.38.0/apps/opencs/view/settings/spinbox.cpp --- openmw-0.37.0/apps/opencs/view/settings/spinbox.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/spinbox.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -#include "spinbox.hpp" - -#include - -CSVSettings::SpinBox::SpinBox(QWidget *parent) - : QSpinBox(parent), mValueList(QStringList()) -{ - setRange (0, 0); -} - -QString CSVSettings::SpinBox::textFromValue(int val) const -{ - if (mValueList.isEmpty()) - return QVariant (val).toString(); - - QString value; - - if (val < mValueList.size()) - value = mValueList.at (val); - - return value; -} - -int CSVSettings::SpinBox::valueFromText(const QString &text) const -{ - if (mValueList.isEmpty()) - return text.toInt(); // TODO: assumed integer, untested error handling for alpha types - - if (mValueList.contains (text)) - return mValueList.indexOf(text); - - return -1; -} - -void CSVSettings::SpinBox::setValue (const QString &value) -{ - if (!mValueList.isEmpty()) - { - lineEdit()->setText (value); - QSpinBox::setValue(valueFromText(value)); - } - else - QSpinBox::setValue (value.toInt()); -} - -void CSVSettings::SpinBox::setValueList (const QStringList &list) -{ - mValueList = list; - setMaximum (list.size() - 1); -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/spinbox.hpp openmw-0.38.0/apps/opencs/view/settings/spinbox.hpp --- openmw-0.37.0/apps/opencs/view/settings/spinbox.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/spinbox.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -#ifndef CSVSETTINGS_SPINBOX_HPP -#define CSVSETTINGS_SPINBOX_HPP - -#include -#include -#include - -namespace CSVSettings -{ - class SpinBox : public QSpinBox - { - Q_OBJECT - - QStringList mValueList; - - public: - explicit SpinBox(QWidget *parent = 0); - - ///set the value displayed in the spin box - void setValue (const QString &value); - - ///set the stringlist that's used as a list of pre-defined values - ///to be displayed as the user scrolls - void setValueList (const QStringList &list); - - ///returns the pre-defined value list. - const QStringList &valueList() const { return mValueList; } - - protected: - - ///converts an index value to corresponding text to be displayed - QString textFromValue (int val) const; - - ///converts a text value to a corresponding index - int valueFromText (const QString &text) const; - }; -} -#endif // CSVSETTINGS_SPINBOX_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/textview.cpp openmw-0.38.0/apps/opencs/view/settings/textview.cpp --- openmw-0.37.0/apps/opencs/view/settings/textview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/textview.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -#include -#include - -#include "textview.hpp" -#include "../../model/settings/setting.hpp" - -CSVSettings::TextView::TextView(CSMSettings::Setting *setting, Page *parent) - : View (setting, parent), mDelimiter (setting->delimiter()) - -{ - if (setting->isMultiLine()) - mTextWidget = new QTextEdit ("", this); - else - mTextWidget = new QLineEdit ("", this); - - if (setting->widgetWidth() > 0) - mTextWidget->setFixedWidth (widgetWidth (setting->widgetWidth())); - - connect (mTextWidget, SIGNAL (textEdited (QString)), - this, SLOT (slotTextEdited (QString))); - - addWidget (mTextWidget, setting->viewRow(), setting->viewColumn()); -} - -bool CSVSettings::TextView::isEquivalent - (const QString &lhs, const QString &rhs) const -{ - return (lhs.trimmed() == rhs.trimmed()); -} - -void CSVSettings::TextView::slotTextEdited (QString value) -{ - QStringList values = value.split (mDelimiter, QString::SkipEmptyParts); - - QStringList returnValues; - - foreach (const QString &splitValue, values) - returnValues.append (splitValue.trimmed()); - - setSelectedValues (returnValues, false); - - View::updateView(); -} - -void CSVSettings::TextView::updateView(bool signalUpdate) const -{ - QString values = selectedValues().join (mDelimiter); - - if (isEquivalent (mTextWidget->property("text").toString(), values)) - return; - - mTextWidget->setProperty("text", values); - - View::updateView (signalUpdate); -} - -CSVSettings::TextView *CSVSettings::TextViewFactory::createView - (CSMSettings::Setting *setting, - Page *parent) -{ - return new TextView (setting, parent); -} - diff -Nru openmw-0.37.0/apps/opencs/view/settings/textview.hpp openmw-0.38.0/apps/opencs/view/settings/textview.hpp --- openmw-0.37.0/apps/opencs/view/settings/textview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/textview.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -#ifndef CSVSETTINGS_TEXTVIEW_HPP -#define CSVSETTINGS_TEXTVIEW_HPP - -#include "view.hpp" -#include "../../model/settings/setting.hpp" - -namespace CSVSettings -{ - class TextView : public View - { - Q_OBJECT - - QWidget *mTextWidget; - - QString mDelimiter; - - public: - explicit TextView (CSMSettings::Setting *setting, - Page *parent = 0); - - protected: - - /// virtual function called through View - void updateView (bool signalUpdate = true) const; - - protected slots: - - ///Receives updates to the widget for signalling - void slotTextEdited (QString value); - - private: - - ///Comparison function that returns true if the trimmed() strings - ///are equal - bool isEquivalent (const QString &lhs, const QString &rhs) const; - }; - - class TextViewFactory : public QObject, public IViewFactory - { - Q_OBJECT - - public: - explicit TextViewFactory (QWidget *parent = 0) - : QObject (parent) - {} - - TextView *createView (CSMSettings::Setting *setting, - Page *parent); - }; -} -#endif // CSVSETTINGS_TEXTVIEW_HPP diff -Nru openmw-0.37.0/apps/opencs/view/settings/view.cpp openmw-0.38.0/apps/opencs/view/settings/view.cpp --- openmw-0.37.0/apps/opencs/view/settings/view.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/view.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,222 +0,0 @@ -#include -#include -#include -#include -#include - -#include "view.hpp" -#include "../../model/settings/support.hpp" -#include "../../model/settings/setting.hpp" -#include "page.hpp" - -CSVSettings::View::View(CSMSettings::Setting *setting, - Page *parent) - - : Frame(true, setting->getLabel(), parent), - mParentPage (parent), mDataModel(0), - mHasFixedValues (!setting->declaredValues().isEmpty()), - mIsMultiValue (setting->isMultiValue()), - mViewKey (setting->page() + '/' + setting->name()), - mSerializable (setting->serializable()) -{ - if (!setting->getToolTip().isEmpty()) - setToolTip (setting->getToolTip()); - - setObjectName (setting->name()); - buildView(); - buildModel (setting); - // apply stylesheet to view's frame if exists - if(setting->styleSheet() != "") - Frame::setStyleSheet (setting->styleSheet()); -} - -void CSVSettings::View::buildModel (const CSMSettings::Setting *setting) -{ - QStringList values = setting->defaultValues(); - - if (mHasFixedValues) - buildFixedValueModel (setting->declaredValues()); - else - buildUpdatableValueModel (values); - - mSelectionModel = new QItemSelectionModel (mDataModel, this); - - setSelectedValues (values, false); -} - -void CSVSettings::View::buildFixedValueModel (const QStringList &values) -{ - //fixed value models are simple string list models, since they are read-only - mDataModel = new QStringListModel (values, this); -} - -void CSVSettings::View::buildUpdatableValueModel (const QStringList &values) -{ - //updateable models are standard item models because they support - //replacing entire columns - QList itemList; - - foreach (const QString &value, values) - itemList.append (new QStandardItem(value)); - - QStandardItemModel *model = new QStandardItemModel (this); - model->appendColumn (itemList); - - mDataModel = model; -} - -void CSVSettings::View::buildView() -{ - setFlat (true); - setHLayout(); -} - -int CSVSettings::View::currentIndex () const -{ - if (selectedValues().isEmpty()) - return -1; - - QString currentValue = selectedValues().at(0); - - for (int i = 0; i < mDataModel->rowCount(); i++) - if (value(i) == currentValue) - return i; - - return -1; -} - -void CSVSettings::View::refresh() const -{ - select (mSelectionModel->selection()); - updateView(); -} - -int CSVSettings::View::rowCount() const -{ - return mDataModel->rowCount(); -} - -void CSVSettings::View::select (const QItemSelection &selection) const -{ - mSelectionModel->clear(); - mSelectionModel->select(selection, QItemSelectionModel::Select); -} - -QStringList CSVSettings::View::selectedValues() const -{ - QStringList selValues; - - foreach (const QModelIndex &idx, mSelectionModel->selectedIndexes()) - selValues.append (value(idx.row())); - - return selValues; -} - -void CSVSettings::View::setSelectedValue (const QString &value, - bool doViewUpdate, bool signalUpdate) -{ - setSelectedValues (QStringList() << value, doViewUpdate, signalUpdate); -} - -void CSVSettings::View::setSelectedValues (const QStringList &list, - bool doViewUpdate, bool signalUpdate) const -{ - QItemSelection selection; - - if (stringListsMatch (list, selectedValues())) - return; - - if (!mHasFixedValues) - { - QStandardItemModel *model = - static_cast (mDataModel); - - model->clear(); - model->appendColumn (toStandardItemList (list)); - - for (int i = 0; i < model->rowCount(); i++) - { - QModelIndex idx = model->index(i, 0); - selection.append (QItemSelectionRange (idx, idx)); - } - } - else - { - for (int i = 0; i < mDataModel->rowCount(); i++) - { - if (list.contains(value(i))) - { - QModelIndex idx = mDataModel->index(i, 0); - selection.append(QItemSelectionRange (idx, idx)); - } - } - } - select (selection); - - //update the view if the selection was set from the model side, not by the - //user - if (doViewUpdate) - updateView (signalUpdate); -} - -void CSVSettings::View::showEvent ( QShowEvent * event ) -{ - refresh(); -} - -bool CSVSettings::View::stringListsMatch ( - const QStringList &list1, - const QStringList &list2) const -{ - //returns a "sloppy" match, verifying that each list contains all the same - //items, though not necessarily in the same order. - - if (list1.size() != list2.size()) - return false; - - QStringList tempList(list2); - - //iterate each value in the list, removing one occurrence of the value in - //the other list. If no corresponding value is found, test fails - foreach (const QString &value, list1) - { - if (!tempList.contains(value)) - return false; - - tempList.removeOne(value); - } - return true; -} - -QList CSVSettings::View::toStandardItemList - (const QStringList &list) const -{ - QList itemList; - - foreach (const QString &value, list) - itemList.append (new QStandardItem (value)); - - return itemList; -} - -void CSVSettings::View::updateView (bool signalUpdate) const -{ - if (signalUpdate) - emit viewUpdated(viewKey(), selectedValues()); -} - -QString CSVSettings::View::value (int row) const -{ - if (row > -1 && row < mDataModel->rowCount()) - return mDataModel->data (mDataModel->index(row, 0)).toString(); - - return QString(); -} - -int CSVSettings::View::widgetWidth(int characterCount) const -{ - QString widthToken = QString().fill ('m', characterCount); - QFontMetrics fm (QApplication::font()); - - return (fm.width (widthToken)); -} diff -Nru openmw-0.37.0/apps/opencs/view/settings/view.hpp openmw-0.38.0/apps/opencs/view/settings/view.hpp --- openmw-0.37.0/apps/opencs/view/settings/view.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/settings/view.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,160 +0,0 @@ -#ifndef CSVSETTINGS_VIEW_HPP -#define CSVSETTINGS_VIEW_HPP - -#include -#include - -#include "frame.hpp" -#include "../../model/settings/support.hpp" - -class QGroupBox; -class QStringList; -class QStandardItem; -class QItemSelection; -class QAbstractItemModel; -class QItemSelectionModel; - -namespace CSMSettings { class Setting; } - -namespace CSVSettings -{ - class Page; - - class View : public Frame - { - Q_OBJECT - - ///Pointer to the owning Page instance - Page *mParentPage; - - ///Pointer to the selection model for the view - QItemSelectionModel *mSelectionModel; - - ///Pointer to the data model for the view's selection model - QAbstractItemModel *mDataModel; - - ///State indicating whether or not the setting has a pre-defined list - ///of values, limiting possible definitions - bool mHasFixedValues; - - ///State indicating whether the view will allow multiple values - bool mIsMultiValue; - - ///'pagename.settingname' form of the view's id - QString mViewKey; - - ///indicates whether or not the setting is written to file - bool mSerializable; - - public: - - explicit View (CSMSettings::Setting *setting, Page *parent); - - ///Returns the index / row of the passed value, -1 if not found. - int currentIndex () const; - - ///Returns the number of rows in the view's data model - int rowCount() const; - - ///Returns bool indicating the data in this view should / should not - ///be serialized to a config file - bool serializable() const { return mSerializable; } - - ///Returns a pointer to the view's owning parent page - const Page *parentPage() const { return mParentPage; } - - ///Returns the selected items in the selection model as a QStringList - QStringList selectedValues() const; - - ///Sets the selected items in the selection model based on passed list. - ///Bools allow opt-out of updating the view - ///or signaling the view was updatedto avoid viscious cylcing. - void setSelectedValues (const QStringList &values, - bool updateView = true, - bool signalUpdate = true) const; - - void setSelectedValue (const QString &value, - bool updateView = true, - bool signalUpdate = true); - - - ///Returns the value of the data model at the specified row - QString value (int row) const; - - QString viewKey() const { return mViewKey; } - - protected: - - /// Returns the model which provides data for the selection model - QAbstractItemModel *dataModel() { return mDataModel; } - - ///Accessor function for subclasses - bool isMultiValue() { return mIsMultiValue; } - - ///Returns the view selection model - QItemSelectionModel *selectionModel() { return mSelectionModel;} - - ///Global callback for basic view initialization - void showEvent ( QShowEvent * event ); - - ///Virtual for updating a specific View subclass - ///bool indicates whether viewUpdated() signal is emitted - virtual void updateView (bool signalUpdate = true) const; - - ///Returns the pixel width corresponding to the specified number of - ///characters. - int widgetWidth(int characterCount) const; - - private: - - ///Constructs the view layout - void buildView(); - - ///Constructs the data and selection models - void buildModel (const CSMSettings::Setting *setting); - - ///In cases where the view has a pre-defined list of possible values, - ///a QStringListModel is created using those values. - ///View changes operate on the selection model only. - void buildFixedValueModel (const QStringList &definitions); - - ///In cases where the view does not have a pre-defined list of possible - ///values, a QStandardItemModel is created, containing the actual - ///setting definitions. View changes first update the data in the - ///model to match the data in the view. The selection model always - ///selects all values. - void buildUpdatableValueModel (const QStringList &definitions); - - ///Refreshes the view - void refresh() const; - - ///Convenince function for selection model's select() method. Also - ///clears out the model beforehand to ensure complete selection. - void select (const QItemSelection &selection) const; - - ///Compares two string lists "loosely", ensuring that all values in - ///one list are contained entirely in the other, and that neither list - ///has more values than the other. List order is not considered. - bool stringListsMatch (const QStringList &list1, - const QStringList &list2) const; - - ///Converts a string list to a list of QStandardItem pointers. - QList toStandardItemList(const QStringList &) const; - - signals: - - ///Signals that the view has been changed. - void viewUpdated(const QString &, const QStringList &) const; - - }; - - class IViewFactory - { - public: - - ///Creation interface for view factories - virtual View *createView (CSMSettings::Setting *setting, - Page *parent) = 0; - }; -} -#endif // CSVSETTINGS_VIEW_HPP diff -Nru openmw-0.37.0/apps/opencs/view/tools/reportsubview.cpp openmw-0.38.0/apps/opencs/view/tools/reportsubview.cpp --- openmw-0.37.0/apps/opencs/view/tools/reportsubview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/tools/reportsubview.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,11 +27,6 @@ // ignored. We don't change document state anyway. } -void CSVTools::ReportSubView::updateUserSetting (const QString &name, const QStringList &list) -{ - mTable->updateUserSetting (name, list); -} - void CSVTools::ReportSubView::refreshRequest() { if (!(mDocument.getState() & mRefreshState)) @@ -39,7 +34,7 @@ if (mRefreshState==CSMDoc::State_Verifying) { mTable->clear(); - mDocument.verify (getUniversalId()); + mDocument.verify (getUniversalId()); } } } diff -Nru openmw-0.37.0/apps/opencs/view/tools/reportsubview.hpp openmw-0.38.0/apps/opencs/view/tools/reportsubview.hpp --- openmw-0.37.0/apps/opencs/view/tools/reportsubview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/tools/reportsubview.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -29,8 +29,6 @@ virtual void setEditLock (bool locked); - virtual void updateUserSetting (const QString &, const QStringList &); - private slots: void refreshRequest(); diff -Nru openmw-0.37.0/apps/opencs/view/tools/reporttable.cpp openmw-0.38.0/apps/opencs/view/tools/reporttable.cpp --- openmw-0.37.0/apps/opencs/view/tools/reporttable.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/tools/reporttable.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -14,6 +14,8 @@ #include "../../model/tools/reportmodel.hpp" +#include "../../model/prefs/state.hpp" + #include "../../view/world/idtypedelegate.hpp" namespace CSVTools @@ -189,6 +191,10 @@ mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_Edit)); mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_Remove)); mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove)); + + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); + CSMPrefs::get()["Reports"].update(); } std::vector CSVTools::ReportTable::getDraggedRecords() const @@ -206,40 +212,6 @@ return ids; } -void CSVTools::ReportTable::updateUserSetting (const QString& name, const QStringList& list) -{ - mIdTypeDelegate->updateUserSetting (name, list); - - QString base ("report-input/double"); - if (name.startsWith (base)) - { - QString modifierString = name.mid (base.size()); - Qt::KeyboardModifiers modifiers = 0; - - if (modifierString=="-s") - modifiers = Qt::ShiftModifier; - else if (modifierString=="-c") - modifiers = Qt::ControlModifier; - else if (modifierString=="-sc") - modifiers = Qt::ShiftModifier | Qt::ControlModifier; - - DoubleClickAction action = Action_None; - - QString value = list.at (0); - - if (value=="Edit") - action = Action_Edit; - else if (value=="Remove") - action = Action_Remove; - else if (value=="Edit And Remove") - action = Action_EditAndRemove; - - mDoubleClickActions[modifiers] = action; - - return; - } -} - std::vector CSVTools::ReportTable::getReplaceIndices (bool selection) const { std::vector indices; @@ -285,6 +257,44 @@ mModel->flagAsReplaced (index); } +void CSVTools::ReportTable::settingChanged (const CSMPrefs::Setting *setting) +{ + if (setting->getParent()->getKey()=="Reports") + { + QString base ("double"); + QString key = setting->getKey().c_str(); + if (key.startsWith (base)) + { + QString modifierString = key.mid (base.size()); + Qt::KeyboardModifiers modifiers = 0; + + if (modifierString=="-s") + modifiers = Qt::ShiftModifier; + else if (modifierString=="-c") + modifiers = Qt::ControlModifier; + else if (modifierString=="-sc") + modifiers = Qt::ShiftModifier | Qt::ControlModifier; + + DoubleClickAction action = Action_None; + + std::string value = setting->toString(); + + if (value=="Edit") + action = Action_Edit; + else if (value=="Remove") + action = Action_Remove; + else if (value=="Edit And Remove") + action = Action_EditAndRemove; + + mDoubleClickActions[modifiers] = action; + + return; + } + } + else if (*setting=="Records/type-format") + mIdTypeDelegate->settingChanged (setting); +} + void CSVTools::ReportTable::showSelection() { QModelIndexList selectedRows = selectionModel()->selectedRows(); diff -Nru openmw-0.37.0/apps/opencs/view/tools/reporttable.hpp openmw-0.38.0/apps/opencs/view/tools/reporttable.hpp --- openmw-0.37.0/apps/opencs/view/tools/reporttable.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/tools/reporttable.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -13,6 +13,11 @@ class ReportModel; } +namespace CSMPrefs +{ + class Setting; +} + namespace CSVWorld { class CommandDelegate; @@ -61,8 +66,6 @@ virtual std::vector getDraggedRecords() const; - void updateUserSetting (const QString& name, const QStringList& list); - void clear(); /// Return indices of rows that are suitable for replacement. @@ -77,6 +80,8 @@ private slots: + void settingChanged (const CSMPrefs::Setting *setting); + void showSelection(); void removeSelection(); diff -Nru openmw-0.37.0/apps/opencs/view/tools/searchsubview.cpp openmw-0.38.0/apps/opencs/view/tools/searchsubview.cpp --- openmw-0.37.0/apps/opencs/view/tools/searchsubview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/tools/searchsubview.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,7 +6,7 @@ #include "../../model/tools/search.hpp" #include "../../model/tools/reportmodel.hpp" #include "../../model/world/idtablebase.hpp" -#include "../../model/settings/usersettings.hpp" +#include "../../model/prefs/state.hpp" #include "reporttable.hpp" #include "searchbox.hpp" @@ -23,8 +23,7 @@ const CSMTools::ReportModel& model = dynamic_cast (*mTable->model()); - bool autoDelete = CSMSettings::UserSettings::instance().setting ( - "search/auto-delete", QString ("true"))=="true"; + bool autoDelete = CSMPrefs::get()["Search & Replace"]["auto-delete"].isTrue(); CSMTools::Search search (mSearch); CSMWorld::IdTableBase *currentTable = 0; @@ -102,11 +101,6 @@ mSearchBox.setEditLock (locked); } -void CSVTools::SearchSubView::updateUserSetting (const QString &name, const QStringList &list) -{ - mTable->updateUserSetting (name, list); -} - void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *document) { mSearchBox.setSearchMode (!(state & CSMDoc::State_Searching)); @@ -114,13 +108,10 @@ void CSVTools::SearchSubView::startSearch (const CSMTools::Search& search) { - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); - - int paddingBefore = userSettings.setting ("search/char-before", QString ("5")).toInt(); - int paddingAfter = userSettings.setting ("search/char-after", QString ("5")).toInt(); + CSMPrefs::Category& settings = CSMPrefs::get()["Search & Replace"]; mSearch = search; - mSearch.setPadding (paddingBefore, paddingAfter); + mSearch.setPadding (settings["char-before"].toInt(), settings["char-after"].toInt()); mTable->clear(); mDocument.runSearch (getUniversalId(), mSearch); diff -Nru openmw-0.37.0/apps/opencs/view/tools/searchsubview.hpp openmw-0.38.0/apps/opencs/view/tools/searchsubview.hpp --- openmw-0.37.0/apps/opencs/view/tools/searchsubview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/tools/searchsubview.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -36,15 +36,13 @@ protected: void showEvent (QShowEvent *event); - + public: SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); - virtual void updateUserSetting (const QString &, const QStringList &); - private slots: void stateChanged (int state, CSMDoc::Document *document); diff -Nru openmw-0.37.0/apps/opencs/view/world/datadisplaydelegate.cpp openmw-0.38.0/apps/opencs/view/world/datadisplaydelegate.cpp --- openmw-0.37.0/apps/opencs/view/world/datadisplaydelegate.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/datadisplaydelegate.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,5 +1,6 @@ #include "datadisplaydelegate.hpp" -#include "../../model/settings/usersettings.hpp" + +#include "../../model/prefs/state.hpp" #include #include @@ -8,8 +9,8 @@ const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, - const QString &pageName, - const QString &settingName, + const std::string &pageName, + const std::string &settingName, QObject *parent) : EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly), mIcons (icons), mIconSize (QSize(16, 16)), @@ -18,10 +19,8 @@ { buildPixmaps(); - QString value = - CSMSettings::UserSettings::instance().settingValue (mSettingKey); - - updateDisplayMode(value); + if (!pageName.empty()) + updateDisplayMode (CSMPrefs::get()[pageName][settingName].toString()); } void CSVWorld::DataDisplayDelegate::buildPixmaps () @@ -52,7 +51,7 @@ QSize CSVWorld::DataDisplayDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QSize size = EnumDelegate::sizeHint(option, index); - + int valueIndex = getValueIndex(index); if (valueIndex != -1) { @@ -105,7 +104,7 @@ textRect.setLeft(iconRect.right() + mTextLeftOffset); textRect.setRight(option.rect.right() - mHorizontalMargin); - QString text = option.fontMetrics.elidedText(mValues.at(index).second, + QString text = option.fontMetrics.elidedText(mValues.at(index).second, option.textElideMode, textRect.width()); QApplication::style()->drawItemText(painter, @@ -118,19 +117,7 @@ QApplication::style()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, mPixmaps.at(index).second); } -void CSVWorld::DataDisplayDelegate::updateUserSetting (const QString &name, - const QStringList &list) -{ - if (list.isEmpty()) - return; - - QString value = list.at(0); - - if (name == mSettingKey) - updateDisplayMode (value); -} - -void CSVWorld::DataDisplayDelegate::updateDisplayMode (const QString &mode) +void CSVWorld::DataDisplayDelegate::updateDisplayMode (const std::string &mode) { if (mode == "Icon and Text") mDisplayMode = Mode_IconAndText; @@ -146,7 +133,14 @@ { } -void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, QString enumName, QString iconFilename) +void CSVWorld::DataDisplayDelegate::settingChanged (const CSMPrefs::Setting *setting) +{ + if (*setting==mSettingKey) + updateDisplayMode (setting->toString()); +} + + +void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, const QString& enumName, const QString& iconFilename) { mIcons.push_back (std::make_pair(enumValue, QIcon(iconFilename))); EnumDelegateFactory::add(enumValue, enumName); @@ -158,5 +152,3 @@ { return new DataDisplayDelegate (mValues, mIcons, dispatcher, document, "", "", parent); } - - diff -Nru openmw-0.37.0/apps/opencs/view/world/datadisplaydelegate.hpp openmw-0.38.0/apps/opencs/view/world/datadisplaydelegate.hpp --- openmw-0.37.0/apps/opencs/view/world/datadisplaydelegate.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/datadisplaydelegate.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,10 +4,13 @@ #include #include "enumdelegate.hpp" -namespace CSVWorld +namespace CSMPrefs { + class Setting; +} - +namespace CSVWorld +{ class DataDisplayDelegate : public EnumDelegate { public: @@ -34,12 +37,12 @@ int mHorizontalMargin; int mTextLeftOffset; - QString mSettingKey; + std::string mSettingKey; public: DataDisplayDelegate (const ValueList & values, const IconList & icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, - const QString &pageName, const QString &settingName, QObject *parent); + const std::string& pageName, const std::string& settingName, QObject *parent); ~DataDisplayDelegate(); @@ -53,13 +56,10 @@ /// offset the horizontal position of the text from the right edge of the icon. Default is 8 pixels. void setTextLeftOffset (int offset); - ///update the display mode for the delegate - void updateUserSetting (const QString &name, const QStringList &list); - private: /// update the display mode based on a passed string - void updateDisplayMode (const QString &); + void updateDisplayMode (const std::string &); /// custom paint function for painting the icon. Mode_IconAndText and Mode_Icon only. void paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int i) const; @@ -67,6 +67,7 @@ /// rebuild the list of pixmaps from the provided icons (called when icon size is changed) void buildPixmaps(); + virtual void settingChanged (const CSMPrefs::Setting *setting); }; class DataDisplayDelegateFactory : public EnumDelegateFactory @@ -82,7 +83,7 @@ protected: - void add (int enumValue,const QString enumName, const QString iconFilename); + void add (int enumValue, const QString& enumName, const QString& iconFilename); }; diff -Nru openmw-0.37.0/apps/opencs/view/world/dialoguesubview.cpp openmw-0.38.0/apps/opencs/view/world/dialoguesubview.cpp --- openmw-0.37.0/apps/opencs/view/world/dialoguesubview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/dialoguesubview.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -31,7 +31,8 @@ #include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" #include "../../model/doc/document.hpp" -#include "../../model/settings/usersettings.hpp" + +#include "../../model/prefs/state.hpp" #include "../widget/coloreditor.hpp" #include "../widget/droplineedit.hpp" @@ -673,8 +674,6 @@ { mNestedTableMapper->addMapping (editor, col); - std::string disString = tree->nestedHeaderData (i, col, - Qt::Horizontal, Qt::DisplayRole).toString().toStdString(); // Need to use Qt::DisplayRole in order to get the correct string // from CSMWorld::Columns QLabel* label = new QLabel (tree->nestedHeaderData (i, col, @@ -883,12 +882,12 @@ connect (mBottom, SIGNAL (requestFocus (const std::string&)), this, SLOT (requestFocus (const std::string&))); - // button bar - if (CSMSettings::UserSettings::instance().setting ("dialogues/toolbar", QString("true")) == "true") - addButtonBar(); - // layout getMainLayout().addWidget (mBottom); + + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); + CSMPrefs::get()["ID Dialogues"].update(); } void CSVWorld::DialogueSubView::setEditLock (bool locked) @@ -899,29 +898,21 @@ mButtons->setEditLock (locked); } -void CSVWorld::DialogueSubView::updateUserSetting (const QString& name, const QStringList& value) +void CSVWorld::DialogueSubView::settingChanged (const CSMPrefs::Setting *setting) { - SimpleDialogueSubView::updateUserSetting (name, value); - - if (name=="dialogues/toolbar") + if (*setting=="ID Dialogues/toolbar") { - if (value.at(0)==QString ("true")) + if (setting->isTrue()) { addButtonBar(); } - else + else if (mButtons) { - if (mButtons) - { - getMainLayout().removeWidget (mButtons); - delete mButtons; - mButtons = 0; - } + getMainLayout().removeWidget (mButtons); + delete mButtons; + mButtons = 0; } } - - if (mButtons) - mButtons->updateUserSetting (name, value); } void CSVWorld::DialogueSubView::showPreview () diff -Nru openmw-0.37.0/apps/opencs/view/world/dialoguesubview.hpp openmw-0.38.0/apps/opencs/view/world/dialoguesubview.hpp --- openmw-0.37.0/apps/opencs/view/world/dialoguesubview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/dialoguesubview.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,6 +27,11 @@ class NestedTableProxyModel; } +namespace CSMPrefs +{ + class Setting; +} + namespace CSMDoc { class Document; @@ -271,10 +276,10 @@ virtual void setEditLock (bool locked); - virtual void updateUserSetting (const QString& name, const QStringList& value); - private slots: + void settingChanged (const CSMPrefs::Setting *setting); + void showPreview(); void viewRecord(); diff -Nru openmw-0.37.0/apps/opencs/view/world/idtypedelegate.cpp openmw-0.38.0/apps/opencs/view/world/idtypedelegate.cpp --- openmw-0.37.0/apps/opencs/view/world/idtypedelegate.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/idtypedelegate.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -5,7 +5,7 @@ CSVWorld::IdTypeDelegate::IdTypeDelegate (const ValueList &values, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) : DataDisplayDelegate (values, icons, dispatcher, document, - "records", "type-format", + "Records", "type-format", parent) {} diff -Nru openmw-0.37.0/apps/opencs/view/world/recordbuttonbar.cpp openmw-0.38.0/apps/opencs/view/world/recordbuttonbar.cpp --- openmw-0.37.0/apps/opencs/view/world/recordbuttonbar.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/recordbuttonbar.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,7 +6,7 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/commanddispatcher.hpp" -#include "../../model/settings/usersettings.hpp" +#include "../../model/prefs/state.hpp" #include "../world/tablebottombox.hpp" @@ -32,7 +32,7 @@ mPrevButton->setDisabled (true); mNextButton->setDisabled (true); } - else if (CSMSettings::UserSettings::instance().settingValue ("general-input/cycle")=="true") + else if (CSMPrefs::get()["General Input"]["cycle"].isTrue()) { mPrevButton->setDisabled (false); mNextButton->setDisabled (false); @@ -131,6 +131,9 @@ connect (&mTable, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (rowNumberChanged (const QModelIndex&, int, int))); + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); + updateModificationButtons(); updatePrevNextButtons(); } @@ -141,18 +144,18 @@ updateModificationButtons(); } -void CSVWorld::RecordButtonBar::updateUserSetting (const QString& name, const QStringList& value) -{ - if (name=="general-input/cycle") - updatePrevNextButtons(); -} - void CSVWorld::RecordButtonBar::universalIdChanged (const CSMWorld::UniversalId& id) { mId = id; updatePrevNextButtons(); } +void CSVWorld::RecordButtonBar::settingChanged (const CSMPrefs::Setting *setting) +{ + if (*setting=="General Input/cycle") + updatePrevNextButtons(); +} + void CSVWorld::RecordButtonBar::cloneRequest() { if (mBottom) @@ -173,8 +176,7 @@ if (newRow >= mTable.rowCount()) { - if (CSMSettings::UserSettings::instance().settingValue ("general-input/cycle") - =="true") + if (CSMPrefs::get()["General Input"]["cycle"].isTrue()) newRow = 0; else return; @@ -189,8 +191,7 @@ if (newRow < 0) { - if (CSMSettings::UserSettings::instance().settingValue ("general-input/cycle") - =="true") + if (CSMPrefs::get()["General Input"]["cycle"].isTrue()) newRow = mTable.rowCount()-1; else return; diff -Nru openmw-0.37.0/apps/opencs/view/world/recordbuttonbar.hpp openmw-0.38.0/apps/opencs/view/world/recordbuttonbar.hpp --- openmw-0.37.0/apps/opencs/view/world/recordbuttonbar.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/recordbuttonbar.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -14,6 +14,11 @@ class CommandDispatcher; } +namespace CSMPrefs +{ + class Setting; +} + namespace CSVWorld { class TableBottomBox; @@ -49,7 +54,7 @@ void updateModificationButtons(); void updatePrevNextButtons(); - + public: RecordButtonBar (const CSMWorld::UniversalId& id, @@ -58,14 +63,14 @@ void setEditLock (bool locked); - void updateUserSetting (const QString& name, const QStringList& value); - public slots: void universalIdChanged (const CSMWorld::UniversalId& id); private slots: + void settingChanged (const CSMPrefs::Setting *setting); + void cloneRequest(); void nextId(); @@ -73,7 +78,7 @@ void prevId(); void rowNumberChanged (const QModelIndex& parent, int start, int end); - + signals: void showPreview(); diff -Nru openmw-0.37.0/apps/opencs/view/world/recordstatusdelegate.cpp openmw-0.38.0/apps/opencs/view/world/recordstatusdelegate.cpp --- openmw-0.37.0/apps/opencs/view/world/recordstatusdelegate.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/recordstatusdelegate.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,14 +4,13 @@ #include #include -#include "../../model/settings/usersettings.hpp" #include "../../model/world/columns.hpp" CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values, const IconList & icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) : DataDisplayDelegate (values, icons, dispatcher, document, - "records", "status-format", + "Records", "status-format", parent) {} diff -Nru openmw-0.37.0/apps/opencs/view/world/scenesubview.cpp openmw-0.38.0/apps/opencs/view/world/scenesubview.cpp --- openmw-0.37.0/apps/opencs/view/world/scenesubview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scenesubview.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -147,12 +147,6 @@ return mTitle; } -void CSVWorld::SceneSubView::updateUserSetting (const QString& name, const QStringList& value) -{ - mScene->updateUserSetting (name, value); - CSVDoc::SubView::updateUserSetting (name, value); -} - void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId& id) { setUniversalId(id); diff -Nru openmw-0.37.0/apps/opencs/view/world/scenesubview.hpp openmw-0.38.0/apps/opencs/view/world/scenesubview.hpp --- openmw-0.37.0/apps/opencs/view/world/scenesubview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scenesubview.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -59,8 +59,6 @@ virtual std::string getTitle() const; - virtual void updateUserSetting (const QString& name, const QStringList& value); - private: void makeConnections(CSVRender::PagedWorldspaceWidget* widget); diff -Nru openmw-0.37.0/apps/opencs/view/world/scriptedit.cpp openmw-0.38.0/apps/opencs/view/world/scriptedit.cpp --- openmw-0.37.0/apps/opencs/view/world/scriptedit.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scriptedit.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -12,8 +12,7 @@ #include "../../model/world/universalid.hpp" #include "../../model/world/tablemimedata.hpp" -#include "../../model/settings/usersettings.hpp" - +#include "../../model/prefs/state.hpp" CSVWorld::ScriptEdit::ChangeLock::ChangeLock (ScriptEdit& edit) : mEdit (edit) { @@ -92,31 +91,24 @@ connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting())); - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); - connect (&userSettings, SIGNAL (userSettingUpdated(const QString &, const QStringList &)), - this, SLOT (updateUserSetting (const QString &, const QStringList &))); + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); + { + ChangeLock lock (*this); + CSMPrefs::get()["Scripts"].update(); + } mUpdateTimer.setSingleShot (true); // TODO: provide a font selector dialogue mMonoFont.setStyleHint(QFont::TypeWriter); - if (userSettings.setting("script-editor/mono-font", "true") == "true") - setFont(mMonoFont); - mLineNumberArea = new LineNumberArea(this); updateLineNumberAreaWidth(0); connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); - - showLineNum(userSettings.settingValue("script-editor/show-linenum") == "true"); -} - -void CSVWorld::ScriptEdit::updateUserSetting (const QString &name, const QStringList &list) -{ - if (mHighlighter->updateUserSetting (name, list)) - updateHighlighting(); + updateHighlighting(); } void CSVWorld::ScriptEdit::showLineNum(bool show) @@ -202,6 +194,16 @@ return !(string.contains(mWhiteListQoutes)); } +void CSVWorld::ScriptEdit::settingChanged (const CSMPrefs::Setting *setting) +{ + if (mHighlighter->settingChanged (setting)) + updateHighlighting(); + else if (*setting=="Scripts/mono-font") + setFont (setting->isTrue() ? mMonoFont : mDefaultFont); + else if (*setting=="Scripts/show-linenum") + showLineNum (setting->isTrue()); +} + void CSVWorld::ScriptEdit::idListChanged() { mHighlighter->invalidateIds(); diff -Nru openmw-0.37.0/apps/opencs/view/world/scriptedit.hpp openmw-0.38.0/apps/opencs/view/world/scriptedit.hpp --- openmw-0.37.0/apps/opencs/view/world/scriptedit.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scriptedit.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -91,6 +91,8 @@ private slots: + void settingChanged (const CSMPrefs::Setting *setting); + void idListChanged(); void updateHighlighting(); @@ -98,10 +100,6 @@ void updateLineNumberAreaWidth(int newBlockCount); void updateLineNumberArea(const QRect &, int); - - public slots: - - void updateUserSetting (const QString &name, const QStringList &list); }; class LineNumberArea : public QWidget diff -Nru openmw-0.37.0/apps/opencs/view/world/scripterrortable.cpp openmw-0.38.0/apps/opencs/view/world/scripterrortable.cpp --- openmw-0.37.0/apps/opencs/view/world/scripterrortable.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scripterrortable.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -9,7 +9,8 @@ #include #include "../../model/doc/document.hpp" -#include "../../model/settings/usersettings.hpp" + +#include "../../model/prefs/state.hpp" void CSVWorld::ScriptErrorTable::report (const std::string& message, const Compiler::TokenLoc& loc, Type type) { @@ -57,7 +58,7 @@ setItem (row, 2, messageItem); } -void CSVWorld::ScriptErrorTable::setWarningsMode (const QString& value) +void CSVWorld::ScriptErrorTable::setWarningsMode (const std::string& value) { if (value=="Ignore") Compiler::ErrorHandler::setWarningsMode (0); @@ -91,17 +92,13 @@ Compiler::registerExtensions (mExtensions); mContext.setExtensions (&mExtensions); - setWarningsMode (CSMSettings::UserSettings::instance().settingValue ("script-editor/warnings")); + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); + CSMPrefs::get()["Scripts"].update(); connect (this, SIGNAL (cellClicked (int, int)), this, SLOT (cellClicked (int, int))); } -void CSVWorld::ScriptErrorTable::updateUserSetting (const QString& name, const QStringList& value) -{ - if (name=="script-editor/warnings" && !value.isEmpty()) - setWarningsMode (value.at (0)); -} - void CSVWorld::ScriptErrorTable::update (const std::string& source) { clear(); @@ -136,6 +133,12 @@ return mContext.clearLocals (script); } +void CSVWorld::ScriptErrorTable::settingChanged (const CSMPrefs::Setting *setting) +{ + if (*setting=="Scripts/warnings") + setWarningsMode (setting->toString()); +} + void CSVWorld::ScriptErrorTable::cellClicked (int row, int column) { if (item (row, 1)) diff -Nru openmw-0.37.0/apps/opencs/view/world/scripterrortable.hpp openmw-0.38.0/apps/opencs/view/world/scripterrortable.hpp --- openmw-0.37.0/apps/opencs/view/world/scripterrortable.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scripterrortable.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -14,6 +14,11 @@ class Document; } +namespace CSMPrefs +{ + class Setting; +} + namespace CSVWorld { class ScriptErrorTable : public QTableWidget, private Compiler::ErrorHandler @@ -32,14 +37,12 @@ void addMessage (const std::string& message, CSMDoc::Message::Severity severity, int line = -1, int column = -1); - void setWarningsMode (const QString& value); + void setWarningsMode (const std::string& value); public: ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent = 0); - void updateUserSetting (const QString& name, const QStringList& value); - void update (const std::string& source); void clear(); @@ -51,6 +54,8 @@ private slots: + void settingChanged (const CSMPrefs::Setting *setting); + void cellClicked (int row, int column); signals: diff -Nru openmw-0.37.0/apps/opencs/view/world/scripthighlighter.cpp openmw-0.38.0/apps/opencs/view/world/scripthighlighter.cpp --- openmw-0.37.0/apps/opencs/view/world/scripthighlighter.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scripthighlighter.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -5,7 +5,8 @@ #include #include -#include "../../model/settings/usersettings.hpp" +#include "../../model/prefs/setting.hpp" +#include "../../model/prefs/category.hpp" bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) @@ -79,79 +80,12 @@ : QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data), mMode (mode) { - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + QColor color ("black"); + QTextCharFormat format; + format.setForeground (color); - QColor color = QColor(); - - { - color.setNamedColor(userSettings.setting("script-editor/colour-int", "Dark magenta")); - if (!color.isValid()) - color = QColor(Qt::darkMagenta); - - QTextCharFormat format; - format.setForeground (color); - mScheme.insert (std::make_pair (Type_Int, format)); - } - - { - color.setNamedColor(userSettings.setting ("script-editor/colour-float", "Magenta")); - if (!color.isValid()) - color = QColor(Qt::magenta); - - QTextCharFormat format; - format.setForeground (color); - mScheme.insert (std::make_pair (Type_Float, format)); - } - - { - color.setNamedColor(userSettings.setting ("script-editor/colour-name", "Gray")); - if (!color.isValid()) - color = QColor(Qt::gray); - - QTextCharFormat format; - format.setForeground (color); - mScheme.insert (std::make_pair (Type_Name, format)); - } - - { - color.setNamedColor(userSettings.setting ("script-editor/colour-keyword", "Red")); - if (!color.isValid()) - color = QColor(Qt::red); - - QTextCharFormat format; - format.setForeground (color); - mScheme.insert (std::make_pair (Type_Keyword, format)); - } - - { - color.setNamedColor(userSettings.setting ("script-editor/colour-special", "Dark yellow")); - if (!color.isValid()) - color = QColor(Qt::darkYellow); - - QTextCharFormat format; - format.setForeground (color); - mScheme.insert (std::make_pair (Type_Special, format)); - } - - { - color.setNamedColor(userSettings.setting ("script-editor/colour-comment", "Green")); - if (!color.isValid()) - color = QColor(Qt::green); - - QTextCharFormat format; - format.setForeground (color); - mScheme.insert (std::make_pair (Type_Comment, format)); - } - - { - color.setNamedColor(userSettings.setting ("script-editor/colour-id", "Blue")); - if (!color.isValid()) - color = QColor(Qt::blue); - - QTextCharFormat format; - format.setForeground (color); - mScheme.insert (std::make_pair (Type_Id, format)); - } + for (int i=0; i<=Type_Id; ++i) + mScheme.insert (std::make_pair (static_cast (i), format)); // configure compiler Compiler::registerExtensions (mExtensions); @@ -176,85 +110,26 @@ mContext.invalidateIds(); } -bool CSVWorld::ScriptHighlighter::updateUserSetting (const QString &name, const QStringList &list) +bool CSVWorld::ScriptHighlighter::settingChanged (const CSMPrefs::Setting *setting) { - if (list.empty()) - return false; - - QColor color = QColor(); - - if (name == "script-editor/colour-int") - { - color.setNamedColor(list.at(0)); - if (!color.isValid()) - return false; - - QTextCharFormat format; - format.setForeground (color); - mScheme[Type_Int] = format; - } - else if (name == "script-editor/colour-float") - { - color.setNamedColor(list.at(0)); - if (!color.isValid()) - return false; - - QTextCharFormat format; - format.setForeground (color); - mScheme[Type_Float] = format; - } - else if (name == "script-editor/colour-name") + if (setting->getParent()->getKey()=="Scripts") { - color.setNamedColor(list.at(0)); - if (!color.isValid()) - return false; - - QTextCharFormat format; - format.setForeground (color); - mScheme[Type_Name] = format; + static const char *const colours[Type_Id+2] = + { + "colour-int", "colour-float", "colour-name", "colour-keyword", + "colour-special", "colour-comment", "colour-id", + 0 + }; + + for (int i=0; colours[i]; ++i) + if (setting->getKey()==colours[i]) + { + QTextCharFormat format; + format.setForeground (setting->toColor()); + mScheme[static_cast (i)] = format; + return true; + } } - else if (name == "script-editor/colour-keyword") - { - color.setNamedColor(list.at(0)); - if (!color.isValid()) - return false; - - QTextCharFormat format; - format.setForeground (color); - mScheme[Type_Keyword] = format; - } - else if (name == "script-editor/colour-special") - { - color.setNamedColor(list.at(0)); - if (!color.isValid()) - return false; - - QTextCharFormat format; - format.setForeground (color); - mScheme[Type_Special] = format; - } - else if (name == "script-editor/colour-comment") - { - color.setNamedColor(list.at(0)); - if (!color.isValid()) - return false; - - QTextCharFormat format; - format.setForeground (color); - mScheme[Type_Comment] = format; - } - else if (name == "script-editor/colour-id") - { - color.setNamedColor(list.at(0)); - if (!color.isValid()) - return false; - - QTextCharFormat format; - format.setForeground (color); - mScheme[Type_Id] = format; - } - else - return false; - return true; + return false; } diff -Nru openmw-0.37.0/apps/opencs/view/world/scripthighlighter.hpp openmw-0.38.0/apps/opencs/view/world/scripthighlighter.hpp --- openmw-0.37.0/apps/opencs/view/world/scripthighlighter.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scripthighlighter.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,6 +11,11 @@ #include "../../model/world/scriptcontext.hpp" +namespace CSMPrefs +{ + class Setting; +} + namespace CSVWorld { class ScriptHighlighter : public QSyntaxHighlighter, private Compiler::Parser @@ -19,13 +24,13 @@ enum Type { - Type_Int, - Type_Float, - Type_Name, - Type_Keyword, - Type_Special, - Type_Comment, - Type_Id + Type_Int = 0, + Type_Float = 1, + Type_Name = 2, + Type_Keyword = 3, + Type_Special = 4, + Type_Comment = 5, + Type_Id = 6 }; enum Mode @@ -88,7 +93,7 @@ void invalidateIds(); - bool updateUserSetting (const QString &name, const QStringList &list); + bool settingChanged (const CSMPrefs::Setting *setting); }; } diff -Nru openmw-0.37.0/apps/opencs/view/world/scriptsubview.cpp openmw-0.38.0/apps/opencs/view/world/scriptsubview.cpp --- openmw-0.37.0/apps/opencs/view/world/scriptsubview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scriptsubview.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -13,7 +13,7 @@ #include "../../model/world/columnbase.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/idtable.hpp" -#include "../../model/settings/usersettings.hpp" +#include "../../model/prefs/state.hpp" #include "scriptedit.hpp" #include "recordbuttonbar.hpp" @@ -39,8 +39,7 @@ void CSVWorld::ScriptSubView::recompile() { if (!mCompileDelay->isActive() && !isDeleted()) - mCompileDelay->start ( - CSMSettings::UserSettings::instance().setting ("script-editor/compile-delay").toInt()); + mCompileDelay->start (CSMPrefs::get()["Scripts"]["compile-delay"].toInt()); } bool CSVWorld::ScriptSubView::isDeleted() const @@ -54,6 +53,7 @@ if (isDeleted()) { mErrors->clear(); + adjustSplitter(); mEditor->setEnabled (false); } else @@ -63,9 +63,32 @@ } } +void CSVWorld::ScriptSubView::adjustSplitter() +{ + QList sizes; + + if (mErrors->rowCount()) + { + if (mErrors->height()) + return; // keep old height if the error panel was already open + + sizes << (mMain->height()-mErrorHeight-mMain->handleWidth()) << mErrorHeight; + } + else + { + if (mErrors->height()) + mErrorHeight = mErrors->height(); + + sizes << 1 << 0; + } + + mMain->setSizes (sizes); +} + CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mDocument (document), mColumn (-1), mBottom(0), mButtons (0), - mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) + mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())), + mErrorHeight (CSMPrefs::get()["Scripts"]["error-height"].toInt()) { std::vector selection (1, id.getId()); mCommandDispatcher.setSelection (selection); @@ -81,6 +104,10 @@ mErrors = new ScriptErrorTable (document, this); mMain->addWidget (mErrors); + QList sizes; + sizes << 1 << 0; + mMain->setSizes (sizes); + QWidget *widget = new QWidget (this);; widget->setLayout (&mLayout); setWidget (widget); @@ -98,9 +125,6 @@ // bottom box and buttons mBottom = new TableBottomBox (CreatorFactory(), document, id, this); - if (CSMSettings::UserSettings::instance().setting ("script-editor/toolbar", QString("true")) == "true") - addButtonBar(); - connect (mBottom, SIGNAL (requestFocus (const std::string&)), this, SLOT (switchToId (const std::string&))); @@ -128,55 +152,40 @@ connect (mCompileDelay, SIGNAL (timeout()), this, SLOT (updateRequest())); updateDeletedState(); + + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); + CSMPrefs::get()["Scripts"].update(); } -void CSVWorld::ScriptSubView::updateUserSetting (const QString& name, const QStringList& value) +void CSVWorld::ScriptSubView::setStatusBar (bool show) { - if (name == "script-editor/show-linenum") - { - std::string showLinenum = value.at(0).toUtf8().constData(); - mEditor->showLineNum(showLinenum == "true"); - mBottom->setVisible(showLinenum == "true"); - } - else if (name == "script-editor/mono-font") - { - mEditor->setMonoFont (value.at(0)==QString ("true")); - } - else if (name=="script-editor/toolbar") + mBottom->setStatusBar (show); +} + +void CSVWorld::ScriptSubView::settingChanged (const CSMPrefs::Setting *setting) +{ + if (*setting=="Scripts/toolbar") { - if (value.at(0)==QString ("true")) + if (setting->isTrue()) { addButtonBar(); } - else + else if (mButtons) { - if (mButtons) - { - mLayout.removeWidget (mButtons); - delete mButtons; - mButtons = 0; - } + mLayout.removeWidget (mButtons); + delete mButtons; + mButtons = 0; } } - else if (name=="script-editor/compile-delay") + else if (*setting=="Scripts/compile-delay") { - mCompileDelay->setInterval (value.at (0).toInt()); + mCompileDelay->setInterval (setting->toInt()); } - - if (mButtons) - mButtons->updateUserSetting (name, value); - - mErrors->updateUserSetting (name, value); - - if (name=="script-editor/warnings") + else if (*setting=="Scripts/warnings") recompile(); } -void CSVWorld::ScriptSubView::setStatusBar (bool show) -{ - mBottom->setStatusBar (show); -} - void CSVWorld::ScriptSubView::updateStatusBar () { mBottom->positionChanged (mEditor->textCursor().blockNumber() + 1, @@ -347,4 +356,6 @@ QString source = mModel->data (index).toString(); mErrors->update (source.toUtf8().constData()); + + adjustSplitter(); } diff -Nru openmw-0.37.0/apps/opencs/view/world/scriptsubview.hpp openmw-0.38.0/apps/opencs/view/world/scriptsubview.hpp --- openmw-0.37.0/apps/opencs/view/world/scriptsubview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/scriptsubview.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,6 +23,11 @@ class IdTable; } +namespace CSMPrefs +{ + class Setting; +} + namespace CSVWorld { class ScriptEdit; @@ -47,6 +52,7 @@ QSplitter *mMain; ScriptErrorTable *mErrors; QTimer *mCompileDelay; + int mErrorHeight; private: @@ -58,6 +64,8 @@ void updateDeletedState(); + void adjustSplitter(); + public: ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); @@ -66,8 +74,6 @@ virtual void useHint (const std::string& hint); - virtual void updateUserSetting (const QString& name, const QStringList& value); - virtual void setStatusBar (bool show); public slots: @@ -80,6 +86,8 @@ private slots: + void settingChanged (const CSMPrefs::Setting *setting); + void updateStatusBar(); void switchToRow (int row); diff -Nru openmw-0.37.0/apps/opencs/view/world/table.cpp openmw-0.38.0/apps/opencs/view/world/table.cpp --- openmw-0.37.0/apps/opencs/view/world/table.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/table.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,7 +23,8 @@ #include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" -#include "../../model/settings/usersettings.hpp" + +#include "../../model/prefs/state.hpp" #include "recordstatusdelegate.hpp" #include "tableeditidaction.hpp" @@ -232,24 +233,6 @@ : DragRecordTable(document), mCreateAction (0), mCloneAction(0),mRecordStatusDisplay (0) { - CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance(); - QString jumpSetting = settings.settingValue ("table-input/jump-to-added"); - if (jumpSetting.isEmpty() || jumpSetting == "Jump and Select") // default - { - mJumpToAddedRecord = true; - mUnselectAfterJump = false; - } - else if(jumpSetting == "Jump Only") - { - mJumpToAddedRecord = true; - mUnselectAfterJump = true; - } - else - { - mJumpToAddedRecord = false; - mUnselectAfterJump = false; - } - mModel = &dynamic_cast (*mDocument.getData().getTableModel (id)); bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || @@ -358,7 +341,7 @@ //connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), // this, SLOT (rowsInsertedEvent(const QModelIndex&, int, int))); - connect (mProxyModel, SIGNAL (rowAdded (const std::string &)), + connect (mProxyModel, SIGNAL (rowAdded (const std::string &)), this, SLOT (rowAdded (const std::string &))); /// \note This signal could instead be connected to a slot that filters out changes not affecting @@ -375,6 +358,10 @@ mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_EditRecord)); mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_View)); mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier | Qt::ControlModifier, Action_EditRecordAndClose)); + + connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), + this, SLOT (settingChanged (const CSMPrefs::Setting *))); + CSMPrefs::get()["ID Tables"].update(); } void CSVWorld::Table::setEditLock (bool locked) @@ -404,7 +391,7 @@ QModelIndexList selectedRows = selectionModel()->selectedRows(); int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - for (QModelIndexList::const_iterator iter (selectedRows.begin()); + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter != selectedRows.end(); ++iter) { @@ -548,9 +535,7 @@ void CSVWorld::Table::executeExtendedDelete() { - CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance(); - QString configSetting = settings.settingValue ("table-input/extended-config"); - if (configSetting == "true") + if (CSMPrefs::get()["ID Tables"]["extended-config"].isTrue()) { emit extendedDeleteConfigRequest(getSelectedIds()); } @@ -562,9 +547,7 @@ void CSVWorld::Table::executeExtendedRevert() { - CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance(); - QString configSetting = settings.settingValue ("table-input/extended-config"); - if (configSetting == "true") + if (CSMPrefs::get()["ID Tables"]["extended-config"].isTrue()) { emit extendedRevertConfigRequest(getSelectedIds()); } @@ -574,16 +557,16 @@ } } -void CSVWorld::Table::updateUserSetting (const QString &name, const QStringList &list) +void CSVWorld::Table::settingChanged (const CSMPrefs::Setting *setting) { - if (name=="table-input/jump-to-added") + if (*setting=="ID Tables/jump-to-added") { - if(list.isEmpty() || list.at(0) == "Jump and Select") // default + if (setting->toString()=="Jump and Select") { mJumpToAddedRecord = true; mUnselectAfterJump = false; } - else if(list.at(0) == "Jump Only") + else if (setting->toString()=="Jump Only") { mJumpToAddedRecord = true; mUnselectAfterJump = true; @@ -594,28 +577,23 @@ mUnselectAfterJump = false; } } - - if (name=="records/type-format" || name=="records/status-format") + else if (*setting=="Records/type-format" || *setting=="Records/status-format") { int columns = mModel->columnCount(); for (int i=0; i - (*delegate).updateUserSetting (name, list); - { - emit dataChanged (mModel->index (0, i), - mModel->index (mModel->rowCount()-1, i)); - } + dynamic_cast (*delegate).settingChanged (setting); + emit dataChanged (mModel->index (0, i), + mModel->index (mModel->rowCount()-1, i)); } - return; } - - QString base ("table-input/double"); - if (name.startsWith (base)) + else if (setting->getParent()->getKey()=="ID Tables" && + setting->getKey().substr (0, 6)=="double") { - QString modifierString = name.mid (base.size()); + std::string modifierString = setting->getKey().substr (6); + Qt::KeyboardModifiers modifiers = 0; if (modifierString=="-s") @@ -627,7 +605,7 @@ DoubleClickAction action = Action_None; - QString value = list.at (0); + std::string value = setting->toString(); if (value=="Edit in Place") action = Action_InPlaceEdit; @@ -645,8 +623,6 @@ action = Action_ViewAndClose; mDoubleClickActions[modifiers] = action; - - return; } } diff -Nru openmw-0.37.0/apps/opencs/view/world/table.hpp openmw-0.38.0/apps/opencs/view/world/table.hpp --- openmw-0.37.0/apps/opencs/view/world/table.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/table.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,6 +27,11 @@ class CommandDispatcher; } +namespace CSMPrefs +{ + class Setting; +} + namespace CSVWorld { class CommandDelegate; @@ -140,6 +145,8 @@ public slots: + void settingChanged (const CSMPrefs::Setting *setting); + void tableSizeUpdate(); void selectionSizeUpdate(); @@ -148,8 +155,6 @@ void recordFilterChanged (boost::shared_ptr filter); - void updateUserSetting (const QString &name, const QStringList &list); - void rowAdded(const std::string &id); }; } diff -Nru openmw-0.37.0/apps/opencs/view/world/tablesubview.cpp openmw-0.38.0/apps/opencs/view/world/tablesubview.cpp --- openmw-0.37.0/apps/opencs/view/world/tablesubview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/tablesubview.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -98,12 +98,6 @@ focusId (id, hint); } -void CSVWorld::TableSubView::updateUserSetting - (const QString &name, const QStringList &list) -{ - mTable->updateUserSetting(name, list); -} - void CSVWorld::TableSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); diff -Nru openmw-0.37.0/apps/opencs/view/world/tablesubview.hpp openmw-0.38.0/apps/opencs/view/world/tablesubview.hpp --- openmw-0.37.0/apps/opencs/view/world/tablesubview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/tablesubview.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -43,9 +43,6 @@ virtual void setEditLock (bool locked); - virtual void updateUserSetting - (const QString& name, const QStringList &list); - virtual void setStatusBar (bool show); virtual void useHint (const std::string& hint); diff -Nru openmw-0.37.0/apps/opencs/view/world/util.cpp openmw-0.38.0/apps/opencs/view/world/util.cpp --- openmw-0.37.0/apps/opencs/view/world/util.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/util.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -166,7 +166,7 @@ const QModelIndex& index) const { CSMWorld::ColumnBase::Display display = getDisplayTypeFromIndex(index); - + // This createEditor() method is called implicitly from tables. // For boolean values in tables use the default editor (combobox). // Checkboxes is looking ugly in the table view. @@ -239,7 +239,7 @@ edit->setUndoRedoEnabled (false); return edit; } - + case CSMWorld::ColumnBase::Display_Boolean: return new QCheckBox(parent); @@ -260,7 +260,7 @@ widget->setMaxLength (32); return widget; } - + default: return QStyledItemDelegate::createEditor (parent, option, index); @@ -329,3 +329,5 @@ } } + +void CSVWorld::CommandDelegate::settingChanged (const CSMPrefs::Setting *setting) {} diff -Nru openmw-0.37.0/apps/opencs/view/world/util.hpp openmw-0.38.0/apps/opencs/view/world/util.hpp --- openmw-0.37.0/apps/opencs/view/world/util.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/opencs/view/world/util.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -18,6 +18,11 @@ class CommandDispatcher; } +namespace CSMPrefs +{ + class Setting; +} + namespace CSVWorld { ///< \brief Getting the data out of an editor widget @@ -138,10 +143,9 @@ virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const; - public slots: - - virtual void updateUserSetting - (const QString &name, const QStringList &list) {} + /// \attention This is not a slot. For ordering reasons this function needs to be + /// called manually from the parent object's settingChanged function. + virtual void settingChanged (const CSMPrefs::Setting *setting); }; } diff -Nru openmw-0.37.0/apps/openmw/android_main.c openmw-0.38.0/apps/openmw/android_main.c --- openmw-0.37.0/apps/openmw/android_main.c 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/android_main.c 2016-01-12 16:11:28.000000000 +0000 @@ -1,4 +1,3 @@ -#include "../../SDL_internal.h" #ifdef __ANDROID__ #include "SDL_main.h" diff -Nru openmw-0.37.0/apps/openmw/CMakeLists.txt openmw-0.38.0/apps/openmw/CMakeLists.txt --- openmw-0.37.0/apps/openmw/CMakeLists.txt 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/CMakeLists.txt 2016-01-12 16:11:28.000000000 +0000 @@ -57,12 +57,12 @@ ) add_openmw_dir (mwsound - soundmanagerimp openal_output ffmpeg_decoder sound sound_decoder sound_output loudness movieaudiofactory + soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output loudness movieaudiofactory ) add_openmw_dir (mwworld refdata worldimp scene globals class action nullaction actionteleport - containerstore actiontalk actiontake manualref player cellfunctors failedaction + containerstore actiontalk actiontake manualref player cellvisitors failedaction cells localscripts customdata inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor @@ -75,7 +75,7 @@ add_openmw_dir (mwclass classes activator creature npc weapon armor potion apparatus book clothing container door - ingredient creaturelevlist itemlevlist light lockpick misc probe repair static actor + ingredient creaturelevlist itemlevlist light lockpick misc probe repair static actor bodypart ) add_openmw_dir (mwmechanics @@ -113,16 +113,24 @@ # Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING # when we change the backend. -include_directories(${SOUND_INPUT_INCLUDES}) +include_directories( + ${FFMPEG_INCLUDE_DIRS} +) target_link_libraries(openmw - ${OPENSCENEGRAPH_LIBRARIES} + ${OSG_LIBRARIES} + ${OPENTHREADS_LIBRARIES} + ${OSGPARTICLE_LIBRARIES} + ${OSGUTIL_LIBRARIES} + ${OSGDB_LIBRARIES} + ${OSGVIEWER_LIBRARIES} + ${OSGGA_LIBRARIES} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${OPENAL_LIBRARY} - ${SOUND_INPUT_LIBRARY} + ${FFMPEG_LIBRARIES} ${BULLET_LIBRARIES} ${MYGUI_LIBRARIES} ${SDL2_LIBRARY} @@ -132,15 +140,27 @@ ) if (ANDROID) + set (OSG_PLUGINS + -Wl,--whole-archive + ${OSG_PLUGINS_DIR}/libosgdb_dds.a + ${OSG_PLUGINS_DIR}/libosgdb_bmp.a + ${OSG_PLUGINS_DIR}/libosgdb_tga.a + ${OSG_PLUGINS_DIR}/libosgdb_gif.a + ${OSG_PLUGINS_DIR}/libosgdb_jpeg.a + ${OSG_PLUGINS_DIR}/libosgdb_png.a + -Wl,--no-whole-archive + ) target_link_libraries(openmw EGL android log dl - MyGUIEngineStatic - cpufeatures - BulletCollision - LinearMath + z + ${OPENSCENEGRAPH_LIBRARIES} + ${OSG_PLUGINS} + jpeg + gif + png ) endif (ANDROID) diff -Nru openmw-0.37.0/apps/openmw/crashcatcher.cpp openmw-0.38.0/apps/openmw/crashcatcher.cpp --- openmw-0.37.0/apps/openmw/crashcatcher.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/crashcatcher.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -387,11 +387,17 @@ kill(crash_info.pid, SIGKILL); } + // delay between killing of the crashed process and showing the message box to + // work around occasional X server lock-up. this can only be a bug in X11 since + // even faulty applications shouldn't be able to freeze the X server. + usleep(100000); + if(logfile) { std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); } + exit(0); } diff -Nru openmw-0.37.0/apps/openmw/engine.cpp openmw-0.38.0/apps/openmw/engine.cpp --- openmw-0.37.0/apps/openmw/engine.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/engine.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -298,8 +298,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) { // Create the settings manager and load default settings file - const std::string localdefault = mCfgMgr.getLocalPath().string() + "/settings-default.cfg"; - const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/settings-default.cfg"; + const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string(); + const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string(); // prefer local if (boost::filesystem::exists(localdefault)) @@ -310,7 +310,7 @@ throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); // load user settings if they exist - const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg"; + const std::string settingspath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string(); if (boost::filesystem::exists(settingspath)) settings.loadUser(settingspath); @@ -451,12 +451,13 @@ mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get())); mResourceSystem->getTextureManager()->setUnRefImageDataAfterApply(true); - osg::Texture::FilterMode min = osg::Texture::LINEAR_MIPMAP_NEAREST; - osg::Texture::FilterMode mag = osg::Texture::LINEAR; - if (Settings::Manager::getString("texture filtering", "General") == "trilinear") - min = osg::Texture::LINEAR_MIPMAP_LINEAR; - int maxAnisotropy = Settings::Manager::getInt("anisotropy", "General"); - mResourceSystem->getTextureManager()->setFilterSettings(min, mag, maxAnisotropy); + mResourceSystem->getTextureManager()->setFilterSettings( + Settings::Manager::getString("texture mag filter", "General"), + Settings::Manager::getString("texture min filter", "General"), + Settings::Manager::getString("texture mipmap", "General"), + Settings::Manager::getInt("anisotropy", "General"), + NULL + ); // Create input and UI first to set up a bootstrapping environment for // showing a loading screen and keeping the window responsive while doing so @@ -473,8 +474,8 @@ } // find correct path to the game controller bindings - const std::string localdefault = mCfgMgr.getLocalPath().string() + "/gamecontrollerdb.cfg"; - const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/gamecontrollerdb.cfg"; + const std::string localdefault = mCfgMgr.getLocalPath().string() + "/gamecontrollerdb.txt"; + const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/gamecontrollerdb.txt"; std::string gameControllerdb; if (boost::filesystem::exists(localdefault)) gameControllerdb = localdefault; @@ -483,8 +484,7 @@ else gameControllerdb = ""; //if it doesn't exist, pass in an empty string - // FIXME: shouldn't depend on Engine - MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, *this, keybinderUser, keybinderUserExists, gameControllerdb, mGrab); + MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, keybinderUser, keybinderUserExists, gameControllerdb, mGrab); mEnvironment.setInputManager (input); std::string myguiResources = (mResDir / "mygui").string(); @@ -514,6 +514,7 @@ mEnvironment.getWorld()->setupPlayer(); input->setPlayer(&mEnvironment.getWorld()->getPlayer()); + window->setStore(mEnvironment.getWorld()->getStore()); window->initUI(); window->renderWorldMap(); @@ -717,41 +718,6 @@ std::cout << "Quitting peacefully." << std::endl; } -void OMW::Engine::activate() -{ - if (mEnvironment.getWindowManager()->isGuiMode()) - return; - - MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); - const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player); - if (playerStats.isParalyzed() || playerStats.getKnockedDown()) - return; - - MWWorld::Ptr ptr = mEnvironment.getWorld()->getFacedObject(); - - if (ptr.isEmpty()) - return; - - if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated - return; - - if (ptr.getClass().isActor()) - { - MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); - - if (stats.getAiSequence().isInCombat() && !stats.isDead()) - return; - } - - mEnvironment.getWorld()->activate(ptr, mEnvironment.getWorld()->getPlayerPtr()); -} - -void OMW::Engine::screenshot() -{ - mScreenCaptureHandler->setFramesToCapture(1); - mScreenCaptureHandler->captureNextFrame(*mViewer); -} - void OMW::Engine::setCompileAll (bool all) { mCompileAll = all; diff -Nru openmw-0.37.0/apps/openmw/engine.hpp openmw-0.38.0/apps/openmw/engine.hpp --- openmw-0.37.0/apps/openmw/engine.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/engine.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -169,12 +169,6 @@ /// Initialise and enter main loop. void go(); - /// Activate the focussed object. - void activate(); - - /// Write screenshot to file. - void screenshot(); - /// Compile all scripts (excludign dialogue scripts) at startup? void setCompileAll (bool all); diff -Nru openmw-0.37.0/apps/openmw/main.cpp openmw-0.38.0/apps/openmw/main.cpp --- openmw-0.37.0/apps/openmw/main.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/main.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,7 +23,7 @@ #endif -#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) +#if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) || defined(__posix)) #define USE_CRASH_CATCHER 1 #else #define USE_CRASH_CATCHER 0 @@ -210,6 +210,9 @@ cfgMgr.readConfiguration(variables, desc); + Version::Version v = Version::getOpenmwVersion(variables["resources"].as()); + std::cout << v.describe() << std::endl; + engine.setGrabMouse(!variables.count("no-grab")); // Font encoding settings diff -Nru openmw-0.37.0/apps/openmw/mwbase/mechanicsmanager.hpp openmw-0.38.0/apps/openmw/mwbase/mechanicsmanager.hpp --- openmw-0.37.0/apps/openmw/mwbase/mechanicsmanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwbase/mechanicsmanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -184,8 +184,9 @@ virtual void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector& objects) = 0; virtual void getActorsInRange(const osg::Vec3f &position, float radius, std::vector &objects) = 0; - ///return the list of actors which are following the given actor - /**ie AiFollow is active and the target is the actor**/ + ///Returns the list of actors which are siding with the given actor in fights + /**ie AiFollow or AiEscort is active and the target is the actor **/ + virtual std::list getActorsSidingWith(const MWWorld::Ptr& actor) = 0; virtual std::list getActorsFollowing(const MWWorld::Ptr& actor) = 0; virtual std::list getActorsFollowingIndices(const MWWorld::Ptr& actor) = 0; @@ -220,6 +221,13 @@ virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0; virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim) = 0; + + /// Turn actor into werewolf or normal form. + virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0; + + /// Sets the NPC's Acrobatics skill to match the fWerewolfAcrobatics GMST. + /// It only applies to the current form the NPC is in. + virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0; }; } diff -Nru openmw-0.37.0/apps/openmw/mwbase/soundmanager.hpp openmw-0.38.0/apps/openmw/mwbase/soundmanager.hpp --- openmw-0.37.0/apps/openmw/mwbase/soundmanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwbase/soundmanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -15,6 +15,7 @@ namespace MWSound { class Sound; + class Stream; struct Sound_Decoder; typedef boost::shared_ptr DecoderPtr; } @@ -22,6 +23,7 @@ namespace MWBase { typedef boost::shared_ptr SoundPtr; + typedef boost::shared_ptr SoundStreamPtr; /// \brief Interface for sound manager (implemented in MWSound) class SoundManager @@ -29,18 +31,17 @@ public: /* These must all fit together */ enum PlayMode { - Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ + Play_Normal = 0, /* non-looping, affected by environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position - * but do not keep it updated (the sound will not move with - * the object and will not stop when the object is deleted. */ - Play_RemoveAtDistance = 1<<3, /* (3D only) If the listener gets further than 2000 units away + Play_RemoveAtDistance = 1<<2, /* (3D only) If the listener gets further than 2000 units away from the sound source, the sound is removed. This is weird stuff but apparently how vanilla works for sounds played by the PlayLoopSound family of script functions. Perhaps we can make this cut off a more subtle fade later, but have to be careful to not change the overall volume of areas by too much. */ + Play_NoPlayerLocal = 1<<3, /* (3D only) Don't play the sound local to the listener even if the + player is making it. */ Play_LoopNoEnv = Play_Loop | Play_NoEnv, Play_LoopRemoveAtDistance = Play_Loop | Play_RemoveAtDistance }; @@ -86,7 +87,7 @@ ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist - virtual void say(const MWWorld::Ptr &reference, const std::string& filename) = 0; + virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename) = 0; ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. @@ -94,58 +95,66 @@ ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. - virtual bool sayDone(const MWWorld::Ptr &reference=MWWorld::Ptr()) const = 0; + virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const = 0; ///< Is actor not speaking? - virtual void stopSay(const MWWorld::Ptr &reference=MWWorld::Ptr()) = 0; + virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) = 0; ///< Stop an actor speaking - virtual float getSaySoundLoudness(const MWWorld::Ptr& reference) const = 0; + virtual float getSaySoundLoudness(const MWWorld::ConstPtr& reference) const = 0; ///< Check the currently playing say sound for this actor /// and get an average loudness value (scale [0,1]) at the current time position. /// If the actor is not saying anything, returns 0. - virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; + virtual SoundStreamPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; ///< Play a 2D audio track, using a custom decoder + virtual void stopTrack(SoundStreamPtr stream) = 0; + ///< Stop the given audio track from playing + + virtual double getTrackTimeDelay(SoundStreamPtr stream) = 0; + ///< Retives the time delay, in seconds, of the audio track (must be a sound + /// returned by \ref playTrack). Only intended to be called by the track + /// decoder's read method. + virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0) = 0; ///< Play a sound, independently of 3D-position - ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + ///< @param offset Number of seconds into the sound to start playback. - virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, + virtual MWBase::SoundPtr playSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0) = 0; ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified. - ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + ///< @param offset Number of seconds into the sound to start playback. - virtual MWBase::SoundPtr playManualSound3D(const osg::Vec3f& initialPos, const std::string& soundId, - float volume, float pitch, PlayType type, PlayMode mode, float offset=0) = 0; - ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition. + virtual MWBase::SoundPtr playSound3D(const osg::Vec3f& initialPos, const std::string& soundId, + float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0) = 0; + ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated using Sound::setPosition. - virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0; + virtual void stopSound(SoundPtr sound) = 0; + ///< Stop the given sound from playing + + virtual void stopSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId) = 0; ///< Stop the given object from playing the given sound, - virtual void stopSound3D(const MWWorld::Ptr &reference) = 0; + virtual void stopSound3D(const MWWorld::ConstPtr &reference) = 0; ///< Stop the given object from playing all sounds. - virtual void stopSound(MWBase::SoundPtr sound) = 0; - ///< Stop the given sound handle - virtual void stopSound(const MWWorld::CellStore *cell) = 0; ///< Stop all sounds for the given cell. virtual void stopSound(const std::string& soundId) = 0; ///< Stop a non-3d looping sound - virtual void fadeOutSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float duration) = 0; + virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration) = 0; ///< Fade out given sound (that is already playing) of given object ///< @param reference Reference to object, whose sound is faded out ///< @param soundId ID of the sound to fade out. ///< @param duration Time until volume reaches 0. - virtual bool getSoundPlaying(const MWWorld::Ptr &reference, const std::string& soundId) const = 0; + virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? /// If you want to check if sound played with playSound is playing, use empty Ptr @@ -157,9 +166,9 @@ virtual void update(float duration) = 0; - virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up) = 0; + virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater) = 0; - virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0; + virtual void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) = 0; virtual void clear() = 0; }; diff -Nru openmw-0.37.0/apps/openmw/mwbase/world.hpp openmw-0.38.0/apps/openmw/mwbase/world.hpp --- openmw-0.37.0/apps/openmw/mwbase/world.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwbase/world.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -14,6 +14,7 @@ namespace osg { class Vec3f; + class Matrixf; class Quat; class Image; } @@ -177,7 +178,7 @@ virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0; ///< Search is limited to the active cells. - virtual MWWorld::Ptr findContainer (const MWWorld::Ptr& ptr) = 0; + virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0; ///< Return a pointer to a liveCellRef which contains \a ptr. /// \note Search is limited to the active cells. @@ -247,7 +248,7 @@ /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis. - virtual std::pair getHitContact(const MWWorld::Ptr &ptr, float distance) = 0; + virtual std::pair getHitContact(const MWWorld::ConstPtr &ptr, float distance) = 0; virtual void adjustPosition (const MWWorld::Ptr& ptr, bool force) = 0; ///< Adjust position after load to be on ground. Must be called after model load. @@ -270,7 +271,7 @@ virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; - virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0; + virtual MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0; ///< place an object in a "safe" location (ie not in the void, etc). virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) @@ -346,14 +347,14 @@ virtual void update (float duration, bool paused) = 0; - virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; + virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) = 0; ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place - virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0; + virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) = 0; ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object @@ -366,13 +367,15 @@ virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0; - virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; - virtual bool isWading(const MWWorld::Ptr &object) const = 0; + virtual bool isSwimming(const MWWorld::ConstPtr &object) const = 0; + virtual bool isWading(const MWWorld::ConstPtr &object) const = 0; ///Is the head of the creature underwater? - virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; + virtual bool isSubmerged(const MWWorld::ConstPtr &object) const = 0; virtual bool isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; + virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0; + virtual void togglePOV() = 0; virtual bool isFirstPerson() const = 0; virtual void togglePreviewMode(bool enable) = 0; @@ -393,25 +396,25 @@ /// @note throws an exception when invoked on a teleport door virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; - 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 bool getPlayerCollidingWith(const MWWorld::Ptr& object) = 0; ///< @return true if the player is colliding with \a object - virtual bool getActorCollidingWith (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is colliding with \a object - virtual void hurtStandingActors (const MWWorld::Ptr& object, float dmgPerSecond) = 0; + virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object + virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object + virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object + virtual bool getActorCollidingWith (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is colliding with \a object + virtual void hurtStandingActors (const MWWorld::ConstPtr& object, float dmgPerSecond) = 0; ///< Apply a health difference to any actors standing on \a object. /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. - virtual void hurtCollidingActors (const MWWorld::Ptr& object, float dmgPerSecond) = 0; + virtual void hurtCollidingActors (const MWWorld::ConstPtr& object, float dmgPerSecond) = 0; ///< Apply a health difference to any actors colliding with \a object. /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. virtual float getWindSpeed() = 0; - virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; + virtual void getContainersOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out) = 0; ///< get all containers in active cells owned by this Npc - virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; + virtual void getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out) = 0; ///< get all items in active cells owned by this Npc - virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) = 0; + virtual bool getLOS(const MWWorld::ConstPtr& actor,const MWWorld::ConstPtr& targetActor) = 0; ///< get Line of Sight (morrowind stupid implementation) virtual float getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist) = 0; @@ -427,6 +430,7 @@ /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; + virtual const MWRender::Animation* getAnimation(const MWWorld::ConstPtr &ptr) const = 0; virtual void reattachPlayerCamera() = 0; /// \todo this does not belong here @@ -452,13 +456,6 @@ /// Returns true if levitation spell effect is allowed. virtual bool isLevitationEnabled() const = 0; - /// Turn actor into werewolf or normal form. - virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0; - - /// Sets the NPC's Acrobatics skill to match the fWerewolfAcrobatics GMST. - /// It only applies to the current form the NPC is in. - virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0; - virtual bool getGodModeState() = 0; virtual bool toggleGodMode() = 0; @@ -478,7 +475,7 @@ virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection) = 0; - virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; virtual const std::vector& getContentFiles() const = 0; @@ -538,11 +535,16 @@ /// Resets all actors in the current active cells to their original location within that cell. virtual void resetActors() = 0; - virtual bool isWalkingOnWater (const MWWorld::Ptr& actor) = 0; + virtual bool isWalkingOnWater (const MWWorld::ConstPtr& actor) = 0; /// Return a vector aiming the actor's weapon towards a target. /// @note The length of the vector is the distance between actor and target. - virtual osg::Vec3f aimToTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) = 0; + virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0; + + /// Return the distance between actor's weapon and target's collision box. + virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0; + + virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/activator.cpp openmw-0.38.0/apps/openmw/mwclass/activator.cpp --- openmw-0.37.0/apps/openmw/mwclass/activator.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/activator.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -26,10 +26,6 @@ namespace MWClass { - std::string Activator::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -45,11 +41,9 @@ MWBase::Environment::get().getMechanicsManager()->add(ptr); } - std::string Activator::getModel(const MWWorld::Ptr &ptr) const + std::string Activator::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -58,17 +52,16 @@ return ""; } - std::string Activator::getName (const MWWorld::Ptr& ptr) const + std::string Activator::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } - std::string Activator::getScript (const MWWorld::Ptr& ptr) const + std::string Activator::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; @@ -81,21 +74,19 @@ registerClass (typeid (ESM::Activator).name(), instance); } - bool Activator::hasToolTip (const MWWorld::Ptr& ptr) const + bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Activator::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Activator::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) @@ -124,12 +115,10 @@ } - MWWorld::Ptr - Activator::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Activator::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/activator.hpp openmw-0.38.0/apps/openmw/mwclass/activator.hpp --- openmw-0.37.0/apps/openmw/mwclass/activator.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/activator.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,30 +8,26 @@ class Activator : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; @@ -39,7 +35,7 @@ static void registerSelf(); - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/actor.cpp openmw-0.38.0/apps/openmw/mwclass/actor.cpp --- openmw-0.37.0/apps/openmw/mwclass/actor.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/actor.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -61,11 +61,6 @@ } } - bool Actor::hasToolTip(const MWWorld::Ptr& ptr) const - { - return !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat() || getCreatureStats(ptr).isDead(); - } - osg::Vec3f Actor::getRotationVector(const MWWorld::Ptr& ptr) const { MWMechanics::Movement &movement = getMovementSettings(ptr); diff -Nru openmw-0.37.0/apps/openmw/mwclass/actor.hpp openmw-0.38.0/apps/openmw/mwclass/actor.hpp --- openmw-0.37.0/apps/openmw/mwclass/actor.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/actor.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -28,9 +28,6 @@ virtual void block(const MWWorld::Ptr &ptr) const; - virtual bool hasToolTip(const MWWorld::Ptr& ptr) const; - ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const; ///< Return desired rotations, as euler angles. diff -Nru openmw-0.37.0/apps/openmw/mwclass/apparatus.cpp openmw-0.38.0/apps/openmw/mwclass/apparatus.cpp --- openmw-0.37.0/apps/openmw/mwclass/apparatus.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/apparatus.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,10 +20,6 @@ namespace MWClass { - std::string Apparatus::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -37,11 +33,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const + std::string Apparatus::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -50,10 +44,9 @@ return ""; } - std::string Apparatus::getName (const MWWorld::Ptr& ptr) const + std::string Apparatus::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -64,18 +57,16 @@ return defaultItemActivate(ptr, actor); } - std::string Apparatus::getScript (const MWWorld::Ptr& ptr) const + std::string Apparatus::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - int Apparatus::getValue (const MWWorld::Ptr& ptr) const + int Apparatus::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -87,39 +78,36 @@ registerClass (typeid (ESM::Apparatus).name(), instance); } - std::string Apparatus::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Apparatus::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Apparatus Up"); } - std::string Apparatus::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Apparatus::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Apparatus Down"); } - std::string Apparatus::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Apparatus::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Apparatus::hasToolTip (const MWWorld::Ptr& ptr) const + bool Apparatus::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -142,24 +130,21 @@ return boost::shared_ptr(new MWWorld::ActionAlchemy()); } - MWWorld::Ptr - Apparatus::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Apparatus::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - bool Apparatus::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Apparatus::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Apparatus) != 0; } - float Apparatus::getWeight(const MWWorld::Ptr &ptr) const + float Apparatus::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/apparatus.hpp openmw-0.38.0/apps/openmw/mwclass/apparatus.hpp --- openmw-0.37.0/apps/openmw/mwclass/apparatus.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/apparatus.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,22 +8,18 @@ class Apparatus : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -31,36 +27,36 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. 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 std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/armor.cpp openmw-0.38.0/apps/openmw/mwclass/armor.cpp --- openmw-0.37.0/apps/openmw/mwclass/armor.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/armor.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -26,10 +26,6 @@ namespace MWClass { - std::string Armor::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -43,11 +39,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Armor::getModel(const MWWorld::Ptr &ptr) const + std::string Armor::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -56,10 +50,9 @@ return ""; } - std::string Armor::getName (const MWWorld::Ptr& ptr) const + std::string Armor::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -70,31 +63,28 @@ return defaultItemActivate(ptr, actor); } - bool Armor::hasItemHealth (const MWWorld::Ptr& ptr) const + bool Armor::hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; } - int Armor::getItemMaxHealth (const MWWorld::Ptr& ptr) const + int Armor::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mHealth; } - std::string Armor::getScript (const MWWorld::Ptr& ptr) const + std::string Armor::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - std::pair, bool> Armor::getEquipmentSlots (const MWWorld::Ptr& ptr) const + std::pair, bool> Armor::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots_; @@ -125,10 +115,9 @@ return std::make_pair (slots_, false); } - int Armor::getEquipmentSkill (const MWWorld::Ptr& ptr) const + int Armor::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); std::string typeGmst; @@ -170,10 +159,9 @@ return ESM::Skill::HeavyArmor; } - int Armor::getValue (const MWWorld::Ptr& ptr) const + int Armor::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -185,7 +173,7 @@ registerClass (typeid (ESM::Armor).name(), instance); } - std::string Armor::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Armor::getUpSoundId (const MWWorld::ConstPtr& ptr) const { int es = getEquipmentSkill(ptr); if (es == ESM::Skill::LightArmor) @@ -196,7 +184,7 @@ return std::string("Item Armor Heavy Up"); } - std::string Armor::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Armor::getDownSoundId (const MWWorld::ConstPtr& ptr) const { int es = getEquipmentSkill(ptr); if (es == ESM::Skill::LightArmor) @@ -207,29 +195,26 @@ return std::string("Item Armor Heavy Down"); } - std::string Armor::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Armor::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Armor::hasToolTip (const MWWorld::Ptr& ptr) const + bool Armor::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -268,18 +253,16 @@ return info; } - std::string Armor::getEnchantment (const MWWorld::Ptr& ptr) const + std::string Armor::getEnchantment (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mEnchant; } - std::string Armor::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + std::string Armor::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); ESM::Armor newItem = *ref->mBase; newItem.mId=""; @@ -290,9 +273,9 @@ return record->mId; } - int Armor::getEffectiveArmorRating(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor) const + int Armor::getEffectiveArmorRating(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &actor) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); int armorSkillType = getEquipmentSkill(ptr); int armorSkill = actor.getClass().getSkill(actor, armorSkillType); @@ -306,7 +289,7 @@ return ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill; } - std::pair Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + std::pair Armor::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc); @@ -376,33 +359,29 @@ return action; } - MWWorld::Ptr - Armor::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Armor::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - int Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Armor::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mEnchant; } - bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Armor::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Armor) || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } - float Armor::getWeight(const MWWorld::Ptr &ptr) const + float Armor::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/armor.hpp openmw-0.38.0/apps/openmw/mwclass/armor.hpp --- openmw-0.37.0/apps/openmw/mwclass/armor.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/armor.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,22 +7,18 @@ { class Armor : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -30,50 +26,50 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasItemHealth (const MWWorld::Ptr& ptr) const; + virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const; ///< \return Item health data available? - virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - virtual int getEquipmentSkill (const MWWorld::Ptr& ptr) const; - /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + virtual int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const; + /// Return the index of the skill this item corresponds to when equipped or -1, if there is /// no such skill. - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. - virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + virtual std::pair canBeEquipped(const MWWorld::ConstPtr &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 @@ -81,14 +77,14 @@ const; ///< Generate action for using via inventory menu - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; /// Get the effective armor rating, factoring in the actor's skills, for the given armor. - virtual int getEffectiveArmorRating(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; + virtual int getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/bodypart.cpp openmw-0.38.0/apps/openmw/mwclass/bodypart.cpp --- openmw-0.37.0/apps/openmw/mwclass/bodypart.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/bodypart.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,52 @@ +#include "bodypart.hpp" + +#include "../mwrender/renderinginterface.hpp" +#include "../mwrender/objects.hpp" + +#include "../mwworld/cellstore.hpp" + +namespace MWClass +{ + + MWWorld::Ptr BodyPart::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const + { + const MWWorld::LiveCellRef *ref = ptr.get(); + + return MWWorld::Ptr(cell.insert(ref), &cell); + } + + void BodyPart::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string &model, MWRender::RenderingInterface &renderingInterface) const + { + if (!model.empty()) { + renderingInterface.getObjects().insertModel(ptr, model); + } + } + + void BodyPart::insertObject(const MWWorld::Ptr &ptr, const std::string &model, MWPhysics::PhysicsSystem &physics) const + { + } + + std::string BodyPart::getName(const MWWorld::ConstPtr &ptr) const + { + return std::string(); + } + + void BodyPart::registerSelf() + { + boost::shared_ptr instance (new BodyPart); + + registerClass (typeid (ESM::BodyPart).name(), instance); + } + + std::string BodyPart::getModel(const MWWorld::ConstPtr &ptr) const + { + const MWWorld::LiveCellRef *ref = ptr.get(); + + const std::string &model = ref->mBase->mModel; + if (!model.empty()) { + return "meshes\\" + model; + } + return ""; + } + +} diff -Nru openmw-0.37.0/apps/openmw/mwclass/bodypart.hpp openmw-0.38.0/apps/openmw/mwclass/bodypart.hpp --- openmw-0.37.0/apps/openmw/mwclass/bodypart.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/bodypart.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,31 @@ +#ifndef GAME_MWCLASS_BODYPART_H +#define GAME_MWCLASS_BODYPART_H + +#include "../mwworld/class.hpp" + +namespace MWClass +{ + + class BodyPart : public MWWorld::Class + { + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; + + public: + + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; + ///< Add reference into a cell for rendering + + virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; + + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; + ///< \return name (the one that is to be presented to the user; not the internal one); + /// can return an empty string. + + static void registerSelf(); + + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; + }; + +} + +#endif diff -Nru openmw-0.37.0/apps/openmw/mwclass/book.cpp openmw-0.38.0/apps/openmw/mwclass/book.cpp --- openmw-0.37.0/apps/openmw/mwclass/book.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/book.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,10 +23,6 @@ namespace MWClass { - std::string Book::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Book::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -40,11 +36,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Book::getModel(const MWWorld::Ptr &ptr) const + std::string Book::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -53,10 +47,9 @@ return ""; } - std::string Book::getName (const MWWorld::Ptr& ptr) const + std::string Book::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -78,18 +71,16 @@ return boost::shared_ptr(new MWWorld::ActionRead(ptr)); } - std::string Book::getScript (const MWWorld::Ptr& ptr) const + std::string Book::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - int Book::getValue (const MWWorld::Ptr& ptr) const + int Book::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -101,39 +92,36 @@ registerClass (typeid (ESM::Book).name(), instance); } - std::string Book::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Book::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Book Up"); } - std::string Book::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Book::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Book Down"); } - std::string Book::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Book::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Book::hasToolTip (const MWWorld::Ptr& ptr) const + bool Book::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -153,18 +141,16 @@ return info; } - std::string Book::getEnchantment (const MWWorld::Ptr& ptr) const + std::string Book::getEnchantment (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mEnchant; } - std::string Book::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + std::string Book::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); ESM::Book newItem = *ref->mBase; newItem.mId=""; @@ -181,33 +167,29 @@ return boost::shared_ptr(new MWWorld::ActionRead(ptr)); } - MWWorld::Ptr - Book::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Book::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - int Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Book::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mEnchant; } - bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Book::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Books) || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } - float Book::getWeight(const MWWorld::Ptr &ptr) const + float Book::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/book.hpp openmw-0.38.0/apps/openmw/mwclass/book.hpp --- openmw-0.37.0/apps/openmw/mwclass/book.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/book.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,20 +7,16 @@ { class Book : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,45 +24,45 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. 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 std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/classes.cpp openmw-0.38.0/apps/openmw/mwclass/classes.cpp --- openmw-0.37.0/apps/openmw/mwclass/classes.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/classes.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,6 +20,7 @@ #include "probe.hpp" #include "repair.hpp" #include "static.hpp" +#include "bodypart.hpp" namespace MWClass { @@ -45,5 +46,6 @@ Probe::registerSelf(); Repair::registerSelf(); Static::registerSelf(); + BodyPart::registerSelf(); } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/clothing.cpp openmw-0.38.0/apps/openmw/mwclass/clothing.cpp --- openmw-0.37.0/apps/openmw/mwclass/clothing.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/clothing.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -22,10 +22,6 @@ namespace MWClass { - std::string Clothing::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -39,11 +35,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Clothing::getModel(const MWWorld::Ptr &ptr) const + std::string Clothing::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -52,10 +46,9 @@ return ""; } - std::string Clothing::getName (const MWWorld::Ptr& ptr) const + std::string Clothing::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -66,18 +59,16 @@ return defaultItemActivate(ptr, actor); } - std::string Clothing::getScript (const MWWorld::Ptr& ptr) const + std::string Clothing::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - std::pair, bool> Clothing::getEquipmentSlots (const MWWorld::Ptr& ptr) const + std::pair, bool> Clothing::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots_; @@ -114,10 +105,9 @@ return std::make_pair (slots_, false); } - int Clothing::getEquipmentSkill (const MWWorld::Ptr& ptr) const + int Clothing::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mData.mType==ESM::Clothing::Shoes) return ESM::Skill::Unarmored; @@ -125,10 +115,9 @@ return -1; } - int Clothing::getValue (const MWWorld::Ptr& ptr) const + int Clothing::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -140,10 +129,9 @@ registerClass (typeid (ESM::Clothing).name(), instance); } - std::string Clothing::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Clothing::getUpSoundId (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mData.mType == 8) { @@ -152,10 +140,9 @@ return std::string("Item Clothes Up"); } - std::string Clothing::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Clothing::getDownSoundId (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mData.mType == 8) { @@ -164,29 +151,26 @@ return std::string("Item Clothes Down"); } - std::string Clothing::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Clothing::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Clothing::hasToolTip (const MWWorld::Ptr& ptr) const + bool Clothing::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -208,18 +192,16 @@ return info; } - std::string Clothing::getEnchantment (const MWWorld::Ptr& ptr) const + std::string Clothing::getEnchantment (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mEnchant; } - std::string Clothing::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + std::string Clothing::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); ESM::Clothing newItem = *ref->mBase; newItem.mId=""; @@ -230,7 +212,7 @@ return record->mId; } - std::pair Clothing::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + std::pair Clothing::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { // slots that this item can be equipped in std::pair, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); @@ -270,33 +252,29 @@ return action; } - MWWorld::Ptr - Clothing::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Clothing::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - int Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Clothing::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mEnchant; } - bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Clothing::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Clothing) || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } - float Clothing::getWeight(const MWWorld::Ptr &ptr) const + float Clothing::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/clothing.hpp openmw-0.38.0/apps/openmw/mwclass/clothing.hpp --- openmw-0.37.0/apps/openmw/mwclass/clothing.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/clothing.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,20 +7,16 @@ { class Clothing : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,44 +24,44 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - virtual int getEquipmentSkill (const MWWorld::Ptr& ptr) const; - /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + virtual int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const; + /// Return the index of the skill this item corresponds to when equipped or -1, if there is /// no such skill. - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. - virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + virtual std::pair canBeEquipped(const MWWorld::ConstPtr &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 @@ -73,13 +69,13 @@ const; ///< Generate action for using via inventory menu - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/container.cpp openmw-0.38.0/apps/openmw/mwclass/container.cpp --- openmw-0.37.0/apps/openmw/mwclass/container.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/container.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,27 +27,25 @@ #include "../mwmechanics/npcstats.hpp" -namespace +namespace MWClass { - struct ContainerCustomData : public MWWorld::CustomData + class ContainerCustomData : public MWWorld::CustomData { + public: MWWorld::ContainerStore mContainerStore; virtual MWWorld::CustomData *clone() const; + + virtual ContainerCustomData& asContainerCustomData() + { + return *this; + } }; MWWorld::CustomData *ContainerCustomData::clone() const { return new ContainerCustomData (*this); } -} - -namespace MWClass -{ - std::string Container::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Container::ensureCustomData (const MWWorld::Ptr& ptr) const { @@ -74,6 +72,7 @@ ptr.get(); if (ref->mBase->mFlags & ESM::Container::Respawn) { + MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); ptr.getRefData().setCustomData(NULL); } } @@ -103,11 +102,9 @@ MWBase::Environment::get().getMechanicsManager()->add(ptr); } - std::string Container::getModel(const MWWorld::Ptr &ptr) const + std::string Container::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -145,11 +142,11 @@ // make key id lowercase std::string keyId = ptr.getCellRef().getKey(); - Misc::StringUtils::toLower(keyId); + Misc::StringUtils::lowerCaseInPlace(keyId); for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { std::string refId = it->getCellRef().getRefId(); - Misc::StringUtils::toLower(refId); + Misc::StringUtils::lowerCaseInPlace(refId); if (refId == keyId) { hasKey = true; @@ -189,10 +186,9 @@ } } - std::string Container::getName (const MWWorld::Ptr& ptr) const + std::string Container::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -202,13 +198,12 @@ { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + return ptr.getRefData().getCustomData()->asContainerCustomData().mContainerStore; } - std::string Container::getScript (const MWWorld::Ptr& ptr) const + std::string Container::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } @@ -220,18 +215,16 @@ registerClass (typeid (ESM::Container).name(), instance); } - bool Container::hasToolTip (const MWWorld::Ptr& ptr) const + bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName; @@ -280,21 +273,22 @@ ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative } - bool Container::canLock(const MWWorld::Ptr &ptr) const + bool Container::canLock(const MWWorld::ConstPtr &ptr) const { return true; } - MWWorld::Ptr Container::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Container::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; const ESM::ContainerState& state2 = dynamic_cast (state); if (!ptr.getRefData().getCustomData()) @@ -308,13 +302,17 @@ readState (state2.mInventory); } - void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const + void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { ESM::ContainerState& state2 = dynamic_cast (state); - ensureCustomData (ptr); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. writeState (state2.mInventory); } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/container.hpp openmw-0.38.0/apps/openmw/mwclass/container.hpp --- openmw-0.37.0/apps/openmw/mwclass/container.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/container.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,20 +10,16 @@ void ensureCustomData (const MWWorld::Ptr& ptr) const; - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -31,16 +27,16 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const; ///< Return container store - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual float getCapacity (const MWWorld::Ptr& ptr) const; @@ -57,13 +53,13 @@ virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object - virtual bool canLock(const MWWorld::Ptr &ptr) const; + virtual bool canLock(const MWWorld::ConstPtr &ptr) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. - virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. @@ -73,7 +69,7 @@ virtual void restock (const MWWorld::Ptr &ptr) const; - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/creature.cpp openmw-0.38.0/apps/openmw/mwclass/creature.cpp --- openmw-0.37.0/apps/openmw/mwclass/creature.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/creature.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -40,14 +40,33 @@ namespace { - struct CreatureCustomData : public MWWorld::CustomData + bool isFlagBitSet(const MWWorld::ConstPtr &ptr, ESM::Creature::Flags bitMask) { + return (ptr.get()->mBase->mFlags & bitMask) != 0; + } +} + +namespace MWClass +{ + + class CreatureCustomData : public MWWorld::CustomData + { + public: MWMechanics::CreatureStats mCreatureStats; MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures MWMechanics::Movement mMovement; virtual MWWorld::CustomData *clone() const; + virtual CreatureCustomData& asCreatureCustomData() + { + return *this; + } + virtual const CreatureCustomData& asCreatureCustomData() const + { + return *this; + } + CreatureCustomData() : mContainerStore(0) {} virtual ~CreatureCustomData() { delete mContainerStore; } }; @@ -59,14 +78,6 @@ return cloned; } - bool isFlagBitSet(const MWWorld::Ptr &ptr, ESM::Creature::Flags bitMask) - { - return (ptr.get()->mBase->mFlags & bitMask) != 0; - } -} - -namespace MWClass -{ const Creature::GMST& Creature::getGmst() { static GMST gmst; @@ -147,32 +158,22 @@ // store ptr.getRefData().setCustomData(data.release()); - getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr)); + getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); if (hasInventory) getInventoryStore(ptr).autoEquip(ptr); } } - std::string Creature::getId (const MWWorld::Ptr& ptr) const - { - MWWorld::LiveCellRef *ref = - ptr.get(); - - return ref->mBase->mId; - } - void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertCreature(ptr, model, hasInventoryStore(ptr)); } - std::string Creature::getModel(const MWWorld::Ptr &ptr) const + std::string Creature::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert (ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -181,10 +182,9 @@ return ""; } - std::string Creature::getName (const MWWorld::Ptr& ptr) const + std::string Creature::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -193,7 +193,7 @@ { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; + return ptr.getRefData().getCustomData()->asCreatureCustomData().mCreatureStats; } @@ -331,7 +331,7 @@ } if(!object.isEmpty()) - getCreatureStats(ptr).setLastHitAttemptObject(object.getClass().getId(object)); + getCreatureStats(ptr).setLastHitAttemptObject(object.getCellRef().getRefId()); if(setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { @@ -349,7 +349,7 @@ } if(!object.isEmpty()) - getCreatureStats(ptr).setLastHitObject(object.getClass().getId(object)); + getCreatureStats(ptr).setLastHitObject(object.getCellRef().getRefId()); if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); @@ -421,7 +421,7 @@ { ensureCustomData (ptr); - return *dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + return *ptr.getRefData().getCustomData()->asCreatureCustomData().mContainerStore; } MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const @@ -437,14 +437,14 @@ return isFlagBitSet(ptr, ESM::Creature::Weapon); } - std::string Creature::getScript (const MWWorld::Ptr& ptr) const + std::string Creature::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - bool Creature::isEssential (const MWWorld::Ptr& ptr) const + bool Creature::isEssential (const MWWorld::ConstPtr& ptr) const { return isFlagBitSet(ptr, ESM::Creature::Essential); } @@ -511,13 +511,21 @@ { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; + return ptr.getRefData().getCustomData()->asCreatureCustomData().mMovement; } - MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::Ptr& ptr) const + bool Creature::hasToolTip(const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + if (!ptr.getRefData().getCustomData()) + return true; + + const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); + return !customData.mCreatureStats.getAiSequence().isInCombat() || customData.mCreatureStats.isDead(); + } + + MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const + { + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName; @@ -542,18 +550,18 @@ return static_cast(stats.getAttribute(0).getModified() * 5); } - int Creature::getServices(const MWWorld::Ptr &actor) const + int Creature::getServices(const MWWorld::ConstPtr &actor) const { - MWWorld::LiveCellRef* ref = actor.get(); + 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 + bool Creature::isPersistent(const MWWorld::ConstPtr &actor) const { - MWWorld::LiveCellRef* ref = actor.get(); + const MWWorld::LiveCellRef* ref = actor.get(); return ref->mBase->mPersistent; } @@ -568,7 +576,7 @@ MWWorld::LiveCellRef* ref = ptr.get(); - const std::string& ourId = (ref->mBase->mOriginal.empty()) ? getId(ptr) : ref->mBase->mOriginal; + const std::string& ourId = (ref->mBase->mOriginal.empty()) ? ptr.getCellRef().getRefId() : ref->mBase->mOriginal; MWWorld::Store::iterator sound = store.begin(); while(sound != store.end()) @@ -587,31 +595,29 @@ return ""; } - MWWorld::Ptr - Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Creature::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - bool Creature::isBipedal(const MWWorld::Ptr &ptr) const + bool Creature::isBipedal(const MWWorld::ConstPtr &ptr) const { return isFlagBitSet(ptr, ESM::Creature::Bipedal); } - bool Creature::canFly(const MWWorld::Ptr &ptr) const + bool Creature::canFly(const MWWorld::ConstPtr &ptr) const { return isFlagBitSet(ptr, ESM::Creature::Flies); } - bool Creature::canSwim(const MWWorld::Ptr &ptr) const + bool Creature::canSwim(const MWWorld::ConstPtr &ptr) const { return isFlagBitSet(ptr, static_cast(ESM::Creature::Swims | ESM::Creature::Bipedal)); } - bool Creature::canWalk(const MWWorld::Ptr &ptr) const + bool Creature::canWalk(const MWWorld::ConstPtr &ptr) const { return isFlagBitSet(ptr, static_cast(ESM::Creature::Walks | ESM::Creature::Bipedal)); } @@ -674,7 +680,7 @@ } } - int Creature::getBloodTexture(const MWWorld::Ptr &ptr) const + int Creature::getBloodTexture(const MWWorld::ConstPtr &ptr) const { int flags = ptr.get()->mBase->mFlags; @@ -711,13 +717,13 @@ else ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless. - CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); customData.mContainerStore->readState (state2.mInventory); customData.mCreatureStats.readState (state2.mCreatureStats); } - void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + void Creature::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { ESM::CreatureState& state2 = dynamic_cast (state); @@ -728,15 +734,13 @@ return; } - ensureCustomData (ptr); - - CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); customData.mContainerStore->writeState (state2.mInventory); customData.mCreatureStats.writeState (state2.mCreatureStats); } - int Creature::getBaseGold(const MWWorld::Ptr& ptr) const + int Creature::getBaseGold(const MWWorld::ConstPtr& ptr) const { return ptr.get()->mBase->mData.mGold; } @@ -755,6 +759,7 @@ // Reset to original position ptr.getRefData().setPosition(ptr.getCellRef().getPosition()); + MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); ptr.getRefData().setCustomData(NULL); } } @@ -768,15 +773,15 @@ store.restock(list, ptr, ptr.getCellRef().getRefId()); } - int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const + int Creature::getBaseFightRating(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mAiData.mFight; } - void Creature::adjustScale(const MWWorld::Ptr &ptr, osg::Vec3f &scale, bool /* rendering */) const + void Creature::adjustScale(const MWWorld::ConstPtr &ptr, osg::Vec3f &scale, bool /* rendering */) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); scale *= ref->mBase->mScale; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/creature.hpp openmw-0.38.0/apps/openmw/mwclass/creature.hpp --- openmw-0.37.0/apps/openmw/mwclass/creature.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/creature.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -14,8 +14,7 @@ { void ensureCustomData (const MWWorld::Ptr& ptr) const; - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name); @@ -41,17 +40,17 @@ public: - virtual std::string getId (const MWWorld::Ptr& ptr) const; - ///< Return ID of \a ptr - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip(const MWWorld::ConstPtr& ptr) const; + ///< @return true if this object has a tooltip when focused (default implementation: false) + + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; @@ -74,7 +73,7 @@ virtual bool hasInventoryStore (const MWWorld::Ptr &ptr) const; - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual float getCapacity (const MWWorld::Ptr& ptr) const; @@ -84,12 +83,12 @@ virtual float getArmorRating (const MWWorld::Ptr& ptr) const; ///< @return combined armor rating of this actor - virtual bool isEssential (const MWWorld::Ptr& ptr) const; + virtual bool isEssential (const MWWorld::ConstPtr& 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 int getServices (const MWWorld::ConstPtr& actor) const; - virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual bool isPersistent (const MWWorld::ConstPtr& ptr) const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; @@ -100,40 +99,38 @@ static void registerSelf(); - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual bool isActor() const { return true; } - virtual bool isBipedal (const MWWorld::Ptr &ptr) const; - virtual bool canFly (const MWWorld::Ptr &ptr) const; - virtual bool canSwim (const MWWorld::Ptr &ptr) const; - virtual bool canWalk (const MWWorld::Ptr &ptr) const; + virtual bool isBipedal (const MWWorld::ConstPtr &ptr) const; + virtual bool canFly (const MWWorld::ConstPtr &ptr) const; + virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; + virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) - virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; + virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; - virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) - const; + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. - virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) - const; + virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. - virtual int getBaseGold(const MWWorld::Ptr& ptr) const; + virtual int getBaseGold(const MWWorld::ConstPtr& ptr) const; virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr &ptr) const; - virtual int getBaseFightRating(const MWWorld::Ptr &ptr) const; + virtual int getBaseFightRating(const MWWorld::ConstPtr &ptr) const; - virtual void adjustScale(const MWWorld::Ptr& ptr, osg::Vec3f& scale, bool rendering) const; + virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/creaturelevlist.cpp openmw-0.38.0/apps/openmw/mwclass/creaturelevlist.cpp --- openmw-0.37.0/apps/openmw/mwclass/creaturelevlist.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/creaturelevlist.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,31 +7,33 @@ #include "../mwworld/customdata.hpp" -namespace +namespace MWClass { - struct CreatureLevListCustomData : public MWWorld::CustomData + class CreatureLevListCustomData : public MWWorld::CustomData { + public: // actorId of the creature we spawned int mSpawnActorId; bool mSpawn; // Should a new creature be spawned? virtual MWWorld::CustomData *clone() const; + + virtual CreatureLevListCustomData& asCreatureLevListCustomData() + { + return *this; + } + virtual const CreatureLevListCustomData& asCreatureLevListCustomData() const + { + return *this; + } }; MWWorld::CustomData *CreatureLevListCustomData::clone() const { return new CreatureLevListCustomData (*this); } -} -namespace MWClass -{ - std::string CreatureLevList::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } - - std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const + std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const { return ""; } @@ -40,7 +42,7 @@ { ensureCustomData(ptr); - CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); customData.mSpawn = true; } @@ -55,7 +57,7 @@ { ensureCustomData(ptr); - CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); if (!customData.mSpawn) return; @@ -101,21 +103,29 @@ void CreatureLevList::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; + const ESM::CreatureLevListState& state2 = dynamic_cast (state); ensureCustomData(ptr); - CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); customData.mSpawnActorId = state2.mSpawnActorId; customData.mSpawn = state2.mSpawn; } - void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + void CreatureLevList::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { ESM::CreatureLevListState& state2 = dynamic_cast (state); - ensureCustomData(ptr); - CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } + + const CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); state2.mSpawnActorId = customData.mSpawnActorId; state2.mSpawn = customData.mSpawn; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/creaturelevlist.hpp openmw-0.38.0/apps/openmw/mwclass/creaturelevlist.hpp --- openmw-0.37.0/apps/openmw/mwclass/creaturelevlist.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/creaturelevlist.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,10 +11,7 @@ public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -23,12 +20,10 @@ virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) - const; + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. - virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) - const; + virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. virtual void respawn (const MWWorld::Ptr& ptr) const; diff -Nru openmw-0.37.0/apps/openmw/mwclass/door.cpp openmw-0.38.0/apps/openmw/mwclass/door.cpp --- openmw-0.37.0/apps/openmw/mwclass/door.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/door.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -28,27 +28,29 @@ #include "../mwmechanics/actorutil.hpp" -namespace +namespace MWClass { - struct DoorCustomData : public MWWorld::CustomData + class DoorCustomData : public MWWorld::CustomData { + public: int mDoorState; // 0 = nothing, 1 = opening, 2 = closing virtual MWWorld::CustomData *clone() const; + + virtual DoorCustomData& asDoorCustomData() + { + return *this; + } + virtual const DoorCustomData& asDoorCustomData() const + { + return *this; + } }; MWWorld::CustomData *DoorCustomData::clone() const { return new DoorCustomData (*this); } -} - -namespace MWClass -{ - std::string Door::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -60,12 +62,12 @@ void Door::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) - physics.addObject(ptr, model); + physics.addObject(ptr, model, MWPhysics::CollisionType_Door); // Resume the door's opening/closing animation if it wasn't finished if (ptr.getRefData().getCustomData()) { - const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); if (customData.mDoorState > 0) { MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState); @@ -75,11 +77,9 @@ MWBase::Environment::get().getMechanicsManager()->add(ptr); } - std::string Door::getModel(const MWWorld::Ptr &ptr) const + std::string Door::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -88,10 +88,9 @@ return ""; } - std::string Door::getName (const MWWorld::Ptr& ptr) const + std::string Door::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -114,11 +113,11 @@ // make key id lowercase std::string keyId = ptr.getCellRef().getKey(); - Misc::StringUtils::toLower(keyId); + Misc::StringUtils::lowerCaseInPlace(keyId); for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { std::string refId = it->getCellRef().getRefId(); - Misc::StringUtils::toLower(refId); + Misc::StringUtils::lowerCaseInPlace(refId); if (refId == keyId) { hasKey = true; @@ -169,7 +168,9 @@ { MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, closeSound, 0.5f); - float offset = doorRot/ 3.14159265f * 2.0f; + // Doors rotate at 90 degrees per second, so start the sound at + // where it would be at the current rotation. + float offset = doorRot/(3.14159265f * 0.5f); action->setSoundOffset(offset); action->setSound(openSound); } @@ -177,10 +178,8 @@ { MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, openSound, 0.5f); - float offset = 1.0f - doorRot/ 3.14159265f * 2.0f; - //most if not all door have closing bang somewhere in the middle of the sound, - //so we divide offset by two - action->setSoundOffset(offset * 0.5f); + float offset = 1.0f - doorRot/(3.14159265f * 0.5f); + action->setSoundOffset(std::max(offset, 0.0f)); action->setSound(closeSound); } @@ -209,15 +208,14 @@ ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative } - bool Door::canLock(const MWWorld::Ptr &ptr) const + bool Door::canLock(const MWWorld::ConstPtr &ptr) const { return true; } - std::string Door::getScript (const MWWorld::Ptr& ptr) const + std::string Door::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } @@ -229,18 +227,16 @@ registerClass (typeid (ESM::Door).name(), instance); } - bool Door::hasToolTip (const MWWorld::Ptr& ptr) const + bool Door::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName; @@ -301,13 +297,11 @@ return "#{sCell=" + dest + "}"; } - MWWorld::Ptr - Door::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Door::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } void Door::ensureCustomData(const MWWorld::Ptr &ptr) const @@ -321,10 +315,11 @@ } } - int Door::getDoorState (const MWWorld::Ptr &ptr) const + int Door::getDoorState (const MWWorld::ConstPtr &ptr) const { - ensureCustomData(ptr); - const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + if (!ptr.getRefData().getCustomData()) + return 0; + const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); return customData.mDoorState; } @@ -334,23 +329,29 @@ throw std::runtime_error("load doors can't be moved"); ensureCustomData(ptr); - DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); customData.mDoorState = state; } void Door::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; ensureCustomData(ptr); - DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); const ESM::DoorState& state2 = dynamic_cast(state); customData.mDoorState = state2.mDoorState; } - void Door::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const + void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { - ensureCustomData(ptr); - const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } + const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); ESM::DoorState& state2 = dynamic_cast(state); state2.mDoorState = customData.mDoorState; diff -Nru openmw-0.37.0/apps/openmw/mwclass/door.hpp openmw-0.38.0/apps/openmw/mwclass/door.hpp --- openmw-0.37.0/apps/openmw/mwclass/door.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/door.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,20 +11,16 @@ { void ensureCustomData (const MWWorld::Ptr& ptr) const; - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -32,10 +28,10 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. static std::string getDestination (const MWWorld::LiveCellRef& door); @@ -47,17 +43,17 @@ virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object - virtual bool canLock(const MWWorld::Ptr &ptr) const; + virtual bool canLock(const MWWorld::ConstPtr &ptr) const; - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; /// 0 = nothing, 1 = opening, 2 = closing - virtual int getDoorState (const MWWorld::Ptr &ptr) const; + virtual int getDoorState (const MWWorld::ConstPtr &ptr) const; /// This does not actually cause the door to move. Use World::activateDoor instead. virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; @@ -66,7 +62,7 @@ const; ///< Read additional state from \a state into \a ptr. - virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. }; diff -Nru openmw-0.37.0/apps/openmw/mwclass/ingredient.cpp openmw-0.38.0/apps/openmw/mwclass/ingredient.cpp --- openmw-0.37.0/apps/openmw/mwclass/ingredient.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/ingredient.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,13 +23,6 @@ namespace MWClass { - std::string Ingredient::getId (const MWWorld::Ptr& ptr) const - { - MWWorld::LiveCellRef *ref = - ptr.get(); - - return ref->mBase->mId; - } void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -43,11 +36,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const + std::string Ingredient::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -56,10 +47,9 @@ return ""; } - std::string Ingredient::getName (const MWWorld::Ptr& ptr) const + std::string Ingredient::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -70,18 +60,16 @@ return defaultItemActivate(ptr, actor); } - std::string Ingredient::getScript (const MWWorld::Ptr& ptr) const + std::string Ingredient::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - int Ingredient::getValue (const MWWorld::Ptr& ptr) const + int Ingredient::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -103,39 +91,36 @@ registerClass (typeid (ESM::Ingredient).name(), instance); } - std::string Ingredient::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Ingredient::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Ingredient Up"); } - std::string Ingredient::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Ingredient::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Ingredient Down"); } - std::string Ingredient::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Ingredient::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Ingredient::hasToolTip (const MWWorld::Ptr& ptr) const + bool Ingredient::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -179,25 +164,22 @@ return info; } - MWWorld::Ptr - Ingredient::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Ingredient::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - bool Ingredient::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Ingredient::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Ingredients) != 0; } - float Ingredient::getWeight(const MWWorld::Ptr &ptr) const + float Ingredient::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/ingredient.hpp openmw-0.38.0/apps/openmw/mwclass/ingredient.hpp --- openmw-0.37.0/apps/openmw/mwclass/ingredient.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/ingredient.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,20 +7,16 @@ { class Ingredient : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - virtual std::string getId (const MWWorld::Ptr& ptr) const; - ///< Return ID of \a ptr - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,16 +24,16 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) @@ -46,20 +42,20 @@ static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/itemlevlist.cpp openmw-0.38.0/apps/openmw/mwclass/itemlevlist.cpp --- openmw-0.37.0/apps/openmw/mwclass/itemlevlist.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/itemlevlist.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,12 +4,8 @@ namespace MWClass { - std::string ItemLevList::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } - std::string ItemLevList::getName (const MWWorld::Ptr& ptr) const + std::string ItemLevList::getName (const MWWorld::ConstPtr& ptr) const { return ""; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/itemlevlist.hpp openmw-0.38.0/apps/openmw/mwclass/itemlevlist.hpp --- openmw-0.37.0/apps/openmw/mwclass/itemlevlist.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/itemlevlist.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -9,10 +9,7 @@ { public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. diff -Nru openmw-0.37.0/apps/openmw/mwclass/light.cpp openmw-0.38.0/apps/openmw/mwclass/light.cpp --- openmw-0.37.0/apps/openmw/mwclass/light.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/light.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,10 +27,6 @@ namespace MWClass { - std::string Light::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Light::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -59,11 +55,9 @@ MWBase::Environment::get().getMechanicsManager()->add(ptr); } - std::string Light::getModel(const MWWorld::Ptr &ptr) const + std::string Light::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert (ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -72,10 +66,9 @@ return ""; } - std::string Light::getName (const MWWorld::Ptr& ptr) const + std::string Light::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mModel.empty()) return ""; @@ -96,18 +89,16 @@ return defaultItemActivate(ptr, actor); } - std::string Light::getScript (const MWWorld::Ptr& ptr) const + std::string Light::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - std::pair, bool> Light::getEquipmentSlots (const MWWorld::Ptr& ptr) const + std::pair, bool> Light::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots_; @@ -117,10 +108,9 @@ return std::make_pair (slots_, false); } - int Light::getValue (const MWWorld::Ptr& ptr) const + int Light::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -132,40 +122,37 @@ registerClass (typeid (ESM::Light).name(), instance); } - std::string Light::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Light::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Misc Up"); } - std::string Light::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Light::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Misc Down"); } - std::string Light::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Light::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Light::hasToolTip (const MWWorld::Ptr& ptr) const + bool Light::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Light::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Light::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -202,40 +189,36 @@ ptr.getCellRef().setChargeFloat(duration); } - float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const + float Light::getRemainingUsageTime (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); if (ptr.getCellRef().getCharge() == -1) return static_cast(ref->mBase->mData.mTime); else return ptr.getCellRef().getChargeFloat(); } - MWWorld::Ptr - Light::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Light::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Light::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Lights) != 0; } - float Light::getWeight(const MWWorld::Ptr &ptr) const + float Light::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } - std::pair Light::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + std::pair Light::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); if (!(ref->mBase->mData.mFlags & ESM::Light::Carry)) return std::make_pair(0,""); @@ -260,7 +243,7 @@ return std::make_pair(1,""); } - std::string Light::getSound(const MWWorld::Ptr& ptr) const + std::string Light::getSound(const MWWorld::ConstPtr& ptr) const { return ptr.get()->mBase->mSound; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/light.hpp openmw-0.38.0/apps/openmw/mwclass/light.hpp --- openmw-0.37.0/apps/openmw/mwclass/light.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/light.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,52 +7,48 @@ { class Light : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) @@ -62,18 +58,18 @@ virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const; ///< Sets the remaining duration of the object. - virtual float getRemainingUsageTime (const MWWorld::Ptr& ptr) const; + virtual float getRemainingUsageTime (const MWWorld::ConstPtr& ptr) const; ///< Returns the remaining duration of the object. - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; - std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + std::pair canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const; - virtual std::string getSound(const MWWorld::Ptr& ptr) const; + virtual std::string getSound(const MWWorld::ConstPtr& ptr) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/lockpick.cpp openmw-0.38.0/apps/openmw/mwclass/lockpick.cpp --- openmw-0.37.0/apps/openmw/mwclass/lockpick.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/lockpick.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -21,10 +21,6 @@ namespace MWClass { - std::string Lockpick::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -38,11 +34,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const + std::string Lockpick::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -51,10 +45,9 @@ return ""; } - std::string Lockpick::getName (const MWWorld::Ptr& ptr) const + std::string Lockpick::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -65,15 +58,14 @@ return defaultItemActivate(ptr, actor); } - std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const + std::string Lockpick::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - std::pair, bool> Lockpick::getEquipmentSlots (const MWWorld::Ptr& ptr) const + std::pair, bool> Lockpick::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { std::vector slots_; @@ -82,10 +74,9 @@ return std::make_pair (slots_, false); } - int Lockpick::getValue (const MWWorld::Ptr& ptr) const + int Lockpick::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -97,39 +88,36 @@ registerClass (typeid (ESM::Lockpick).name(), instance); } - std::string Lockpick::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Lockpick::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Lockpick Up"); } - std::string Lockpick::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Lockpick::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Lockpick Down"); } - std::string Lockpick::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Lockpick::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Lockpick::hasToolTip (const MWWorld::Ptr& ptr) const + bool Lockpick::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -160,32 +148,28 @@ return action; } - MWWorld::Ptr - Lockpick::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Lockpick::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - bool Lockpick::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Lockpick::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Picks) != 0; } - int Lockpick::getItemMaxHealth (const MWWorld::Ptr& ptr) const + int Lockpick::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mUses; } - float Lockpick::getWeight(const MWWorld::Ptr &ptr) const + float Lockpick::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/lockpick.hpp openmw-0.38.0/apps/openmw/mwclass/lockpick.hpp --- openmw-0.37.0/apps/openmw/mwclass/lockpick.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/lockpick.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,20 +7,16 @@ { class Lockpick : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,47 +24,47 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. 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 std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health - virtual bool hasItemHealth (const MWWorld::Ptr& ptr) const { return true; } + virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; } ///< \return Item health data available? (default implementation: false) }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/misc.cpp openmw-0.38.0/apps/openmw/mwclass/misc.cpp --- openmw-0.37.0/apps/openmw/mwclass/misc.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/misc.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -26,7 +26,7 @@ namespace MWClass { - bool Miscellaneous::isGold (const MWWorld::Ptr& ptr) const + bool Miscellaneous::isGold (const MWWorld::ConstPtr& ptr) const { return Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_001") || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_005") @@ -34,10 +34,6 @@ || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_100"); } - std::string Miscellaneous::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -51,11 +47,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const + std::string Miscellaneous::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -64,10 +58,9 @@ return ""; } - std::string Miscellaneous::getName (const MWWorld::Ptr& ptr) const + std::string Miscellaneous::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -78,18 +71,16 @@ return defaultItemActivate(ptr, actor); } - std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const + std::string Miscellaneous::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - int Miscellaneous::getValue (const MWWorld::Ptr& ptr) const + int Miscellaneous::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); int value = ref->mBase->mData.mValue; if (ptr.getCellRef().getGoldValue() > 1 && ptr.getRefData().getCount() == 1) @@ -111,47 +102,42 @@ registerClass (typeid (ESM::Miscellaneous).name(), instance); } - std::string Miscellaneous::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Miscellaneous::getUpSoundId (const MWWorld::ConstPtr& ptr) const { if (isGold(ptr)) return std::string("Item Gold Up"); return std::string("Item Misc Up"); } - std::string Miscellaneous::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Miscellaneous::getDownSoundId (const MWWorld::ConstPtr& ptr) const { if (isGold(ptr)) return std::string("Item Gold Down"); return std::string("Item Misc Down"); } - std::string Miscellaneous::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Miscellaneous::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Miscellaneous::hasToolTip (const MWWorld::Ptr& ptr) const + bool Miscellaneous::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - int count = ptr.getRefData().getCount(); - bool gold = isGold(ptr); if (gold) count *= getValue(ptr); @@ -189,8 +175,7 @@ return info; } - MWWorld::Ptr - Miscellaneous::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Miscellaneous::copyToCell(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell, int count) const { MWWorld::Ptr newPtr; @@ -198,7 +183,7 @@ MWBase::Environment::get().getWorld()->getStore(); if (isGold(ptr)) { - int goldAmount = getValue(ptr) * ptr.getRefData().getCount(); + int goldAmount = getValue(ptr) * count; std::string base = "Gold_001"; if (goldAmount >= 100) @@ -213,16 +198,20 @@ // Really, I have no idea why moving ref out of conditional // scope causes list::push_back throwing std::bad_alloc MWWorld::ManualRef newRef(store, base); - MWWorld::LiveCellRef *ref = + const MWWorld::LiveCellRef *ref = newRef.getPtr().get(); - newPtr = MWWorld::Ptr(&cell.get().insert(*ref), &cell); + + newPtr = MWWorld::Ptr(cell.insert(ref), &cell); newPtr.getCellRef().setGoldValue(goldAmount); newPtr.getRefData().setCount(1); } else { - MWWorld::LiveCellRef *ref = + const MWWorld::LiveCellRef *ref = ptr.get(); - newPtr = MWWorld::Ptr(&cell.get().insert(*ref), &cell); + newPtr = MWWorld::Ptr(cell.insert(ref), &cell); + newPtr.getRefData().setCount(count); } + newPtr.getCellRef().unsetRefNum(); + return newPtr; } @@ -234,25 +223,22 @@ return boost::shared_ptr(new MWWorld::ActionSoulgem(ptr)); } - bool Miscellaneous::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Miscellaneous::canSell (const MWWorld::ConstPtr& item, int npcServices) const { - MWWorld::LiveCellRef *ref = - item.get(); + const MWWorld::LiveCellRef *ref = item.get(); return !ref->mBase->mData.mIsKey && (npcServices & ESM::NPC::Misc) && !isGold(item); } - float Miscellaneous::getWeight(const MWWorld::Ptr &ptr) const + float Miscellaneous::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } - bool Miscellaneous::isKey(const MWWorld::Ptr &ptr) const + bool Miscellaneous::isKey(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mIsKey != 0; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/misc.hpp openmw-0.38.0/apps/openmw/mwclass/misc.hpp --- openmw-0.37.0/apps/openmw/mwclass/misc.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/misc.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,20 +7,16 @@ { class Miscellaneous : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; - public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual MWWorld::Ptr copyToCell(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell, int count) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,42 +24,42 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &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 float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; - virtual bool isKey (const MWWorld::Ptr &ptr) const; + virtual bool isKey (const MWWorld::ConstPtr &ptr) const; - virtual bool isGold (const MWWorld::Ptr& ptr) const; + virtual bool isGold (const MWWorld::ConstPtr& ptr) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/npc.cpp openmw-0.38.0/apps/openmw/mwclass/npc.cpp --- openmw-0.37.0/apps/openmw/mwclass/npc.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/npc.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -42,19 +42,6 @@ namespace { - struct NpcCustomData : public MWWorld::CustomData - { - MWMechanics::NpcStats mNpcStats; - MWMechanics::Movement mMovement; - MWWorld::InventoryStore mInventoryStore; - - virtual MWWorld::CustomData *clone() const; - }; - - MWWorld::CustomData *NpcCustomData::clone() const - { - return new NpcCustomData (*this); - } int is_even(double d) { double int_part; @@ -251,6 +238,31 @@ namespace MWClass { + + class NpcCustomData : public MWWorld::CustomData + { + public: + MWMechanics::NpcStats mNpcStats; + MWMechanics::Movement mMovement; + MWWorld::InventoryStore mInventoryStore; + + virtual MWWorld::CustomData *clone() const; + + virtual NpcCustomData& asNpcCustomData() + { + return *this; + } + virtual const NpcCustomData& asNpcCustomData() const + { + return *this; + } + }; + + MWWorld::CustomData *NpcCustomData::clone() const + { + return new NpcCustomData (*this); + } + const Npc::GMST& Npc::getGmst() { static GMST gmst; @@ -383,7 +395,7 @@ // inventory // setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items - data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr)); + data->mInventoryStore.fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); data->mNpcStats.setGoldPool(gold); @@ -394,30 +406,20 @@ } } - std::string Npc::getId (const MWWorld::Ptr& ptr) const - { - MWWorld::LiveCellRef *ref = - ptr.get(); - - return ref->mBase->mId; - } - void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { renderingInterface.getObjects().insertNPC(ptr); } - bool Npc::isPersistent(const MWWorld::Ptr &actor) const + bool Npc::isPersistent(const MWWorld::ConstPtr &actor) const { - MWWorld::LiveCellRef* ref = actor.get(); + const MWWorld::LiveCellRef* ref = actor.get(); return ref->mBase->mPersistent; } - std::string Npc::getModel(const MWWorld::Ptr &ptr) const + std::string Npc::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); std::string model = "meshes\\base_anim.nif"; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); @@ -425,12 +427,11 @@ model = "meshes\\base_animkna.nif"; return model; - } - std::string Npc::getName (const MWWorld::Ptr& ptr) const + std::string Npc::getName (const MWWorld::ConstPtr& ptr) const { - if(getNpcStats(ptr).isWerewolf()) + if(ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) { const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); @@ -438,7 +439,7 @@ return store.find("sWerewolfPopup")->getString(); } - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -446,14 +447,14 @@ { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; + return ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats; } MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; + return ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats; } @@ -594,7 +595,7 @@ } if(!object.isEmpty()) - getCreatureStats(ptr).setLastHitAttemptObject(object.getClass().getId(object)); + getCreatureStats(ptr).setLastHitAttemptObject(object.getCellRef().getRefId()); if(setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { @@ -612,7 +613,7 @@ } if(!object.isEmpty()) - getCreatureStats(ptr).setLastHitObject(object.getClass().getId(object)); + getCreatureStats(ptr).setLastHitObject(object.getCellRef().getRefId()); if (damage > 0.0f && !object.isEmpty()) @@ -780,7 +781,7 @@ { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; + return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore; } MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr) @@ -788,13 +789,12 @@ { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; + return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore; } - std::string Npc::getScript (const MWWorld::Ptr& ptr) const + std::string Npc::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } @@ -897,13 +897,12 @@ { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; + return ptr.getRefData().getCustomData()->asNpcCustomData().mMovement; } - bool Npc::isEssential (const MWWorld::Ptr& ptr) const + bool Npc::isEssential (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mFlags & ESM::NPC::Essential) != 0; } @@ -914,15 +913,24 @@ registerClass (typeid (ESM::NPC).name(), instance); } - MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::Ptr& ptr) const + bool Npc::hasToolTip(const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + if (!ptr.getRefData().getCustomData()) + return true; + + const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); + return !customData.mNpcStats.getAiSequence().isInCombat() || customData.mNpcStats.isDead(); + } + + MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const + { + const MWWorld::LiveCellRef *ref = ptr.get(); bool fullHelp = MWBase::Environment::get().getWindowManager()->getFullHelp(); MWGui::ToolTipInfo info; info.caption = getName(ptr); - if(fullHelp && getNpcStats(ptr).isWerewolf()) + if(fullHelp && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) { info.caption += " ("; info.caption += ref->mBase->mName; @@ -1012,14 +1020,13 @@ + shield; } - void Npc::adjustScale(const MWWorld::Ptr &ptr, osg::Vec3f&scale, bool rendering) const + void Npc::adjustScale(const MWWorld::ConstPtr &ptr, osg::Vec3f&scale, bool rendering) const { if (!rendering) return; // collision meshes are not scaled based on race height // having the same collision extents for all races makes the environments easier to test - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); @@ -1039,9 +1046,9 @@ } - int Npc::getServices(const MWWorld::Ptr &actor) const + int Npc::getServices(const MWWorld::ConstPtr &actor) const { - MWWorld::LiveCellRef* ref = actor.get(); + const MWWorld::LiveCellRef* ref = actor.get(); if (ref->mBase->mHasAI) return ref->mBase->mAiData.mServices; else @@ -1095,7 +1102,7 @@ if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) return "DefaultLandWater"; if(world->isOnGround(ptr)) - return "Body Fall Medium"; + return "DefaultLand"; return ""; } if(name == "swimleft") @@ -1116,13 +1123,11 @@ throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } - MWWorld::Ptr - Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Npc::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const @@ -1130,9 +1135,9 @@ return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified(); } - int Npc::getBloodTexture(const MWWorld::Ptr &ptr) const + int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mFlags & ESM::NPC::Skeleton) return 1; @@ -1161,14 +1166,14 @@ else ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless. - NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); customData.mInventoryStore.readState (state2.mInventory); customData.mNpcStats.readState (state2.mNpcStats); static_cast (customData.mNpcStats).readState (state2.mCreatureStats); } - void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + void Npc::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { ESM::NpcState& state2 = dynamic_cast (state); @@ -1179,29 +1184,37 @@ return; } - ensureCustomData (ptr); - - NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); customData.mInventoryStore.writeState (state2.mInventory); customData.mNpcStats.writeState (state2.mNpcStats); static_cast (customData.mNpcStats).writeState (state2.mCreatureStats); } - int Npc::getBaseGold(const MWWorld::Ptr& ptr) const + int Npc::getBaseGold(const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) return ref->mBase->mNpdt52.mGold; else return ref->mBase->mNpdt12.mGold; } - bool Npc::isClass(const MWWorld::Ptr& ptr, const std::string &className) const + bool Npc::isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const { return Misc::StringUtils::ciEqual(ptr.get()->mBase->mClass, className); } + bool Npc::canSwim(const MWWorld::ConstPtr &ptr) const + { + return true; + } + + bool Npc::canWalk(const MWWorld::ConstPtr &ptr) const + { + return true; + } + void Npc::respawn(const MWWorld::Ptr &ptr) const { if (ptr.get()->mBase->mFlags & ESM::NPC::Respawn) @@ -1216,6 +1229,7 @@ // Reset to original position ptr.getRefData().setPosition(ptr.getCellRef().getPosition()); + MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); ptr.getRefData().setCustomData(NULL); } } @@ -1229,26 +1243,26 @@ store.restock(list, ptr, ptr.getCellRef().getRefId()); } - int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const + int Npc::getBaseFightRating (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mAiData.mFight; } - bool Npc::isBipedal(const MWWorld::Ptr &ptr) const + bool Npc::isBipedal(const MWWorld::ConstPtr &ptr) const { return true; } - std::string Npc::getPrimaryFaction (const MWWorld::Ptr& ptr) const + std::string Npc::getPrimaryFaction (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mFaction; } - int Npc::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const + int Npc::getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->getFactionRank(); } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/npc.hpp openmw-0.38.0/apps/openmw/mwclass/npc.hpp --- openmw-0.37.0/apps/openmw/mwclass/npc.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/npc.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -14,8 +14,7 @@ { void ensureCustomData (const MWWorld::Ptr& ptr) const; - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; struct GMST { @@ -45,13 +44,10 @@ public: - virtual std::string getId (const MWWorld::Ptr& ptr) const; - ///< Return ID of \a ptr - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -64,7 +60,10 @@ virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const; ///< Return container store - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip(const MWWorld::ConstPtr& ptr) const; + ///< @return true if this object has a tooltip when focused (default implementation: false) + + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; @@ -80,7 +79,7 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual float getSpeed (const MWWorld::Ptr& ptr) const; @@ -109,29 +108,29 @@ /// \param actor Actor that is resposible for the ID being applied to \a ptr. /// \return Any effect? - virtual void adjustScale (const MWWorld::Ptr &ptr, osg::Vec3f &scale, bool rendering) const; + virtual void adjustScale (const MWWorld::ConstPtr &ptr, osg::Vec3f &scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor=1.f) const; ///< Inform actor \a ptr that a skill use has succeeded. - virtual bool isEssential (const MWWorld::Ptr& ptr) const; + virtual bool isEssential (const MWWorld::ConstPtr& 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 int getServices (const MWWorld::ConstPtr& actor) const; - virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual bool isPersistent (const MWWorld::ConstPtr& ptr) const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; static void registerSelf(); - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) - virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; + virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; virtual bool isActor() const { return true; @@ -145,32 +144,28 @@ const; ///< Read additional state from \a state into \a ptr. - virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. - virtual int getBaseGold(const MWWorld::Ptr& ptr) const; + virtual int getBaseGold(const MWWorld::ConstPtr& ptr) const; - virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const; + virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const; - virtual bool canSwim (const MWWorld::Ptr &ptr) const { - return true; - } + virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; - virtual bool canWalk (const MWWorld::Ptr &ptr) const { - return true; - } + virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; - virtual bool isBipedal (const MWWorld::Ptr &ptr) const; + virtual bool isBipedal (const MWWorld::ConstPtr &ptr) const; virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr& ptr) const; - virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const; + virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const; - virtual std::string getPrimaryFaction(const MWWorld::Ptr &ptr) const; - virtual int getPrimaryFactionRank(const MWWorld::Ptr &ptr) const; + virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const; + virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/potion.cpp openmw-0.38.0/apps/openmw/mwclass/potion.cpp --- openmw-0.37.0/apps/openmw/mwclass/potion.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/potion.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -24,10 +24,6 @@ namespace MWClass { - std::string Potion::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -41,11 +37,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Potion::getModel(const MWWorld::Ptr &ptr) const + std::string Potion::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -54,10 +48,9 @@ return ""; } - std::string Potion::getName (const MWWorld::Ptr& ptr) const + std::string Potion::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -68,18 +61,17 @@ return defaultItemActivate(ptr, actor); } - std::string Potion::getScript (const MWWorld::Ptr& ptr) const + std::string Potion::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - int Potion::getValue (const MWWorld::Ptr& ptr) const + int Potion::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -91,39 +83,36 @@ registerClass (typeid (ESM::Potion).name(), instance); } - std::string Potion::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Potion::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Potion Up"); } - std::string Potion::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Potion::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Potion Down"); } - std::string Potion::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Potion::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Potion::hasToolTip (const MWWorld::Ptr& ptr) const + bool Potion::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -172,24 +161,21 @@ return action; } - MWWorld::Ptr - Potion::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Potion::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - bool Potion::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Potion::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Potions) != 0; } - float Potion::getWeight(const MWWorld::Ptr &ptr) const + float Potion::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/potion.hpp openmw-0.38.0/apps/openmw/mwclass/potion.hpp --- openmw-0.37.0/apps/openmw/mwclass/potion.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/potion.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,20 +7,16 @@ { class Potion : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,16 +24,16 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; @@ -45,20 +41,20 @@ static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/probe.cpp openmw-0.38.0/apps/openmw/mwclass/probe.cpp --- openmw-0.37.0/apps/openmw/mwclass/probe.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/probe.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -21,10 +21,6 @@ namespace MWClass { - std::string Probe::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -38,11 +34,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Probe::getModel(const MWWorld::Ptr &ptr) const + std::string Probe::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -51,10 +45,9 @@ return ""; } - std::string Probe::getName (const MWWorld::Ptr& ptr) const + std::string Probe::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -64,15 +57,15 @@ return defaultItemActivate(ptr, actor); } - std::string Probe::getScript (const MWWorld::Ptr& ptr) const + std::string Probe::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - std::pair, bool> Probe::getEquipmentSlots (const MWWorld::Ptr& ptr) const + std::pair, bool> Probe::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { std::vector slots_; @@ -81,10 +74,9 @@ return std::make_pair (slots_, false); } - int Probe::getValue (const MWWorld::Ptr& ptr) const + int Probe::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -96,39 +88,36 @@ registerClass (typeid (ESM::Probe).name(), instance); } - std::string Probe::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Probe::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Probe Up"); } - std::string Probe::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Probe::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Probe Down"); } - std::string Probe::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Probe::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Probe::hasToolTip (const MWWorld::Ptr& ptr) const + bool Probe::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -159,32 +148,28 @@ return action; } - MWWorld::Ptr - Probe::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Probe::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - bool Probe::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Probe::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Probes) != 0; } - int Probe::getItemMaxHealth (const MWWorld::Ptr& ptr) const + int Probe::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mUses; } - float Probe::getWeight(const MWWorld::Ptr &ptr) const + float Probe::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/probe.hpp openmw-0.38.0/apps/openmw/mwclass/probe.hpp --- openmw-0.37.0/apps/openmw/mwclass/probe.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/probe.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,20 +7,16 @@ { class Probe : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,47 +24,47 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. 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 std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health - virtual bool hasItemHealth (const MWWorld::Ptr& ptr) const { return true; } + virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; } ///< \return Item health data available? (default implementation: false) }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/repair.cpp openmw-0.38.0/apps/openmw/mwclass/repair.cpp --- openmw-0.37.0/apps/openmw/mwclass/repair.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/repair.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,10 +20,6 @@ namespace MWClass { - std::string Repair::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -37,11 +33,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Repair::getModel(const MWWorld::Ptr &ptr) const + std::string Repair::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -50,10 +44,9 @@ return ""; } - std::string Repair::getName (const MWWorld::Ptr& ptr) const + std::string Repair::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -64,18 +57,17 @@ return defaultItemActivate(ptr, actor); } - std::string Repair::getScript (const MWWorld::Ptr& ptr) const + std::string Repair::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - int Repair::getValue (const MWWorld::Ptr& ptr) const + int Repair::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -87,52 +79,48 @@ registerClass (typeid (ESM::Repair).name(), instance); } - std::string Repair::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Repair::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Repair Up"); } - std::string Repair::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Repair::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Repair Down"); } - std::string Repair::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Repair::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Repair::hasToolTip (const MWWorld::Ptr& ptr) const + bool Repair::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - bool Repair::hasItemHealth (const MWWorld::Ptr& ptr) const + bool Repair::hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; } - int Repair::getItemMaxHealth (const MWWorld::Ptr& ptr) const + int Repair::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mUses; } - MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; @@ -154,13 +142,11 @@ return info; } - MWWorld::Ptr - Repair::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Repair::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } boost::shared_ptr Repair::use (const MWWorld::Ptr& ptr) const @@ -168,15 +154,14 @@ return boost::shared_ptr(new MWWorld::ActionRepair(ptr)); } - bool Repair::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Repair::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::RepairItem) != 0; } - float Repair::getWeight(const MWWorld::Ptr &ptr) const + float Repair::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/repair.hpp openmw-0.38.0/apps/openmw/mwclass/repair.hpp --- openmw-0.37.0/apps/openmw/mwclass/repair.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/repair.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,20 +7,16 @@ { class Repair : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,46 +24,46 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &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; + virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const; ///< \return Item health data available? (default implementation: false) - virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exception) - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/static.cpp openmw-0.38.0/apps/openmw/mwclass/static.cpp --- openmw-0.37.0/apps/openmw/mwclass/static.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/static.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,10 +11,6 @@ namespace MWClass { - std::string Static::getId (const MWWorld::Ptr& ptr) const - { - return ptr.get()->mBase->mId; - } void Static::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -29,11 +25,9 @@ physics.addObject(ptr, model); } - std::string Static::getModel(const MWWorld::Ptr &ptr) const + std::string Static::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -42,7 +36,7 @@ return ""; } - std::string Static::getName (const MWWorld::Ptr& ptr) const + std::string Static::getName (const MWWorld::ConstPtr& ptr) const { return ""; } @@ -54,12 +48,10 @@ registerClass (typeid (ESM::Static).name(), instance); } - MWWorld::Ptr - Static::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Static::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/static.hpp openmw-0.38.0/apps/openmw/mwclass/static.hpp --- openmw-0.37.0/apps/openmw/mwclass/static.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/static.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,26 +7,22 @@ { class Static : public MWWorld::Class { - virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - /// Return ID of \a ptr - virtual std::string getId (const MWWorld::Ptr& ptr) const; - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. static void registerSelf(); - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwclass/weapon.cpp openmw-0.38.0/apps/openmw/mwclass/weapon.cpp --- openmw-0.37.0/apps/openmw/mwclass/weapon.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/weapon.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -22,12 +22,6 @@ namespace MWClass { - std::string Weapon::getId (const MWWorld::Ptr& ptr) const - { - MWWorld::LiveCellRef *ref = ptr.get(); - - return ref->mBase->mId; - } void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { @@ -41,11 +35,9 @@ // TODO: add option somewhere to enable collision for placeable objects } - std::string Weapon::getModel(const MWWorld::Ptr &ptr) const + std::string Weapon::getModel(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert(ref->mBase != NULL); + const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { @@ -54,10 +46,9 @@ return ""; } - std::string Weapon::getName (const MWWorld::Ptr& ptr) const + std::string Weapon::getName (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -68,34 +59,31 @@ return defaultItemActivate(ptr, actor); } - bool Weapon::hasItemHealth (const MWWorld::Ptr& ptr) const + bool Weapon::hasItemHealth (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const 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 + int Weapon::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mHealth; } - std::string Weapon::getScript (const MWWorld::Ptr& ptr) const + std::string Weapon::getScript (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } - std::pair, bool> Weapon::getEquipmentSlots (const MWWorld::Ptr& ptr) const + std::pair, bool> Weapon::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots_; bool stack = false; @@ -116,10 +104,9 @@ return std::make_pair (slots_, stack); } - int Weapon::getEquipmentSkill (const MWWorld::Ptr& ptr) const + int Weapon::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); const int size = 12; @@ -146,10 +133,9 @@ return -1; } - int Weapon::getValue (const MWWorld::Ptr& ptr) const + int Weapon::getValue (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } @@ -161,10 +147,9 @@ registerClass (typeid (ESM::Weapon).name(), instance); } - std::string Weapon::getUpSoundId (const MWWorld::Ptr& ptr) const + std::string Weapon::getUpSoundId (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); int type = ref->mBase->mData.mType; // Ammo @@ -206,10 +191,9 @@ return std::string("Item Misc Up"); } - std::string Weapon::getDownSoundId (const MWWorld::Ptr& ptr) const + std::string Weapon::getDownSoundId (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); int type = ref->mBase->mData.mType; // Ammo @@ -251,29 +235,26 @@ return std::string("Item Misc Down"); } - std::string Weapon::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Weapon::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } - bool Weapon::hasToolTip (const MWWorld::Ptr& ptr) const + bool Weapon::hasToolTip (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } - MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::Ptr& ptr) const + MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); + info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); @@ -355,18 +336,16 @@ return info; } - std::string Weapon::getEnchantment (const MWWorld::Ptr& ptr) const + std::string Weapon::getEnchantment (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mEnchant; } - std::string Weapon::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + std::string Weapon::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); ESM::Weapon newItem = *ref->mBase; newItem.mId=""; @@ -378,7 +357,7 @@ return record->mId; } - std::pair Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + std::pair Weapon::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0) return std::make_pair(0, "#{sInventoryMessage1}"); @@ -411,33 +390,29 @@ return action; } - MWWorld::Ptr - Weapon::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Weapon::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } - int Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Weapon::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mEnchant; } - bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Weapon::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Weapon) || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } - float Weapon::getWeight(const MWWorld::Ptr &ptr) const + float Weapon::getWeight(const MWWorld::ConstPtr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } diff -Nru openmw-0.37.0/apps/openmw/mwclass/weapon.hpp openmw-0.38.0/apps/openmw/mwclass/weapon.hpp --- openmw-0.37.0/apps/openmw/mwclass/weapon.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwclass/weapon.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,19 +8,16 @@ class Weapon : public MWWorld::Class { virtual MWWorld::Ptr - copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: - virtual std::string getId (const MWWorld::Ptr& ptr) const; - ///< Return ID of \a ptr - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; - virtual std::string getName (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -28,50 +25,50 @@ const MWWorld::Ptr& actor) const; ///< Generate action for activation - virtual bool hasToolTip (const MWWorld::Ptr& ptr) const; + virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. - virtual bool hasItemHealth (const MWWorld::Ptr& ptr) const; + virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const; ///< \return Item health data available? - virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health - virtual std::string getScript (const MWWorld::Ptr& ptr) const; + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - virtual int getEquipmentSkill (const MWWorld::Ptr& ptr) const; - /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + virtual int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const; + /// Return the index of the skill this item corresponds to when equipped or -1, if there is /// no such skill. - virtual int getValue (const MWWorld::Ptr& ptr) const; + virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); - virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id - virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. - virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + virtual std::pair canBeEquipped(const MWWorld::ConstPtr &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 @@ -79,13 +76,13 @@ const; ///< Generate action for using via inventory menu - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; - virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwdialogue/dialoguemanagerimp.cpp openmw-0.38.0/apps/openmw/mwdialogue/dialoguemanagerimp.cpp --- openmw-0.37.0/apps/openmw/mwdialogue/dialoguemanagerimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwdialogue/dialoguemanagerimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -114,6 +114,8 @@ void DialogueManager::startDialogue (const MWWorld::Ptr& actor) { + updateGlobals(); + // Dialogue with dead actor (e.g. through script) should not be allowed. if (actor.getClass().getCreatureStats(actor).isDead()) return; @@ -298,15 +300,18 @@ MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); - // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, - // in which case it should not be added to the journal. - for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); - iter!=dialogue.mInfo.end(); ++iter) + if (dialogue.mType == ESM::Dialogue::Topic) { - if (iter->mId == info->mId) + // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, + // in which case it should not be added to the journal. + for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); + iter!=dialogue.mInfo.end(); ++iter) { - MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor); - break; + if (iter->mId == info->mId) + { + MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor); + break; + } } } @@ -328,6 +333,8 @@ void DialogueManager::updateTopics() { + updateGlobals(); + std::list keywordList; int choice = mChoice; mChoice = -1; @@ -414,8 +421,6 @@ win->setKeywords(keywordList); mChoice = choice; - - updateGlobals(); } void DialogueManager::keywordSelected (const std::string& keyword) diff -Nru openmw-0.37.0/apps/openmw/mwdialogue/filter.cpp openmw-0.38.0/apps/openmw/mwdialogue/filter.cpp --- openmw-0.37.0/apps/openmw/mwdialogue/filter.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwdialogue/filter.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -28,7 +28,7 @@ // actor id if (!info.mActor.empty()) { - if ( !Misc::StringUtils::ciEqual(info.mActor, mActor.getClass().getId (mActor))) + if ( !Misc::StringUtils::ciEqual(info.mActor, mActor.getCellRef().getRefId())) return false; } else if (isCreature) @@ -41,7 +41,7 @@ if (!info.mRace.empty()) { if (isCreature) - return false; + return true; MWWorld::LiveCellRef *cellRef = mActor.get(); @@ -53,7 +53,7 @@ if (!info.mClass.empty()) { if (isCreature) - return false; + return true; MWWorld::LiveCellRef *cellRef = mActor.get(); @@ -65,7 +65,7 @@ if (!info.mFaction.empty()) { if (isCreature) - return false; + return true; if (!Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), info.mFaction)) return false; @@ -77,7 +77,7 @@ else if (info.mData.mRank != -1) { if (isCreature) - return false; + return true; // Rank requirement, but no faction given. Use the actor's faction, if there is one. // check rank @@ -155,11 +155,12 @@ bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const { 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; + // If the actor is a creature, we pass all conditions only applicable to NPCs. + return true; + + if (select.getFunction() == SelectWrapper::Function_Choice && mChoice == -1) + // If not currently in a choice, we reject all conditions that test against choices. + return false; switch (select.getType()) { @@ -313,7 +314,7 @@ int value = 0; - for (int i=0; i<=15; ++i) // everything except thigns held in hands and amunition + for (int i=0; i<=15; ++i) // everything except things held in hands and ammunition { MWWorld::ContainerStoreIterator slot = store.getSlot (i); @@ -440,7 +441,7 @@ case SelectWrapper::Function_NotId: - return !Misc::StringUtils::ciEqual(mActor.getClass().getId (mActor), select.getName()); + return !Misc::StringUtils::ciEqual(mActor.getCellRef().getRefId(), select.getName()); case SelectWrapper::Function_NotFaction: @@ -480,7 +481,7 @@ case SelectWrapper::Function_SameRace: - return !Misc::StringUtils::ciEqual(mActor.get()->mBase->mRace, player.get()->mBase->mRace); + return Misc::StringUtils::ciEqual(mActor.get()->mBase->mRace, player.get()->mBase->mRace); case SelectWrapper::Function_SameFaction: diff -Nru openmw-0.37.0/apps/openmw/mwdialogue/keywordsearch.hpp openmw-0.38.0/apps/openmw/mwdialogue/keywordsearch.hpp --- openmw-0.37.0/apps/openmw/mwdialogue/keywordsearch.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwdialogue/keywordsearch.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -2,7 +2,7 @@ #define GAME_MWDIALOGUE_KEYWORDSEARCH_H #include -#include +#include #include #include #include // std::reverse @@ -44,7 +44,7 @@ typename Entry::childen_t::iterator current; typename Entry::childen_t::iterator next; - current = mRoot.mChildren.find (std::tolower (*keyword.begin(), mLocale)); + current = mRoot.mChildren.find (Misc::StringUtils::toLower (*keyword.begin())); if (current == mRoot.mChildren.end()) return false; else if (current->second.mKeyword.size() && Misc::StringUtils::ciEqual(current->second.mKeyword, keyword)) @@ -55,7 +55,7 @@ for (Point i = ++keyword.begin(); i != keyword.end(); ++i) { - next = current->second.mChildren.find(std::tolower (*i, mLocale)); + next = current->second.mChildren.find(Misc::StringUtils::toLower (*i)); if (next == current->second.mChildren.end()) return false; if (Misc::StringUtils::ciEqual(next->second.mKeyword, keyword)) @@ -89,7 +89,7 @@ // check first character - typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (std::tolower (*i, mLocale)); + typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (Misc::StringUtils::toLower (*i)); // no match, on to next character if (candidate == mRoot.mChildren.end ()) @@ -104,7 +104,7 @@ while ((j + 1) != end) { - typename Entry::childen_t::iterator next = candidate->second.mChildren.find (std::tolower (*++j, mLocale)); + typename Entry::childen_t::iterator next = candidate->second.mChildren.find (Misc::StringUtils::toLower (*++j)); if (next == candidate->second.mChildren.end ()) { @@ -136,7 +136,7 @@ while (k != end && t != candidate->second.mKeyword.end ()) { - if (std::tolower (*k, mLocale) != std::tolower (*t, mLocale)) + if (Misc::StringUtils::toLower (*k) != Misc::StringUtils::toLower (*t)) break; ++k, ++t; @@ -212,7 +212,7 @@ void seed_impl (string_t keyword, value_t value, size_t depth, Entry & entry) { - int ch = tolower (keyword.at (depth), mLocale); + int ch = Misc::StringUtils::toLower (keyword.at (depth)); typename Entry::childen_t::iterator j = entry.mChildren.find (ch); @@ -249,7 +249,6 @@ } Entry mRoot; - std::locale mLocale; }; } diff -Nru openmw-0.37.0/apps/openmw/mwdialogue/selectwrapper.cpp openmw-0.38.0/apps/openmw/mwdialogue/selectwrapper.cpp --- openmw-0.37.0/apps/openmw/mwdialogue/selectwrapper.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwdialogue/selectwrapper.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -263,10 +263,6 @@ { Function_NotFaction, Function_NotClass, Function_NotRace, Function_SameGender, Function_SameRace, Function_SameFaction, - Function_PcSkill, - Function_PcExpelled, - Function_PcVampire, - Function_PcCrimeLevel, Function_RankRequirement, Function_Reputation, Function_FactionRankDiff, Function_Werewolf, Function_WerewolfKills, diff -Nru openmw-0.37.0/apps/openmw/mwgui/console.cpp openmw-0.38.0/apps/openmw/mwgui/console.cpp --- openmw-0.37.0/apps/openmw/mwgui/console.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/console.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -365,7 +365,7 @@ /* Is the beginning of the string different from the input string? If yes skip it. */ for( std::string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();++iter, ++iter2) { - if( tolower(*iter) != tolower(*iter2) ) { + if( Misc::StringUtils::toLower(*iter) != Misc::StringUtils::toLower(*iter2) ) { string_different=true; break; } @@ -405,7 +405,7 @@ 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) ) { + if( Misc::StringUtils::toLower((*it)[i]) != Misc::StringUtils::toLower(*iter) ) { /* Append the longest match to the end of the output string*/ output.append(matches.front().substr( 0, i)); return output; diff -Nru openmw-0.37.0/apps/openmw/mwgui/formatting.cpp openmw-0.38.0/apps/openmw/mwgui/formatting.cpp --- openmw-0.37.0/apps/openmw/mwgui/formatting.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/formatting.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -129,7 +129,7 @@ size_t tagNameEndPos = tag.find(' '); mAttributes.clear(); mTag = tag.substr(0, tagNameEndPos); - Misc::StringUtils::toLower(mTag); + Misc::StringUtils::lowerCaseInPlace(mTag); if (mTag.empty()) return; @@ -151,7 +151,7 @@ return; std::string key = tag.substr(0, sepPos); - Misc::StringUtils::toLower(key); + Misc::StringUtils::lowerCaseInPlace(key); tag.erase(0, sepPos+1); std::string value; diff -Nru openmw-0.37.0/apps/openmw/mwgui/hud.cpp openmw-0.38.0/apps/openmw/mwgui/hud.cpp --- openmw-0.37.0/apps/openmw/mwgui/hud.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/hud.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -196,7 +196,7 @@ mMagicka->setProgressRange (modified); mMagicka->setProgressPosition (current); getWidget(w, "MagickaFrame"); - w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + w->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr); } else if (id == "FBar") { diff -Nru openmw-0.37.0/apps/openmw/mwgui/inventorywindow.cpp openmw-0.38.0/apps/openmw/mwgui/inventorywindow.cpp --- openmw-0.37.0/apps/openmw/mwgui/inventorywindow.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/inventorywindow.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -167,13 +167,15 @@ MyGUI::IntSize size(static_cast(Settings::Manager::getFloat(setting + " w", "Windows") * viewSize.width), static_cast(Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height)); + bool needUpdate = (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight()); + mMainWidget->setPosition(pos); mMainWidget->setSize(size); - if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight()) - updatePreviewSize(); - adjustPanes(); + + if (needUpdate) + updatePreviewSize(); } SortFilterItemModel* InventoryWindow::getSortFilterModel() @@ -471,16 +473,18 @@ MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); } - if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0) + mSkippedToEquip = MWWorld::Ptr(); + if (ptr.getRefData().getCount()) // make sure the item is still there, the script might have removed it { - boost::shared_ptr action = ptr.getClass().use(ptr); - - action->execute (player); + if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0) + { + boost::shared_ptr action = ptr.getClass().use(ptr); - mSkippedToEquip = MWWorld::Ptr(); + action->execute (player); + } + else + mSkippedToEquip = ptr; } - else - mSkippedToEquip = ptr; if (isVisible()) { diff -Nru openmw-0.37.0/apps/openmw/mwgui/itemmodel.cpp openmw-0.38.0/apps/openmw/mwgui/itemmodel.cpp --- openmw-0.37.0/apps/openmw/mwgui/itemmodel.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/itemmodel.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -35,7 +35,7 @@ { const ESM::GameSetting ¤tSetting = *currentIteration; std::string currentGMSTID = currentSetting.mId; - Misc::StringUtils::toLower(currentGMSTID); + Misc::StringUtils::lowerCaseInPlace(currentGMSTID); // Don't bother checking this GMST if it's not a sMagicBound* one. const std::string& toFind = "smagicbound"; @@ -44,7 +44,7 @@ // All sMagicBound* GMST's should be of type string std::string currentGMSTValue = currentSetting.getString(); - Misc::StringUtils::toLower(currentGMSTValue); + Misc::StringUtils::lowerCaseInPlace(currentGMSTValue); boundItemIDCache.insert(currentGMSTValue); } @@ -52,7 +52,7 @@ // Perform bound item check and assign the Flag_Bound bit if it passes std::string tempItemID = base.getCellRef().getRefId(); - Misc::StringUtils::toLower(tempItemID); + Misc::StringUtils::lowerCaseInPlace(tempItemID); if (boundItemIDCache.count(tempItemID) != 0) mFlags |= Flag_Bound; diff -Nru openmw-0.37.0/apps/openmw/mwgui/journalviewmodel.cpp openmw-0.38.0/apps/openmw/mwgui/journalviewmodel.cpp --- openmw-0.37.0/apps/openmw/mwgui/journalviewmodel.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/journalviewmodel.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -29,8 +29,6 @@ mutable bool mKeywordSearchLoaded; mutable KeywordSearchT mKeywordSearch; - std::locale mLocale; - JournalViewModelImpl () { mKeywordSearchLoaded = false; @@ -74,8 +72,6 @@ } } - wchar_t tolower (wchar_t ch) const { return std::tolower (ch, mLocale); } - bool isEmpty () const { MWBase::Journal * journal = MWBase::Environment::get().getJournal(); @@ -319,7 +315,7 @@ for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i) { - if (i->first [0] != std::tolower (character, mLocale)) + if (i->first [0] != Misc::StringUtils::toLower(character)) continue; visitor (i->second.getName()); diff -Nru openmw-0.37.0/apps/openmw/mwgui/journalwindow.cpp openmw-0.38.0/apps/openmw/mwgui/journalwindow.cpp --- openmw-0.37.0/apps/openmw/mwgui/journalwindow.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/journalwindow.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -96,7 +96,7 @@ return getWidget (name); } - JournalWindowImpl (MWGui::JournalViewModel::Ptr Model) + JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList) : WindowBase("openmw_journal.layout"), JournalBooks (Model) { mMainWidget->setVisible(false); @@ -142,17 +142,17 @@ getPage (RightTopicIndex)->adviseLinkClicked (callback); } - adjustButton(OptionsBTN, true); adjustButton(PrevPageBTN); adjustButton(NextPageBTN); adjustButton(CloseBTN); adjustButton(CancelBTN); - adjustButton(ShowAllBTN, true); - adjustButton(ShowActiveBTN, true); adjustButton(JournalBTN); Gui::ImageButton* optionsButton = getWidget(OptionsBTN); - if (optionsButton->getWidth() == 0) + Gui::ImageButton* showActiveButton = getWidget(ShowActiveBTN); + Gui::ImageButton* showAllButton = getWidget(ShowAllBTN); + Gui::ImageButton* questsButton = getWidget(QuestsBTN); + if (!questList) { // If tribunal is not installed (-> no options button), we still want the Topics button available, // so place it where the options button would have been @@ -162,6 +162,23 @@ topicsButton->setPosition(optionsButton->getPosition()); topicsButton->eventMouseButtonClick.clear(); topicsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &JournalWindowImpl::notifyOptions); + + optionsButton->setVisible(false); + showActiveButton->setVisible(false); + showAllButton->setVisible(false); + questsButton->setVisible(false); + } + else + { + optionsButton->setImage("textures/tx_menubook_options.dds"); + showActiveButton->setImage("textures/tx_menubook_quests_active.dds"); + showAllButton->setImage("textures/tx_menubook_quests_all.dds"); + questsButton->setImage("textures/tx_menubook_quests.dds"); + + adjustButton(ShowAllBTN); + adjustButton(ShowActiveBTN); + adjustButton(OptionsBTN); + adjustButton(QuestsBTN); } Gui::ImageButton* nextButton = getWidget(NextPageBTN); @@ -173,7 +190,6 @@ } adjustButton(TopicsBTN); - adjustButton(QuestsBTN, true); int width = getWidget(TopicsBTN)->getSize().width + getWidget(QuestsBTN)->getSize().width; int topicsWidth = getWidget(TopicsBTN)->getSize().width; int pageWidth = getWidget(RightBookPage)->getSize().width; @@ -186,12 +202,12 @@ mOptionsMode = false; } - void adjustButton (char const * name, bool optional = false) + void adjustButton (char const * name) { Gui::ImageButton* button = getWidget(name); - MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(!optional); - button->setSize(button->getRequestedSize(!optional)); + MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); + button->setSize(button->getRequestedSize()); if (button->getAlign().isRight()) button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); @@ -551,7 +567,7 @@ } // glue the implementation to the interface -MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model) +MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model, bool questList) { - return new JournalWindowImpl (Model); + return new JournalWindowImpl (Model, questList); } diff -Nru openmw-0.37.0/apps/openmw/mwgui/journalwindow.hpp openmw-0.38.0/apps/openmw/mwgui/journalwindow.hpp --- openmw-0.37.0/apps/openmw/mwgui/journalwindow.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/journalwindow.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -12,7 +12,7 @@ struct JournalWindow { /// construct a new instance of the one JournalWindow implementation - static JournalWindow * create (boost::shared_ptr Model); + static JournalWindow * create (boost::shared_ptr Model, bool questList); /// destroy this instance of the JournalWindow implementation virtual ~JournalWindow () {}; diff -Nru openmw-0.37.0/apps/openmw/mwgui/loadingscreen.cpp openmw-0.38.0/apps/openmw/mwgui/loadingscreen.cpp --- openmw-0.37.0/apps/openmw/mwgui/loadingscreen.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/loadingscreen.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -36,6 +36,7 @@ , mLastWallpaperChangeTime(0.0) , mLastRenderTime(0.0) , mLoadingOnTime(0.0) + , mImportantLabel(false) , mProgress(0) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); @@ -82,8 +83,10 @@ std::cerr << "No splash screens found!" << std::endl; } - void LoadingScreen::setLabel(const std::string &label) + void LoadingScreen::setLabel(const std::string &label, bool important) { + mImportantLabel = important; + mLoadingText->setCaptionWithReplacing(label); int padding = mLoadingBox->getWidth() - mLoadingText->getWidth(); MyGUI::IntSize size(mLoadingText->getTextSize().width+padding, mLoadingBox->getHeight()); @@ -176,6 +179,19 @@ void LoadingScreen::loadingOff() { + if (mLastRenderTime < mLoadingOnTime) + { + // the loading was so fast that we didn't show loading screen at all + // we may still want to show the label if the caller requested it + if (mImportantLabel) + { + MWBase::Environment::get().getWindowManager()->messageBox(mLoadingText->getCaption()); + mImportantLabel = false; + } + } + else + mImportantLabel = false; // label was already shown on loading screen + //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; setVisible(false); @@ -209,6 +225,7 @@ // skip expensive update if there isn't enough visible progress if (value - mProgress < mProgressBar->getScrollRange()/200.f) return; + value = std::min(value, mProgressBar->getScrollRange()-1); mProgress = value; mProgressBar->setScrollPosition(0); mProgressBar->setTrackSize(static_cast(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize())); @@ -219,22 +236,12 @@ { mProgressBar->setScrollPosition(0); size_t value = mProgress + increase; + value = std::min(value, mProgressBar->getScrollRange()-1); mProgress = value; mProgressBar->setTrackSize(static_cast(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize())); draw(); } - void LoadingScreen::indicateProgress() - { - float time = (static_cast(mTimer.time_m()) % 2001) / 1000.f; - if (time > 1) - time = (time-2)*-1; - - mProgressBar->setTrackSize(50); - mProgressBar->setScrollPosition(static_cast(time * (mProgressBar->getScrollRange() - 1))); - draw(); - } - bool LoadingScreen::needToDrawLoadingScreen() { if ( mTimer.time_m() <= mLastRenderTime + (1.0/mTargetFrameRate) * 1000.0) diff -Nru openmw-0.37.0/apps/openmw/mwgui/loadingscreen.hpp openmw-0.38.0/apps/openmw/mwgui/loadingscreen.hpp --- openmw-0.37.0/apps/openmw/mwgui/loadingscreen.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/loadingscreen.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -33,23 +33,16 @@ LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer); virtual ~LoadingScreen(); - virtual void setLabel (const std::string& label); - - /// Indicate that some progress has been made, without specifying how much - virtual void indicateProgress (); - + /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details + virtual void setLabel (const std::string& label, bool important); virtual void loadingOn(); virtual void loadingOff(); - virtual void setProgressRange (size_t range); virtual void setProgress (size_t value); virtual void increaseProgress (size_t increase=1); virtual void setVisible(bool visible); - void setLoadingProgress (const std::string& stage, int depth, int current, int total); - void loadingDone(); - private: void findSplashScreens(); bool needToDrawLoadingScreen(); @@ -64,6 +57,8 @@ osg::Timer mTimer; double mLoadingOnTime; + bool mImportantLabel; + size_t mProgress; MyGUI::Widget* mLoadingBox; diff -Nru openmw-0.37.0/apps/openmw/mwgui/messagebox.cpp openmw-0.38.0/apps/openmw/mwgui/messagebox.cpp --- openmw-0.37.0/apps/openmw/mwgui/messagebox.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/messagebox.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -117,8 +117,11 @@ bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) { - if(mInterMessageBoxe != NULL) { - throw std::runtime_error("There is a message box already"); + if (mInterMessageBoxe != NULL) + { + std::cerr << "Warning: replacing an interactive message box that was not answered yet" << std::endl; + delete mInterMessageBoxe; + mInterMessageBoxe = NULL; } mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); diff -Nru openmw-0.37.0/apps/openmw/mwgui/review.cpp openmw-0.38.0/apps/openmw/mwgui/review.cpp --- openmw-0.37.0/apps/openmw/mwgui/review.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/review.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -154,7 +154,7 @@ { mMagicka->setValue(static_cast(value.getCurrent()), static_cast(value.getModified())); std::string valStr = MyGUI::utility::toString(value.getCurrent()) + "/" + MyGUI::utility::toString(value.getModified()); - mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + mMagicka->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr); } void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) diff -Nru openmw-0.37.0/apps/openmw/mwgui/savegamedialog.cpp openmw-0.38.0/apps/openmw/mwgui/savegamedialog.cpp --- openmw-0.37.0/apps/openmw/mwgui/savegamedialog.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/savegamedialog.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -360,12 +360,17 @@ timeinfo = localtime(&time); // Use system/environment locale settings for datetime formatting + char* oldLctime = setlocale(LC_TIME, NULL); setlocale(LC_TIME, ""); const int size=1024; char buffer[size]; if (std::strftime(buffer, size, "%x %X", timeinfo) > 0) text << buffer << "\n"; + + // reset + setlocale(LC_TIME, oldLctime); + text << "#{sLevel} " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; text << "#{sCell=" << mCurrentSlot->mProfile.mPlayerCell << "}\n"; diff -Nru openmw-0.37.0/apps/openmw/mwgui/screenfader.cpp openmw-0.38.0/apps/openmw/mwgui/screenfader.cpp --- openmw-0.37.0/apps/openmw/mwgui/screenfader.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/screenfader.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,6 +1,7 @@ #include "screenfader.hpp" #include +#include namespace MWGui { @@ -66,20 +67,25 @@ mFader->notifyOperationFinished(); } - ScreenFader::ScreenFader(const std::string & texturePath, const std::string& layout) + ScreenFader::ScreenFader(const std::string & texturePath, const std::string& layout, const MyGUI::FloatCoord& texCoordOverride) : WindowBase(layout) , mCurrentAlpha(0.f) , mFactor(1.f) , mRepeat(false) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); - setTexture(texturePath); setVisible(false); - } - void ScreenFader::setTexture(const std::string & texturePath) - { - mMainWidget->setProperty("ImageTexture", texturePath); + MyGUI::ImageBox* imageBox = mMainWidget->castType(false); + if (imageBox) + { + imageBox->setImageTexture(texturePath); + const MyGUI::IntSize imageSize = imageBox->getImageSize(); + imageBox->setImageCoord(MyGUI::IntCoord(texCoordOverride.left * imageSize.width, + texCoordOverride.top * imageSize.height, + texCoordOverride.width * imageSize.width, + texCoordOverride.height * imageSize.height)); + } } void ScreenFader::update(float dt) diff -Nru openmw-0.37.0/apps/openmw/mwgui/screenfader.hpp openmw-0.38.0/apps/openmw/mwgui/screenfader.hpp --- openmw-0.37.0/apps/openmw/mwgui/screenfader.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/screenfader.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -36,9 +36,7 @@ class ScreenFader : public WindowBase { public: - ScreenFader(const std::string & texturePath, const std::string& layout = "openmw_screen_fader.layout"); - - void setTexture(const std::string & texturePath); + ScreenFader(const std::string & texturePath, const std::string& layout = "openmw_screen_fader.layout", const MyGUI::FloatCoord& texCoordOverride = MyGUI::FloatCoord(0,0,1,1)); void update(float dt); diff -Nru openmw-0.37.0/apps/openmw/mwgui/settingswindow.cpp openmw-0.38.0/apps/openmw/mwgui/settingswindow.cpp --- openmw-0.37.0/apps/openmw/mwgui/settingswindow.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/settingswindow.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -35,12 +35,13 @@ return "#{sOn}"; } - std::string textureFilteringToStr(const std::string& val) + std::string textureMipmappingToStr(const std::string& val) { - if (val == "trilinear") - return "Trilinear"; - else - return "Bilinear"; + if (val == "linear") return "Trilinear"; + if (val == "nearest") return "Bilinear"; + if (val != "none") + std::cerr<< "Invalid texture mipmap option: "<setCaption(textureFilteringToStr(tf)); + std::string tmip = Settings::Manager::getString("texture mipmap", "General"); + mTextureFilteringButton->setCaption(textureMipmappingToStr(tmip)); mAnisotropyLabel->setCaption("Anisotropy (" + MyGUI::utility::toString(Settings::Manager::getInt("anisotropy", "General")) + ")"); int waterTextureSize = Settings::Manager::getInt ("rtt size", "Water"); @@ -256,7 +257,7 @@ MyGUI::TextBox* fovText; getWidget(fovText, "FovText"); - fovText->setCaption("Field of View (" + MyGUI::utility::toString(int(Settings::Manager::getInt("field of view", "General"))) + ")"); + fovText->setCaption("Field of View (" + MyGUI::utility::toString(int(Settings::Manager::getInt("field of view", "Camera"))) + ")"); MyGUI::TextBox* diffText; getWidget(diffText, "DifficultyText"); @@ -425,7 +426,12 @@ void SettingsWindow::onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos) { - Settings::Manager::setString("texture filtering", "General", Misc::StringUtils::lowerCase(_sender->getItemNameAt(pos))); + if(pos == 0) + Settings::Manager::setString("texture mipmap", "General", "nearest"); + else if(pos == 1) + Settings::Manager::setString("texture mipmap", "General", "linear"); + else + std::cerr<< "Unexpected option pos "<getStore().get().find (iter->first); + const ESM::Spell* spell = iter->first; if (spell->mData.mType!=ESM::Spell::ST_Spell) continue; // don't try to sell diseases, curses or powers @@ -110,10 +109,10 @@ continue; } - if (playerHasSpell(iter->first)) + if (playerHasSpell(iter->first->mId)) continue; - addSpell (iter->first); + addSpell (iter->first->mId); } updateLabels(); diff -Nru openmw-0.37.0/apps/openmw/mwgui/spellcreationdialog.cpp openmw-0.38.0/apps/openmw/mwgui/spellcreationdialog.cpp --- openmw-0.37.0/apps/openmw/mwgui/spellcreationdialog.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/spellcreationdialog.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -516,8 +516,7 @@ for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find (it->first); + const ESM::Spell* spell = it->first; // only normal spells count if (spell->mData.mType != ESM::Spell::ST_Spell) diff -Nru openmw-0.37.0/apps/openmw/mwgui/spellmodel.cpp openmw-0.38.0/apps/openmw/mwgui/spellmodel.cpp --- openmw-0.37.0/apps/openmw/mwgui/spellmodel.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/spellmodel.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -52,7 +52,7 @@ for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - const ESM::Spell* spell = esmStore.get().find(it->first); + const ESM::Spell* spell = it->first; if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell) continue; @@ -67,9 +67,9 @@ } else newSpell.mType = Spell::Type_Power; - newSpell.mId = it->first; + newSpell.mId = spell->mId; - newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == it->first); + newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == spell->mId); newSpell.mActive = true; mSpells.push_back(newSpell); } @@ -93,7 +93,7 @@ Spell newSpell; newSpell.mItem = item; - newSpell.mId = item.getClass().getId(item); + newSpell.mId = item.getCellRef().getRefId(); newSpell.mName = item.getClass().getName(item); newSpell.mType = Spell::Type_EnchantedItem; newSpell.mSelected = invStore.getSelectedEnchantItem() == it; diff -Nru openmw-0.37.0/apps/openmw/mwgui/statswindow.cpp openmw-0.38.0/apps/openmw/mwgui/statswindow.cpp --- openmw-0.37.0/apps/openmw/mwgui/statswindow.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/statswindow.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -159,7 +159,7 @@ else if (id == "MBar") { getWidget(w, "Magicka"); - w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + w->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr); } else if (id == "FBar") { diff -Nru openmw-0.37.0/apps/openmw/mwgui/tooltips.cpp openmw-0.38.0/apps/openmw/mwgui/tooltips.cpp --- openmw-0.37.0/apps/openmw/mwgui/tooltips.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/tooltips.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -87,9 +87,9 @@ return; } - bool gameMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); + bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); - if (gameMode) + if (guiMode) { const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition(); @@ -112,10 +112,10 @@ if (info.caption.empty()) info.caption=mFocusObject.getCellRef().getRefId(); info.icon=""; - tooltipSize = createToolTip(info); + tooltipSize = createToolTip(info, true); } else - tooltipSize = getToolTipViaPtr(true); + tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true); MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition(); position(tooltipPosition, tooltipSize, viewSize); @@ -178,26 +178,22 @@ ToolTipInfo info; info.text = data.caption; info.notes = data.notes; - tooltipSize = createToolTip(info); + tooltipSize = createToolTip(info, false); } else if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); - tooltipSize = getToolTipViaPtr(false); + tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), 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); + tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false); } else if (type == "ToolTipInfo") { - tooltipSize = createToolTip(*focus->getUserData()); + tooltipSize = createToolTip(*focus->getUserData(), false); } else if (type == "AvatarItemSelection") { @@ -207,7 +203,7 @@ mFocusObject = item; if (!mFocusObject.isEmpty ()) - tooltipSize = getToolTipViaPtr(false); + tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false); } else if (type == "Spell") { @@ -240,7 +236,7 @@ info.text = "#{sSchool}: " + sSchoolNames[school]; } info.effects = effects; - tooltipSize = createToolTip(info); + tooltipSize = createToolTip(info, false); } else if (type == "Layout") { @@ -294,7 +290,7 @@ { if (!mFocusObject.isEmpty()) { - MyGUI::IntSize tooltipSize = getToolTipViaPtr(); + MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount()); setCoord(viewSize.width/2 - tooltipSize.width/2, std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), @@ -321,12 +317,12 @@ } } - void ToolTips::setFocusObject(const MWWorld::Ptr& focus) + void ToolTips::setFocusObject(const MWWorld::ConstPtr& focus) { mFocusObject = focus; } - MyGUI::IntSize ToolTips::getToolTipViaPtr (bool image) + MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image) { // this the maximum width of the tooltip before it starts word-wrapping setCoord(0, 0, 300, 300); @@ -342,10 +338,10 @@ { mDynamicToolTipBox->setVisible(true); - ToolTipInfo info = object.getToolTipInfo(mFocusObject); + ToolTipInfo info = object.getToolTipInfo(mFocusObject, count); if (!image) info.icon = ""; - tooltipSize = createToolTip(info); + tooltipSize = createToolTip(info, true); } return tooltipSize; @@ -370,13 +366,13 @@ } } - MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) + MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isFocusObject) { mDynamicToolTipBox->setVisible(true); if(mShowOwned == 1 || mShowOwned == 3) { - if(checkOwned()) + if(isFocusObject && checkOwned()) { mDynamicToolTipBox->changeWidgetSkin("HUD_Box_NoTransp_Owned"); } diff -Nru openmw-0.37.0/apps/openmw/mwgui/tooltips.hpp openmw-0.38.0/apps/openmw/mwgui/tooltips.hpp --- openmw-0.37.0/apps/openmw/mwgui/tooltips.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/tooltips.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -58,7 +58,7 @@ void setDelay(float delay); - void setFocusObject(const MWWorld::Ptr& focus); + void setFocusObject(const MWWorld::ConstPtr& focus); void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); ///< set the screen-space position of the tooltip for focused object @@ -93,13 +93,14 @@ private: MyGUI::Widget* mDynamicToolTipBox; - MWWorld::Ptr mFocusObject; + MWWorld::ConstPtr mFocusObject; - MyGUI::IntSize getToolTipViaPtr (bool image=true); + MyGUI::IntSize getToolTipViaPtr (int count, bool image=true); ///< @return requested tooltip size - MyGUI::IntSize createToolTip(const ToolTipInfo& info); + MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isFocusObject); ///< @return requested tooltip size + /// @param isFocusObject Is the object this tooltips originates from mFocusObject? float mFocusToolTipX; float mFocusToolTipY; diff -Nru openmw-0.37.0/apps/openmw/mwgui/windowmanagerimp.cpp openmw-0.38.0/apps/openmw/mwgui/windowmanagerimp.cpp --- openmw-0.37.0/apps/openmw/mwgui/windowmanagerimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/windowmanagerimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -117,7 +117,8 @@ osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem , const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription) - : mResourceSystem(resourceSystem) + : mStore(NULL) + , mResourceSystem(resourceSystem) , mViewer(viewer) , mConsoleOnlyScripts(consoleOnlyScripts) , mCurrentModals() @@ -288,9 +289,10 @@ trackWindow(mStatsWindow, "stats"); mConsole = new Console(w,h, mConsoleOnlyScripts); trackWindow(mConsole, "console"); - mJournal = JournalWindow::create(JournalViewModel::create ()); - mMessageBoxManager = new MessageBoxManager( - MWBase::Environment::get().getWorld()->getStore().get().find("fMessageTimePerChar")->getFloat()); + + bool questList = mResourceSystem->getVFS()->exists("textures/tx_menubook_options_over.dds"); + mJournal = JournalWindow::create(JournalViewModel::create (), questList); + mMessageBoxManager = new MessageBoxManager(mStore->get().find("fMessageTimePerChar")->getFloat()); mInventoryWindow = new InventoryWindow(mDragAndDrop, mViewer, mResourceSystem); mTradeWindow = new TradeWindow(); trackWindow(mTradeWindow, "barter"); @@ -324,14 +326,22 @@ trackWindow(mCompanionWindow, "companion"); mJailScreen = new JailScreen(); - mWerewolfFader = new ScreenFader("textures\\werewolfoverlay.dds"); + std::string werewolfFaderTex = "textures\\werewolfoverlay.dds"; + if (mResourceSystem->getVFS()->exists(werewolfFaderTex)) + mWerewolfFader = new ScreenFader(werewolfFaderTex); mBlindnessFader = new ScreenFader("black"); - std::string hitFaderTexture = "textures\\bm_player_hit_01.dds"; + // fall back to player_hit_01.dds if bm_player_hit_01.dds is not available - // TODO: check if non-BM versions actually use player_hit_01.dds + std::string hitFaderTexture = "textures\\bm_player_hit_01.dds"; + const std::string hitFaderLayout = "openmw_screen_fader_hit.layout"; + MyGUI::FloatCoord hitFaderCoord (0,0,1,1); if(!mResourceSystem->getVFS()->exists(hitFaderTexture)) + { hitFaderTexture = "textures\\player_hit_01.dds"; - mHitFader = new ScreenFader(hitFaderTexture, "openmw_screen_fader_hit.layout"); + hitFaderCoord = MyGUI::FloatCoord(0.2, 0.25, 0.6, 0.5); + } + mHitFader = new ScreenFader(hitFaderTexture, hitFaderLayout, hitFaderCoord); + mScreenFader = new ScreenFader("black"); mDebugWindow = new DebugWindow(); @@ -451,6 +461,11 @@ delete mGuiPlatform; } + void WindowManager::setStore(const MWWorld::ESMStore &store) + { + mStore = &store; + } + void WindowManager::cleanupGarbage() { // Delete any dialogs which are no longer in use @@ -898,8 +913,7 @@ std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_) { - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().search(id); + const ESM::GameSetting *setting = mStore->get().search(id); if (setting && setting->mValue.getType()==ESM::VT_String) return setting->mValue.getString(); @@ -984,7 +998,8 @@ mCompanionWindow->onFrame(); mJailScreen->onFrame(frameDuration); - mWerewolfFader->update(frameDuration); + if (mWerewolfFader) + mWerewolfFader->update(frameDuration); mBlindnessFader->update(frameDuration); mHitFader->update(frameDuration); mScreenFader->update(frameDuration); @@ -1128,8 +1143,12 @@ } else { - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().find(tag); + if (!mStore) + { + std::cerr << "WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'" << std::endl; + return; + } + const ESM::GameSetting *setting = mStore->get().find(tag); if (setting && setting->mValue.getType()==ESM::VT_String) _result = setting->mValue.getString(); @@ -1252,8 +1271,7 @@ mSelectedSpell = spellId; mHud->setSelectedSpell(spellId, successChancePercent); - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + const ESM::Spell* spell = mStore->get().find(spellId); mSpellWindow->setTitle(spell->mName); } @@ -1261,7 +1279,7 @@ void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { mSelectedSpell = ""; - const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() + const ESM::Enchantment* ench = mStore->get() .find(item.getClass().getEnchantment(item)); int chargePercent = (item.getCellRef().getEnchantmentCharge() == -1) ? 100 @@ -1696,7 +1714,7 @@ { reader.getSubNameIs("ID__"); std::string spell = reader.getHString(); - if (MWBase::Environment::get().getWorld()->getStore().get().search(spell)) + if (mStore->get().search(spell)) mSelectedSpell = spell; } else if (type == ESM::REC_MARK) @@ -1743,6 +1761,7 @@ MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); sizeVideo(screenSize.width, screenSize.height); + MyGUI::Widget* oldKeyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); setKeyFocusWidget(mVideoWidget); mVideoBackground->setVisible(true); @@ -1770,6 +1789,8 @@ MWBase::Environment::get().getSoundManager()->resumeSounds(); + setKeyFocusWidget(oldKeyFocus); + setCursorVisible(cursorWasVisible); // Restore normal rendering @@ -1875,7 +1896,8 @@ if (!mWerewolfOverlayEnabled) return; - mWerewolfFader->notifyAlphaChanged(set ? 1.0f : 0.0f); + if (mWerewolfFader) + mWerewolfFader->notifyAlphaChanged(set ? 1.0f : 0.0f); } void WindowManager::onClipboardChanged(const std::string &_type, const std::string &_data) diff -Nru openmw-0.37.0/apps/openmw/mwgui/windowmanagerimp.hpp openmw-0.38.0/apps/openmw/mwgui/windowmanagerimp.hpp --- openmw-0.37.0/apps/openmw/mwgui/windowmanagerimp.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwgui/windowmanagerimp.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -28,6 +28,11 @@ class ImageBox; } +namespace MWWorld +{ + class ESMStore; +} + namespace Compiler { class Extensions; @@ -119,6 +124,9 @@ Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription); virtual ~WindowManager(); + /// Set the ESMStore to use for retrieving of GUI-related strings. + void setStore (const MWWorld::ESMStore& store); + void initUI(); void renderWorldMap(); @@ -372,6 +380,7 @@ void writeFog(MWWorld::CellStore* cell); private: + const MWWorld::ESMStore* mStore; Resource::ResourceSystem* mResourceSystem; osgMyGUI::Platform* mGuiPlatform; diff -Nru openmw-0.37.0/apps/openmw/mwinput/inputmanagerimp.cpp openmw-0.38.0/apps/openmw/mwinput/inputmanagerimp.cpp --- openmw-0.37.0/apps/openmw/mwinput/inputmanagerimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwinput/inputmanagerimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,6 +4,8 @@ #include +#include + #include #include #include @@ -15,12 +17,11 @@ #include #include -#include "../engine.hpp" - #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/statemanager.hpp" +#include "../mwbase/environment.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -30,22 +31,20 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" -using namespace ICS; - namespace MWInput { InputManager::InputManager( SDL_Window* window, osg::ref_ptr viewer, - OMW::Engine& engine, + osg::ref_ptr screenCaptureHandler, const std::string& userFile, bool userFileExists, const std::string& controllerBindingsFile, bool grab) : mWindow(window) , mWindowVisible(true) , mViewer(viewer) + , mScreenCaptureHandler(screenCaptureHandler) , mJoystickLastUsed(false) , mPlayer(NULL) - , mEngine(engine) , mInputManager(NULL) , mVideoWrapper(NULL) , mUserFile(userFile) @@ -54,7 +53,6 @@ , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) , mControlsDisabled(false) , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) - , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) @@ -121,10 +119,11 @@ SDL_ControllerDeviceEvent evt; evt.which = i; controllerAdded(mFakeDeviceID, evt); + std::cout << "Detected game controller: " << SDL_GameControllerNameForIndex(i) << std::endl; } else { - //ICS_LOG(std::string("Unusable controller plugged in: ")+SDL_JoystickNameForIndex(i)); + std::cout << "Detected unusable controller: " << SDL_JoystickNameForIndex(i) << std::endl; } } @@ -303,16 +302,20 @@ quickLoad(); break; case A_CycleSpellLeft: - MWBase::Environment::get().getWindowManager()->cycleSpell(false); + if (checkAllowedToUseItems()) + MWBase::Environment::get().getWindowManager()->cycleSpell(false); break; case A_CycleSpellRight: - MWBase::Environment::get().getWindowManager()->cycleSpell(true); + if (checkAllowedToUseItems()) + MWBase::Environment::get().getWindowManager()->cycleSpell(true); break; case A_CycleWeaponLeft: - MWBase::Environment::get().getWindowManager()->cycleWeapon(false); + if (checkAllowedToUseItems()) + MWBase::Environment::get().getWindowManager()->cycleWeapon(false); break; case A_CycleWeaponRight: - MWBase::Environment::get().getWindowManager()->cycleWeapon(true); + if (checkAllowedToUseItems()) + MWBase::Environment::get().getWindowManager()->cycleWeapon(true); break; case A_Sneak: if (mSneakToggles) @@ -347,6 +350,18 @@ } } + bool InputManager::checkAllowedToUseItems() const + { + MWWorld::Ptr player = MWMechanics::getPlayer(); + if (player.getClass().getNpcStats(player).isWerewolf()) + { + // Cannot use items or spells while in werewolf form + MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + return false; + } + return true; + } + void InputManager::update(float dt, bool disableControls, bool disableEvents) { mControlsDisabled = disableControls; @@ -587,9 +602,6 @@ if (it->first == "Input" && it->second == "camera sensitivity") mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); - if (it->first == "Input" && it->second == "ui sensitivity") - mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); - if (it->first == "Input" && it->second == "grab cursor") mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); @@ -906,6 +918,9 @@ if (!mControlSwitch["playermagic"] || !mControlSwitch["playercontrols"]) return; + if (!checkAllowedToUseItems()) + return; + // Not allowed if no spell selected MWWorld::InventoryStore& inventory = mPlayer->getPlayer().getClass().getInventoryStore(mPlayer->getPlayer()); if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty() && @@ -952,7 +967,8 @@ void InputManager::screenshot() { - mEngine.screenshot(); + mScreenCaptureHandler->setFramesToCapture(1); + mScreenCaptureHandler->captureNextFrame(*mViewer); MWBase::Environment::get().getWindowManager()->messageBox ("Screenshot saved"); } @@ -1019,13 +1035,8 @@ { if (!mControlSwitch["playercontrols"]) return; - MWWorld::Ptr player = MWMechanics::getPlayer(); - if (player.getClass().getNpcStats(player).isWerewolf()) - { - // Cannot use items or spells while in werewolf form - MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + if (!checkAllowedToUseItems()) return; - } if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) MWBase::Environment::get().getWindowManager()->activateQuickKey (index); @@ -1036,13 +1047,8 @@ if (!MWBase::Environment::get().getWindowManager()->isGuiMode () && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) { - MWWorld::Ptr player = MWMechanics::getPlayer(); - if (player.getClass().getNpcStats(player).isWerewolf()) - { - // Cannot use items or spells while in werewolf form - MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + if (!checkAllowedToUseItems()) return; - } MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); @@ -1058,7 +1064,7 @@ void InputManager::activate() { if (mControlSwitch["playercontrols"]) - mEngine.activate(); + mPlayer->activate(); } void InputManager::toggleAutoMove() diff -Nru openmw-0.37.0/apps/openmw/mwinput/inputmanagerimp.hpp openmw-0.38.0/apps/openmw/mwinput/inputmanagerimp.hpp --- openmw-0.37.0/apps/openmw/mwinput/inputmanagerimp.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwinput/inputmanagerimp.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -25,11 +25,6 @@ class WindowManager; } -namespace OMW -{ - class Engine; -} - namespace ICS { class InputControlSystem; @@ -54,6 +49,7 @@ namespace osgViewer { class Viewer; + class ScreenCaptureHandler; } struct SDL_Window; @@ -77,7 +73,7 @@ InputManager( SDL_Window* window, osg::ref_ptr viewer, - OMW::Engine& engine, + osg::ref_ptr screenCaptureHandler, const std::string& userFile, bool userFileExists, const std::string& controllerBindingsFile, bool grab); @@ -157,10 +153,10 @@ SDL_Window* mWindow; bool mWindowVisible; osg::ref_ptr mViewer; + osg::ref_ptr mScreenCaptureHandler; bool mJoystickLastUsed; MWWorld::Player* mPlayer; - OMW::Engine& mEngine; ICS::InputControlSystem* mInputBinder; @@ -178,7 +174,6 @@ bool mControlsDisabled; float mCameraSensitivity; - float mUISensitivity; float mCameraYMultiplier; float mPreviewPOVDelay; float mTimeIdle; @@ -218,6 +213,8 @@ void updateCursorMode(); + bool checkAllowedToUseItems() const; + private: void toggleMainMenu(); void toggleSpell(); diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/activespells.cpp openmw-0.38.0/apps/openmw/mwmechanics/activespells.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/activespells.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/activespells.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -132,15 +132,11 @@ return scaledDuration-usedUp; } - bool ActiveSpells::isSpellActive(std::string id) const + bool ActiveSpells::isSpellActive(const std::string& id) const { - Misc::StringUtils::toLower(id); for (TContainer::iterator iter = mSpells.begin(); iter != mSpells.end(); ++iter) { - std::string left = iter->first; - Misc::StringUtils::toLower(left); - - if (iter->first == id) + if (Misc::StringUtils::ciEqual(iter->first, id)) return true; } return false; diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/activespells.hpp openmw-0.38.0/apps/openmw/mwmechanics/activespells.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/activespells.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/activespells.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -97,7 +97,7 @@ /// Remove all spells void clear(); - bool isSpellActive (std::string id) const; + bool isSpellActive (const std::string& id) const; ///< case insensitive const MagicEffects& getMagicEffects() const; diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/actors.cpp openmw-0.38.0/apps/openmw/mwmechanics/actors.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/actors.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/actors.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,11 +3,10 @@ #include #include -#include - #include #include #include +#include #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" @@ -150,14 +149,20 @@ { MWWorld::Ptr mCreature; MWWorld::Ptr mActor; + bool mTrapped; public: SoulTrap(MWWorld::Ptr trappedCreature) - : mCreature(trappedCreature) {} + : mCreature(trappedCreature) + , mTrapped(false) + { + } virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { + if (mTrapped) + return; if (key.mId != ESM::MagicEffect::Soultrap) return; if (magnitude <= 0) @@ -204,6 +209,8 @@ gem->getContainerStore()->unstack(*gem, caster); gem->getCellRef().setSoul(mCreature.getCellRef().getRefId()); + mTrapped = true; + if (caster == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sSoultrapSuccess}"); @@ -213,8 +220,9 @@ MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, "", mCreature.getRefData().getPosition().asVec3()); - MWBase::Environment::get().getSoundManager()->playSound3D(mCreature, "conjuration hit", 1.f, 1.f, - MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D( + mCreature.getRefData().getPosition().asVec3(), "conjuration hit", 1.f, 1.f + ); } }; @@ -303,7 +311,7 @@ if (againstPlayer) { // followers with high fight should not engage in combat with the player (e.g. bm_bear_black_summon) - const std::list& followers = getActorsFollowing(actor2); + const std::list& followers = getActorsSidingWith(actor2); if (std::find(followers.begin(), followers.end(), actor1) != followers.end()) return; @@ -322,7 +330,7 @@ } // start combat if target actor is in combat with one of our followers - const std::list& followers = getActorsFollowing(actor1); + const std::list& followers = getActorsSidingWith(actor1); const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); for (std::list::const_iterator it = followers.begin(); it != followers.end(); ++it) { @@ -336,20 +344,21 @@ // start combat if target actor is in combat with someone we are following for (std::list::const_iterator it = creatureStats.getAiSequence().begin(); it != creatureStats.getAiSequence().end(); ++it) { - if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) - { - MWWorld::Ptr followTarget = static_cast(*it)->getTarget(); - if (followTarget.isEmpty()) - continue; + if (!(*it)->sideWithTarget()) + continue; - if (creatureStats.getAiSequence().isInCombat(followTarget)) - continue; + MWWorld::Ptr followTarget = (*it)->getTarget(); - // need to check both ways since player doesn't use AI packages - if (creatureStats2.getAiSequence().isInCombat(followTarget) - || followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2)) - aggressive = true; - } + if (followTarget.isEmpty()) + continue; + + if (creatureStats.getAiSequence().isInCombat(followTarget)) + continue; + + // need to check both ways since player doesn't use AI packages + if (creatureStats2.getAiSequence().isInCombat(followTarget) + || followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2)) + aggressive = true; } if(aggressive) @@ -485,11 +494,25 @@ bool wasDead = creatureStats.isDead(); - // tickable effects (i.e. effects having a lasting impact after expiry) - // these effects can be applied as "instant" (handled in spellcasting.cpp) or with a duration, handled here - for (MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it) + if (duration > 0) { - effectTick(creatureStats, ptr, it->first, it->second.getMagnitude() * duration); + for (MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it) + { + // tickable effects (i.e. effects having a lasting impact after expiry) + effectTick(creatureStats, ptr, it->first, it->second.getMagnitude() * duration); + + // instant effects are already applied on spell impact in spellcasting.cpp, but may also come from permanent abilities + if (it->second.getMagnitude() > 0) + { + CastSpell cast(ptr, ptr); + if (cast.applyInstantEffect(ptr, ptr, it->first, it->second.getMagnitude())) + { + creatureStats.getActiveSpells().purgeEffect(it->first.mId); + if (ptr.getClass().hasInventoryStore(ptr)) + ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first.mId); + } + } + } } // attributes @@ -838,7 +861,7 @@ if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat()) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - int cutoff = esmStore.get().find("iCrimeThreshold")->getInt(); + static const int cutoff = esmStore.get().find("iCrimeThreshold")->getInt(); // Force dialogue on sight if bounty is greater than the cutoff // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty) if ( player.getClass().getNpcStats(player).getBounty() >= cutoff @@ -846,7 +869,7 @@ && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - static int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->getInt(); + static const int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->getInt(); if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); else @@ -926,7 +949,7 @@ PtrActorMap::iterator iter = mActors.begin(); while(iter != mActors.end()) { - if(iter->first.getCell()==cellStore && iter->first != ignore) + if((iter->first.isInCell() && iter->first.getCell()==cellStore) && iter->first != ignore) { delete iter->second; mActors.erase(iter++); @@ -1290,6 +1313,28 @@ } } + std::list Actors::getActorsSidingWith(const MWWorld::Ptr& actor) + { + std::list list; + for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + const MWWorld::Class &cls = iter->first.getClass(); + CreatureStats &stats = cls.getCreatureStats(iter->first); + if (stats.isDead()) + continue; + + // An actor counts as following if AiFollow or AiEscort is the current AiPackage, or there are only Combat packages before the AiFollow/AiEscort package + for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + { + if ((*it)->sideWithTarget() && (*it)->getTarget() == actor) + list.push_back(iter->first); + else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + break; + } + } + return list; + } + std::list Actors::getActorsFollowing(const MWWorld::Ptr& actor) { std::list list; @@ -1303,16 +1348,8 @@ // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) { - if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) - { - MWWorld::Ptr followTarget = static_cast(*it)->getTarget(); - if (followTarget.isEmpty()) - continue; - if (followTarget == actor) - list.push_back(iter->first); - else - break; - } + if ((*it)->followTargetThroughDoors() && (*it)->getTarget() == actor) + list.push_back(iter->first); else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) break; } @@ -1340,8 +1377,7 @@ continue; if (followTarget == actor) list.push_back(static_cast(*it)->getFollowIndex()); - else - break; + break; } else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) break; diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/actors.hpp openmw-0.38.0/apps/openmw/mwmechanics/actors.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/actors.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/actors.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -111,8 +111,9 @@ void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector& out); - ///Returns the list of actors which are following the given actor - /**ie AiFollow is active and the target is the actor **/ + ///Returns the list of actors which are siding with the given actor in fights + /**ie AiFollow or AiEscort is active and the target is the actor **/ + std::list getActorsSidingWith(const MWWorld::Ptr& actor); std::list getActorsFollowing(const MWWorld::Ptr& actor); /// Get the list of AiFollow::mFollowIndex for all actors following this target diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aiavoiddoor.cpp openmw-0.38.0/apps/openmw/mwmechanics/aiavoiddoor.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/aiavoiddoor.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aiavoiddoor.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,7 +11,7 @@ #include "steering.hpp" -MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::Ptr& doorPtr) +MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr) : AiPackage(), mDuration(1), mDoorPtr(doorPtr), mAdjAngle(0) { diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aiavoiddoor.hpp openmw-0.38.0/apps/openmw/mwmechanics/aiavoiddoor.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/aiavoiddoor.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aiavoiddoor.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -16,7 +16,7 @@ { public: /// Avoid door until the door is fully open - AiAvoidDoor(const MWWorld::Ptr& doorPtr); + AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); virtual AiAvoidDoor *clone() const; @@ -28,7 +28,7 @@ private: float mDuration; - MWWorld::Ptr mDoorPtr; + MWWorld::ConstPtr mDoorPtr; ESM::Position mLastPos; float mAdjAngle; }; diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aicombataction.cpp openmw-0.38.0/apps/openmw/mwmechanics/aicombataction.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/aicombataction.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aicombataction.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -541,7 +541,7 @@ for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(it->first); + const ESM::Spell* spell = it->first; float rating = rateSpell(spell, actor, target); if (rating > bestActionRating) diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aicombat.cpp openmw-0.38.0/apps/openmw/mwmechanics/aicombat.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/aicombat.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aicombat.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -305,7 +305,6 @@ else { distantCombat = (rangeAttack > 500); - weapRange = 150.f; } @@ -347,7 +346,7 @@ osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3()); osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); - float distToTarget = (vTargetPos - vActorPos).length(); + float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); osg::Vec3f& lastActorPos = storage.mLastActorPos; bool& followTarget = storage.mFollowTarget; diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aiescort.cpp openmw-0.38.0/apps/openmw/mwmechanics/aiescort.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/aiescort.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aiescort.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,12 +1,14 @@ #include "aiescort.hpp" #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -73,6 +75,12 @@ return true; } + if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) + return false; + + if (!mCellId.empty() && mCellId != actor.getCell()->getCell()->getCellId().mWorldspace) + return false; // Not in the correct cell, pause and rely on the player to go back through a teleport door + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); @@ -114,6 +122,11 @@ return TypeIdEscort; } + MWWorld::Ptr AiEscort::getTarget() + { + return MWBase::Environment::get().getWorld()->getPtr(mActorId, false); + } + void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr escort(new ESM::AiSequence::AiEscort()); diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aiescort.hpp openmw-0.38.0/apps/openmw/mwmechanics/aiescort.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/aiescort.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aiescort.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -37,6 +37,9 @@ virtual int getTypeId() const; + MWWorld::Ptr getTarget(); + virtual bool sideWithTarget() const { return true; } + void writeState(ESM::AiSequence::AiSequence &sequence) const; private: diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aifollow.cpp openmw-0.38.0/apps/openmw/mwmechanics/aifollow.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/aifollow.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aifollow.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -129,12 +129,14 @@ ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; float dist = distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]); - const float threshold = 10; if (storage.mMoving) //Stop when you get close storage.mMoving = (dist > followDistance); else + { + const float threshold = 10; storage.mMoving = (dist > followDistance + threshold); + } if(!storage.mMoving) { diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aifollow.hpp openmw-0.38.0/apps/openmw/mwmechanics/aifollow.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/aifollow.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aifollow.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -32,6 +32,8 @@ AiFollow(const ESM::AiSequence::AiFollow* follow); MWWorld::Ptr getTarget(); + virtual bool sideWithTarget() const { return true; } + virtual bool followTargetThroughDoors() const { return true; } virtual AiFollow *clone() const; diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aipackage.cpp openmw-0.38.0/apps/openmw/mwmechanics/aipackage.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/aipackage.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aipackage.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,6 +20,21 @@ MWMechanics::AiPackage::~AiPackage() {} +MWWorld::Ptr MWMechanics::AiPackage::getTarget() +{ + return MWWorld::Ptr(); +} + +bool MWMechanics::AiPackage::sideWithTarget() const +{ + return false; +} + +bool MWMechanics::AiPackage::followTargetThroughDoors() const +{ + return false; +} + MWMechanics::AiPackage::AiPackage() : mTimer(0.26f) { //mTimer starts at .26 to force initial pathbuild } diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aipackage.hpp openmw-0.38.0/apps/openmw/mwmechanics/aipackage.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/aipackage.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aipackage.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -69,6 +69,15 @@ /// Simulates the passing of time virtual void fastForward(const MWWorld::Ptr& actor, AiState& state) {} + /// Get the target actor the AI is targeted at (not applicable to all AI packages, default return empty Ptr) + virtual MWWorld::Ptr getTarget(); + + /// Return true if having this AiPackage makes the actor side with the target in fights (default false) + virtual bool sideWithTarget() const; + + /// Return true if the actor should follow the target through teleport doors (default false) + virtual bool followTargetThroughDoors() const; + bool isTargetMagicallyHidden(const MWWorld::Ptr& target); protected: @@ -88,10 +97,19 @@ ESM::Pathgrid::Point mPrevDest; + bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) const + { + // Maximum travel distance for vanilla compatibility. + // Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well. + // We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways. + return (pos1 - pos2).length2() <= 7168*7168; + } + private: bool isNearInactiveCell(const ESM::Position& actorPos); }; + } #endif diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aisequence.cpp openmw-0.38.0/apps/openmw/mwmechanics/aisequence.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/aisequence.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aisequence.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -72,7 +72,7 @@ targetActor = combat->getTarget(); - return true; + return !targetActor.isEmpty(); } std::list::const_iterator AiSequence::begin() const diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/aitravel.cpp openmw-0.38.0/apps/openmw/mwmechanics/aitravel.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/aitravel.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/aitravel.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -13,18 +13,6 @@ #include "movement.hpp" #include "creaturestats.hpp" -namespace -{ - -bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) -{ - // Maximum travel distance for vanilla compatibility. - // Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well. - // We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways. - return (pos1 - pos2).length2() <= 7168*7168; -} - -} namespace MWMechanics { diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/alchemy.cpp openmw-0.38.0/apps/openmw/mwmechanics/alchemy.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/alchemy.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/alchemy.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -421,8 +421,8 @@ return -1; for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) - if (!iter->isEmpty() && Misc::StringUtils::ciEqual(ingredient.getClass().getId(ingredient), - iter->getClass().getId(*iter))) + if (!iter->isEmpty() && Misc::StringUtils::ciEqual(ingredient.getCellRef().getRefId(), + iter->getCellRef().getRefId())) return -1; mIngredients[slot] = ingredient; diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/character.cpp openmw-0.38.0/apps/openmw/mwmechanics/character.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/character.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/character.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -21,7 +21,6 @@ #include -#include #include "movement.hpp" #include "npcstats.hpp" @@ -33,6 +32,8 @@ #include +#include + #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" @@ -396,10 +397,17 @@ } else { - if (weap != sWeaponTypeListEnd) - movemask = MWRender::Animation::BlendMask_LowerBody; movementAnimName.erase(swimpos, 4); - if(!mAnimation->hasAnimation(movementAnimName)) + if (weap != sWeaponTypeListEnd) + { + std::string weapMovementAnimName = movementAnimName + weap->shortgroup; + if(mAnimation->hasAnimation(weapMovementAnimName)) + movementAnimName = weapMovementAnimName; + else + movemask = MWRender::Animation::BlendMask_LowerBody; + } + + if (!mAnimation->hasAnimation(movementAnimName)) movementAnimName.clear(); } } @@ -512,6 +520,8 @@ const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); if(info != sWeaponTypeListEnd) group = info->longgroup; + else + group.clear(); } @@ -776,10 +786,15 @@ if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx; if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0) - type = MWBase::SoundManager::Play_TypeFoot; - sndMgr->playSound3D(mPtr, sound, volume, pitch, type); + { + // Don't make foot sounds local for the player, it makes sense to keep them + // positioned on the ground. + sndMgr->playSound3D(mPtr, sound, volume, pitch, MWBase::SoundManager::Play_TypeFoot, + MWBase::SoundManager::Play_NoPlayerLocal); + } + else + sndMgr->playSound3D(mPtr, sound, volume, pitch); } return; } @@ -1079,11 +1094,14 @@ std::string weapgroup; if(weaptype == WeapType_None) { - getWeaponGroup(mWeaponType, weapgroup); - mAnimation->play(weapgroup, priorityWeapon, - MWRender::Animation::BlendMask_All, true, - 1.0f, "unequip start", "unequip stop", 0.0f, 0); - mUpperBodyState = UpperCharState_UnEquipingWeap; + if ((!isWerewolf || mWeaponType != WeapType_Spell)) + { + getWeaponGroup(mWeaponType, weapgroup); + mAnimation->play(weapgroup, priorityWeapon, + MWRender::Animation::BlendMask_All, true, + 1.0f, "unequip start", "unequip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_UnEquipingWeap; + } } else { @@ -2101,7 +2119,7 @@ mAnimation->setActive(active); } -void CharacterController::setHeadTrackTarget(const MWWorld::Ptr &target) +void CharacterController::setHeadTrackTarget(const MWWorld::ConstPtr &target) { mHeadTrackTarget = target; } @@ -2124,7 +2142,7 @@ osg::Vec3f headPos = mat.getTrans(); osg::Vec3f direction; - if (MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) + if (const MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) { const osg::Node* node = anim->getNode("Head"); if (node == NULL) diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/character.hpp openmw-0.38.0/apps/openmw/mwmechanics/character.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/character.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/character.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -180,7 +180,7 @@ float mSecondsOfSwimming; float mSecondsOfRunning; - MWWorld::Ptr mHeadTrackTarget; + MWWorld::ConstPtr mHeadTrackTarget; float mTurnAnimationThreshold; // how long to continue playing turning animation after actor stopped turning @@ -255,7 +255,7 @@ void setActive(bool active); /// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr. - void setHeadTrackTarget(const MWWorld::Ptr& target); + void setHeadTrackTarget(const MWWorld::ConstPtr& target); }; MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype); diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/combat.cpp openmw-0.38.0/apps/openmw/mwmechanics/combat.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/combat.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/combat.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,9 +1,9 @@ #include "combat.hpp" -#include - #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/creaturestats.cpp openmw-0.38.0/apps/openmw/mwmechanics/creaturestats.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/creaturestats.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/creaturestats.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -48,8 +48,10 @@ const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - return gmst.find ("fFatigueBase")->getFloat() - - gmst.find ("fFatigueMult")->getFloat() * (1-normalised); + static const float fFatigueBase = gmst.find("fFatigueBase")->getFloat(); + static const float fFatigueMult = gmst.find("fFatigueMult")->getFloat(); + + return fFatigueBase - fFatigueMult * (1-normalised); } const AttributeValue &CreatureStats::getAttribute(int index) const @@ -265,9 +267,11 @@ { if (mDead) { + if (mDynamic[0].getModified() < 1) + mDynamic[0].setModified(1, 0); + mDynamic[0].setCurrent(mDynamic[0].getModified()); - if (mDynamic[0].getCurrent()>=1) - mDead = false; + mDead = false; } } diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/disease.hpp openmw-0.38.0/apps/openmw/mwmechanics/disease.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/disease.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/disease.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -34,8 +34,7 @@ Spells& spells = carrier.getClass().getCreatureStats(carrier).getSpells(); for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(it->first); - + const ESM::Spell* spell = it->first; if (actor.getClass().getCreatureStats(actor).getSpells().hasSpell(spell->mId)) continue; diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp openmw-0.38.0/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -2,13 +2,13 @@ #include -#include - #include #include #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" @@ -604,7 +604,7 @@ int rank = 0; std::string npcFaction = ptr.getClass().getPrimaryFaction(ptr); - Misc::StringUtils::toLower(npcFaction); + Misc::StringUtils::lowerCaseInPlace(npcFaction); if (playerStats.getFactionRanks().find(npcFaction) != playerStats.getFactionRanks().end()) { @@ -983,7 +983,7 @@ MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getClass().getId(*it))); + StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getCellRef().getRefId())); if (stolenIt == mStolenItems.end()) continue; OwnerMap& owners = stolenIt->second; @@ -1042,10 +1042,10 @@ owner.first = ownerCellRef->getFaction(); owner.second = true; } - Misc::StringUtils::toLower(owner.first); + Misc::StringUtils::lowerCaseInPlace(owner.first); if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) - mStolenItems[Misc::StringUtils::lowerCase(item.getClass().getId(item))][owner] += count; + mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count; commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } @@ -1053,7 +1053,7 @@ void getFollowers (const MWWorld::Ptr& actor, std::set& out) { - std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); + std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsSidingWith(actor); for(std::list::iterator it = followers.begin();it != followers.end();++it) { if (out.insert(*it).second) @@ -1310,7 +1310,7 @@ if (ptr == getPlayer()) return false; - std::list followers = getActorsFollowing(attacker); + std::list followers = getActorsSidingWith(attacker); MWMechanics::CreatureStats& targetStats = ptr.getClass().getCreatureStats(ptr); if (std::find(followers.begin(), followers.end(), ptr) != followers.end()) { @@ -1435,7 +1435,7 @@ osg::Vec3f observerDir = (observer.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0)); float angleRadians = std::acos(observerDir * vec / (observerDir.length() * vec.length())); - if (angleRadians < osg::DegreesToRadians(90.f)) + if (angleRadians > osg::DegreesToRadians(90.f)) y = obsTerm * observerStats.getFatigueTerm() * fSneakNoViewMult; else y = obsTerm * observerStats.getFatigueTerm() * fSneakViewMult; @@ -1487,6 +1487,11 @@ mActors.getObjectsInRange(position, radius, objects); } + std::list MechanicsManager::getActorsSidingWith(const MWWorld::Ptr& actor) + { + return mActors.getActorsSidingWith(actor); + } + std::list MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor) { return mActors.getActorsFollowing(actor); @@ -1580,4 +1585,112 @@ { return mActors.isReadyToBlock(ptr); } + + void MechanicsManager::setWerewolf(const MWWorld::Ptr& actor, bool werewolf) + { + MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor); + + // The actor does not have to change state + if (npcStats.isWerewolf() == werewolf) + return; + + MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer(); + + if (actor == player->getPlayer()) + { + if (werewolf) + { + player->saveSkillsAttributes(); + player->setWerewolfSkillsAttributes(); + } + else + player->restoreSkillsAttributes(); + } + + // Equipped items other than WerewolfRobe may reference bones that do not even + // exist with the werewolf object root, so make sure to unequip all items + // *before* we become a werewolf. + MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); + invStore.unequipAll(actor); + + // Werewolfs can not cast spells, so we need to unset the prepared spell if there is one. + if (npcStats.getDrawState() == MWMechanics::DrawState_Spell) + npcStats.setDrawState(MWMechanics::DrawState_Nothing); + + npcStats.setWerewolf(werewolf); + + if(werewolf) + { + MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor); + + inv.equip(MWWorld::InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor); + } + else + { + actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); + } + + if(actor == player->getPlayer()) + { + MWBase::Environment::get().getWorld()->reattachPlayerCamera(); + + // Update the GUI only when called on the player + MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); + + if (werewolf) + { + windowManager->forceHide(MWGui::GW_Inventory); + windowManager->forceHide(MWGui::GW_Magic); + } + else + { + windowManager->unsetForceHide(MWGui::GW_Inventory); + windowManager->unsetForceHide(MWGui::GW_Magic); + } + + windowManager->setWerewolfOverlay(werewolf); + + // Witnesses of the player's transformation will make them a globally known werewolf + std::vector closeActors; + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->getFloat(), closeActors); + + bool detected = false, reported = false; + for (std::vector::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it) + { + if (*it == actor) + continue; + + if (!it->getClass().isNpc()) + continue; + + if (MWBase::Environment::get().getWorld()->getLOS(*it, actor) && awarenessCheck(actor, *it)) + detected = true; + if (it->getClass().getCreatureStats(*it).getAiSetting(MWMechanics::CreatureStats::AI_Alarm).getModified() > 0) + reported = true; + } + + if (detected) + { + windowManager->messageBox("#{sWerewolfAlarmMessage}"); + MWBase::Environment::get().getWorld()->setGlobalInt("pcknownwerewolf", 1); + + if (reported) + { + npcStats.setBounty(npcStats.getBounty()+ + gmst.find("iWereWolfBounty")->getInt()); + windowManager->messageBox("#{sCrimeMessage}"); + } + } + } + } + + void MechanicsManager::applyWerewolfAcrobatics(const MWWorld::Ptr &actor) + { + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWMechanics::NpcStats &stats = actor.getClass().getNpcStats(actor); + + stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getInt()); + } + } diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp openmw-0.38.0/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -150,6 +150,7 @@ virtual void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector& objects); virtual void getActorsInRange(const osg::Vec3f &position, float radius, std::vector &objects); + virtual std::list getActorsSidingWith(const MWWorld::Ptr& actor); virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); virtual std::list getActorsFollowingIndices(const MWWorld::Ptr& actor); @@ -186,6 +187,9 @@ /// @return is \a ptr allowed to take/use \a cellref or is it a crime? virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim); + virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf); + virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor); + private: void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/obstacle.cpp openmw-0.38.0/apps/openmw/mwmechanics/obstacle.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/obstacle.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/obstacle.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -44,9 +44,9 @@ return MWWorld::Ptr(); // check interior cells only // Check all the doors in this cell - MWWorld::CellRefList& doors = cell->get(); - MWWorld::CellRefList::List& refList = doors.mList; - MWWorld::CellRefList::List::iterator it = refList.begin(); + const MWWorld::CellRefList& doors = cell->getReadOnlyDoors(); + const MWWorld::CellRefList::List& refList = doors.mList; + MWWorld::CellRefList::List::const_iterator it = refList.begin(); osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); /// TODO: How to check whether the actor is facing a door? Below code is for @@ -59,11 +59,12 @@ /// opposite of the code in World::activateDoor() ::confused:: for (; it != refList.end(); ++it) { - MWWorld::LiveCellRef& ref = *it; + const MWWorld::LiveCellRef& ref = *it; if((pos - ref.mData.getPosition().asVec3()).length2() < minSqr && ref.mData.getPosition().rot[2] == ref.mRef.getPosition().rot[2]) { - return MWWorld::Ptr(&ref, actor.getCell()); // found, stop searching + // FIXME cast + return MWWorld::Ptr(&const_cast &>(ref), actor.getCell()); // found, stop searching } } return MWWorld::Ptr(); // none found diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/spellcasting.cpp openmw-0.38.0/apps/openmw/mwmechanics/spellcasting.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/spellcasting.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/spellcasting.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -124,7 +124,7 @@ } if (spell->mData.mType == ESM::Spell::ST_Power) - return stats.getSpells().canUsePower(spell->mId) ? 100 : 0; + return stats.getSpells().canUsePower(spell) ? 100 : 0; if (spell->mData.mType != ESM::Spell::ST_Spell) return 100; @@ -365,26 +365,6 @@ bool castByPlayer = (!caster.isEmpty() && caster == getPlayer()); - // Try absorbing if it's a spell - // NOTE: Vanilla does this once per effect source instead of adding the % from all sources together, not sure - // if that is worth replicating. - bool absorbed = false; - if (spell && caster != target && target.getClass().isActor()) - { - float absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude(); - absorbed = (Misc::Rng::roll0to99() < absorb); - if (absorbed) - { - const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); - MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( - "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); - // Magicka is increased by cost of spell - DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); - magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); - target.getClass().getCreatureStats(target).setMagicka(magicka); - } - } - for (std::vector::const_iterator effectIt (effects.mList.begin()); effectIt!=effects.mList.end(); ++effectIt) { @@ -404,6 +384,26 @@ && target.getClass().isActor()) MWBase::Environment::get().getWindowManager()->setEnemy(target); + // Try absorbing if it's a spell + // NOTE: Vanilla does this once per spell absorption effect source instead of adding the % from all sources together, not sure + // if that is worth replicating. + bool absorbed = false; + if (spell && caster != target && target.getClass().isActor()) + { + float absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude(); + absorbed = (Misc::Rng::roll0to99() < absorb); + if (absorbed) + { + const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); + MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( + "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); + // Magicka is increased by cost of spell + DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); + magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); + target.getClass().getCreatureStats(target).setMagicka(magicka); + } + } + float magnitudeMult = 1; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor()) { @@ -452,42 +452,18 @@ float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; magnitude *= magnitudeMult; - bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); - if (target.getClass().isActor() && hasDuration && effectIt->mDuration > 0) + if (!target.getClass().isActor()) { - ActiveSpells::ActiveEffect effect; - effect.mEffectId = effectIt->mEffectID; - effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; - effect.mDuration = static_cast(effectIt->mDuration); - effect.mMagnitude = magnitude; - - targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); - - appliedLastingEffects.push_back(effect); - - // For absorb effects, also apply the effect to the caster - but with a negative - // magnitude, since we're transfering stats from the target to the caster - if (!caster.isEmpty() && caster.getClass().isActor()) - { - for (int i=0; i<5; ++i) - { - if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) - { - std::vector effects; - ActiveSpells::ActiveEffect effect_ = effect; - effect_.mMagnitude *= -1; - effects.push_back(effect_); - // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies - caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, - effects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); - } - } - } + // non-actor objects have no list of active magic effects, so have to apply instantly + if (!applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude)) + continue; } - else + else // target.getClass().isActor() == true { - if (hasDuration && target.getClass().isActor()) + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); + if (hasDuration && effectIt->mDuration == 0) { + // duration 0 means apply full magnitude instantly bool wasDead = target.getClass().getCreatureStats(target).isDead(); effectTick(target.getClass().getCreatureStats(target), target, EffectKey(*effectIt), magnitude); bool isDead = target.getClass().getCreatureStats(target).isDead(); @@ -495,8 +471,38 @@ if (!wasDead && isDead) MWBase::Environment::get().getMechanicsManager()->actorKilled(target, caster); } - else if (!applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude)) - continue; + else + { + // add to list of active effects, to apply in next frame + ActiveSpells::ActiveEffect effect; + effect.mEffectId = effectIt->mEffectID; + effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; + effect.mDuration = static_cast(effectIt->mDuration); + effect.mMagnitude = magnitude; + + targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); + + appliedLastingEffects.push_back(effect); + + // For absorb effects, also apply the effect to the caster - but with a negative + // magnitude, since we're transfering stats from the target to the caster + if (!caster.isEmpty() && caster.getClass().isActor()) + { + for (int i=0; i<5; ++i) + { + if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) + { + std::vector effects; + ActiveSpells::ActiveEffect effect_ = effect; + effect_.mMagnitude *= -1; + effects.push_back(effect_); + // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies + caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, + effects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); + } + } + } + } } // Re-casting a summon effect will remove the creature from previous castings of that effect. @@ -823,7 +829,7 @@ // A power can be used once per 24h if (spell->mData.mType == ESM::Spell::ST_Power) - stats.getSpells().usePower(spell->mId); + stats.getSpells().usePower(spell); } if (mCaster == getPlayer() && spellIncreasesSkill(spell)) diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/spells.cpp openmw-0.38.0/apps/openmw/mwmechanics/spells.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/spells.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/spells.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -25,9 +25,24 @@ return mSpells.end(); } + const ESM::Spell* Spells::getSpell(const std::string& id) const + { + return MWBase::Environment::get().getWorld()->getStore().get().find(id); + } + + bool Spells::hasSpell(const std::string &spell) const + { + return hasSpell(getSpell(spell)); + } + + bool Spells::hasSpell(const ESM::Spell *spell) const + { + return mSpells.find(spell) != mSpells.end(); + } + void Spells::add (const ESM::Spell* spell) { - if (mSpells.find (spell->mId)==mSpells.end()) + if (mSpells.find (spell)==mSpells.end()) { std::map random; @@ -48,32 +63,32 @@ corprus.mWorsenings = 0; corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; - mCorprusSpells[spell->mId] = corprus; + mCorprusSpells[spell] = corprus; } - mSpells.insert (std::make_pair (spell->mId, random)); + mSpells.insert (std::make_pair (spell, random)); } } void Spells::add (const std::string& spellId) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - add(spell); + add(getSpell(spellId)); } void Spells::remove (const std::string& spellId) { - std::string lower = Misc::StringUtils::lowerCase(spellId); - TContainer::iterator iter = mSpells.find (lower); - std::map::iterator corprusIt = mCorprusSpells.find(lower); + const ESM::Spell* spell = getSpell(spellId); + TContainer::iterator iter = mSpells.find (spell); + + std::map::iterator corprusIt = mCorprusSpells.find(spell); // if it's corprus, remove negative and keep positive effects if (corprusIt != mCorprusSpells.end()) { - worsenCorprus(lower); - if (mPermanentSpellEffects.find(lower) != mPermanentSpellEffects.end()) + worsenCorprus(spell); + if (mPermanentSpellEffects.find(spell) != mPermanentSpellEffects.end()) { - MagicEffects & effects = mPermanentSpellEffects[lower]; + MagicEffects & effects = mPermanentSpellEffects[spell]; for (MagicEffects::Collection::const_iterator effectIt = effects.begin(); effectIt != effects.end();) { const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->first.mId); @@ -101,8 +116,7 @@ for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); + const ESM::Spell *spell = iter->first; if (spell->mData.mType==ESM::Spell::ST_Ability || spell->mData.mType==ESM::Spell::ST_Blight || spell->mData.mType==ESM::Spell::ST_Disease || spell->mData.mType==ESM::Spell::ST_Curse) @@ -120,7 +134,7 @@ } } - for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) + for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) { effects += it->second; } @@ -145,11 +159,10 @@ bool Spells::isSpellActive(const std::string &id) const { - TContainer::const_iterator found = mSpells.find(id); + TContainer::const_iterator found = mSpells.find(getSpell(id)); if (found != mSpells.end()) { - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (id); + const ESM::Spell *spell = found->first; return (spell->mData.mType==ESM::Spell::ST_Ability || spell->mData.mType==ESM::Spell::ST_Blight || spell->mData.mType==ESM::Spell::ST_Disease || spell->mData.mType==ESM::Spell::ST_Curse); @@ -161,9 +174,7 @@ { for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - + const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Disease) return true; } @@ -175,9 +186,7 @@ { for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - + const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Blight) return true; } @@ -189,9 +198,7 @@ { for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) { - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - + const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Disease) mSpells.erase(iter++); else @@ -203,9 +210,7 @@ { for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) { - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - + const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Blight && !hasCorprusEffect(spell)) mSpells.erase(iter++); else @@ -217,9 +222,7 @@ { for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) { - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - + const ESM::Spell *spell = iter->first; if (hasCorprusEffect(spell)) mSpells.erase(iter++); else @@ -231,9 +234,7 @@ { for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) { - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - + const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Curse) mSpells.erase(iter++); else @@ -245,7 +246,7 @@ { for (TIterator it = begin(); it != end(); ++it) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(it->first); + const ESM::Spell* spell = it->first; // these are the spell types that are permanently in effect if (!(spell->mData.mType == ESM::Spell::ST_Ability) @@ -268,14 +269,13 @@ } } - void Spells::worsenCorprus(const std::string &corpSpellId) + void Spells::worsenCorprus(const ESM::Spell* spell) { - mCorprusSpells[corpSpellId].mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; - mCorprusSpells[corpSpellId].mWorsenings++; + mCorprusSpells[spell].mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; + mCorprusSpells[spell].mWorsenings++; // update worsened effects - mPermanentSpellEffects[corpSpellId] = MagicEffects(); - const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get().find(corpSpellId); + mPermanentSpellEffects[spell] = MagicEffects(); int i=0; for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt, ++i) { @@ -283,12 +283,12 @@ if ((effectIt->mEffectID != ESM::MagicEffect::Corprus) && (magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) // APPLIED_ONCE { float random = 1.f; - if (mSpells[corpSpellId].find(i) != mSpells[corpSpellId].end()) - random = mSpells[corpSpellId].at(i); + if (mSpells[spell].find(i) != mSpells[spell].end()) + random = mSpells[spell].at(i); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; - magnitude *= std::max(1, mCorprusSpells[corpSpellId].mWorsenings); - mPermanentSpellEffects[corpSpellId].add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(magnitude)); + magnitude *= std::max(1, mCorprusSpells[spell].mWorsenings); + mPermanentSpellEffects[spell].add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(magnitude)); } } } @@ -305,43 +305,47 @@ return false; } - const std::map &Spells::getCorprusSpells() const + const std::map &Spells::getCorprusSpells() const { return mCorprusSpells; } - bool Spells::canUsePower(const std::string &power) const + bool Spells::canUsePower(const ESM::Spell* spell) const { - std::map::const_iterator it = mUsedPowers.find(Misc::StringUtils::lowerCase(power)); + std::map::const_iterator it = mUsedPowers.find(spell); if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp()) return true; else return false; } - void Spells::usePower(const std::string &power) + void Spells::usePower(const ESM::Spell* spell) { - mUsedPowers[Misc::StringUtils::lowerCase(power)] = MWBase::Environment::get().getWorld()->getTimeStamp(); + mUsedPowers[spell] = MWBase::Environment::get().getWorld()->getTimeStamp(); } void Spells::readState(const ESM::SpellState &state) { - for (TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) + for (ESM::SpellState::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) { // Discard spells that are no longer available due to changed content files const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); if (spell) { - mSpells[it->first] = it->second; + mSpells[spell] = it->second; if (it->first == state.mSelectedSpell) mSelectedSpell = it->first; } } - // No need to discard spells here (doesn't really matter if non existent ids are kept) for (std::map::const_iterator it = state.mUsedPowers.begin(); it != state.mUsedPowers.end(); ++it) - mUsedPowers[it->first] = MWWorld::TimeStamp(it->second); + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); + if (!spell) + continue; + mUsedPowers[spell] = MWWorld::TimeStamp(it->second); + } for (std::map >::const_iterator it = state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it) @@ -350,33 +354,35 @@ if (!spell) continue; - mPermanentSpellEffects[it->first] = MagicEffects(); + mPermanentSpellEffects[spell] = MagicEffects(); for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) { - mPermanentSpellEffects[it->first].add(EffectKey(effectIt->mId, effectIt->mArg), effectIt->mMagnitude); + mPermanentSpellEffects[spell].add(EffectKey(effectIt->mId, effectIt->mArg), effectIt->mMagnitude); } } mCorprusSpells.clear(); for (std::map::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it) { - if (mSpells.find(it->first) != mSpells.end()) // Discard unavailable corprus spells - { - mCorprusSpells[it->first].mWorsenings = state.mCorprusSpells.at(it->first).mWorsenings; - mCorprusSpells[it->first].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); - } + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); + if (!spell) // Discard unavailable corprus spells + continue; + mCorprusSpells[spell].mWorsenings = state.mCorprusSpells.at(it->first).mWorsenings; + mCorprusSpells[spell].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); } } void Spells::writeState(ESM::SpellState &state) const { - state.mSpells = mSpells; + for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it) + state.mSpells.insert(std::make_pair(it->first->mId, it->second)); + state.mSelectedSpell = mSelectedSpell; - for (std::map::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it) - state.mUsedPowers[it->first] = it->second.toEsm(); + for (std::map::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it) + state.mUsedPowers[it->first->mId] = it->second.toEsm(); - for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) + for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) { std::vector effectList; for (MagicEffects::Collection::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) @@ -388,13 +394,13 @@ effectList.push_back(info); } - state.mPermanentSpellEffects[it->first] = effectList; + state.mPermanentSpellEffects[it->first->mId] = effectList; } - for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) + for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) { - state.mCorprusSpells[it->first].mWorsenings = mCorprusSpells.at(it->first).mWorsenings; - state.mCorprusSpells[it->first].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm(); + state.mCorprusSpells[it->first->mId].mWorsenings = mCorprusSpells.at(it->first).mWorsenings; + state.mCorprusSpells[it->first->mId].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm(); } } } diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/spells.hpp openmw-0.38.0/apps/openmw/mwmechanics/spells.hpp --- openmw-0.37.0/apps/openmw/mwmechanics/spells.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/spells.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -31,7 +31,9 @@ { public: - typedef std::map > TContainer; // ID, + typedef const ESM::Spell* SpellKey; + + typedef std::map > TContainer; // ID, typedef TContainer::const_iterator TIterator; struct CorprusStats @@ -47,23 +49,26 @@ TContainer mSpells; // spell-tied effects that will be applied even after removing the spell (currently used to keep positive effects when corprus is removed) - std::map mPermanentSpellEffects; + std::map mPermanentSpellEffects; // Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different) std::string mSelectedSpell; - std::map mUsedPowers; + std::map mUsedPowers; + + std::map mCorprusSpells; - std::map mCorprusSpells; + /// Get spell from ID, throws exception if not found + const ESM::Spell* getSpell(const std::string& id) const; public: - void worsenCorprus(const std::string &corpSpellId); + void worsenCorprus(const ESM::Spell* spell); static bool hasCorprusEffect(const ESM::Spell *spell); - const std::map & getCorprusSpells() const; + const std::map & getCorprusSpells() const; - bool canUsePower (const std::string& power) const; - void usePower (const std::string& power); + bool canUsePower (const ESM::Spell* spell) const; + void usePower (const ESM::Spell* spell); void purgeCommonDisease(); void purgeBlightDisease(); @@ -74,7 +79,8 @@ TIterator end() const; - bool hasSpell(const std::string& spell) const { return mSpells.find(Misc::StringUtils::lowerCase(spell)) != mSpells.end(); } + bool hasSpell(const std::string& spell) const; + bool hasSpell(const ESM::Spell* spell) const; void add (const std::string& spell); ///< Adding a spell that is already listed in *this is a no-op. diff -Nru openmw-0.37.0/apps/openmw/mwmechanics/summoning.cpp openmw-0.38.0/apps/openmw/mwmechanics/summoning.cpp --- openmw-0.37.0/apps/openmw/mwmechanics/summoning.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwmechanics/summoning.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,5 +1,7 @@ #include "summoning.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -34,7 +36,7 @@ MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, "", ptr.getRefData().getPosition().asVec3()); } - else + else if (creatureActorId != -1) { // We didn't find the creature. It's probably in an inactive cell. // Add to graveyard so we can delete it when the cell becomes active. @@ -132,25 +134,34 @@ if (!creatureID.empty()) { MWWorld::CellStore* store = mActor.getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); - ref.getPtr().getCellRef().setPosition(ipos); - - MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); - - // Make the summoned creature follow its master and help in fights - AiFollow package(mActor.getCellRef().getRefId()); - summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); - int creatureActorId = summonedCreatureStats.getActorId(); + int creatureActorId = -1; + try + { + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); + ref.getPtr().getCellRef().setPosition(ipos); - MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); + MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); - MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); - if (anim) + // Make the summoned creature follow its master and help in fights + AiFollow package(mActor.getCellRef().getRefId()); + summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); + creatureActorId = summonedCreatureStats.getActorId(); + + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); + + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); + if (anim) + { + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_Start"); + if (fx) + anim->addEffect("meshes\\" + fx->mModel, -1, false); + } + } + catch (std::exception& e) { - const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() - .search("VFX_Summon_Start"); - if (fx) - anim->addEffect("meshes\\" + fx->mModel, -1, false); + std::cerr << "Failed to spawn summoned creature: " << e.what() << std::endl; + // still insert into creatureMap so we don't try to spawn again every frame, that would spam the warning log } creatureMap.insert(std::make_pair(*it, creatureActorId)); diff -Nru openmw-0.37.0/apps/openmw/mwphysics/actor.cpp openmw-0.38.0/apps/openmw/mwphysics/actor.cpp --- openmw-0.37.0/apps/openmw/mwphysics/actor.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwphysics/actor.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,12 +1,11 @@ #include "actor.hpp" -#include - #include #include #include -#include +#include +#include #include "../mwworld/class.hpp" @@ -17,7 +16,7 @@ { -Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world) +Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world) : mCanWaterWalk(false), mWalkingOnWater(false) , mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false) , mInternalCollisionMode(true) @@ -77,7 +76,7 @@ mCollisionWorld->removeCollisionObject(mCollisionObject.get()); int collisionMask = CollisionType_World | CollisionType_HeightMap; if (mExternalCollisionMode) - collisionMask |= CollisionType_Actor | CollisionType_Projectile; + collisionMask |= CollisionType_Actor | CollisionType_Projectile | CollisionType_Door; if (mCanWaterWalk) collisionMask |= CollisionType_Water; mCollisionWorld->addCollisionObject(mCollisionObject.get(), CollisionType_Actor, collisionMask); diff -Nru openmw-0.37.0/apps/openmw/mwphysics/actor.hpp openmw-0.38.0/apps/openmw/mwphysics/actor.hpp --- openmw-0.37.0/apps/openmw/mwphysics/actor.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwphysics/actor.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -13,7 +13,7 @@ class btCollisionShape; class btCollisionObject; -namespace NifBullet +namespace Resource { class BulletShapeInstance; } @@ -31,7 +31,12 @@ mPtr = updated; } - MWWorld::Ptr getPtr() const + MWWorld::Ptr getPtr() + { + return mPtr; + } + + MWWorld::ConstPtr getPtr() const { return mPtr; } @@ -43,7 +48,7 @@ class Actor : public PtrHolder { public: - Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world); + Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world); ~Actor(); /** diff -Nru openmw-0.37.0/apps/openmw/mwphysics/collisiontype.hpp openmw-0.38.0/apps/openmw/mwphysics/collisiontype.hpp --- openmw-0.37.0/apps/openmw/mwphysics/collisiontype.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwphysics/collisiontype.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,8 +6,9 @@ enum CollisionType { CollisionType_World = 1<<0, - CollisionType_Actor = 1<<1, - CollisionType_HeightMap = 1<<2, + CollisionType_Door = 1<<1, + CollisionType_Actor = 1<<2, + CollisionType_HeightMap = 1<<3, CollisionType_Projectile = 1<<4, CollisionType_Water = 1<<5 }; diff -Nru openmw-0.37.0/apps/openmw/mwphysics/physicssystem.cpp openmw-0.38.0/apps/openmw/mwphysics/physicssystem.cpp --- openmw-0.37.0/apps/openmw/mwphysics/physicssystem.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwphysics/physicssystem.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -17,11 +16,12 @@ #include #include -#include #include #include +#include #include +#include #include // FindRecIndexVisitor @@ -218,6 +218,7 @@ resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap; collisionWorld->rayTest(from, to, resultCallback1); + if (resultCallback1.hasHit() && ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length() > 30 || getSlope(tracer.mPlaneNormal) > sMaxSlope)) @@ -233,9 +234,8 @@ } static osg::Vec3f move(const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time, - bool isFlying, float waterlevel, float slowFall, btCollisionWorld* collisionWorld - , std::map& collisionTracker - , std::map& standingCollisionTracker) + bool isFlying, float waterlevel, float slowFall, btCollisionWorld* collisionWorld, + std::map& standingCollisionTracker) { const ESM::Position& refpos = ptr.getRefData().getPosition(); osg::Vec3f position(refpos.asVec3()); @@ -344,13 +344,6 @@ newPosition = tracer.mEndPos; // ok to move, so set newPosition break; } - else - { - const btCollisionObject* standingOn = tracer.mHitObject; - const PtrHolder* ptrHolder = static_cast(standingOn->getUserPointer()); - if (ptrHolder) - collisionTracker[ptr] = ptrHolder->getPtr(); - } } else { @@ -416,7 +409,7 @@ && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor) { const btCollisionObject* standingOn = tracer.mHitObject; - const PtrHolder* ptrHolder = static_cast(standingOn->getUserPointer()); + PtrHolder* ptrHolder = static_cast(standingOn->getUserPointer()); if (ptrHolder) standingCollisionTracker[ptr] = ptrHolder->getPtr(); @@ -513,6 +506,9 @@ private: btHeightfieldTerrainShape* mShape; btCollisionObject* mCollisionObject; + + void operator=(const HeightField&); + HeightField(const HeightField&); }; // -------------------------------------------------------------- @@ -520,8 +516,9 @@ class Object : public PtrHolder { public: - Object(const MWWorld::Ptr& ptr, osg::ref_ptr shapeInstance) + Object(const MWWorld::Ptr& ptr, osg::ref_ptr shapeInstance) : mShapeInstance(shapeInstance) + , mSolid(true) { mPtr = ptr; @@ -556,6 +553,22 @@ return mCollisionObject.get(); } + /// Return solid flag. Not used by the object itself, true by default. + bool isSolid() const + { + return mSolid; + } + + void setSolid(bool solid) + { + mSolid = solid; + } + + bool isAnimated() const + { + return !mShapeInstance->mAnimatedShapes.empty(); + } + void animateCollisionShapes(btCollisionWorld* collisionWorld) { if (mShapeInstance->mAnimatedShapes.empty()) @@ -565,12 +578,12 @@ btCompoundShape* compound = dynamic_cast(mShapeInstance->getCollisionShape()); - for (std::map::const_iterator it = mShapeInstance->mAnimatedShapes.begin(); it != mShapeInstance->mAnimatedShapes.end(); ++it) + for (std::map::iterator it = mShapeInstance->mAnimatedShapes.begin(); it != mShapeInstance->mAnimatedShapes.end();) { int recIndex = it->first; int shapeIndex = it->second; - NifOsg::FindRecIndexVisitor visitor(recIndex); + NifOsg::FindGroupByRecIndex visitor(recIndex); mPtr.getRefData().getBaseNode()->accept(visitor); if (!visitor.mFound) { @@ -580,6 +593,24 @@ osg::NodePath path = visitor.mFoundPath; path.erase(path.begin()); + + // Attempt to remove "animated" shapes that are not actually animated + // We may get these because the BulletNifLoader does not know if a .kf file with additional controllers will be attached later on. + // On the first animateCollisionShapes call, we'll consider the graph completely loaded (with extra controllers and what not), + // so now we can better decide if the shape is really animated. + bool animated = false; + for (osg::NodePath::iterator nodePathIt = path.begin(); nodePathIt != path.end(); ++nodePathIt) + { + osg::Node* node = *nodePathIt; + if (node->getUpdateCallback()) + animated = true; + } + if (!animated) + { + mShapeInstance->mAnimatedShapes.erase(it++); + break; + } + osg::Matrixf matrix = osg::computeLocalToWorld(path); osg::Vec3f scale = matrix.getScale(); matrix.orthoNormalize(matrix); @@ -592,6 +623,8 @@ compound->getChildShape(shapeIndex)->setLocalScaling(compound->getLocalScaling() * toBullet(scale)); compound->updateChildTransform(shapeIndex, transform); + + ++it; } collisionWorld->updateSingleAabb(mCollisionObject.get()); @@ -599,13 +632,14 @@ private: std::auto_ptr mCollisionObject; - osg::ref_ptr mShapeInstance; + osg::ref_ptr mShapeInstance; + bool mSolid; }; // --------------------------------------------------------------- PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode) - : mShapeManager(new NifBullet::BulletShapeManager(resourceSystem->getVFS())) + : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager())) , mDebugDrawEnabled(false) , mTimeAccum(0.0f) , mWaterHeight(0) @@ -666,6 +700,35 @@ return mDebugDrawEnabled; } + void PhysicsSystem::markAsNonSolid(const MWWorld::ConstPtr &ptr) + { + ObjectMap::iterator found = mObjects.find(ptr); + if (found == mObjects.end()) + return; + + found->second->setSolid(false); + } + + bool PhysicsSystem::isOnSolidGround (const MWWorld::Ptr& actor) const + { + const Actor* physactor = getActor(actor); + if (!physactor || !physactor->getOnGround()) + return false; + + CollisionMap::const_iterator found = mStandingCollisions.find(actor); + if (found == mStandingCollisions.end()) + return true; // assume standing on terrain (which is a non-object, so not collision tracked) + + ObjectMap::const_iterator foundObj = mObjects.find(found->second); + if (foundObj == mObjects.end()) + return false; + + if (!foundObj->second->isSolid()) + return false; + + return true; + } + class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback { const btCollisionObject* mMe; @@ -710,7 +773,7 @@ } }; - std::pair PhysicsSystem::getHitContact(const MWWorld::Ptr& actor, + std::pair PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orient, float queryDistance) @@ -730,24 +793,54 @@ object.setWorldTransform(btTransform(toBullet(orient), toBullet(center))); const btCollisionObject* me = NULL; - Actor* physactor = getActor(actor); + const Actor* physactor = getActor(actor); if (physactor) me = physactor->getCollisionObject(); DeepestNotMeContactTestResultCallback resultCallback(me, toBullet(origin)); resultCallback.m_collisionFilterGroup = CollisionType_Actor; - resultCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; + resultCallback.m_collisionFilterMask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor; mCollisionWorld->contactTest(&object, resultCallback); if (resultCallback.mObject) { - const PtrHolder* holder = static_cast(resultCallback.mObject->getUserPointer()); + PtrHolder* holder = static_cast(resultCallback.mObject->getUserPointer()); if (holder) return std::make_pair(holder->getPtr(), toOsg(resultCallback.mContactPoint)); } return std::make_pair(MWWorld::Ptr(), osg::Vec3f()); } + float PhysicsSystem::getHitDistance(const osg::Vec3f &point, const MWWorld::ConstPtr &target) const + { + btCollisionObject* targetCollisionObj = NULL; + const Actor* actor = getActor(target); + if (actor) + targetCollisionObj = actor->getCollisionObject(); + if (!targetCollisionObj) + return 0.f; + + btTransform rayFrom; + rayFrom.setIdentity(); + rayFrom.setOrigin(toBullet(point)); + + // target the collision object's world origin, this should be the center of the collision object + btTransform rayTo; + rayTo.setIdentity(); + rayTo.setOrigin(targetCollisionObj->getWorldTransform().getOrigin()); + + btCollisionWorld::ClosestRayResultCallback cb(rayFrom.getOrigin(), rayTo.getOrigin()); + + btCollisionWorld::rayTestSingle(rayFrom, rayTo, targetCollisionObj, targetCollisionObj->getCollisionShape(), targetCollisionObj->getWorldTransform(), cb); + if (!cb.hasHit()) + { + // didn't hit the target. this could happen if point is already inside the collision box + return 0.f; + } + else + return (point - toOsg(cb.m_hitPointWorld)).length(); + } + class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { public: @@ -767,7 +860,7 @@ const btCollisionObject* mMe; }; - PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::Ptr ignore, int mask, int group) + PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore, int mask, int group) const { btVector3 btFrom = toBullet(from); btVector3 btTo = toBullet(to); @@ -775,7 +868,7 @@ const btCollisionObject* me = NULL; if (!ignore.isEmpty()) { - Actor* actor = getActor(ignore); + const Actor* actor = getActor(ignore); if (actor) me = actor->getCollisionObject(); } @@ -802,7 +895,7 @@ { btCollisionWorld::ClosestConvexResultCallback callback(toBullet(from), toBullet(to)); callback.m_collisionFilterGroup = 0xff; - callback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap; + callback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door; btSphereShape shape(radius); const btQuaternion btrot = btQuaternion::getIdentity(); @@ -822,10 +915,10 @@ return result; } - bool PhysicsSystem::getLineOfSight(const MWWorld::Ptr &actor1, const MWWorld::Ptr &actor2) + bool PhysicsSystem::getLineOfSight(const MWWorld::ConstPtr &actor1, const MWWorld::ConstPtr &actor2) const { - Actor* physactor1 = getActor(actor1); - Actor* physactor2 = getActor(actor2); + const Actor* physactor1 = getActor(actor1); + const Actor* physactor2 = getActor(actor2); if (!physactor1 || !physactor2) return false; @@ -833,7 +926,7 @@ osg::Vec3f pos1 (physactor1->getPosition() + osg::Vec3f(0,0,physactor1->getHalfExtents().z() * 0.8)); // eye level osg::Vec3f pos2 (physactor2->getPosition() + osg::Vec3f(0,0,physactor2->getHalfExtents().z() * 0.8)); - RayResult result = castRay(pos1, pos2, MWWorld::Ptr(), CollisionType_World|CollisionType_HeightMap); + RayResult result = castRay(pos1, pos2, MWWorld::Ptr(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door); return !result.mHit; } @@ -875,7 +968,7 @@ } } - osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::Ptr &actor) const + osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); if (physactor) @@ -884,7 +977,7 @@ return osg::Vec3f(); } - osg::Vec3f PhysicsSystem::getRenderingHalfExtents(const MWWorld::Ptr &actor) const + osg::Vec3f PhysicsSystem::getRenderingHalfExtents(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); if (physactor) @@ -893,7 +986,7 @@ return osg::Vec3f(); } - osg::Vec3f PhysicsSystem::getPosition(const MWWorld::Ptr &actor) const + osg::Vec3f PhysicsSystem::getPosition(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); if (physactor) @@ -905,6 +998,13 @@ class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback { public: + ContactTestResultCallback(const btCollisionObject* testedAgainst) + : mTestedAgainst(testedAgainst) + { + } + + const btCollisionObject* mTestedAgainst; + std::vector mResult; #if BT_BULLET_VERSION >= 281 @@ -913,30 +1013,34 @@ const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) { const btCollisionObject* collisionObject = col0Wrap->m_collisionObject; + if (collisionObject == mTestedAgainst) + collisionObject = col1Wrap->m_collisionObject; #else virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0, const btCollisionObject* col1, int partId1, int index1) { const btCollisionObject* collisionObject = col0; + if (collisionObject == mTestedAgainst) + collisionObject = col1; #endif - const PtrHolder* holder = static_cast(collisionObject->getUserPointer()); + PtrHolder* holder = static_cast(collisionObject->getUserPointer()); if (holder) mResult.push_back(holder->getPtr()); return 0.f; } }; - std::vector PhysicsSystem::getCollisions(const MWWorld::Ptr &ptr, int collisionGroup, int collisionMask) + std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const { btCollisionObject* me = NULL; - ObjectMap::iterator found = mObjects.find(ptr); + ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) me = found->second->getCollisionObject(); else return std::vector(); - ContactTestResultCallback resultCallback; + ContactTestResultCallback resultCallback (me); resultCallback.m_collisionFilterGroup = collisionGroup; resultCallback.m_collisionFilterMask = collisionMask; mCollisionWorld->contactTest(me, resultCallback); @@ -972,16 +1076,19 @@ } } - void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh) + void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType) { - osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); - if (!shapeInstance->getCollisionShape()) + osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); + if (!shapeInstance || !shapeInstance->getCollisionShape()) return; Object *obj = new Object(ptr, shapeInstance); mObjects.insert(std::make_pair(ptr, obj)); - mCollisionWorld->addCollisionObject(obj->getCollisionObject(), CollisionType_World, + if (obj->isAnimated()) + mAnimatedObjects.insert(obj); + + mCollisionWorld->addCollisionObject(obj->getCollisionObject(), collisionType, CollisionType_Actor|CollisionType_HeightMap|CollisionType_Projectile); } @@ -991,6 +1098,9 @@ if (found != mObjects.end()) { mCollisionWorld->removeCollisionObject(found->second->getCollisionObject()); + + mAnimatedObjects.erase(found->second); + delete found->second; mObjects.erase(found); } @@ -1039,7 +1149,6 @@ mActors.insert(std::make_pair(updated, actor)); } - updateCollisionMapPtr(mCollisions, old, updated); updateCollisionMapPtr(mStandingCollisions, old, updated); } @@ -1051,7 +1160,7 @@ return NULL; } - const Actor *PhysicsSystem::getActor(const MWWorld::Ptr &ptr) const + const Actor *PhysicsSystem::getActor(const MWWorld::ConstPtr &ptr) const { ActorMap::const_iterator found = mActors.find(ptr); if (found != mActors.end()) @@ -1114,9 +1223,10 @@ } } - void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) - { - osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); + void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) { + osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); + if (!shapeInstance) + return; Actor* actor = new Actor(ptr, shapeInstance, mCollisionWorld); mActors.insert(std::make_pair(ptr, actor)); @@ -1154,7 +1264,6 @@ void PhysicsSystem::clearQueuedMovement() { mMovementQueue.clear(); - mCollisions.clear(); mStandingCollisions.clear(); } @@ -1166,7 +1275,6 @@ if(mTimeAccum >= 1.0f/60.0f) { // Collision events should be available on every frame - mCollisions.clear(); mStandingCollisions.clear(); const MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -1198,9 +1306,9 @@ // Slow fall reduces fall speed by a factor of (effect magnitude / 200) float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f)); - osg::Vec3f newpos = MovementSolver::move(iter->first, physicActor, iter->second, mTimeAccum, + osg::Vec3f newpos = MovementSolver::move(physicActor->getPtr(), physicActor, iter->second, mTimeAccum, world->isFlying(iter->first), - waterlevel, slowFall, mCollisionWorld, mCollisions, mStandingCollisions); + waterlevel, slowFall, mCollisionWorld, mStandingCollisions); float heightDiff = newpos.z() - oldHeight; @@ -1219,8 +1327,8 @@ void PhysicsSystem::stepSimulation(float dt) { - for (ObjectMap::iterator it = mObjects.begin(); it != mObjects.end(); ++it) - it->second->animateCollisionShapes(mCollisionWorld); + for (std::set::iterator it = mAnimatedObjects.begin(); it != mAnimatedObjects.end(); ++it) + (*it)->animateCollisionShapes(mCollisionWorld); CProfileManager::Reset(); CProfileManager::Increment_Frame_Counter(); @@ -1232,7 +1340,7 @@ mDebugDrawer->step(); } - bool PhysicsSystem::isActorStandingOn(const MWWorld::Ptr &actor, const MWWorld::Ptr &object) const + bool PhysicsSystem::isActorStandingOn(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const { for (CollisionMap::const_iterator it = mStandingCollisions.begin(); it != mStandingCollisions.end(); ++it) { @@ -1242,7 +1350,7 @@ return false; } - void PhysicsSystem::getActorsStandingOn(const MWWorld::Ptr &object, std::vector &out) const + void PhysicsSystem::getActorsStandingOn(const MWWorld::ConstPtr &object, std::vector &out) const { for (CollisionMap::const_iterator it = mStandingCollisions.begin(); it != mStandingCollisions.end(); ++it) { @@ -1251,23 +1359,16 @@ } } - bool PhysicsSystem::isActorCollidingWith(const MWWorld::Ptr &actor, const MWWorld::Ptr &object) const + bool PhysicsSystem::isActorCollidingWith(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const { - for (CollisionMap::const_iterator it = mCollisions.begin(); it != mCollisions.end(); ++it) - { - if (it->first == actor && it->second == object) - return true; - } - return false; + std::vector collisions = getCollisions(object, CollisionType_World, CollisionType_Actor); + return (std::find(collisions.begin(), collisions.end(), actor) != collisions.end()); } - void PhysicsSystem::getActorsCollidingWith(const MWWorld::Ptr &object, std::vector &out) const + void PhysicsSystem::getActorsCollidingWith(const MWWorld::ConstPtr &object, std::vector &out) const { - for (CollisionMap::const_iterator it = mCollisions.begin(); it != mCollisions.end(); ++it) - { - if (it->second == object) - out.push_back(it->first); - } + std::vector collisions = getCollisions(object, CollisionType_World, CollisionType_Actor); + out.insert(out.end(), collisions.begin(), collisions.end()); } void PhysicsSystem::disableWater() diff -Nru openmw-0.37.0/apps/openmw/mwphysics/physicssystem.hpp openmw-0.38.0/apps/openmw/mwphysics/physicssystem.hpp --- openmw-0.37.0/apps/openmw/mwphysics/physicssystem.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwphysics/physicssystem.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -21,7 +22,7 @@ class DebugDrawer; } -namespace NifBullet +namespace Resource { class BulletShapeManager; } @@ -56,13 +57,13 @@ void setWaterHeight(float height); void disableWater(); - void addObject (const MWWorld::Ptr& ptr, const std::string& mesh); + void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World); void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated); Actor* getActor(const MWWorld::Ptr& ptr); - const Actor* getActor(const MWWorld::Ptr& ptr) const; + const Actor* getActor(const MWWorld::ConstPtr& ptr) const; // Object or Actor void remove (const MWWorld::Ptr& ptr); @@ -81,14 +82,21 @@ void stepSimulation(float dt); void debugDraw(); - std::vector getCollisions(const MWWorld::Ptr &ptr, int collisionGroup, int collisionMask); ///< get handles this object collides with + std::vector getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with osg::Vec3f traceDown(const MWWorld::Ptr &ptr, float maxHeight); - std::pair getHitContact(const MWWorld::Ptr& actor, + std::pair getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orientation, float queryDistance); + + /// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the + /// target vector hits the collision shape and then calculates distance from the intersection point. + /// This can be used to find out how much nearer we need to move to the target for a "getHitContact" to be successful. + /// \note Only Actor targets are supported at the moment. + float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const; + struct RayResult { bool mHit; @@ -98,25 +106,25 @@ }; /// @param me Optional, a Ptr to ignore in the list of results - RayResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::Ptr ignore = MWWorld::Ptr(), int mask = - CollisionType_World|CollisionType_HeightMap|CollisionType_Actor, int group=0xff); + RayResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore = MWWorld::ConstPtr(), int mask = + CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const; RayResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius); /// Return true if actor1 can see actor2. - bool getLineOfSight(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2); + bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const; bool isOnGround (const MWWorld::Ptr& actor); /// Get physical half extents (scaled) of the given actor. - osg::Vec3f getHalfExtents(const MWWorld::Ptr& actor) const; + osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const; /// @see MWPhysics::Actor::getRenderingHalfExtents - osg::Vec3f getRenderingHalfExtents(const MWWorld::Ptr& actor) const; + osg::Vec3f getRenderingHalfExtents(const MWWorld::ConstPtr& actor) const; /// Get the position of the collision shape for the actor. Use together with getHalfExtents() to get the collision bounds in world space. /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. - osg::Vec3f getPosition(const MWWorld::Ptr& actor) const; + osg::Vec3f getPosition(const MWWorld::ConstPtr& actor) const; /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. @@ -131,20 +139,26 @@ /// Return true if \a actor has been standing on \a object in this frame /// This will trigger whenever the object is directly below the actor. /// It doesn't matter if the actor is stationary or moving. - bool isActorStandingOn(const MWWorld::Ptr& actor, const MWWorld::Ptr& object) const; + bool isActorStandingOn(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const; /// Get the handle of all actors standing on \a object in this frame. - void getActorsStandingOn(const MWWorld::Ptr& object, std::vector& out) const; + void getActorsStandingOn(const MWWorld::ConstPtr& object, std::vector& out) const; /// Return true if \a actor has collided with \a object in this frame. /// This will detect running into objects, but will not detect climbing stairs, stepping up a small object, etc. - bool isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::Ptr& object) const; + bool isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const; /// Get the handle of all actors colliding with \a object in this frame. - void getActorsCollidingWith(const MWWorld::Ptr& object, std::vector& out) const; + void getActorsCollidingWith(const MWWorld::ConstPtr& object, std::vector& out) const; bool toggleDebugRendering(); + /// Mark the given object as a 'non-solid' object. A non-solid object means that + /// \a isOnSolidGround will return false for actors standing on that object. + void markAsNonSolid (const MWWorld::ConstPtr& ptr); + + bool isOnSolidGround (const MWWorld::Ptr& actor) const; + private: void updateWater(); @@ -154,12 +168,14 @@ btCollisionDispatcher* mDispatcher; btCollisionWorld* mCollisionWorld; - std::auto_ptr mShapeManager; + std::auto_ptr mShapeManager; - typedef std::map ObjectMap; + typedef std::map ObjectMap; ObjectMap mObjects; - typedef std::map ActorMap; + std::set mAnimatedObjects; // stores pointers to elements in mObjects + + typedef std::map ActorMap; ActorMap mActors; typedef std::map, HeightField*> HeightFieldMap; @@ -167,11 +183,9 @@ bool mDebugDrawEnabled; - // Tracks all movement collisions happening during a single frame. - // This will detect e.g. running against a vertical wall. It will not detect climbing up stairs, - // stepping up small objects, etc. + // Tracks standing collisions happening during a single frame. + // This will detect standing on an object, but won't detect running e.g. against a wall. typedef std::map CollisionMap; - CollisionMap mCollisions; CollisionMap mStandingCollisions; // replaces all occurences of 'old' in the map by 'updated', no matter if its a key or value diff -Nru openmw-0.37.0/apps/openmw/mwrender/animation.cpp openmw-0.38.0/apps/openmw/mwrender/animation.cpp --- openmw-0.37.0/apps/openmw/mwrender/animation.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/animation.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,6 +19,7 @@ #include #include +#include #include #include // KeyframeHolder @@ -32,6 +33,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -310,6 +312,7 @@ Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem) : mInsert(parentNode) + , mSkeleton(NULL) , mPtr(ptr) , mResourceSystem(resourceSystem) , mAccumulate(1.f, 1.f, 0.f) @@ -337,10 +340,8 @@ void Animation::setActive(bool active) { - if (SceneUtil::Skeleton* skel = dynamic_cast(mObjectRoot.get())) - { - skel->setActive(active); - } + if (mSkeleton) + mSkeleton->setActive(active); } void Animation::updatePtr(const MWWorld::Ptr &ptr) @@ -390,19 +391,21 @@ void Animation::addAnimSource(const std::string &model) { std::string kfname = model; - Misc::StringUtils::toLower(kfname); + Misc::StringUtils::lowerCaseInPlace(kfname); if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) kfname.replace(kfname.size()-4, 4, ".kf"); + else + return; if(!mResourceSystem->getVFS()->exists(kfname)) return; boost::shared_ptr animsrc; animsrc.reset(new AnimSource); - animsrc->mKeyframes = mResourceSystem->getSceneManager()->getKeyframes(kfname); + animsrc->mKeyframes = mResourceSystem->getKeyframeManager()->get(kfname); - if (animsrc->mKeyframes->mTextKeys.empty() || animsrc->mKeyframes->mKeyframeControllers.empty()) + if (!animsrc->mKeyframes || animsrc->mKeyframes->mTextKeys.empty() || animsrc->mKeyframes->mKeyframeControllers.empty()) return; for (NifOsg::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin(); @@ -515,7 +518,16 @@ } if (mTextKeyListener) - mTextKeyListener->handleTextKey(groupname, key, map); + { + try + { + mTextKeyListener->handleTextKey(groupname, key, map); + } + catch (std::exception& e) + { + std::cerr << "Error handling text key " << evt << ": " << e.what() << std::endl; + } + } } void Animation::play(const std::string &groupname, const AnimPriority& priority, int blendMask, bool autodisable, float speedmult, @@ -964,6 +976,7 @@ mObjectRoot->getParent(0)->removeChild(mObjectRoot); } mObjectRoot = NULL; + mSkeleton = NULL; mNodeMap.clear(); mActiveControllers.clear(); @@ -971,18 +984,29 @@ mAccumCtrl = NULL; if (!forceskeleton) - mObjectRoot = mResourceSystem->getSceneManager()->createInstance(model, mInsert); + { + osg::ref_ptr created = mResourceSystem->getSceneManager()->createInstance(model, mInsert); + mObjectRoot = created->asGroup(); + if (!mObjectRoot) + { + mInsert->removeChild(created); + mObjectRoot = new osg::Group; + mObjectRoot->addChild(created); + mInsert->addChild(mObjectRoot); + } + } else { - osg::ref_ptr newObjectRoot = mResourceSystem->getSceneManager()->createInstance(model); - if (!dynamic_cast(newObjectRoot.get())) + osg::ref_ptr created = mResourceSystem->getSceneManager()->createInstance(model); + osg::ref_ptr skel = dynamic_cast(created.get()); + if (!skel) { - osg::ref_ptr skel = new SceneUtil::Skeleton; - skel->addChild(newObjectRoot); - newObjectRoot = skel; + skel = new SceneUtil::Skeleton; + skel->addChild(created); } - mInsert->addChild(newObjectRoot); - mObjectRoot = newObjectRoot; + mSkeleton = skel.get(); + mObjectRoot = skel; + mInsert->addChild(mObjectRoot); } if (previousStateset) @@ -1011,17 +1035,17 @@ osg::Group* Animation::getObjectRoot() { - return static_cast(mObjectRoot.get()); + return mObjectRoot.get(); } osg::Group* Animation::getOrCreateObjectRoot() { if (mObjectRoot) - return static_cast(mObjectRoot.get()); + return mObjectRoot.get(); mObjectRoot = new osg::Group; mInsert->addChild(mObjectRoot); - return static_cast(mObjectRoot.get()); + return mObjectRoot.get(); } void Animation::addGlow(osg::ref_ptr node, osg::Vec4f glowColor) @@ -1086,8 +1110,7 @@ } osg::ref_ptr lightSource = new SceneUtil::LightSource; - osg::Light* light = new osg::Light; - lightSource->setLight(light); + osg::ref_ptr light (new osg::Light); lightSource->setNodeMask(Mask_Lighting); const MWWorld::Fallback* fallback = MWBase::Environment::get().getWorld()->getFallback(); @@ -1118,6 +1141,8 @@ light->setAmbient(osg::Vec4f(0,0,0,1)); light->setSpecular(osg::Vec4f(0,0,0,0)); + lightSource->setLight(light); + osg::ref_ptr ctrl (new SceneUtil::LightController); ctrl->setDiffuse(light->getDiffuse()); if (esmLight->mData.mFlags & ESM::Light::Flicker) @@ -1313,22 +1338,31 @@ } else { - if (!mGlowLight) + effect += 3; + float radius = effect * 66.f; + float linearAttenuation = 0.5f / effect; + + if (!mGlowLight || linearAttenuation != mGlowLight->getLight(0)->getLinearAttenuation()) { - mGlowLight = new SceneUtil::LightSource; - mGlowLight->setLight(new osg::Light); - mGlowLight->setNodeMask(Mask_Lighting); - osg::Light* light = mGlowLight->getLight(); + if (mGlowLight) + { + mInsert->removeChild(mGlowLight); + mGlowLight = NULL; + } + + osg::ref_ptr light (new osg::Light); light->setDiffuse(osg::Vec4f(0,0,0,0)); light->setSpecular(osg::Vec4f(0,0,0,0)); light->setAmbient(osg::Vec4f(1.5f,1.5f,1.5f,1.f)); + light->setLinearAttenuation(linearAttenuation); + + mGlowLight = new SceneUtil::LightSource; + mGlowLight->setNodeMask(Mask_Lighting); mInsert->addChild(mGlowLight); + mGlowLight->setLight(light); } - effect += 3; - osg::Light* light = mGlowLight->getLight(); - mGlowLight->setRadius(effect * 66.f); - light->setLinearAttenuation(0.5f/effect); + mGlowLight->setRadius(radius); } } diff -Nru openmw-0.37.0/apps/openmw/mwrender/animation.hpp openmw-0.38.0/apps/openmw/mwrender/animation.hpp --- openmw-0.37.0/apps/openmw/mwrender/animation.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/animation.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -24,6 +24,7 @@ namespace SceneUtil { class LightSource; + class Skeleton; } namespace MWRender @@ -207,7 +208,8 @@ osg::ref_ptr mInsert; - osg::ref_ptr mObjectRoot; + osg::ref_ptr mObjectRoot; + SceneUtil::Skeleton* mSkeleton; // The node expected to accumulate movement during movement animations. osg::ref_ptr mAccumRoot; diff -Nru openmw-0.37.0/apps/openmw/mwrender/bulletdebugdraw.cpp openmw-0.38.0/apps/openmw/mwrender/bulletdebugdraw.cpp --- openmw-0.37.0/apps/openmw/mwrender/bulletdebugdraw.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/bulletdebugdraw.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -78,6 +78,7 @@ mWorld->debugDrawWorld(); mDrawArrays->setCount(mVertices->size()); mVertices->dirty(); + mGeometry->dirtyBound(); } } diff -Nru openmw-0.37.0/apps/openmw/mwrender/camera.cpp openmw-0.38.0/apps/openmw/mwrender/camera.cpp --- openmw-0.37.0/apps/openmw/mwrender/camera.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/camera.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,8 +1,9 @@ #include "camera.hpp" -#include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -378,7 +379,7 @@ else { mAnimation->setViewMode(NpcAnimation::VM_Normal); - osg::PositionAttitudeTransform* transform = mTrackingPtr.getRefData().getBaseNode(); + SceneUtil::PositionAttitudeTransform* transform = mTrackingPtr.getRefData().getBaseNode(); mTrackingNode = transform; if (transform) mHeightScale = transform->getScale().z(); diff -Nru openmw-0.37.0/apps/openmw/mwrender/characterpreview.cpp openmw-0.38.0/apps/openmw/mwrender/characterpreview.cpp --- openmw-0.37.0/apps/openmw/mwrender/characterpreview.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/characterpreview.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -30,6 +30,7 @@ public: DrawOnceCallback () : mRendered(false) + , mLastRenderedFrame(0) { } @@ -38,13 +39,14 @@ if (!mRendered) { mRendered = true; + + mLastRenderedFrame = nv->getTraversalNumber(); + traverse(node, nv); } else { node->setNodeMask(0); } - - traverse(node, nv); } void redrawNextFrame() @@ -52,8 +54,14 @@ mRendered = false; } + unsigned int getLastRenderedFrame() const + { + return mLastRenderedFrame; + } + private: bool mRendered; + unsigned int mLastRenderedFrame; }; CharacterPreview::CharacterPreview(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem, @@ -261,9 +269,19 @@ int InventoryPreview::getSlotSelected (int posX, int posY) { - osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, posX, posY)); - intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_ONE); + float projX = (posX / mCamera->getViewport()->width()) * 2 - 1.f; + float projY = (posY / mCamera->getViewport()->height()) * 2 - 1.f; + // With Intersector::WINDOW, the intersection ratios are slightly inaccurate. Seems to be a + // precision issue - compiling with OSG_USE_FLOAT_MATRIX=0, Intersector::WINDOW works ok. + // Using Intersector::PROJECTION results in better precision because the start/end points and the model matrices + // don't go through as many transformations. + osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::PROJECTION, projX, projY)); + + intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST); osgUtil::IntersectionVisitor visitor(intersector); + visitor.setTraversalMode(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); + // Set the traversal number from the last draw, so that the frame switch used for RigGeometry double buffering works correctly + visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame()); osg::Node::NodeMask nodeMask = mCamera->getNodeMask(); mCamera->setNodeMask(~0); diff -Nru openmw-0.37.0/apps/openmw/mwrender/characterpreview.hpp openmw-0.38.0/apps/openmw/mwrender/characterpreview.hpp --- openmw-0.37.0/apps/openmw/mwrender/characterpreview.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/characterpreview.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,6 +4,8 @@ #include #include +#include + #include #include diff -Nru openmw-0.37.0/apps/openmw/mwrender/creatureanimation.cpp openmw-0.38.0/apps/openmw/mwrender/creatureanimation.cpp --- openmw-0.37.0/apps/openmw/mwrender/creatureanimation.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/creatureanimation.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -2,12 +2,11 @@ #include -#include - #include #include #include #include +#include #include "../mwbase/world.hpp" diff -Nru openmw-0.37.0/apps/openmw/mwrender/localmap.cpp openmw-0.38.0/apps/openmw/mwrender/localmap.cpp --- openmw-0.37.0/apps/openmw/mwrender/localmap.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/localmap.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff -Nru openmw-0.37.0/apps/openmw/mwrender/npcanimation.cpp openmw-0.38.0/apps/openmw/mwrender/npcanimation.cpp --- openmw-0.37.0/apps/openmw/mwrender/npcanimation.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/npcanimation.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -5,6 +5,7 @@ #include #include +#include #include @@ -274,13 +275,15 @@ mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr); } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, bool disableListener, bool disableSounds, ViewMode viewMode) +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, + bool disableListener, bool disableSounds, ViewMode viewMode, float firstPersonFieldOfView) : Animation(ptr, parentNode, resourceSystem), mListenerDisabled(disableListener), mViewMode(viewMode), mShowWeapons(false), mShowCarriedLeft(true), mNpcType(Type_Normal), + mFirstPersonFieldOfView(firstPersonFieldOfView), mSoundsDisabled(disableSounds), mAccurateAiming(false), mAimingFactor(0.f) @@ -336,6 +339,37 @@ osg::ref_ptr mDepth; }; +/// Overrides Field of View to given value for rendering the subgraph. +/// Must be added as cull callback. +class OverrideFieldOfViewCallback : public osg::NodeCallback +{ +public: + OverrideFieldOfViewCallback(float fov) + : mFov(fov) + { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osgUtil::CullVisitor* cv = static_cast(nv); + osg::RefMatrix* projectionMatrix = new osg::RefMatrix(*cv->getProjectionMatrix()); + float fov, aspect, zNear, zFar; + if (projectionMatrix->getPerspective(fov, aspect, zNear, zFar)) + { + fov = mFov; + projectionMatrix->makePerspective(fov, aspect, zNear, zFar); + cv->pushProjectionMatrix(projectionMatrix); + traverse(node, nv); + cv->popProjectionMatrix(); + } + else + traverse(node, nv); + } + +private: + float mFov; +}; + void NpcAnimation::setRenderBin() { if (mViewMode == VM_FirstPerson) @@ -445,6 +479,7 @@ else { mObjectRoot->setNodeMask(Mask_FirstPerson); + mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView)); if(isWerewolf) addAnimSource(smodel); else diff -Nru openmw-0.37.0/apps/openmw/mwrender/npcanimation.hpp openmw-0.38.0/apps/openmw/mwrender/npcanimation.hpp --- openmw-0.37.0/apps/openmw/mwrender/npcanimation.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/npcanimation.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -61,6 +61,8 @@ int mPartPriorities[ESM::PRT_Count]; osg::Vec3f mFirstPersonOffset; + // Field of view to use when rendering first person meshes + float mFirstPersonFieldOfView; boost::shared_ptr mHeadAnimationTime; boost::shared_ptr mWeaponAnimationTime; @@ -102,7 +104,7 @@ * @param viewMode */ NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, bool disableListener = false, - bool disableSounds = false, ViewMode viewMode=VM_Normal); + bool disableSounds = false, ViewMode viewMode=VM_Normal, float firstPersonFieldOfView=55.f); virtual ~NpcAnimation(); virtual void enableHeadAnimation(bool enable); diff -Nru openmw-0.37.0/apps/openmw/mwrender/objects.cpp openmw-0.38.0/apps/openmw/mwrender/objects.cpp --- openmw-0.37.0/apps/openmw/mwrender/objects.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/objects.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -14,6 +13,7 @@ #include #include +#include #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" @@ -116,7 +116,7 @@ else cellnode = found->second; - osg::ref_ptr insert (new osg::PositionAttitudeTransform); + osg::ref_ptr insert (new SceneUtil::PositionAttitudeTransform); cellnode->addChild(insert); insert->getOrCreateUserDataContainer()->addUserObject(new PtrHolder(ptr)); @@ -258,6 +258,15 @@ { PtrAnimationMap::const_iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) + return iter->second; + + return NULL; +} + +const Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const +{ + PtrAnimationMap::const_iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) return iter->second; return NULL; diff -Nru openmw-0.37.0/apps/openmw/mwrender/objects.hpp openmw-0.38.0/apps/openmw/mwrender/objects.hpp --- openmw-0.37.0/apps/openmw/mwrender/objects.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/objects.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -57,7 +57,7 @@ }; class Objects{ - typedef std::map PtrAnimationMap; + typedef std::map PtrAnimationMap; typedef std::map > CellMap; CellMap mCellSceneNodes; @@ -81,6 +81,7 @@ void insertCreature (const MWWorld::Ptr& ptr, const std::string& model, bool weaponsShields); Animation* getAnimation(const MWWorld::Ptr &ptr); + const Animation* getAnimation(const MWWorld::ConstPtr &ptr) const; bool removeObject (const MWWorld::Ptr& ptr); ///< \return found? diff -Nru openmw-0.37.0/apps/openmw/mwrender/renderingmanager.cpp openmw-0.38.0/apps/openmw/mwrender/renderingmanager.cpp --- openmw-0.37.0/apps/openmw/mwrender/renderingmanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/renderingmanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -26,6 +25,7 @@ #include #include #include +#include #include @@ -136,6 +136,8 @@ , mUnderwaterFog(0.f) , mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) + , mFieldOfViewOverride(0.f) + , mFieldOfViewOverridden(false) { resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); @@ -205,7 +207,8 @@ mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); - mFieldOfView = Settings::Manager::getFloat("field of view", "General"); + mFieldOfView = Settings::Manager::getFloat("field of view", "Camera"); + mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera"); updateProjectionMatrix(); mStateUpdater->setFogEnd(mViewDistance); @@ -227,6 +230,11 @@ return mResourceSystem; } + osg::Group* RenderingManager::getLightRoot() + { + return mLightRoot.get(); + } + void RenderingManager::setNightEyeFactor(float factor) { if (factor != mNightEyeFactor) @@ -387,6 +395,7 @@ osg::Vec3f focal, cameraPos; mCamera->getPosition(focal, cameraPos); + mCurrentCameraPos = cameraPos; if (mWater->isUnderwater(cameraPos)) { setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); @@ -697,16 +706,19 @@ return mObjects->getAnimation(ptr); } - MWRender::Animation* RenderingManager::getPlayerAnimation() + const MWRender::Animation* RenderingManager::getAnimation(const MWWorld::ConstPtr &ptr) const { - return mPlayerAnimation.get(); + if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr()) + return mPlayerAnimation.get(); + + return mObjects->getAnimation(ptr); } void RenderingManager::setupPlayer(const MWWorld::Ptr &player) { if (!mPlayerNode) { - mPlayerNode = new osg::PositionAttitudeTransform; + mPlayerNode = new SceneUtil::PositionAttitudeTransform; mPlayerNode->setNodeMask(Mask_Player); mLightRoot->addChild(mPlayerNode); } @@ -721,7 +733,8 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &player) { - mPlayerAnimation.reset(new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0)); + mPlayerAnimation.reset(new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, false, NpcAnimation::VM_Normal, + mFirstPersonFieldOfView)); mCamera->setAnimation(mPlayerAnimation.get()); mCamera->attachTo(player); @@ -755,25 +768,29 @@ mWater->removeEmitter(ptr); } + void RenderingManager::emitWaterRipple(const osg::Vec3f &pos) + { + mWater->emitRipple(pos); + } + void RenderingManager::updateProjectionMatrix() { double aspect = mViewer->getCamera()->getViewport()->aspectRatio(); - mViewer->getCamera()->setProjectionMatrixAsPerspective(mFieldOfView, aspect, mNearClip, mViewDistance); + float fov = mFieldOfView; + if (mFieldOfViewOverridden) + fov = mFieldOfViewOverride; + mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); } void RenderingManager::updateTextureFiltering() { - osg::Texture::FilterMode min = osg::Texture::LINEAR_MIPMAP_NEAREST; - osg::Texture::FilterMode mag = osg::Texture::LINEAR; - - if (Settings::Manager::getString("texture filtering", "General") == "trilinear") - min = osg::Texture::LINEAR_MIPMAP_LINEAR; - - int maxAnisotropy = Settings::Manager::getInt("anisotropy", "General"); - - mViewer->stopThreading(); - mResourceSystem->getTextureManager()->setFilterSettings(min, mag, maxAnisotropy); - mViewer->startThreading(); + mResourceSystem->getTextureManager()->setFilterSettings( + Settings::Manager::getString("texture mag filter", "General"), + Settings::Manager::getString("texture min filter", "General"), + Settings::Manager::getString("texture mipmap", "General"), + Settings::Manager::getInt("anisotropy", "General"), + mViewer + ); } void RenderingManager::updateAmbient() @@ -797,9 +814,9 @@ { for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) { - if (it->first == "General" && it->second == "field of view") + if (it->first == "Camera" && it->second == "field of view") { - mFieldOfView = Settings::Manager::getFloat("field of view", "General"); + mFieldOfView = Settings::Manager::getFloat("field of view", "Camera"); updateProjectionMatrix(); } else if (it->first == "Camera" && it->second == "viewing distance") @@ -808,7 +825,9 @@ mStateUpdater->setFogEnd(mViewDistance); updateProjectionMatrix(); } - else if (it->first == "General" && (it->second == "texture filtering" || it->second == "anisotropy")) + else if (it->first == "General" && (it->second == "texture filter" || + it->second == "texture mipmap" || + it->second == "anisotropy")) updateTextureFiltering(); else if (it->first == "Water") mWater->processChangedSettings(changed); @@ -865,6 +884,11 @@ return mCamera.get(); } + const osg::Vec3f &RenderingManager::getCameraPosition() const + { + return mCurrentCameraPos; + } + void RenderingManager::togglePOV() { mCamera->toggleViewMode(); @@ -896,4 +920,23 @@ mCamera->setCameraDistance(-factor/120.f*10, true, true); } + void RenderingManager::overrideFieldOfView(float val) + { + if (mFieldOfViewOverridden != true || mFieldOfViewOverride != val) + { + mFieldOfViewOverridden = true; + mFieldOfViewOverride = val; + updateProjectionMatrix(); + } + } + + void RenderingManager::resetFieldOfView() + { + if (mFieldOfViewOverridden == true) + { + mFieldOfViewOverridden = false; + updateProjectionMatrix(); + } + } + } diff -Nru openmw-0.37.0/apps/openmw/mwrender/renderingmanager.hpp openmw-0.38.0/apps/openmw/mwrender/renderingmanager.hpp --- openmw-0.37.0/apps/openmw/mwrender/renderingmanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/renderingmanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -65,6 +65,8 @@ Resource::ResourceSystem* getResourceSystem(); + osg::Group* getLightRoot(); + void setNightEyeFactor(float factor); void setAmbientColour(const osg::Vec4f& colour); @@ -134,10 +136,11 @@ void update(float dt, bool paused); Animation* getAnimation(const MWWorld::Ptr& ptr); - Animation* getPlayerAnimation(); + const Animation* getAnimation(const MWWorld::ConstPtr& ptr) const; void addWaterRippleEmitter(const MWWorld::Ptr& ptr); void removeWaterRippleEmitter(const MWWorld::Ptr& ptr); + void emitWaterRipple(const osg::Vec3f& pos); void updatePlayerPtr(const MWWorld::Ptr &ptr); @@ -159,6 +162,7 @@ void resetCamera(); float getCameraDistance() const; Camera* getCamera(); + const osg::Vec3f& getCameraPosition() const; void togglePOV(); void togglePreviewMode(bool enable); bool toggleVanityMode(bool enable); @@ -166,6 +170,11 @@ void togglePlayerLooking(bool enable); void changeVanityModeScale(float factor); + /// temporarily override the field of view with given value. + void overrideFieldOfView(float val); + /// reset a previous overrideFieldOfView() call, i.e. revert to field of view specified in the settings file. + void resetFieldOfView(); + private: void updateProjectionMatrix(); void updateTextureFiltering(); @@ -186,8 +195,9 @@ std::auto_ptr mSky; std::auto_ptr mEffectManager; std::auto_ptr mPlayerAnimation; - osg::ref_ptr mPlayerNode; + osg::ref_ptr mPlayerNode; std::auto_ptr mCamera; + osg::Vec3f mCurrentCameraPos; osg::ref_ptr mStateUpdater; @@ -203,7 +213,10 @@ float mNearClip; float mViewDistance; + float mFieldOfViewOverride; + bool mFieldOfViewOverridden; float mFieldOfView; + float mFirstPersonFieldOfView; void operator = (const RenderingManager&); RenderingManager(const RenderingManager&); diff -Nru openmw-0.37.0/apps/openmw/mwrender/ripplesimulation.cpp openmw-0.38.0/apps/openmw/mwrender/ripplesimulation.cpp --- openmw-0.37.0/apps/openmw/mwrender/ripplesimulation.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/ripplesimulation.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -127,7 +127,6 @@ } osg::Vec3f currentPos (it->mPtr.getRefData().getPosition().asVec3()); - currentPos.z() = 0; // Z is set by the Scene Node if ( (currentPos - it->mLastEmitPosition).length() > 10 // Only emit when close to the water surface, not above it and not too deep in the water @@ -136,18 +135,18 @@ { it->mLastEmitPosition = currentPos; + currentPos.z() = mParticleNode->getPosition().z(); + if (mParticleSystem->numParticles()-mParticleSystem->numDeadParticles() > 500) continue; // TODO: remove the oldest particle to make room? - osgParticle::Particle* p = mParticleSystem->createParticle(NULL); - p->setPosition(currentPos); - p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI)); + emitRipple(currentPos); } } } -void RippleSimulation::addEmitter(const MWWorld::Ptr& ptr, float scale, float force) +void RippleSimulation::addEmitter(const MWWorld::ConstPtr& ptr, float scale, float force) { Emitter newEmitter; newEmitter.mPtr = ptr; @@ -157,7 +156,7 @@ mEmitters.push_back (newEmitter); } -void RippleSimulation::removeEmitter (const MWWorld::Ptr& ptr) +void RippleSimulation::removeEmitter (const MWWorld::ConstPtr& ptr) { for (std::vector::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it) { @@ -169,7 +168,7 @@ } } -void RippleSimulation::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr) +void RippleSimulation::updateEmitterPtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& ptr) { for (std::vector::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it) { @@ -185,7 +184,7 @@ { for (std::vector::iterator it = mEmitters.begin(); it != mEmitters.end();) { - if (it->mPtr.getCell() == store && it->mPtr != MWMechanics::getPlayer()) + if ((it->mPtr.isInCell() && it->mPtr.getCell() == store) && it->mPtr != MWMechanics::getPlayer()) { it = mEmitters.erase(it); } @@ -194,6 +193,16 @@ } } +void RippleSimulation::emitRipple(const osg::Vec3f &pos) +{ + if (std::abs(pos.z() - mParticleNode->getPosition().z()) < 20) + { + osgParticle::Particle* p = mParticleSystem->createParticle(NULL); + p->setPosition(osg::Vec3f(pos.x(), pos.y(), 0.f)); + p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI)); + } +} + void RippleSimulation::setWaterHeight(float height) { mParticleNode->setPosition(osg::Vec3f(0,0,height)); diff -Nru openmw-0.37.0/apps/openmw/mwrender/ripplesimulation.hpp openmw-0.38.0/apps/openmw/mwrender/ripplesimulation.hpp --- openmw-0.37.0/apps/openmw/mwrender/ripplesimulation.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/ripplesimulation.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,6 +8,7 @@ namespace osg { class Group; + class PositionAttitudeTransform; } namespace osgParticle @@ -30,7 +31,7 @@ struct Emitter { - MWWorld::Ptr mPtr; + MWWorld::ConstPtr mPtr; osg::Vec3f mLastEmitPosition; float mScale; float mForce; @@ -46,11 +47,13 @@ void update(float dt); /// adds an emitter, position will be tracked automatically - void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f); - void removeEmitter (const MWWorld::Ptr& ptr); - void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); + void addEmitter (const MWWorld::ConstPtr& ptr, float scale = 1.f, float force = 1.f); + void removeEmitter (const MWWorld::ConstPtr& ptr); + void updateEmitterPtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& ptr); void removeCell(const MWWorld::CellStore* store); + void emitRipple(const osg::Vec3f& pos); + /// Change the height of the water surface, thus moving all ripples with it void setWaterHeight(float height); diff -Nru openmw-0.37.0/apps/openmw/mwrender/sky.cpp openmw-0.38.0/apps/openmw/mwrender/sky.cpp --- openmw-0.37.0/apps/openmw/mwrender/sky.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/sky.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -2,6 +2,8 @@ #include +#include +#include #include #include #include @@ -364,7 +366,7 @@ for (unsigned int i=0; isize(); ++i) { float alpha = 1.f; - if (mMeshType == 0) alpha = i%2 ? 0.f : 1.f; // this is a cylinder, so every second vertex belongs to the bottom-most row + if (mMeshType == 0) alpha = (i%2) ? 0.f : 1.f; // this is a cylinder, so every second vertex belongs to the bottom-most row else if (mMeshType == 1) { if (i>= 49 && i <= 64) alpha = 0.f; // bottom-most row @@ -1550,11 +1552,13 @@ { mNextClouds = weather.mNextCloudTexture; - std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); + if (!mNextClouds.empty()) + { + std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); - if (!texture.empty()) mCloudUpdater2->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture, osg::Texture::REPEAT, osg::Texture::REPEAT)); + } } if (mCloudBlendFactor != weather.mCloudBlendFactor) diff -Nru openmw-0.37.0/apps/openmw/mwrender/terrainstorage.cpp openmw-0.38.0/apps/openmw/mwrender/terrainstorage.cpp --- openmw-0.37.0/apps/openmw/mwrender/terrainstorage.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/terrainstorage.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,7 +20,7 @@ MWWorld::Store::iterator it = esmStore.get().begin(); for (; it != esmStore.get().end(); ++it) { - ESM::Land* land = const_cast(&*it); // TODO: fix store interface + const ESM::Land* land = &*it; land->loadData(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX); } } @@ -69,7 +69,7 @@ { const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); - return esmStore.get().find(index, plugin); + return esmStore.get().search(index, plugin); } } diff -Nru openmw-0.37.0/apps/openmw/mwrender/water.cpp openmw-0.38.0/apps/openmw/mwrender/water.cpp --- openmw-0.37.0/apps/openmw/mwrender/water.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/water.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -198,6 +199,7 @@ mClipNode->getClipPlaneList().clear(); mClipNode->addClipPlane(new osg::ClipPlane(0, osg::Plane(mPlane.getNormal(), 0))); // mPlane.d() applied in FlipCallback mClipNode->setStateSetModes(*getOrCreateStateSet(), osg::StateAttribute::ON); + mClipNode->setCullingActive(false); } private: @@ -550,6 +552,8 @@ stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin"); + node->setStateSet(stateset); + std::vector > textures; int frameCount = mFallback->getFallbackInt("Water_SurfaceFrameCount"); std::string texture = mFallback->getFallbackString("Water_SurfaceTexture"); @@ -560,12 +564,15 @@ textures.push_back(mResourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT)); } + if (!textures.size()) + return; + float fps = mFallback->getFallbackFloat("Water_SurfaceFPS"); osg::ref_ptr controller (new NifOsg::FlipController(0, 1.f/fps, textures)); controller->setSource(boost::shared_ptr(new SceneUtil::FrameTimeSource)); node->setUpdateCallback(controller); - node->setStateSet(stateset); + stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON); } @@ -725,6 +732,11 @@ mSimulation->updateEmitterPtr(old, ptr); } +void Water::emitRipple(const osg::Vec3f &pos) +{ + mSimulation->emitRipple(pos); +} + void Water::removeCell(const MWWorld::CellStore *store) { mSimulation->removeCell(store); diff -Nru openmw-0.37.0/apps/openmw/mwrender/water.hpp openmw-0.38.0/apps/openmw/mwrender/water.hpp --- openmw-0.37.0/apps/openmw/mwrender/water.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwrender/water.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -91,6 +91,8 @@ void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f); void removeEmitter (const MWWorld::Ptr& ptr); void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); + void emitRipple(const osg::Vec3f& pos); + void removeCell(const MWWorld::CellStore* store); ///< remove all emitters in this cell void clearRipples(); diff -Nru openmw-0.37.0/apps/openmw/mwscript/aiextensions.cpp openmw-0.38.0/apps/openmw/mwscript/aiextensions.cpp --- openmw-0.37.0/apps/openmw/mwscript/aiextensions.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwscript/aiextensions.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,5 +1,8 @@ #include "aiextensions.hpp" +#include +#include + #include #include @@ -8,6 +11,7 @@ #include #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/aiactivate.hpp" @@ -18,13 +22,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" -#include - -#include "../mwbase/mechanicsmanager.hpp" namespace MWScript { @@ -144,6 +146,11 @@ // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetStore().get().find(cellID); + MWMechanics::AiEscort escortPackage(actorID, cellID, static_cast(duration), x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); @@ -424,7 +431,7 @@ MWWorld::Ptr targetPtr; if (creatureStats.getAiSequence().getCombatTarget (targetPtr)) { - if (targetPtr.getCellRef().getRefId() == testedTargetId) + if (!targetPtr.isEmpty() && targetPtr.getCellRef().getRefId() == testedTargetId) targetsAreEqual = true; } runtime.push(int(targetsAreEqual)); diff -Nru openmw-0.37.0/apps/openmw/mwscript/cellextensions.cpp openmw-0.38.0/apps/openmw/mwscript/cellextensions.cpp --- openmw-0.37.0/apps/openmw/mwscript/cellextensions.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwscript/cellextensions.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -123,7 +123,7 @@ const MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); std::string current = MWBase::Environment::get().getWorld()->getCellName(cell); - Misc::StringUtils::toLower(current); + Misc::StringUtils::lowerCaseInPlace(current); bool match = current.length()>=name.length() && current.substr (0, name.length())==name; @@ -144,7 +144,9 @@ return; } MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); - if (cell->getCell()->hasWater()) + if (cell->isExterior()) + runtime.push(0.f); // vanilla oddity, return 0 even though water is actually at -1 + else if (cell->getCell()->hasWater()) runtime.push (cell->getWaterLevel()); else runtime.push (-std::numeric_limits::max()); diff -Nru openmw-0.37.0/apps/openmw/mwscript/guiextensions.cpp openmw-0.38.0/apps/openmw/mwscript/guiextensions.cpp --- openmw-0.37.0/apps/openmw/mwscript/guiextensions.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwscript/guiextensions.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -116,7 +116,7 @@ virtual void execute (Interpreter::Runtime& runtime) { std::string cell = (runtime.getStringLiteral (runtime[0].mInteger)); - ::Misc::StringUtils::toLower(cell); + ::Misc::StringUtils::lowerCaseInPlace(cell); runtime.pop(); // "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's House as well." @@ -129,7 +129,7 @@ for (; it != cells.extEnd(); ++it) { std::string name = it->mName; - ::Misc::StringUtils::toLower(name); + ::Misc::StringUtils::lowerCaseInPlace(name); if (name.find(cell) != std::string::npos) MWBase::Environment::get().getWindowManager()->addVisitedLocation ( it->mName, diff -Nru openmw-0.37.0/apps/openmw/mwscript/interpretercontext.cpp openmw-0.38.0/apps/openmw/mwscript/interpretercontext.cpp --- openmw-0.37.0/apps/openmw/mwscript/interpretercontext.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwscript/interpretercontext.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -145,7 +145,7 @@ // selected), store the ID of that reference store it so it can be inherited by // targeted scripts started from this one. if (targetId.empty() && !reference.isEmpty()) - mTargetId = reference.getClass().getId (reference); + mTargetId = reference.getCellRef().getRefId(); } int InterpreterContext::getLocalShort (int index) const @@ -601,9 +601,9 @@ return mTargetId; } - void InterpreterContext::updatePtr(const MWWorld::Ptr& updated) + void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated) { - if (!mReference.isEmpty()) + if (!mReference.isEmpty() && base == mReference) mReference = updated; } } diff -Nru openmw-0.37.0/apps/openmw/mwscript/interpretercontext.hpp openmw-0.38.0/apps/openmw/mwscript/interpretercontext.hpp --- openmw-0.37.0/apps/openmw/mwscript/interpretercontext.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwscript/interpretercontext.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -167,7 +167,7 @@ MWWorld::Ptr getReference(bool required=true); ///< Reference, that the script is running from (can be empty) - void updatePtr(const MWWorld::Ptr& updated); + void updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated); ///< Update the Ptr stored in mReference, if there is one stored there. Should be called after the reference has been moved to a new cell. virtual std::string getTargetId() const; diff -Nru openmw-0.37.0/apps/openmw/mwscript/miscextensions.cpp openmw-0.38.0/apps/openmw/mwscript/miscextensions.cpp --- openmw-0.37.0/apps/openmw/mwscript/miscextensions.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwscript/miscextensions.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -422,9 +422,17 @@ if(key < 0 || key > 32767 || *end != '\0') key = ESM::MagicEffect::effectStringToId(effect); - runtime.push(ptr.getClass().getCreatureStats(ptr).getMagicEffects().get( - MWMechanics::EffectKey(key)).getMagnitude() > 0); - } + const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); + for (MWMechanics::MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it) + { + if (it->first.mId == key && it->second.getModifier() > 0) + { + runtime.push(1); + return; + } + } + runtime.push(0); + } }; template @@ -1043,6 +1051,11 @@ msg << "RefNum: " << ptr.getCellRef().getRefNum().mIndex << std::endl; } + if (ptr.getRefData().isDeletedByContentFile()) + msg << "[Deleted by content file]" << std::endl; + if (!ptr.getRefData().getCount()) + msg << "[Deleted]" << std::endl; + msg << "RefID: " << ptr.getCellRef().getRefId() << std::endl; if (ptr.isInCell()) diff -Nru openmw-0.37.0/apps/openmw/mwscript/statsextensions.cpp openmw-0.38.0/apps/openmw/mwscript/statsextensions.cpp --- openmw-0.37.0/apps/openmw/mwscript/statsextensions.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwscript/statsextensions.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -32,7 +32,7 @@ namespace { - std::string getDialogueActorFaction(MWWorld::Ptr actor) + std::string getDialogueActorFaction(MWWorld::ConstPtr actor) { std::string factionId = actor.getClass().getPrimaryFaction(actor); if (factionId.empty()) @@ -530,7 +530,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr actor = R()(runtime, false); + MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; @@ -543,7 +543,7 @@ factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - ::Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); @@ -562,7 +562,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr actor = R()(runtime, false); + MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; @@ -575,7 +575,7 @@ factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - ::Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); @@ -601,7 +601,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr actor = R()(runtime, false); + MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; @@ -614,7 +614,7 @@ factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - ::Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); @@ -633,7 +633,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime, false); + MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0) @@ -645,7 +645,7 @@ { factionID = ptr.getClass().getPrimaryFaction(ptr); } - ::Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); @@ -739,7 +739,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime, false); + MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionId; @@ -756,7 +756,7 @@ if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - ::Misc::StringUtils::toLower (factionId); + ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); runtime.push ( @@ -771,7 +771,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime, false); + MWWorld::ConstPtr ptr = R()(runtime, false); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); @@ -791,7 +791,7 @@ if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - ::Misc::StringUtils::toLower (factionId); + ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats (player).setFactionReputation (factionId, value); @@ -805,7 +805,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime, false); + MWWorld::ConstPtr ptr = R()(runtime, false); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); @@ -825,7 +825,7 @@ if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - ::Misc::StringUtils::toLower (factionId); + ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats (player).setFactionReputation (factionId, @@ -867,14 +867,14 @@ virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = R()(runtime); + MWWorld::ConstPtr ptr = R()(runtime); std::string race = runtime.getStringLiteral(runtime[0].mInteger); - ::Misc::StringUtils::toLower(race); + ::Misc::StringUtils::lowerCaseInPlace(race); runtime.pop(); std::string npcRace = ptr.get()->mBase->mRace; - ::Misc::StringUtils::toLower(npcRace); + ::Misc::StringUtils::lowerCaseInPlace(npcRace); runtime.push (npcRace == race); } @@ -899,7 +899,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime, false); + MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) @@ -911,7 +911,7 @@ { factionID = ptr.getClass().getPrimaryFaction(ptr); } - ::Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::lowerCaseInPlace(factionID); MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") { @@ -931,7 +931,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime, false); + MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) @@ -958,7 +958,7 @@ virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime, false); + MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) @@ -1096,7 +1096,7 @@ virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - MWBase::Environment::get().getWorld()->setWerewolf(ptr, set); + MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, set); } }; @@ -1108,7 +1108,7 @@ virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - MWBase::Environment::get().getWorld()->applyWerewolfAcrobatics(ptr); + MWBase::Environment::get().getMechanicsManager()->applyWerewolfAcrobatics(ptr); } }; @@ -1127,6 +1127,7 @@ { MWBase::Environment::get().getWorld()->undeleteObject(ptr); // resets runtime state such as inventory, stats and AI. does not reset position in the world + MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); ptr.getRefData().setCustomData(NULL); } } diff -Nru openmw-0.37.0/apps/openmw/mwscript/transformationextensions.cpp openmw-0.38.0/apps/openmw/mwscript/transformationextensions.cpp --- openmw-0.37.0/apps/openmw/mwscript/transformationextensions.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwscript/transformationextensions.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,6 +1,6 @@ #include -#include +#include #include @@ -186,7 +186,7 @@ runtime.push(ptr.getRefData().getPosition().pos[2]); } else - throw std::runtime_error ("invalid axis: " + axis); + throw std::runtime_error ("invalid axis: " + axis); } }; @@ -232,7 +232,7 @@ else throw std::runtime_error ("invalid axis: " + axis); - dynamic_cast(runtime.getContext()).updatePtr(updated); + dynamic_cast(runtime.getContext()).updatePtr(ptr,updated); } }; @@ -300,21 +300,23 @@ } catch(std::exception&) { - const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); + // cell not found, move to exterior instead (vanilla PositionCell compatibility) + const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); if(!cell) { - runtime.getContext().report ("unknown cell (" + cellID + ")"); - std::cerr << "unknown cell (" << cellID << ")\n"; + std::string error = "PositionCell: unknown interior cell (" + cellID + "), moving to exterior instead"; + runtime.getContext().report (error); + std::cerr << error << std::endl; } } if(store) { + MWWorld::Ptr base = ptr; ptr = MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z); - - dynamic_cast(runtime.getContext()).updatePtr(ptr); + dynamic_cast(runtime.getContext()).updatePtr(base,ptr); float ax = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]); float ay = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]); @@ -360,6 +362,7 @@ // another morrowind oddity: player will be moved to the exterior cell at this location, // non-player actors will move within the cell they are in. + MWWorld::Ptr base = ptr; if (ptr == MWMechanics::getPlayer()) { MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(cx,cy); @@ -369,7 +372,7 @@ { ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z); } - dynamic_cast(runtime.getContext()).updatePtr(ptr); + dynamic_cast(runtime.getContext()).updatePtr(base,ptr); float ax = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]); float ay = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]); @@ -457,6 +460,10 @@ runtime.pop(); MWWorld::Ptr player = MWMechanics::getPlayer(); + + if (!player.isInCell()) + throw std::runtime_error("player not in a cell"); + MWWorld::CellStore* store = NULL; if (player.getCell()->isExterior()) { @@ -504,6 +511,9 @@ if (count<0) throw std::runtime_error ("count must be non-negative"); + if (!actor.isInCell()) + throw std::runtime_error ("actor is not in a cell"); + for (int i=0; irotateObject(ptr, xr, yr, zr); - dynamic_cast(runtime.getContext()).updatePtr( + dynamic_cast(runtime.getContext()).updatePtr(ptr, MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], ptr.getCellRef().getPosition().pos[1], ptr.getCellRef().getPosition().pos[2])); @@ -744,8 +754,8 @@ interpreter.installSegment5(Compiler::Transformation::opcodePositionExplicit,new OpPosition); interpreter.installSegment5(Compiler::Transformation::opcodePositionCell,new OpPositionCell); interpreter.installSegment5(Compiler::Transformation::opcodePositionCellExplicit,new OpPositionCell); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAt); diff -Nru openmw-0.37.0/apps/openmw/mwsound/loudness.cpp openmw-0.38.0/apps/openmw/mwsound/loudness.cpp --- openmw-0.37.0/apps/openmw/mwsound/loudness.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/loudness.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,49 +8,63 @@ namespace MWSound { - void analyzeLoudness(const std::vector &data, int sampleRate, ChannelConfig chans, - SampleType type, std::vector &out, float valuesPerSecond) +void Sound_Loudness::analyzeLoudness(const std::vector< char >& data, int sampleRate, ChannelConfig chans, SampleType type, float valuesPerSecond) +{ + int samplesPerSegment = static_cast(sampleRate / valuesPerSecond); + int numSamples = bytesToFrames(data.size(), chans, type); + int advance = framesToBytes(1, chans, type); + + mSamplesPerSec = valuesPerSecond; + mSamples.clear(); + mSamples.reserve(numSamples/samplesPerSegment); + + int segment=0; + int sample=0; + while (segment < numSamples/samplesPerSegment) { - int samplesPerSegment = static_cast(sampleRate / valuesPerSecond); - int numSamples = bytesToFrames(data.size(), chans, type); - int advance = framesToBytes(1, chans, type); - - out.reserve(numSamples/samplesPerSegment); - - int segment=0; - int sample=0; - while (segment < numSamples/samplesPerSegment) + float sum=0; + int samplesAdded = 0; + while (sample < numSamples && sample < (segment+1)*samplesPerSegment) { - float sum=0; - int samplesAdded = 0; - while (sample < numSamples && sample < (segment+1)*samplesPerSegment) + // get sample on a scale from -1 to 1 + float value = 0; + if (type == SampleType_UInt8) + value = ((char)(data[sample*advance]^0x80))/128.f; + else if (type == SampleType_Int16) + { + value = *reinterpret_cast(&data[sample*advance]); + value /= float(std::numeric_limits::max()); + } + else if (type == SampleType_Float32) { - // get sample on a scale from -1 to 1 - float value = 0; - if (type == SampleType_UInt8) - value = ((char)(data[sample*advance]^0x80))/128.f; - else if (type == SampleType_Int16) - { - value = *reinterpret_cast(&data[sample*advance]); - value /= float(std::numeric_limits::max()); - } - else if (type == SampleType_Float32) - { - value = *reinterpret_cast(&data[sample*advance]); - value = std::max(-1.f, std::min(1.f, value)); // Float samples *should* be scaled to [-1,1] already. - } - - sum += value*value; - ++samplesAdded; - ++sample; + value = *reinterpret_cast(&data[sample*advance]); + value = std::max(-1.f, std::min(1.f, value)); // Float samples *should* be scaled to [-1,1] already. } - float rms = 0; // root mean square - if (samplesAdded > 0) - rms = std::sqrt(sum / samplesAdded); - out.push_back(rms); - ++segment; + sum += value*value; + ++samplesAdded; + ++sample; } + + float rms = 0; // root mean square + if (samplesAdded > 0) + rms = std::sqrt(sum / samplesAdded); + mSamples.push_back(rms); + ++segment; } + mReady = true; +} + + +float Sound_Loudness::getLoudnessAtTime(float sec) const +{ + if(mSamplesPerSec <= 0.0f || mSamples.empty() || sec < 0.0f) + return 0.0f; + + size_t index = static_cast(sec * mSamplesPerSec); + index = std::max(0, std::min(index, mSamples.size()-1)); + return mSamples[index]; +} + } diff -Nru openmw-0.37.0/apps/openmw/mwsound/loudness.hpp openmw-0.38.0/apps/openmw/mwsound/loudness.hpp --- openmw-0.37.0/apps/openmw/mwsound/loudness.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/loudness.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,20 +1,40 @@ +#ifndef GAME_SOUND_LOUDNESS_H +#define GAME_SOUND_LOUDNESS_H + +#include + #include "sound_decoder.hpp" namespace MWSound { -/** - * Analyzes the energy (closely related to loudness) of a sound buffer. - * The buffer will be divided into segments according to \a valuesPerSecond, - * and for each segment a loudness value in the range of [0,1] will be computed. - * @param data the sound buffer to analyze, containing raw samples - * @param sampleRate the sample rate of the sound buffer - * @param chans channel layout of the buffer - * @param type sample type of the buffer - * @param out Will contain the output loudness values. - * @param valuesPerSecond How many loudness values per second of audio to compute. - */ -void analyzeLoudness (const std::vector& data, int sampleRate, ChannelConfig chans, SampleType type, - std::vector& out, float valuesPerSecond); +class Sound_Loudness { + // Loudness sample info + float mSamplesPerSec; + std::vector mSamples; + volatile bool mReady; + +public: + Sound_Loudness() : mSamplesPerSec(0.0f), mReady(false) { } + + /** + * Analyzes the energy (closely related to loudness) of a sound buffer. + * The buffer will be divided into segments according to \a valuesPerSecond, + * and for each segment a loudness value in the range of [0,1] will be computed. + * @param data the sound buffer to analyze, containing raw samples + * @param sampleRate the sample rate of the sound buffer + * @param chans channel layout of the buffer + * @param type sample type of the buffer + * @param valuesPerSecond How many loudness values per second of audio to compute. + */ + void analyzeLoudness(const std::vector& data, int sampleRate, + ChannelConfig chans, SampleType type, + float valuesPerSecond); + + bool isReady() { return mReady; } + float getLoudnessAtTime(float sec) const; +}; } + +#endif /* GAME_SOUND_LOUDNESS_H */ diff -Nru openmw-0.37.0/apps/openmw/mwsound/movieaudiofactory.cpp openmw-0.38.0/apps/openmw/mwsound/movieaudiofactory.cpp --- openmw-0.37.0/apps/openmw/mwsound/movieaudiofactory.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/movieaudiofactory.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -60,7 +60,8 @@ virtual double getAudioClock() { - return mAudioTrack->getTimeOffset(); + return (double)getSampleOffset()/(double)mAVStream->codec->sample_rate - + MWBase::Environment::get().getSoundManager()->getTrackTimeDelay(mAudioTrack); } virtual void adjustAudioSettings(AVSampleFormat& sampleFormat, uint64_t& channelLayout, int& sampleRate) @@ -85,11 +86,13 @@ public: ~MovieAudioDecoder() { + if(mAudioTrack.get()) + MWBase::Environment::get().getSoundManager()->stopTrack(mAudioTrack); mAudioTrack.reset(); mDecoderBridge.reset(); } - MWBase::SoundPtr mAudioTrack; + MWBase::SoundStreamPtr mAudioTrack; boost::shared_ptr mDecoderBridge; }; @@ -160,7 +163,8 @@ boost::shared_ptr decoder(new MWSound::MovieAudioDecoder(videoState)); decoder->setupFormat(); - MWBase::SoundPtr sound = MWBase::Environment::get().getSoundManager()->playTrack(decoder->mDecoderBridge, MWBase::SoundManager::Play_TypeMovie); + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + MWBase::SoundStreamPtr sound = sndMgr->playTrack(decoder->mDecoderBridge, MWBase::SoundManager::Play_TypeMovie); if (!sound.get()) { decoder.reset(); diff -Nru openmw-0.37.0/apps/openmw/mwsound/openal_output.cpp openmw-0.38.0/apps/openmw/mwsound/openal_output.cpp --- openmw-0.37.0/apps/openmw/mwsound/openal_output.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/openal_output.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,9 +19,51 @@ #define ALC_ALL_DEVICES_SPECIFIER 0x1013 #endif +#ifndef ALC_SOFT_HRTF +#define ALC_SOFT_HRTF 1 +#define ALC_HRTF_SOFT 0x1992 +#define ALC_DONT_CARE_SOFT 0x0002 +#define ALC_HRTF_STATUS_SOFT 0x1993 +#define ALC_HRTF_DISABLED_SOFT 0x0000 +#define ALC_HRTF_ENABLED_SOFT 0x0001 +#define ALC_HRTF_DENIED_SOFT 0x0002 +#define ALC_HRTF_REQUIRED_SOFT 0x0003 +#define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004 +#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005 +#define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994 +#define ALC_HRTF_SPECIFIER_SOFT 0x1995 +#define ALC_HRTF_ID_SOFT 0x1996 +typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index); +typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index); +ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs); +#endif +#endif + + +#define MAKE_PTRID(id) ((void*)(uintptr_t)id) +#define GET_PTRID(ptr) ((ALuint)(uintptr_t)ptr) + namespace { - const int loudnessFPS = 20; // loudness values per second of audio + +const int sLoudnessFPS = 20; // loudness values per second of audio + +// Helper to get an OpenAL extension function +template +void convertPointer(T& dest, R src) +{ + memcpy(&dest, &src, sizeof(src)); +} + +template +void getFunc(T& func, ALCdevice *device, const char *name) +{ + void* funcPtr = alcGetProcAddress(device, name); + convertPointer(func, funcPtr); +} + } namespace MWSound @@ -147,41 +189,30 @@ return AL_NONE; } -static ALint getBufferSampleCount(ALuint buf) -{ - ALint size, bits, channels; - - alGetBufferi(buf, AL_SIZE, &size); - alGetBufferi(buf, AL_BITS, &bits); - alGetBufferi(buf, AL_CHANNELS, &channels); - throwALerror(); - - return size / channels * 8 / bits; -} // // A streaming OpenAL sound. // -class OpenAL_SoundStream : public Sound +class OpenAL_SoundStream { static const ALuint sNumBuffers = 6; static const ALfloat sBufferLength; - OpenAL_Output &mOutput; - +private: ALuint mSource; + ALuint mBuffers[sNumBuffers]; + ALint mCurrentBufIdx; ALenum mFormat; ALsizei mSampleRate; ALuint mBufferSize; - - ALuint mSamplesQueued; + ALuint mFrameSize; + ALint mSilence; DecoderPtr mDecoder; volatile bool mIsFinished; - volatile bool mIsInitialBatchEnqueued; void updateAll(bool local); @@ -191,44 +222,52 @@ friend class OpenAL_Output; public: - OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags); - virtual ~OpenAL_SoundStream(); + OpenAL_SoundStream(ALuint src, DecoderPtr decoder); + ~OpenAL_SoundStream(); - virtual void stop(); - virtual bool isPlaying(); - virtual double getTimeOffset(); - virtual void update(); + bool isPlaying(); + double getStreamDelay() const; + double getStreamOffset() const; - void play(); bool process(); + ALint refillQueue(); }; - const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f; + // // A background streaming thread (keeps active streams processed) // struct OpenAL_Output::StreamThread { typedef std::vector StreamVec; StreamVec mStreams; - boost::recursive_mutex mMutex; + + typedef std::vector > DecoderLoudnessVec; + DecoderLoudnessVec mDecoderLoudness; + + volatile bool mQuitNow; + boost::mutex mMutex; + boost::condition_variable mCondVar; boost::thread mThread; StreamThread() - : mThread(boost::ref(*this)) + : mQuitNow(false), mThread(boost::ref(*this)) { } ~StreamThread() { - mThread.interrupt(); + mQuitNow = true; + mMutex.lock(); mMutex.unlock(); + mCondVar.notify_all(); + mThread.join(); } // boost::thread entry point void operator()() { - while(1) + boost::unique_lock lock(mMutex); + while(!mQuitNow) { - mMutex.lock(); StreamVec::iterator iter = mStreams.begin(); while(iter != mStreams.end()) { @@ -237,33 +276,68 @@ else ++iter; } - mMutex.unlock(); - boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + + // Only do one loudness decode at a time, in case it takes particularly long we don't + // want to block up anything. + DecoderLoudnessVec::iterator dliter = mDecoderLoudness.begin(); + if(dliter != mDecoderLoudness.end()) + { + DecoderPtr decoder = dliter->first; + Sound_Loudness *loudness = dliter->second; + mDecoderLoudness.erase(dliter); + lock.unlock(); + + std::vector data; + ChannelConfig chans = ChannelConfig_Mono; + SampleType type = SampleType_Int16; + int srate = 48000; + try { + decoder->getInfo(&srate, &chans, &type); + decoder->readAll(data); + } + catch(std::exception &e) { + std::cerr<< "Failed to decode audio: "<analyzeLoudness(data, srate, chans, type, static_cast(sLoudnessFPS)); + lock.lock(); + continue; + } + mCondVar.timed_wait(lock, boost::posix_time::milliseconds(50)); } } void add(OpenAL_SoundStream *stream) { - mMutex.lock(); + boost::unique_lock lock(mMutex); if(std::find(mStreams.begin(), mStreams.end(), stream) == mStreams.end()) + { mStreams.push_back(stream); - mMutex.unlock(); + lock.unlock(); + mCondVar.notify_all(); + } } void remove(OpenAL_SoundStream *stream) { - mMutex.lock(); + boost::lock_guard lock(mMutex); StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream); - if(iter != mStreams.end()) - mStreams.erase(iter); - mMutex.unlock(); + if(iter != mStreams.end()) mStreams.erase(iter); } void removeAll() { - mMutex.lock(); + boost::lock_guard lock(mMutex); mStreams.clear(); - mMutex.unlock(); + mDecoderLoudness.clear(); + } + + void add(DecoderPtr decoder, Sound_Loudness *loudness) + { + boost::unique_lock lock(mMutex); + mDecoderLoudness.push_back(std::make_pair(decoder, loudness)); + lock.unlock(); + mCondVar.notify_all(); } private: @@ -272,12 +346,9 @@ }; -OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags) - : Sound(osg::Vec3f(0.f, 0.f, 0.f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags) - , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true), mIsInitialBatchEnqueued(false) +OpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder) + : mSource(src), mCurrentBufIdx(0), mFrameSize(0), mSilence(0), mDecoder(decoder), mIsFinished(false) { - throwALerror(); - alGenBuffers(sNumBuffers, mBuffers); throwALerror(); try @@ -290,10 +361,16 @@ mFormat = getALFormat(chans, type); mSampleRate = srate; - mBufferSize = static_cast(sBufferLength*srate); - mBufferSize = framesToBytes(mBufferSize, chans, type); + switch(type) + { + case SampleType_UInt8: mSilence = 0x80; break; + case SampleType_Int16: mSilence = 0x00; break; + case SampleType_Float32: mSilence = 0x00; break; + } - mOutput.mActiveSounds.push_back(this); + mFrameSize = framesToBytes(1, chans, type); + mBufferSize = static_cast(sBufferLength*srate); + mBufferSize *= mFrameSize; } catch(std::exception&) { @@ -301,47 +378,14 @@ alGetError(); throw; } + mIsFinished = false; } OpenAL_SoundStream::~OpenAL_SoundStream() { - mOutput.mStreamThread->remove(this); - - alSourceStop(mSource); - alSourcei(mSource, AL_BUFFER, 0); - - mOutput.mFreeSources.push_back(mSource); alDeleteBuffers(sNumBuffers, mBuffers); alGetError(); mDecoder->close(); - - mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(), - mOutput.mActiveSounds.end(), this)); -} - -void OpenAL_SoundStream::play() -{ - alSourceStop(mSource); - alSourcei(mSource, AL_BUFFER, 0); - throwALerror(); - mSamplesQueued = 0; - mIsFinished = false; - mIsInitialBatchEnqueued = false; - mOutput.mStreamThread->add(this); -} - -void OpenAL_SoundStream::stop() -{ - mOutput.mStreamThread->remove(this); - mIsFinished = true; - mIsInitialBatchEnqueued = false; - - alSourceStop(mSource); - alSourcei(mSource, AL_BUFFER, 0); - throwALerror(); - mSamplesQueued = 0; - - mDecoder->rewind(); } bool OpenAL_SoundStream::isPlaying() @@ -356,294 +400,108 @@ return !mIsFinished; } -double OpenAL_SoundStream::getTimeOffset() +double OpenAL_SoundStream::getStreamDelay() const { ALint state = AL_STOPPED; - ALfloat offset = 0.0f; - double t; + double d = 0.0; + ALint offset; - mOutput.mStreamThread->mMutex.lock(); - alGetSourcef(mSource, AL_SEC_OFFSET, &offset); + alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset); alGetSourcei(mSource, AL_SOURCE_STATE, &state); if(state == AL_PLAYING || state == AL_PAUSED) - t = (double)(mDecoder->getSampleOffset() - mSamplesQueued)/(double)mSampleRate + offset; - else - t = (double)mDecoder->getSampleOffset() / (double)mSampleRate; - mOutput.mStreamThread->mMutex.unlock(); + { + ALint queued; + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); + ALint inqueue = mBufferSize/mFrameSize*queued - offset; + d = (double)inqueue / (double)mSampleRate; + } throwALerror(); - return t; + return d; } -void OpenAL_SoundStream::updateAll(bool local) +double OpenAL_SoundStream::getStreamOffset() const { - alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance); - alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance); - if(local) + ALint state = AL_STOPPED; + ALint offset; + double t; + + alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + if(state == AL_PLAYING || state == AL_PAUSED) { - alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f); - alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE); + ALint queued; + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); + ALint inqueue = mBufferSize/mFrameSize*queued - offset; + t = (double)(mDecoder->getSampleOffset() - inqueue) / (double)mSampleRate; } else { - alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f); - alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE); - } - alSourcei(mSource, AL_LOOPING, AL_FALSE); - - update(); -} - -void OpenAL_SoundStream::update() -{ - ALfloat gain = mVolume*mBaseVolume; - ALfloat pitch = mPitch; - if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) - { - gain *= 0.9f; - pitch *= 0.7f; + /* Underrun, or not started yet. The decoder offset is where we'll play + * next. */ + t = (double)mDecoder->getSampleOffset() / (double)mSampleRate; } - alSourcef(mSource, AL_GAIN, gain); - alSourcef(mSource, AL_PITCH, pitch); - 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(); + return t; } bool OpenAL_SoundStream::process() { try { - bool finished = mIsFinished; - ALint processed, state; - - alGetSourcei(mSource, AL_SOURCE_STATE, &state); - alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed); - throwALerror(); - - if(processed > 0) + if(refillQueue() > 0) { - std::vector data(mBufferSize); - do { - ALuint bufid = 0; - size_t got; - - alSourceUnqueueBuffers(mSource, 1, &bufid); - mSamplesQueued -= getBufferSampleCount(bufid); - processed--; - - if(finished) - continue; - - got = mDecoder->read(&data[0], data.size()); - finished = (got < data.size()); - if(got > 0) - { - alBufferData(bufid, mFormat, &data[0], got, mSampleRate); - alSourceQueueBuffers(mSource, 1, &bufid); - mSamplesQueued += getBufferSampleCount(bufid); - } - } while(processed > 0); - throwALerror(); - } - else if (!mIsInitialBatchEnqueued) { // nothing enqueued yet - std::vector data(mBufferSize); - - for(ALuint i = 0;i < sNumBuffers && !finished;i++) + ALint state; + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + if(state != AL_PLAYING && state != AL_PAUSED) { - size_t got = mDecoder->read(&data[0], data.size()); - finished = (got < data.size()); - if(got > 0) - { - ALuint bufid = mBuffers[i]; - alBufferData(bufid, mFormat, &data[0], got, mSampleRate); - alSourceQueueBuffers(mSource, 1, &bufid); - throwALerror(); - mSamplesQueued += getBufferSampleCount(bufid); - } - } - mIsInitialBatchEnqueued = true; - } - - if(state != AL_PLAYING && state != AL_PAUSED) - { - ALint queued = 0; - - alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); - if(queued > 0) + refillQueue(); alSourcePlay(mSource); - throwALerror(); + } } - - mIsFinished = finished; } catch(std::exception&) { std::cout<< "Error updating stream \""<getName()<<"\"" < 0) { - alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f); - alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE); + ALuint buf; + alSourceUnqueueBuffers(mSource, 1, &buf); + --processed; } - alSourcei(mSource, AL_LOOPING, (mFlags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); - - update(); -} - -void OpenAL_Sound::update() -{ - ALfloat gain = mVolume*mBaseVolume; - ALfloat pitch = mPitch; - if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + ALint queued; + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); + if(!mIsFinished && (ALuint)queued < sNumBuffers) { - gain *= 0.9f; - pitch *= 0.7f; - } - - alSourcef(mSource, AL_GAIN, gain); - alSourcef(mSource, AL_PITCH, pitch); - 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(); -} - -void OpenAL_Sound3D::update() -{ - ALfloat gain = mVolume*mBaseVolume; - ALfloat pitch = mPitch; - if((mPos - mOutput.mPos).length2() > mMaxDistance*mMaxDistance) - gain = 0.0f; - else if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) - { - gain *= 0.9f; - pitch *= 0.7f; + std::vector data(mBufferSize); + for(;!mIsFinished && (ALuint)queued < sNumBuffers;++queued) + { + size_t got = mDecoder->read(&data[0], data.size()); + if(got < data.size()) + { + mIsFinished = true; + memset(&data[got], mSilence, data.size()-got); + } + if(got > 0) + { + ALuint bufid = mBuffers[mCurrentBufIdx]; + alBufferData(bufid, mFormat, &data[0], data.size(), mSampleRate); + alSourceQueueBuffers(mSource, 1, &bufid); + mCurrentBufIdx = (mCurrentBufIdx+1) % sNumBuffers; + } + } } - alSourcef(mSource, AL_GAIN, gain); - alSourcef(mSource, AL_PITCH, pitch); - 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(); + return queued; } @@ -737,14 +595,6 @@ alDeleteSources(1, &mFreeSources[i]); mFreeSources.clear(); - mBufferRefs.clear(); - mUnusedBuffers.clear(); - while(!mBufferCache.empty()) - { - alDeleteBuffers(1, &mBufferCache.begin()->second.mALBuffer); - mBufferCache.erase(mBufferCache.begin()); - } - alcMakeContextCurrent(0); if(mContext) alcDestroyContext(mContext); @@ -757,42 +607,133 @@ } -const CachedSound& OpenAL_Output::getBuffer(const std::string &fname) +std::vector OpenAL_Output::enumerateHrtf() { - ALuint buf = 0; + if(!mDevice) + fail("Device not initialized"); - NameMap::iterator iditer = mBufferCache.find(fname); - if(iditer != mBufferCache.end()) + std::vector ret; + if(!alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF")) + return ret; + + LPALCGETSTRINGISOFT alcGetStringiSOFT = 0; + getFunc(alcGetStringiSOFT, mDevice, "alcGetStringiSOFT"); + + ALCint num_hrtf; + alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf); + ret.reserve(num_hrtf); + for(ALCint i = 0;i < num_hrtf;++i) + { + const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i); + ret.push_back(entry); + } + + return ret; +} + +void OpenAL_Output::enableHrtf(const std::string &hrtfname, bool auto_enable) +{ + if(!alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF")) { - buf = iditer->second.mALBuffer; - if(mBufferRefs[buf]++ == 0) + std::cerr<< "HRTF extension not present" < attrs; + attrs.push_back(ALC_HRTF_SOFT); + attrs.push_back(auto_enable ? ALC_DONT_CARE_SOFT : ALC_TRUE); + if(!hrtfname.empty()) + { + ALCint index = -1; + ALCint num_hrtf; + alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf); + for(ALCint i = 0;i < num_hrtf;++i) { - IDDq::iterator iter = std::find(mUnusedBuffers.begin(), - mUnusedBuffers.end(), buf); - if(iter != mUnusedBuffers.end()) - mUnusedBuffers.erase(iter); + const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i); + if(hrtfname == entry) + { + index = i; + break; + } } - return iditer->second; + if(index < 0) + std::cerr<< "Failed to find HRTF name \""< data; - ChannelConfig chans; - SampleType type; - ALenum format; - int srate; + ALCint hrtf_state; + alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state); + if(!hrtf_state) + std::cerr<< "Failed to enable HRTF" < attrs; + attrs.push_back(ALC_HRTF_SOFT); + attrs.push_back(ALC_FALSE); + attrs.push_back(0); + alcResetDeviceSOFT(mDevice, &attrs[0]); + + ALCint hrtf_state; + alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state); + if(hrtf_state) + std::cerr<< "Failed to disable HRTF" <mResourceMgr->exists(file)) + if(decoder->mResourceMgr->exists(fname)) + decoder->open(fname); + else { + std::string file = fname; std::string::size_type pos = file.rfind('.'); if(pos != std::string::npos) file = file.substr(0, pos)+".mp3"; + decoder->open(file); } - decoder->open(file); + + std::vector data; + ChannelConfig chans; + SampleType type; + ALenum format; + int srate; decoder->getInfo(&srate, &chans, &type); format = getALFormat(chans, type); @@ -800,210 +741,375 @@ decoder->readAll(data); decoder->close(); - CachedSound cached; - analyzeLoudness(data, srate, chans, type, cached.mLoudnessVector, static_cast(loudnessFPS)); - - alGenBuffers(1, &buf); - throwALerror(); - - alBufferData(buf, format, &data[0], data.size(), srate); - mBufferRefs[buf] = 1; - cached.mALBuffer = buf; - mBufferCache[fname] = cached; - - ALint bufsize = 0; - alGetBufferi(buf, AL_SIZE, &bufsize); - mBufferCacheMemSize += bufsize; + ALuint buf = 0; + try { + alGenBuffers(1, &buf); + alBufferData(buf, format, &data[0], data.size(), srate); + throwALerror(); + } + catch(...) { + if(buf && alIsBuffer(buf)) + alDeleteBuffers(1, &buf); + throw; + } + return MAKE_PTRID(buf); +} - // NOTE: Max buffer cache: 15MB - while(mBufferCacheMemSize > 15*1024*1024) +void OpenAL_Output::unloadSound(Sound_Handle data) +{ + ALuint buffer = GET_PTRID(data); + // Make sure no sources are playing this buffer before unloading it. + SoundVec::const_iterator iter = mActiveSounds.begin(); + for(;iter != mActiveSounds.end();++iter) { - if(mUnusedBuffers.empty()) + if(!(*iter)->mHandle) + continue; + + ALuint source = GET_PTRID((*iter)->mHandle); + ALint srcbuf; + alGetSourcei(source, AL_BUFFER, &srcbuf); + if((ALuint)srcbuf == buffer) { - std::cout <<"No more unused buffers to clear!"<< std::endl; - break; + alSourceStop(source); + alSourcei(source, AL_BUFFER, 0); } + } + alDeleteBuffers(1, &buffer); +} - ALuint oldbuf = mUnusedBuffers.front(); - mUnusedBuffers.pop_front(); +size_t OpenAL_Output::getSoundDataSize(Sound_Handle data) const +{ + ALuint buffer = GET_PTRID(data); + ALint size = 0; - NameMap::iterator nameiter = mBufferCache.begin(); - while(nameiter != mBufferCache.end()) - { - if(nameiter->second.mALBuffer == oldbuf) - mBufferCache.erase(nameiter++); - else - ++nameiter; - } + alGetBufferi(buffer, AL_SIZE, &size); + throwALerror(); + + return (ALuint)size; +} + + +void OpenAL_Output::initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv) +{ + alSourcef(source, AL_REFERENCE_DISTANCE, 1.0f); + alSourcef(source, AL_MAX_DISTANCE, 1000.0f); + alSourcef(source, AL_ROLLOFF_FACTOR, 0.0f); + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); - bufsize = 0; - alGetBufferi(oldbuf, AL_SIZE, &bufsize); - alDeleteBuffers(1, &oldbuf); - mBufferCacheMemSize -= bufsize; + if(useenv && mListenerEnv == Env_Underwater) + { + gain *= 0.9f; + pitch *= 0.7f; } - return mBufferCache[fname]; + alSourcef(source, AL_GAIN, gain); + alSourcef(source, AL_PITCH, pitch); + alSourcefv(source, AL_POSITION, pos.ptr()); + alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); +} + +void OpenAL_Output::initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv) +{ + alSourcef(source, AL_REFERENCE_DISTANCE, mindist); + alSourcef(source, AL_MAX_DISTANCE, maxdist); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.0f); + alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); + alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); + + if((pos - mListenerPos).length2() > maxdist*maxdist) + gain = 0.0f; + if(useenv && mListenerEnv == Env_Underwater) + { + gain *= 0.9f; + pitch *= 0.7f; + } + + alSourcef(source, AL_GAIN, gain); + alSourcef(source, AL_PITCH, pitch); + alSourcefv(source, AL_POSITION, pos.ptr()); + alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } -void OpenAL_Output::bufferFinished(ALuint buf) +void OpenAL_Output::updateCommon(ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d) { - if(mBufferRefs.at(buf)-- == 1) + if(is3d) + { + if((pos - mListenerPos).length2() > maxdist*maxdist) + gain = 0.0f; + } + if(useenv && mListenerEnv == Env_Underwater) { - mBufferRefs.erase(mBufferRefs.find(buf)); - mUnusedBuffers.push_back(buf); + gain *= 0.9f; + pitch *= 0.7f; } + + alSourcef(source, AL_GAIN, gain); + alSourcef(source, AL_PITCH, pitch); + alSourcefv(source, AL_POSITION, pos.ptr()); + alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } -MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, float basevol, float pitch, int flags,float offset) + +void OpenAL_Output::playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset) { - boost::shared_ptr sound; - ALuint src=0, buf=0; + ALuint source; if(mFreeSources.empty()) fail("No free sources"); - src = mFreeSources.front(); + source = mFreeSources.front(); mFreeSources.pop_front(); - try - { - buf = getBuffer(fname).mALBuffer; - sound.reset(new OpenAL_Sound(*this, src, buf, osg::Vec3f(0.f, 0.f, 0.f), vol, basevol, pitch, 1.0f, 1000.0f, flags)); + try { + initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), + sound->getIsLooping(), sound->getUseEnv()); + + alSourcef(source, AL_SEC_OFFSET, offset); + throwALerror(); + + alSourcei(source, AL_BUFFER, GET_PTRID(data)); + alSourcePlay(source); + throwALerror(); + + mActiveSounds.push_back(sound); } - catch(std::exception&) - { - mFreeSources.push_back(src); - if(buf && alIsBuffer(buf)) - bufferFinished(buf); - alGetError(); + catch(std::exception&) { + mFreeSources.push_back(source); throw; } - sound->updateAll(true); - if(offset<0) - offset=0; - if(offset>1) - offset=1; - - alSourcei(src, AL_BUFFER, buf); - alSourcef(src, AL_SEC_OFFSET, static_cast(sound->getLength()*offset / pitch)); - alSourcePlay(src); - throwALerror(); - - return sound; + sound->mHandle = MAKE_PTRID(source); } -MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const osg::Vec3f &pos, float vol, float basevol, float pitch, - float min, float max, int flags, float offset, bool extractLoudness) +void OpenAL_Output::playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset) { - boost::shared_ptr sound; - ALuint src=0, buf=0; + ALuint source; if(mFreeSources.empty()) fail("No free sources"); - src = mFreeSources.front(); + source = mFreeSources.front(); mFreeSources.pop_front(); - try - { - const CachedSound& cached = getBuffer(fname); - buf = cached.mALBuffer; + try { + initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), + sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), + sound->getUseEnv()); + + alSourcef(source, AL_SEC_OFFSET, offset); + throwALerror(); + + alSourcei(source, AL_BUFFER, GET_PTRID(data)); + alSourcePlay(source); + throwALerror(); - sound.reset(new OpenAL_Sound3D(*this, src, buf, pos, vol, basevol, pitch, min, max, flags)); - if (extractLoudness) - sound->setLoudnessVector(cached.mLoudnessVector, static_cast(loudnessFPS)); + mActiveSounds.push_back(sound); } - catch(std::exception&) - { - mFreeSources.push_back(src); - if(buf && alIsBuffer(buf)) - bufferFinished(buf); - alGetError(); + catch(std::exception&) { + mFreeSources.push_back(source); throw; } - sound->updateAll(false); + sound->mHandle = MAKE_PTRID(source); +} + +void OpenAL_Output::finishSound(MWBase::SoundPtr sound) +{ + if(!sound->mHandle) return; + ALuint source = GET_PTRID(sound->mHandle); + sound->mHandle = 0; + + alSourceStop(source); + alSourcei(source, AL_BUFFER, 0); - if(offset<0) - offset=0; - if(offset>1) - offset=1; + mFreeSources.push_back(source); + mActiveSounds.erase(std::find(mActiveSounds.begin(), mActiveSounds.end(), sound)); +} - alSourcei(src, AL_BUFFER, buf); - alSourcef(src, AL_SEC_OFFSET, static_cast(sound->getLength()*offset / pitch)); +bool OpenAL_Output::isSoundPlaying(MWBase::SoundPtr sound) +{ + if(!sound->mHandle) return false; + ALuint source = GET_PTRID(sound->mHandle); + ALint state; - alSourcePlay(src); + alGetSourcei(source, AL_SOURCE_STATE, &state); throwALerror(); - return sound; + return state == AL_PLAYING || state == AL_PAUSED; } +void OpenAL_Output::updateSound(MWBase::SoundPtr sound) +{ + if(!sound->mHandle) return; + ALuint source = GET_PTRID(sound->mHandle); + + updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), + sound->getPitch(), sound->getUseEnv(), sound->getIs3D()); +} -MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, float pitch, int flags) + +void OpenAL_Output::streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound) { - boost::shared_ptr sound; - ALuint src; + OpenAL_SoundStream *stream = 0; + ALuint source; if(mFreeSources.empty()) fail("No free sources"); - src = mFreeSources.front(); + source = mFreeSources.front(); mFreeSources.pop_front(); - if((flags&MWBase::SoundManager::Play_Loop)) + if(sound->getIsLooping()) std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; - try - { - sound.reset(new OpenAL_SoundStream(*this, src, decoder, volume, pitch, flags)); + try { + initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), + false, sound->getUseEnv()); + throwALerror(); + + stream = new OpenAL_SoundStream(source, decoder); + mStreamThread->add(stream); + mActiveStreams.push_back(sound); } - catch(std::exception&) - { - mFreeSources.push_back(src); + catch(std::exception&) { + mStreamThread->remove(stream); + delete stream; + mFreeSources.push_back(source); + throw; + } + + sound->mHandle = stream; +} + +void OpenAL_Output::streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound) +{ + OpenAL_SoundStream *stream = 0; + ALuint source; + + if(mFreeSources.empty()) + fail("No free sources"); + source = mFreeSources.front(); + mFreeSources.pop_front(); + + if(sound->getIsLooping()) + std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; + try { + initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), + sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); + throwALerror(); + + stream = new OpenAL_SoundStream(source, decoder); + mStreamThread->add(stream); + mActiveStreams.push_back(sound); + } + catch(std::exception&) { + mStreamThread->remove(stream); + delete stream; + mFreeSources.push_back(source); throw; } - sound->updateAll(true); + sound->mHandle = stream; +} + +void OpenAL_Output::finishStream(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + ALuint source = stream->mSource; + + sound->mHandle = 0; + mStreamThread->remove(stream); + + alSourceStop(source); + alSourcei(source, AL_BUFFER, 0); + + mFreeSources.push_back(source); + mActiveStreams.erase(std::find(mActiveStreams.begin(), mActiveStreams.end(), sound)); - sound->play(); - return sound; + delete stream; } +double OpenAL_Output::getStreamDelay(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return 0.0; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + return stream->getStreamDelay(); +} -void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) +double OpenAL_Output::getStreamOffset(MWBase::SoundStreamPtr sound) { - mPos = pos; - mLastEnvironment = env; + if(!sound->mHandle) return 0.0; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + boost::lock_guard lock(mStreamThread->mMutex); + return stream->getStreamOffset(); +} + +bool OpenAL_Output::isStreamPlaying(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return false; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + boost::lock_guard lock(mStreamThread->mMutex); + return stream->isPlaying(); +} + +void OpenAL_Output::updateStream(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + ALuint source = stream->mSource; + + updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), + sound->getPitch(), sound->getUseEnv(), sound->getIs3D()); +} + + +void OpenAL_Output::startUpdate() +{ + alcSuspendContext(alcGetCurrentContext()); +} +void OpenAL_Output::finishUpdate() +{ + alcProcessContext(alcGetCurrentContext()); +} + + +void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) +{ if(mContext) { ALfloat orient[6] = { atdir.x(), atdir.y(), atdir.z(), updir.x(), updir.y(), updir.z() }; - alListener3f(AL_POSITION, mPos.x(), mPos.y(), mPos.z()); + alListenerfv(AL_POSITION, pos.ptr()); alListenerfv(AL_ORIENTATION, orient); throwALerror(); } + + mListenerPos = pos; + mListenerEnv = env; } void OpenAL_Output::pauseSounds(int types) { std::vector sources; - SoundVec::const_iterator iter = mActiveSounds.begin(); - while(iter != mActiveSounds.end()) + SoundVec::const_iterator sound = mActiveSounds.begin(); + for(;sound != mActiveSounds.end();++sound) { - const OpenAL_SoundStream *stream = dynamic_cast(*iter); - if(stream) - { - if(stream->mSource && (stream->getPlayType()&types)) - sources.push_back(stream->mSource); - } - else + if(*sound && (*sound)->mHandle && ((*sound)->getPlayType()&types)) + sources.push_back(GET_PTRID((*sound)->mHandle)); + } + StreamVec::const_iterator stream = mActiveStreams.begin(); + for(;stream != mActiveStreams.end();++stream) + { + if(*stream && (*stream)->mHandle && ((*stream)->getPlayType()&types)) { - const OpenAL_Sound *sound = dynamic_cast(*iter); - if(sound && sound->mSource && (sound->getPlayType()&types)) - sources.push_back(sound->mSource); + OpenAL_SoundStream *strm = reinterpret_cast((*stream)->mHandle); + sources.push_back(strm->mSource); } - ++iter; } if(!sources.empty()) { @@ -1015,22 +1121,20 @@ void OpenAL_Output::resumeSounds(int types) { std::vector sources; - SoundVec::const_iterator iter = mActiveSounds.begin(); - while(iter != mActiveSounds.end()) + SoundVec::const_iterator sound = mActiveSounds.begin(); + for(;sound != mActiveSounds.end();++sound) { - const OpenAL_SoundStream *stream = dynamic_cast(*iter); - if(stream) - { - if(stream->mSource && (stream->getPlayType()&types)) - sources.push_back(stream->mSource); - } - else + if(*sound && (*sound)->mHandle && ((*sound)->getPlayType()&types)) + sources.push_back(GET_PTRID((*sound)->mHandle)); + } + StreamVec::const_iterator stream = mActiveStreams.begin(); + for(;stream != mActiveStreams.end();++stream) + { + if(*stream && (*stream)->mHandle && ((*stream)->getPlayType()&types)) { - const OpenAL_Sound *sound = dynamic_cast(*iter); - if(sound && sound->mSource && (sound->getPlayType()&types)) - sources.push_back(sound->mSource); + OpenAL_SoundStream *strm = reinterpret_cast((*stream)->mHandle); + sources.push_back(strm->mSource); } - ++iter; } if(!sources.empty()) { @@ -1040,9 +1144,16 @@ } +void OpenAL_Output::loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness) +{ + mStreamThread->add(decoder, loudness); +} + + OpenAL_Output::OpenAL_Output(SoundManager &mgr) - : Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0), - mLastEnvironment(Env_Normal), mStreamThread(new StreamThread) + : Sound_Output(mgr), mDevice(0), mContext(0) + , mListenerPos(0.0f, 0.0f, 0.0f), mListenerEnv(Env_Normal) + , mStreamThread(new StreamThread) { } diff -Nru openmw-0.37.0/apps/openmw/mwsound/openal_output.hpp openmw-0.38.0/apps/openmw/mwsound/openal_output.hpp --- openmw-0.37.0/apps/openmw/mwsound/openal_output.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/openal_output.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -16,12 +16,6 @@ class SoundManager; class Sound; - struct CachedSound - { - ALuint mALBuffer; - std::vector mLoudnessVector; - }; - class OpenAL_Output : public Sound_Output { ALCdevice *mDevice; @@ -29,53 +23,65 @@ typedef std::deque IDDq; IDDq mFreeSources; - IDDq mUnusedBuffers; - typedef std::map NameMap; - NameMap mBufferCache; + typedef std::vector SoundVec; + SoundVec mActiveSounds; + typedef std::vector StreamVec; + StreamVec mActiveStreams; - typedef std::map IDRefMap; - IDRefMap mBufferRefs; + osg::Vec3f mListenerPos; + Environment mListenerEnv; - uint64_t mBufferCacheMemSize; + struct StreamThread; + std::auto_ptr mStreamThread; - typedef std::vector SoundVec; - SoundVec mActiveSounds; + void initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv); + void initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv); - const CachedSound& getBuffer(const std::string &fname); - void bufferFinished(ALuint buffer); + void updateCommon(ALuint source, const osg::Vec3f &pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d); - Environment mLastEnvironment; + OpenAL_Output& operator=(const OpenAL_Output &rhs); + OpenAL_Output(const OpenAL_Output &rhs); + public: virtual std::vector enumerate(); - virtual void init(const std::string &devname=""); + virtual void init(const std::string &devname=std::string()); virtual void deinit(); - /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. - virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags, float offset); - /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. - virtual MWBase::SoundPtr playSound3D(const std::string &fname, const osg::Vec3f &pos, - float vol, float basevol, float pitch, float min, float max, int flags, float offset, bool extractLoudness=false); - virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags); + virtual std::vector enumerateHrtf(); + virtual void enableHrtf(const std::string &hrtfname, bool auto_enable); + virtual void disableHrtf(); + + virtual Sound_Handle loadSound(const std::string &fname); + virtual void unloadSound(Sound_Handle data); + virtual size_t getSoundDataSize(Sound_Handle data) const; + + virtual void playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset); + virtual void playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset); + virtual void finishSound(MWBase::SoundPtr sound); + virtual bool isSoundPlaying(MWBase::SoundPtr sound); + virtual void updateSound(MWBase::SoundPtr sound); + + virtual void streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound); + virtual void streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound); + virtual void finishStream(MWBase::SoundStreamPtr sound); + virtual double getStreamDelay(MWBase::SoundStreamPtr sound); + virtual double getStreamOffset(MWBase::SoundStreamPtr sound); + virtual bool isStreamPlaying(MWBase::SoundStreamPtr sound); + virtual void updateStream(MWBase::SoundStreamPtr sound); + + virtual void startUpdate(); + virtual void finishUpdate(); virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env); virtual void pauseSounds(int types); virtual void resumeSounds(int types); - OpenAL_Output& operator=(const OpenAL_Output &rhs); - OpenAL_Output(const OpenAL_Output &rhs); + virtual void loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness); OpenAL_Output(SoundManager &mgr); virtual ~OpenAL_Output(); - - struct StreamThread; - std::auto_ptr mStreamThread; - - friend class OpenAL_Sound; - friend class OpenAL_Sound3D; - friend class OpenAL_SoundStream; - friend class SoundManager; }; #ifndef DEFAULT_OUTPUT #define DEFAULT_OUTPUT(x) ::MWSound::OpenAL_Output((x)) diff -Nru openmw-0.37.0/apps/openmw/mwsound/sound_buffer.hpp openmw-0.38.0/apps/openmw/mwsound/sound_buffer.hpp --- openmw-0.37.0/apps/openmw/mwsound/sound_buffer.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/sound_buffer.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,32 @@ +#ifndef GAME_SOUND_SOUND_BUFFER_H +#define GAME_SOUND_SOUND_BUFFER_H + +#include + +#include "soundmanagerimp.hpp" +#include "sound_output.hpp" +#include "loudness.hpp" + +#include "../mwworld/ptr.hpp" + +namespace MWSound +{ + class Sound_Buffer + { + public: + std::string mResourceName; + + float mVolume; + float mMinDist, mMaxDist; + + Sound_Handle mHandle; + + size_t mUses; + + Sound_Buffer(std::string resname, float volume, float mindist, float maxdist) + : mResourceName(resname), mVolume(volume), mMinDist(mindist), mMaxDist(maxdist), mHandle(0), mUses(0) + { } + }; +} + +#endif /* GAME_SOUND_SOUND_BUFFER_H */ diff -Nru openmw-0.37.0/apps/openmw/mwsound/sound.cpp openmw-0.38.0/apps/openmw/mwsound/sound.cpp --- openmw-0.37.0/apps/openmw/mwsound/sound.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/sound.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -#include "sound.hpp" - -namespace MWSound -{ - - float Sound::getCurrentLoudness() - { - if (mLoudnessVector.empty()) - return 0.f; - int index = static_cast(getTimeOffset() * mLoudnessFPS); - - index = std::max(0, std::min(index, int(mLoudnessVector.size()-1))); - - return mLoudnessVector[index]; - } - - void Sound::setLoudnessVector(const std::vector &loudnessVector, float loudnessFPS) - { - mLoudnessVector = loudnessVector; - mLoudnessFPS = loudnessFPS; - } - -} diff -Nru openmw-0.37.0/apps/openmw/mwsound/sound.hpp openmw-0.38.0/apps/openmw/mwsound/sound.hpp --- openmw-0.37.0/apps/openmw/mwsound/sound.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/sound.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,18 +1,14 @@ #ifndef GAME_SOUND_SOUND_H #define GAME_SOUND_SOUND_H -#include "soundmanagerimp.hpp" +#include "sound_output.hpp" namespace MWSound { - class Sound - { - virtual void update() = 0; - + class Sound { Sound& operator=(const Sound &rhs); Sound(const Sound &rhs); - protected: osg::Vec3f mPos; float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */ float mBaseVolume; @@ -20,43 +16,66 @@ float mMinDistance; float mMaxDistance; int mFlags; + float mFadeOutTime; - std::vector mLoudnessVector; - float mLoudnessFPS; + protected: + Sound_Instance mHandle; + + friend class OpenAL_Output; public: - virtual void stop() = 0; - virtual bool isPlaying() = 0; - virtual double getTimeOffset() = 0; void setPosition(const osg::Vec3f &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } - void setFadeout(float duration) { mFadeOutTime=duration; } - void setLoudnessVector(const std::vector& loudnessVector, float loudnessFPS); - - /// Get loudness at the current time position on a [0,1] scale. - /// Requires that loudnessVector was filled in by the user. - float getCurrentLoudness(); + void setBaseVolume(float volume) { mBaseVolume = volume; } + void setFadeout(float duration) { mFadeOutTime = duration; } + void updateFade(float duration) + { + if(mFadeOutTime > 0.0f) + { + float soundDuration = std::min(duration, mFadeOutTime); + mVolume *= (mFadeOutTime-soundDuration) / mFadeOutTime; + mFadeOutTime -= soundDuration; + } + } + + const osg::Vec3f &getPosition() const { return mPos; } + float getRealVolume() const { return mVolume * mBaseVolume; } + float getPitch() const { return mPitch; } + float getMinDistance() const { return mMinDistance; } + float getMaxDistance() const { return mMaxDistance; } MWBase::SoundManager::PlayType getPlayType() const { return (MWBase::SoundManager::PlayType)(mFlags&MWBase::SoundManager::Play_TypeMask); } - + bool getUseEnv() const { return !(mFlags&MWBase::SoundManager::Play_NoEnv); } + bool getIsLooping() const { return mFlags&MWBase::SoundManager::Play_Loop; } + bool getDistanceCull() const { return mFlags&MWBase::SoundManager::Play_RemoveAtDistance; } + bool getIs3D() const { return mFlags&Play_3D; } Sound(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) - : mPos(pos) - , mVolume(vol) - , mBaseVolume(basevol) - , mPitch(pitch) - , mMinDistance(mindist) - , mMaxDistance(maxdist) - , mFlags(flags) - , mFadeOutTime(0) - , mLoudnessFPS(20) + : mPos(pos), mVolume(vol), mBaseVolume(basevol), mPitch(pitch) + , mMinDistance(mindist), mMaxDistance(maxdist), mFlags(flags) + , mFadeOutTime(0.0f), mHandle(0) { } - virtual ~Sound() { } + Sound(float vol, float basevol, float pitch, int flags) + : mPos(0.0f, 0.0f, 0.0f), mVolume(vol), mBaseVolume(basevol), mPitch(pitch) + , mMinDistance(1.0f), mMaxDistance(1000.0f), mFlags(flags) + , mFadeOutTime(0.0f), mHandle(0) + { } + }; - friend class OpenAL_Output; - friend class SoundManager; + // Same as above, but it's a different type since the output handles them differently + class Stream : public Sound { + Stream& operator=(const Stream &rhs); + Stream(const Stream &rhs); + + public: + Stream(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) + : Sound(pos, vol, basevol, pitch, mindist, maxdist, flags) + { } + Stream(float vol, float basevol, float pitch, int flags) + : Sound(vol, basevol, pitch, flags) + { } }; } diff -Nru openmw-0.37.0/apps/openmw/mwsound/soundmanagerimp.cpp openmw-0.38.0/apps/openmw/mwsound/soundmanagerimp.cpp --- openmw-0.37.0/apps/openmw/mwsound/soundmanagerimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/soundmanagerimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,6 +4,8 @@ #include #include +#include + #include #include @@ -18,6 +20,7 @@ #include "../mwmechanics/actorutil.hpp" #include "sound_output.hpp" +#include "sound_buffer.hpp" #include "sound_decoder.hpp" #include "sound.hpp" @@ -39,15 +42,14 @@ , mMusicVolume(1.0f) , mVoiceVolume(1.0f) , mFootstepsVolume(1.0f) + , mSoundBuffers(new SoundBufferList::element_type()) + , mBufferCacheSize(0) , mListenerUnderwater(false) , mListenerPos(0,0,0) , mListenerDir(1,0,0) , mListenerUp(0,0,1) , mPausedSoundTypes(0) { - if(!useSound) - return; - mMasterVolume = Settings::Manager::getFloat("master volume", "Sound"); mMasterVolume = std::min(std::max(mMasterVolume, 0.0f), 1.0f); mSFXVolume = Settings::Manager::getFloat("sfx volume", "Sound"); @@ -59,41 +61,67 @@ mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); mFootstepsVolume = std::min(std::max(mFootstepsVolume, 0.0f), 1.0f); + mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1); + mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1); + mBufferCacheMax *= 1024*1024; + mBufferCacheMin = std::min(mBufferCacheMin*1024*1024, mBufferCacheMax); + + if(!useSound) + return; + + std::string hrtfname = Settings::Manager::getString("hrtf", "Sound"); + int hrtfstate = Settings::Manager::getInt("hrtf enable", "Sound"); + std::cout << "Sound output: " << SOUND_OUT << std::endl; std::cout << "Sound decoder: " << SOUND_IN << std::endl; - try - { + try { std::vector names = mOutput->enumerate(); std::cout <<"Enumerated output devices:"<< std::endl; for(size_t i = 0;i < names.size();i++) std::cout <<" "<init(devname); } - catch(std::exception &e) - { + catch(std::exception &e) { if(devname.empty()) throw; std::cerr <<"Failed to open device \""<init(); Settings::Manager::setString("device", "Sound", ""); } + + names = mOutput->enumerateHrtf(); + if(!names.empty()) + { + std::cout <<"Enumerated HRTF names:"<< std::endl; + for(size_t i = 0;i < names.size();i++) + std::cout <<" "<disableHrtf(); + else if(!hrtfname.empty()) + mOutput->enableHrtf(hrtfname, hrtfstate<0); } - catch(std::exception &e) - { + catch(std::exception &e) { std::cout <<"Sound init failed: "<begin(); + for(;sfxiter != mSoundBuffers->end();++sfxiter) + { + if(sfxiter->mHandle) + mOutput->unloadSound(sfxiter->mHandle); + sfxiter->mHandle = 0; + } + mUnusedBuffers.clear(); mOutput.reset(); } @@ -103,40 +131,154 @@ return DecoderPtr(new DEFAULT_DECODER (mVFS)); } - // Convert a soundId to file name, and modify the volume - // according to the sounds local volume setting, minRange and - // maxRange. - std::string SoundManager::lookup(const std::string &soundId, - float &volume, float &min, float &max) + Sound_Buffer *SoundManager::insertSound(const std::string &soundId, const ESM::Sound *sound) { MWBase::World* world = MWBase::Environment::get().getWorld(); - const ESM::Sound *snd = world->getStore().get().find(soundId); - - volume *= static_cast(pow(10.0, (snd->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); + static const float fAudioDefaultMinDistance = world->getStore().get().find("fAudioDefaultMinDistance")->getFloat(); + static const float fAudioDefaultMaxDistance = world->getStore().get().find("fAudioDefaultMaxDistance")->getFloat(); + static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); + static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); + float volume, min, max; - if(snd->mData.mMinRange == 0 && snd->mData.mMaxRange == 0) + volume = static_cast(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); + if(sound->mData.mMinRange == 0 && sound->mData.mMaxRange == 0) { - static const float fAudioDefaultMinDistance = world->getStore().get().find("fAudioDefaultMinDistance")->getFloat(); - static const float fAudioDefaultMaxDistance = world->getStore().get().find("fAudioDefaultMaxDistance")->getFloat(); min = fAudioDefaultMinDistance; max = fAudioDefaultMaxDistance; } else { - min = snd->mData.mMinRange; - max = snd->mData.mMaxRange; + min = sound->mData.mMinRange; + max = sound->mData.mMaxRange; } - static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); - static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); min *= fAudioMinDistanceMult; max *= fAudioMaxDistanceMult; min = std::max(min, 1.0f); max = std::max(min, max); - return "Sound/"+snd->mSound; + Sound_Buffer *sfx = &*mSoundBuffers->insert(mSoundBuffers->end(), + Sound_Buffer("Sound/"+sound->mSound, volume, min, max) + ); + mVFS->normalizeFilename(sfx->mResourceName); + + mBufferNameMap.insert(std::make_pair(soundId, sfx)); + + return sfx; } + // Lookup a soundId for its sound data (resource name, local volume, + // minRange, and maxRange) + Sound_Buffer *SoundManager::lookupSound(const std::string &soundId) const + { + NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId); + if(snd != mBufferNameMap.end()) return snd->second; + return 0; + } + + // Lookup a soundId for its sound data (resource name, local volume, + // minRange, and maxRange), and ensure it's ready for use. + Sound_Buffer *SoundManager::loadSound(const std::string &soundId) + { + Sound_Buffer *sfx; + NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId); + if(snd != mBufferNameMap.end()) + sfx = snd->second; + else + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const ESM::Sound *sound = world->getStore().get().find(soundId); + sfx = insertSound(soundId, sound); + } + + if(!sfx->mHandle) + { + sfx->mHandle = mOutput->loadSound(sfx->mResourceName); + mBufferCacheSize += mOutput->getSoundDataSize(sfx->mHandle); + + if(mBufferCacheSize > mBufferCacheMax) + { + do { + if(mUnusedBuffers.empty()) + { + std::cerr<< "No unused sound buffers to free, using "<getSoundDataSize(unused->mHandle); + mOutput->unloadSound(unused->mHandle); + unused->mHandle = 0; + + mUnusedBuffers.pop_back(); + } while(mBufferCacheSize > mBufferCacheMin); + } + mUnusedBuffers.push_front(sfx); + } + + return sfx; + } + + DecoderPtr SoundManager::loadVoice(const std::string &voicefile, Sound_Loudness **lipdata) + { + DecoderPtr decoder = getDecoder(); + // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. + if(mVFS->exists(voicefile)) + decoder->open(voicefile); + else + { + std::string file = voicefile; + std::string::size_type pos = file.rfind('.'); + if(pos != std::string::npos) + file = file.substr(0, pos)+".mp3"; + decoder->open(file); + } + + NameLoudnessRefMap::iterator lipiter = mVoiceLipNameMap.find(voicefile); + if(lipiter != mVoiceLipNameMap.end()) + { + *lipdata = lipiter->second; + return decoder; + } + + mVoiceLipBuffers.insert(mVoiceLipBuffers.end(), Sound_Loudness()); + lipiter = mVoiceLipNameMap.insert( + std::make_pair(voicefile, &mVoiceLipBuffers.back()) + ).first; + + mOutput->loadLoudnessAsync(decoder, lipiter->second); + + *lipdata = lipiter->second; + return decoder; + } + + MWBase::SoundStreamPtr SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal) + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); + static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); + static const float fAudioVoiceDefaultMinDistance = world->getStore().get().find("fAudioVoiceDefaultMinDistance")->getFloat(); + static const float fAudioVoiceDefaultMaxDistance = world->getStore().get().find("fAudioVoiceDefaultMaxDistance")->getFloat(); + static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f); + static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance); + + MWBase::SoundStreamPtr sound; + float basevol = volumeFromType(Play_TypeVoice); + if(playlocal) + { + sound.reset(new Stream(1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice|Play_2D)); + mOutput->streamSound(decoder, sound); + } + else + { + sound.reset(new Stream(pos, 1.0f, basevol, 1.0f, minDistance, maxDistance, + Play_Normal|Play_TypeVoice|Play_3D)); + mOutput->streamSound3D(decoder, sound); + } + return sound; + } + + // Gets the combined volume settings for the given sound type float SoundManager::volumeFromType(PlayType type) const { @@ -163,23 +305,11 @@ return volume; } - bool SoundManager::isPlaying(const MWWorld::Ptr &ptr, const std::string &id) const - { - SoundMap::const_iterator snditer = mActiveSounds.begin(); - while(snditer != mActiveSounds.end()) - { - if(snditer->second.first == ptr && snditer->second.second == id && snditer->first->isPlaying()) - return true; - ++snditer; - } - return false; - } - void SoundManager::stopMusic() { if(mMusic) - mMusic->stop(); + mOutput->finishStream(mMusic); mMusic.reset(); } @@ -189,19 +319,19 @@ return; std::cout <<"Playing "<open(filename); - mMusic = mOutput->streamSound(decoder, volumeFromType(Play_TypeMusic), - 1.0f, Play_NoEnv|Play_TypeMusic); + mMusic.reset(new Stream(1.0f, volumeFromType(Play_TypeMusic), 1.0f, + Play_NoEnv|Play_TypeMusic|Play_2D)); + mOutput->streamSound(decoder, mMusic); } - catch(std::exception &e) - { + catch(std::exception &e) { std::cout << "Music Error: " << e.what() << "\n"; + mMusic.reset(); } } @@ -252,7 +382,7 @@ bool SoundManager::isMusicPlaying() { - return mMusic && mMusic->isPlaying(); + return mMusic && mOutput->isStreamPlaying(mMusic); } void SoundManager::playPlaylist(const std::string &playlist) @@ -261,31 +391,37 @@ startRandomTitle(); } - void SoundManager::say(const MWWorld::Ptr &ptr, const std::string& filename) + + void SoundManager::say(const MWWorld::ConstPtr &ptr, const std::string &filename) { if(!mOutput->isInitialized()) return; try { - float basevol = volumeFromType(Play_TypeVoice); - std::string filePath = "Sound/"+filename; - const ESM::Position &pos = ptr.getRefData().getPosition(); - const osg::Vec3f objpos(pos.asVec3()); + std::string voicefile = "Sound/"+filename; + + Sound_Loudness *loudness; + mVFS->normalizeFilename(voicefile); + DecoderPtr decoder = loadVoice(voicefile, &loudness); + + if(!loudness->isReady()) + mPendingSaySounds[ptr] = std::make_pair(decoder, loudness); + else + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); + + SaySoundMap::iterator oldIt = mActiveSaySounds.find(ptr); + if (oldIt != mActiveSaySounds.end()) + { + mOutput->finishStream(oldIt->second.first); + mActiveSaySounds.erase(oldIt); + } + + MWBase::SoundStreamPtr sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer())); - MWBase::World* world = MWBase::Environment::get().getWorld(); - static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); - static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); - static const float fAudioVoiceDefaultMinDistance = world->getStore().get().find("fAudioVoiceDefaultMinDistance")->getFloat(); - static const float fAudioVoiceDefaultMaxDistance = world->getStore().get().find("fAudioVoiceDefaultMaxDistance")->getFloat(); - - float minDistance = fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult; - float maxDistance = fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult; - minDistance = std::max(minDistance, 1.f); - maxDistance = std::max(minDistance, maxDistance); - - MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, - minDistance, maxDistance, Play_Normal|Play_TypeVoice, 0, true); - mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); + mActiveSaySounds.insert(std::make_pair(ptr, std::make_pair(sound, loudness))); + } } catch(std::exception &e) { @@ -293,19 +429,18 @@ } } - float SoundManager::getSaySoundLoudness(const MWWorld::Ptr &ptr) const + float SoundManager::getSaySoundLoudness(const MWWorld::ConstPtr &ptr) const { - SoundMap::const_iterator snditer = mActiveSounds.begin(); - while(snditer != mActiveSounds.end()) + SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr); + if(snditer != mActiveSaySounds.end()) { - if(snditer->second.first == ptr && snditer->second.second == "_say_sound") - break; - ++snditer; + MWBase::SoundStreamPtr sound = snditer->second.first; + Sound_Loudness *loudness = snditer->second.second; + float sec = mOutput->getStreamOffset(sound); + return loudness->getLoudnessAtTime(sec); } - if (snditer == mActiveSounds.end()) - return 0.f; - return snditer->first->getCurrentLoudness(); + return 0.0f; } void SoundManager::say(const std::string& filename) @@ -314,11 +449,26 @@ return; try { - float basevol = volumeFromType(Play_TypeVoice); - std::string filePath = "Sound/"+filename; + std::string voicefile = "Sound/"+filename; + + Sound_Loudness *loudness; + mVFS->normalizeFilename(voicefile); + DecoderPtr decoder = loadVoice(voicefile, &loudness); + + if(!loudness->isReady()) + mPendingSaySounds[MWWorld::ConstPtr()] = std::make_pair(decoder, loudness); + else + { + SaySoundMap::iterator oldIt = mActiveSaySounds.find(MWWorld::ConstPtr()); + if (oldIt != mActiveSaySounds.end()) + { + mOutput->finishStream(oldIt->second.first); + mActiveSaySounds.erase(oldIt); + } - MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice, 0); - mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); + mActiveSaySounds.insert(std::make_pair(MWWorld::ConstPtr(), + std::make_pair(playVoice(decoder, osg::Vec3f(), true), loudness))); + } } catch(std::exception &e) { @@ -326,35 +476,42 @@ } } - bool SoundManager::sayDone(const MWWorld::Ptr &ptr) const + bool SoundManager::sayDone(const MWWorld::ConstPtr &ptr) const { - return !isPlaying(ptr, "_say_sound"); + SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr); + if(snditer != mActiveSaySounds.end()) + { + if(mOutput->isStreamPlaying(snditer->second.first)) + return false; + return true; + } + return mPendingSaySounds.find(ptr) == mPendingSaySounds.end(); } - void SoundManager::stopSay(const MWWorld::Ptr &ptr) + void SoundManager::stopSay(const MWWorld::ConstPtr &ptr) { - SoundMap::iterator snditer = mActiveSounds.begin(); - while(snditer != mActiveSounds.end()) + SaySoundMap::iterator snditer = mActiveSaySounds.find(ptr); + if(snditer != mActiveSaySounds.end()) { - if(snditer->second.first == ptr && snditer->second.second == "_say_sound") - { - snditer->first->stop(); - mActiveSounds.erase(snditer++); - } - else - ++snditer; + mOutput->finishStream(snditer->second.first); + mActiveSaySounds.erase(snditer); } + mPendingSaySounds.erase(ptr); } - MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder, PlayType type) + MWBase::SoundStreamPtr SoundManager::playTrack(const DecoderPtr& decoder, PlayType type) { - MWBase::SoundPtr track; + MWBase::SoundStreamPtr track; if(!mOutput->isInitialized()) return track; try { - track = mOutput->streamSound(decoder, volumeFromType(type), 1.0f, Play_NoEnv|type); + track.reset(new Stream(1.0f, volumeFromType(type), 1.0f, Play_NoEnv|type|Play_2D)); + mOutput->streamSound(decoder, track); + + TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), track); + mActiveTracks.insert(iter, track); } catch(std::exception &e) { @@ -363,6 +520,19 @@ return track; } + void SoundManager::stopTrack(MWBase::SoundStreamPtr stream) + { + mOutput->finishStream(stream); + TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), stream); + if(iter != mActiveTracks.end() && *iter == stream) + mActiveTracks.erase(iter); + } + + double SoundManager::getTrackTimeDelay(MWBase::SoundStreamPtr stream) + { + return mOutput->getStreamDelay(stream); + } + MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, PlayType type, PlayMode mode, float offset) { @@ -371,21 +541,28 @@ return sound; try { + Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); float basevol = volumeFromType(type); - float min, max; - std::string file = lookup(soundId, volume, min, max); - sound = mOutput->playSound(file, volume, basevol, pitch, mode|type, offset); - mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); + sound.reset(new Sound(volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D)); + mOutput->playSound(sound, sfx->mHandle, offset); + if(sfx->mUses++ == 0) + { + SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); + if(iter != mUnusedBuffers.end()) + mUnusedBuffers.erase(iter); + } + mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx)); } catch(std::exception&) { //std::cout <<"Sound Error: "< 2000*2000) - { + if((mode&Play_RemoveAtDistance) && (mListenerPos-objpos).length2() > 2000*2000) return MWBase::SoundPtr(); - } - sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|type, offset); - if((mode&Play_NoTrack)) - mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); + if(!(mode&Play_NoPlayerLocal) && ptr == MWMechanics::getPlayer()) + { + sound.reset(new Sound(volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D)); + mOutput->playSound(sound, sfx->mHandle, offset); + } else - mActiveSounds[sound] = std::make_pair(ptr, soundId); + { + sound.reset(new Sound(objpos, volume * sfx->mVolume, basevol, pitch, + sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D)); + mOutput->playSound3D(sound, sfx->mHandle, offset); + } + if(sfx->mUses++ == 0) + { + SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); + if(iter != mUnusedBuffers.end()) + mUnusedBuffers.erase(iter); + } + mActiveSounds[ptr].push_back(std::make_pair(sound, sfx)); } catch(std::exception&) { //std::cout <<"Sound Error: "<isInitialized()) @@ -427,62 +615,57 @@ try { // Look up the sound in the ESM data + Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); float basevol = volumeFromType(type); - float min, max; - std::string file = lookup(soundId, volume, min, max); - sound = mOutput->playSound3D(file, initialPos, volume, basevol, pitch, min, max, mode|type, offset); - mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); + sound.reset(new Sound(initialPos, volume * sfx->mVolume, basevol, pitch, + sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D)); + mOutput->playSound3D(sound, sfx->mHandle, offset); + if(sfx->mUses++ == 0) + { + SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); + if(iter != mUnusedBuffers.end()) + mUnusedBuffers.erase(iter); + } + mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx)); } catch(std::exception &) { //std::cout <<"Sound Error: "<first == sound) - { - snditer->first->stop(); - mActiveSounds.erase(snditer++); - } - else - ++snditer; - } + if (sound.get()) + mOutput->finishSound(sound); } - void SoundManager::stopSound3D(const MWWorld::Ptr &ptr, const std::string& soundId) + void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId) { - SoundMap::iterator snditer = mActiveSounds.begin(); - while(snditer != mActiveSounds.end()) + SoundMap::iterator snditer = mActiveSounds.find(ptr); + if(snditer != mActiveSounds.end()) { - if(snditer->second.first == ptr && snditer->second.second == soundId) + Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); + SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); + for(;sndidx != snditer->second.end();++sndidx) { - snditer->first->stop(); - mActiveSounds.erase(snditer++); + if(sndidx->second == sfx) + mOutput->finishSound(sndidx->first); } - else - ++snditer; } } - void SoundManager::stopSound3D(const MWWorld::Ptr &ptr) + void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr) { - SoundMap::iterator snditer = mActiveSounds.begin(); - while(snditer != mActiveSounds.end()) + SoundMap::iterator snditer = mActiveSounds.find(ptr); + if(snditer != mActiveSounds.end()) { - if(snditer->second.first == ptr) - { - snditer->first->stop(); - mActiveSounds.erase(snditer++); - } - else - ++snditer; + SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); + for(;sndidx != snditer->second.end();++sndidx) + mOutput->finishSound(sndidx->first); } } @@ -491,51 +674,74 @@ SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { - if(snditer->second.first != MWWorld::Ptr() && - snditer->second.first != MWMechanics::getPlayer() && - snditer->second.first.getCell() == cell) + if(snditer->first != MWWorld::ConstPtr() && + snditer->first != MWMechanics::getPlayer() && + snditer->first.getCell() == cell) { - snditer->first->stop(); - mActiveSounds.erase(snditer++); + SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); + for(;sndidx != snditer->second.end();++sndidx) + mOutput->finishSound(sndidx->first); } - else - ++snditer; + ++snditer; + } + SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); + while(sayiter != mActiveSaySounds.end()) + { + if(sayiter->first != MWWorld::ConstPtr() && + sayiter->first != MWMechanics::getPlayer() && + sayiter->first.getCell() == cell) + { + mOutput->finishStream(sayiter->second.first); + } + ++sayiter; } } void SoundManager::stopSound(const std::string& soundId) { - SoundMap::iterator snditer = mActiveSounds.begin(); - while(snditer != mActiveSounds.end()) + SoundMap::iterator snditer = mActiveSounds.find(MWWorld::ConstPtr()); + if(snditer != mActiveSounds.end()) { - if(snditer->second.first == MWWorld::Ptr() && - snditer->second.second == soundId) + Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); + SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); + for(;sndidx != snditer->second.end();++sndidx) { - snditer->first->stop(); - mActiveSounds.erase(snditer++); + if(sndidx->second == sfx) + mOutput->finishSound(sndidx->first); } - else - ++snditer; } } - void SoundManager::fadeOutSound3D(const MWWorld::Ptr &ptr, + void SoundManager::fadeOutSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId, float duration) { - SoundMap::iterator snditer = mActiveSounds.begin(); - while(snditer != mActiveSounds.end()) + SoundMap::iterator snditer = mActiveSounds.find(ptr); + if(snditer != mActiveSounds.end()) { - if(snditer->second.first == ptr && snditer->second.second == soundId) + Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); + SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); + for(;sndidx != snditer->second.end();++sndidx) { - snditer->first->setFadeout(duration); + if(sndidx->second == sfx) + sndidx->first->setFadeout(duration); } - ++snditer; } } - bool SoundManager::getSoundPlaying(const MWWorld::Ptr &ptr, const std::string& soundId) const + bool SoundManager::getSoundPlaying(const MWWorld::ConstPtr &ptr, const std::string& soundId) const { - return isPlaying(ptr, soundId); + SoundMap::const_iterator snditer = mActiveSounds.find(ptr); + if(snditer != mActiveSounds.end()) + { + Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId)); + SoundBufferRefPairList::const_iterator sndidx = snditer->second.begin(); + for(;sndidx != snditer->second.end();++sndidx) + { + if(sndidx->second == sfx && mOutput->isSoundPlaying(sndidx->first)) + return true; + } + } + return false; } @@ -567,7 +773,7 @@ static std::string regionName = ""; static float sTimePassed = 0.0; MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Ptr player = world->getPlayerPtr(); + const MWWorld::ConstPtr player = world->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->getCell(); sTimePassed += duration; @@ -636,18 +842,14 @@ Environment env = Env_Normal; if (mListenerUnderwater) - { env = Env_Underwater; - //play underwater sound - if(!(mUnderwaterSound && mUnderwaterSound->isPlaying())) - mUnderwaterSound = playSound("Underwater", 1.0f, 1.0f, Play_TypeSfx, Play_LoopNoEnv); - } else if(mUnderwaterSound) { - mUnderwaterSound->stop(); + mOutput->finishSound(mUnderwaterSound); mUnderwaterSound.reset(); } + mOutput->startUpdate(); mOutput->updateListener( mListenerPos, mListenerDir, @@ -656,44 +858,149 @@ ); // Check if any sounds are finished playing, and trash them - // Lower volume on fading out sounds SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { - if(!snditer->first->isPlaying()) - mActiveSounds.erase(snditer++); - else + SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); + while(sndidx != snditer->second.end()) { - const MWWorld::Ptr &ptr = snditer->second.first; - if(!ptr.isEmpty()) + MWWorld::ConstPtr ptr = snditer->first; + MWBase::SoundPtr sound = sndidx->first; + if(!ptr.isEmpty() && sound->getIs3D()) { const ESM::Position &pos = ptr.getRefData().getPosition(); const osg::Vec3f objpos(pos.asVec3()); - snditer->first->setPosition(objpos); + sound->setPosition(objpos); - if ((snditer->first->mFlags & Play_RemoveAtDistance) - && (mListenerPos - ptr.getRefData().getPosition().asVec3()).length2() > 2000*2000) + if(sound->getDistanceCull()) { - mActiveSounds.erase(snditer++); - continue; + if((mListenerPos - objpos).length2() > 2000*2000) + mOutput->finishSound(sound); } } - //update fade out - if(snditer->first->mFadeOutTime>0) + + if(!mOutput->isSoundPlaying(sound)) + { + mOutput->finishSound(sound); + Sound_Buffer *sfx = sndidx->second; + if(sfx->mUses-- == 1) + mUnusedBuffers.push_front(sfx); + sndidx = snditer->second.erase(sndidx); + } + else { - float soundDuration=duration; - if(soundDuration>snditer->first->mFadeOutTime) - soundDuration=snditer->first->mFadeOutTime; - snditer->first->setVolume(snditer->first->mVolume - - soundDuration / snditer->first->mFadeOutTime * snditer->first->mVolume); - snditer->first->mFadeOutTime -= soundDuration; + sound->updateFade(duration); + + mOutput->updateSound(sound); + ++sndidx; } - snditer->first->update(); + } + if(snditer->second.empty()) + mActiveSounds.erase(snditer++); + else ++snditer; + } + + SayDecoderMap::iterator penditer = mPendingSaySounds.begin(); + while(penditer != mPendingSaySounds.end()) + { + Sound_Loudness *loudness = penditer->second.second; + if(loudness->isReady()) + { + try { + DecoderPtr decoder = penditer->second.first; + decoder->rewind(); + + MWBase::SoundStreamPtr sound; + MWWorld::ConstPtr ptr = penditer->first; + + SaySoundMap::iterator old = mActiveSaySounds.find(ptr); + if (old != mActiveSaySounds.end()) + { + mOutput->finishStream(old->second.first); + mActiveSaySounds.erase(old); + } + + if(ptr == MWWorld::ConstPtr()) + sound = playVoice(decoder, osg::Vec3f(), true); + else + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); + sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer())); + } + mActiveSaySounds.insert(std::make_pair(ptr, std::make_pair(sound, loudness))); + } + catch(std::exception &e) { + std::cerr<< "Sound Error: "<first; + MWBase::SoundStreamPtr sound = sayiter->second.first; + if(!ptr.isEmpty() && sound->getIs3D()) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); + sound->setPosition(pos); + + if(sound->getDistanceCull()) + { + if((mListenerPos - pos).length2() > 2000*2000) + mOutput->finishStream(sound); + } + } + + if(!mOutput->isStreamPlaying(sound)) + { + mOutput->finishStream(sound); + mActiveSaySounds.erase(sayiter++); + } + else + { + sound->updateFade(duration); + + mOutput->updateStream(sound); + ++sayiter; + } + } + + TrackList::iterator trkiter = mActiveTracks.begin(); + for(;trkiter != mActiveTracks.end();++trkiter) + { + MWBase::SoundStreamPtr sound = *trkiter; + if(!mOutput->isStreamPlaying(sound)) + { + mOutput->finishStream(sound); + trkiter = mActiveTracks.erase(trkiter); + } + else + { + sound->updateFade(duration); + + mOutput->updateStream(sound); + ++trkiter; } } + + if(mListenerUnderwater) + { + // Play underwater sound (after updating sounds) + if(!(mUnderwaterSound && mOutput->isSoundPlaying(mUnderwaterSound))) + mUnderwaterSound = playSound("Underwater", 1.0f, 1.0f, Play_TypeSfx, Play_LoopNoEnv); + } + mOutput->finishUpdate(); } + void SoundManager::update(float duration) { if(!mOutput->isInitialized()) @@ -716,39 +1023,73 @@ mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); mVoiceVolume = Settings::Manager::getFloat("voice volume", "Sound"); + if(!mOutput->isInitialized()) + return; + mOutput->startUpdate(); SoundMap::iterator snditer = mActiveSounds.begin(); - while(snditer != mActiveSounds.end()) + for(;snditer != mActiveSounds.end();++snditer) { - snditer->first->mBaseVolume = volumeFromType(snditer->first->getPlayType()); - snditer->first->update(); - ++snditer; + SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); + for(;sndidx != snditer->second.end();++sndidx) + { + MWBase::SoundPtr sound = sndidx->first; + sound->setBaseVolume(volumeFromType(sound->getPlayType())); + mOutput->updateSound(sound); + } + } + SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); + for(;sayiter != mActiveSaySounds.end();++sayiter) + { + MWBase::SoundStreamPtr sound = sayiter->second.first; + sound->setBaseVolume(volumeFromType(sound->getPlayType())); + mOutput->updateStream(sound); + } + TrackList::iterator trkiter = mActiveTracks.begin(); + for(;trkiter != mActiveTracks.end();++trkiter) + { + MWBase::SoundStreamPtr sound = *trkiter; + sound->setBaseVolume(volumeFromType(sound->getPlayType())); + mOutput->updateStream(sound); } if(mMusic) { - mMusic->mBaseVolume = volumeFromType(mMusic->getPlayType()); - mMusic->update(); + mMusic->setBaseVolume(volumeFromType(mMusic->getPlayType())); + mOutput->updateStream(mMusic); } + mOutput->finishUpdate(); } - void SoundManager::setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up) + void SoundManager::setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater) { mListenerPos = pos; mListenerDir = dir; mListenerUp = up; - MWWorld::Ptr player = - MWMechanics::getPlayer(); - const MWWorld::CellStore *cell = player.getCell(); - - mListenerUnderwater = ((cell->getCell()->mData.mFlags&ESM::Cell::HasWater) && mListenerPos.z() < cell->getWaterLevel()); + mListenerUnderwater = underwater; } - void SoundManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) + void SoundManager::updatePtr(const MWWorld::ConstPtr &old, const MWWorld::ConstPtr &updated) { - for (SoundMap::iterator snditer = mActiveSounds.begin(); snditer != mActiveSounds.end(); ++snditer) + SoundMap::iterator snditer = mActiveSounds.find(old); + if(snditer != mActiveSounds.end()) + { + SoundBufferRefPairList sndlist = snditer->second; + mActiveSounds.erase(snditer); + mActiveSounds[updated] = sndlist; + } + SaySoundMap::iterator sayiter = mActiveSaySounds.find(old); + if(sayiter != mActiveSaySounds.end()) + { + SoundLoudnessPair sndlist = sayiter->second; + mActiveSaySounds.erase(sayiter); + mActiveSaySounds[updated] = sndlist; + } + SayDecoderMap::iterator penditer = mPendingSaySounds.find(old); + if(penditer != mPendingSaySounds.end()) { - if (snditer->second.first == old) - snditer->second.first = updated; + DecoderLoudnessPair dl = penditer->second; + mPendingSaySounds.erase(penditer); + mPendingSaySounds[updated] = dl; } } @@ -819,10 +1160,29 @@ void SoundManager::clear() { - for (SoundMap::iterator iter (mActiveSounds.begin()); iter!=mActiveSounds.end(); ++iter) - iter->first->stop(); - + SoundMap::iterator snditer = mActiveSounds.begin(); + for(;snditer != mActiveSounds.end();++snditer) + { + SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); + for(;sndidx != snditer->second.end();++sndidx) + { + mOutput->finishSound(sndidx->first); + Sound_Buffer *sfx = sndidx->second; + if(sfx->mUses-- == 1) + mUnusedBuffers.push_front(sfx); + } + } mActiveSounds.clear(); + SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); + for(;sayiter != mActiveSaySounds.end();++sayiter) + mOutput->finishStream(sayiter->second.first); + mActiveSaySounds.clear(); + TrackList::iterator trkiter = mActiveTracks.begin(); + for(;trkiter != mActiveTracks.end();++trkiter) + mOutput->finishStream(*trkiter); + mActiveTracks.clear(); + mPendingSaySounds.clear(); + mUnderwaterSound.reset(); stopMusic(); } } diff -Nru openmw-0.37.0/apps/openmw/mwsound/soundmanagerimp.hpp openmw-0.38.0/apps/openmw/mwsound/soundmanagerimp.hpp --- openmw-0.37.0/apps/openmw/mwsound/soundmanagerimp.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/soundmanagerimp.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,14 +1,17 @@ #ifndef GAME_SOUND_SOUNDMANAGER_H #define GAME_SOUND_SOUNDMANAGER_H +#include #include #include +#include #include #include #include +#include "loudness.hpp" #include "../mwbase/soundmanager.hpp" namespace VFS @@ -16,16 +19,27 @@ class Manager; } +namespace ESM +{ + struct Sound; +} + namespace MWSound { class Sound_Output; struct Sound_Decoder; class Sound; + class Sound_Buffer; enum Environment { Env_Normal, Env_Underwater }; + // Extra play flags, not intended for caller use + enum PlayModeEx { + Play_2D = 0, + Play_3D = 1<<31 + }; class SoundManager : public MWBase::SoundManager { @@ -43,14 +57,46 @@ float mVoiceVolume; float mFootstepsVolume; - boost::shared_ptr mMusic; - std::string mCurrentPlaylist; - - typedef std::pair PtrIDPair; - typedef std::map SoundMap; + typedef std::auto_ptr > SoundBufferList; + // List of sound buffers, grown as needed. New enties are added to the + // back, allowing existing Sound_Buffer references/pointers to remain + // valid. + SoundBufferList mSoundBuffers; + size_t mBufferCacheMin; + size_t mBufferCacheMax; + size_t mBufferCacheSize; + + typedef std::map NameBufferMap; + NameBufferMap mBufferNameMap; + + typedef std::deque LoudnessList; + LoudnessList mVoiceLipBuffers; + + typedef std::map NameLoudnessRefMap; + NameLoudnessRefMap mVoiceLipNameMap; + + // NOTE: unused buffers are stored in front-newest order. + typedef std::deque SoundList; + SoundList mUnusedBuffers; + + typedef std::pair SoundBufferRefPair; + typedef std::vector SoundBufferRefPairList; + typedef std::map SoundMap; SoundMap mActiveSounds; - MWBase::SoundPtr mUnderwaterSound; + typedef std::pair SoundLoudnessPair; + typedef std::map SaySoundMap; + SaySoundMap mActiveSaySounds; + + typedef std::pair DecoderLoudnessPair; + typedef std::map SayDecoderMap; + SayDecoderMap mPendingSaySounds; + + typedef std::vector TrackList; + TrackList mActiveTracks; + + MWBase::SoundStreamPtr mMusic; + std::string mCurrentPlaylist; bool mListenerUnderwater; osg::Vec3f mListenerPos; @@ -59,10 +105,20 @@ int mPausedSoundTypes; - std::string lookup(const std::string &soundId, - float &volume, float &min, float &max); + MWBase::SoundPtr mUnderwaterSound; + + Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); + + Sound_Buffer *lookupSound(const std::string &soundId) const; + Sound_Buffer *loadSound(const std::string &soundId); + + // Ensures the loudness/"lip" data gets loaded, and returns a decoder + // to start streaming + DecoderPtr loadVoice(const std::string &voicefile, Sound_Loudness **lipdata); + + MWBase::SoundStreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal); + void streamMusicFull(const std::string& filename); - bool isPlaying(const MWWorld::Ptr &ptr, const std::string &id) const; void updateSounds(float duration); void updateRegionSound(float duration); @@ -98,7 +154,7 @@ ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist - virtual void say(const MWWorld::Ptr &reference, const std::string& filename); + virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. @@ -106,59 +162,66 @@ ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. - virtual bool sayDone(const MWWorld::Ptr &reference=MWWorld::Ptr()) const; + virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const; ///< Is actor not speaking? - virtual void stopSay(const MWWorld::Ptr &reference=MWWorld::Ptr()); + virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()); ///< Stop an actor speaking - virtual float getSaySoundLoudness(const MWWorld::Ptr& reference) const; + virtual float getSaySoundLoudness(const MWWorld::ConstPtr& reference) const; ///< Check the currently playing say sound for this actor /// and get an average loudness value (scale [0,1]) at the current time position. /// If the actor is not saying anything, returns 0. - virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); + virtual MWBase::SoundStreamPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder + virtual void stopTrack(MWBase::SoundStreamPtr stream); + ///< Stop the given audio track from playing + + virtual double getTrackTimeDelay(MWBase::SoundStreamPtr stream); + ///< Retives the time delay, in seconds, of the audio track (must be a sound + /// returned by \ref playTrack). Only intended to be called by the track + /// decoder's read method. + virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0); ///< Play a sound, independently of 3D-position - ///< @param offset value from [0,1], when to start playback. 0 is beginning, 1 is end. + ///< @param offset Number of seconds into the sound to start playback. - virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, + virtual MWBase::SoundPtr playSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0); ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified. - ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + ///< @param offset Number of seconds into the sound to start playback. - virtual MWBase::SoundPtr playManualSound3D(const osg::Vec3f& initialPos, const std::string& soundId, - float volume, float pitch, PlayType type, PlayMode mode, float offset=0); - ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition. + virtual MWBase::SoundPtr playSound3D(const osg::Vec3f& initialPos, const std::string& soundId, + float volume, float pitch, PlayType type, PlayMode mode, float offset=0); + ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated using Sound::setPosition. + ///< @param offset Number of seconds into the sound to start playback. - ///< Play a sound from an object - ///< @param offset value from [0,1], when to start playback. 0 is beginning, 1 is end. + virtual void stopSound(MWBase::SoundPtr sound); + ///< Stop the given sound from playing + /// @note no-op if \a sound is null - virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId); + virtual void stopSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId); ///< Stop the given object from playing the given sound, - virtual void stopSound3D(const MWWorld::Ptr &reference); + virtual void stopSound3D(const MWWorld::ConstPtr &reference); ///< Stop the given object from playing all sounds. - virtual void stopSound(MWBase::SoundPtr sound); - ///< Stop the given sound handle - virtual void stopSound(const MWWorld::CellStore *cell); ///< Stop all sounds for the given cell. virtual void stopSound(const std::string& soundId); ///< Stop a non-3d looping sound - virtual void fadeOutSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float duration); + virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration); ///< Fade out given sound (that is already playing) of given object ///< @param reference Reference to object, whose sound is faded out ///< @param soundId ID of the sound to fade out. ///< @param duration Time until volume reaches 0. - virtual bool getSoundPlaying(const MWWorld::Ptr &reference, const std::string& soundId) const; + virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? virtual void pauseSounds(int types=Play_TypeMask); @@ -169,9 +232,9 @@ virtual void update(float duration); - virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up); + virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater); - virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated); + virtual void updatePtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated); virtual void clear(); }; diff -Nru openmw-0.37.0/apps/openmw/mwsound/sound_output.hpp openmw-0.38.0/apps/openmw/mwsound/sound_output.hpp --- openmw-0.37.0/apps/openmw/mwsound/sound_output.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwsound/sound_output.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,48 +3,73 @@ #include #include +#include #include "soundmanagerimp.hpp" -#include "../mwworld/ptr.hpp" - namespace MWSound { class SoundManager; struct Sound_Decoder; class Sound; + class Sound_Loudness; + + // An opaque handle for the implementation's sound buffers. + typedef void *Sound_Handle; + // An opaque handle for the implementation's sound instances. + typedef void *Sound_Instance; class Sound_Output { SoundManager &mManager; virtual std::vector enumerate() = 0; - virtual void init(const std::string &devname="") = 0; + virtual void init(const std::string &devname=std::string()) = 0; virtual void deinit() = 0; - /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. - virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags, float offset) = 0; - /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. - virtual MWBase::SoundPtr playSound3D(const std::string &fname, const osg::Vec3f &pos, - float vol, float basevol, float pitch, float min, float max, int flags, float offset, bool extractLoudness=false) = 0; - virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0; + virtual std::vector enumerateHrtf() = 0; + virtual void enableHrtf(const std::string &hrtfname, bool auto_enable) = 0; + virtual void disableHrtf() = 0; + + virtual Sound_Handle loadSound(const std::string &fname) = 0; + virtual void unloadSound(Sound_Handle data) = 0; + virtual size_t getSoundDataSize(Sound_Handle data) const = 0; + + virtual void playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset) = 0; + virtual void playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset) = 0; + virtual void finishSound(MWBase::SoundPtr sound) = 0; + virtual bool isSoundPlaying(MWBase::SoundPtr sound) = 0; + virtual void updateSound(MWBase::SoundPtr sound) = 0; + + virtual void streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound) = 0; + virtual void streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound) = 0; + virtual void finishStream(MWBase::SoundStreamPtr sound) = 0; + virtual double getStreamDelay(MWBase::SoundStreamPtr sound) = 0; + virtual double getStreamOffset(MWBase::SoundStreamPtr sound) = 0; + virtual bool isStreamPlaying(MWBase::SoundStreamPtr sound) = 0; + virtual void updateStream(MWBase::SoundStreamPtr sound) = 0; + + virtual void startUpdate() = 0; + virtual void finishUpdate() = 0; virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) = 0; virtual void pauseSounds(int types) = 0; virtual void resumeSounds(int types) = 0; + // HACK: The sound output implementation really shouldn't be handling + // asynchronous loudness data loading, but it's currently where we have + // a background processing thread. + virtual void loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness) = 0; + Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); protected: bool mInitialized; - osg::Vec3f mPos; Sound_Output(SoundManager &mgr) - : mManager(mgr) - , mInitialized(false) - , mPos(0.0f, 0.0f, 0.0f) + : mManager(mgr), mInitialized(false) { } public: virtual ~Sound_Output() { } diff -Nru openmw-0.37.0/apps/openmw/mwstate/statemanagerimp.cpp openmw-0.38.0/apps/openmw/mwstate/statemanagerimp.cpp --- openmw-0.37.0/apps/openmw/mwstate/statemanagerimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwstate/statemanagerimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -207,7 +207,9 @@ else slot = getCurrentCharacter()->updateSlot (slot, profile); - boost::filesystem::ofstream stream (slot->mPath, std::ios::binary); + // Write to a memory stream first. If there is an exception during the save process, we don't want to trash the + // existing save file we are overwriting. + std::stringstream stream; ESM::ESMWriter writer; @@ -240,7 +242,7 @@ Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); // Using only Cells for progress information, since they typically have the largest records by far listener.setProgressRange(MWBase::Environment::get().getWorld()->countSavedGameCells()); - listener.setLabel("#{sNotifyMessage4}"); + listener.setLabel("#{sNotifyMessage4}", true); Loading::ScopedLoad load(&listener); @@ -262,7 +264,14 @@ writer.close(); if (stream.fail()) - throw std::runtime_error("Write operation failed"); + throw std::runtime_error("Write operation failed (memory stream)"); + + // All good, write to file + boost::filesystem::ofstream filestream (slot->mPath, std::ios::binary); + filestream << stream.rdbuf(); + + if (filestream.fail()) + throw std::runtime_error("Write operation failed (file stream)"); Settings::Manager::setString ("character", "Saves", slot->mPath.parent_path().filename().string()); @@ -473,9 +482,9 @@ if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson()) MWBase::Environment::get().getWorld()->togglePOV(); - MWWorld::Ptr ptr = MWMechanics::getPlayer(); + MWWorld::ConstPtr ptr = MWMechanics::getPlayer(); - ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); + const ESM::CellId& cellId = ptr.getCell()->getCell()->getCellId(); // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false); @@ -520,7 +529,7 @@ MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) { - MWWorld::Ptr player = MWMechanics::getPlayer(); + MWWorld::ConstPtr player = MWMechanics::getPlayer(); std::string name = player.get()->mBase->mName; return mCharacterManager.getCurrentCharacter (create, name); diff -Nru openmw-0.37.0/apps/openmw/mwworld/action.cpp openmw-0.38.0/apps/openmw/mwworld/action.cpp --- openmw-0.37.0/apps/openmw/mwworld/action.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/action.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,19 +19,26 @@ void MWWorld::Action::execute (const Ptr& actor) { - if (!mSoundId.empty()) + if(!mSoundId.empty()) { - if (mKeepSound && actor == MWMechanics::getPlayer()) + if(mKeepSound && actor == MWMechanics::getPlayer()) MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Normal,mSoundOffset); + MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Normal, mSoundOffset + ); else { bool local = mTarget.isEmpty() || !mTarget.isInCell(); // no usable target - - MWBase::Environment::get().getSoundManager()->playSound3D(local ? actor : mTarget, - mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, - mKeepSound ? MWBase::SoundManager::Play_NoTrack : MWBase::SoundManager::Play_Normal, - mSoundOffset); + if(mKeepSound) + MWBase::Environment::get().getSoundManager()->playSound3D( + (local ? actor : mTarget).getRefData().getPosition().asVec3(), + mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, + MWBase::SoundManager::Play_Normal, mSoundOffset + ); + else + MWBase::Environment::get().getSoundManager()->playSound3D(local ? actor : mTarget, + mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, + MWBase::SoundManager::Play_Normal, mSoundOffset + ); } } diff -Nru openmw-0.37.0/apps/openmw/mwworld/actioneat.cpp openmw-0.38.0/apps/openmw/mwworld/actioneat.cpp --- openmw-0.37.0/apps/openmw/mwworld/actioneat.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/actioneat.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,7 +19,7 @@ getTarget().getContainerStore()->remove(getTarget(), 1, actor); // apply to actor - std::string id = getTarget().getClass().getId (getTarget()); + std::string id = getTarget().getCellRef().getRefId(); if (actor.getClass().apply (actor, id, actor) && actor == MWMechanics::getPlayer()) actor.getClass().skillUsageSucceeded (actor, ESM::Skill::Alchemy, 1); diff -Nru openmw-0.37.0/apps/openmw/mwworld/actionequip.cpp openmw-0.38.0/apps/openmw/mwworld/actionequip.cpp --- openmw-0.37.0/apps/openmw/mwworld/actionequip.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/actionequip.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -52,7 +52,12 @@ } } - assert(it != invStore.end()); + if (it == invStore.end()) + { + std::stringstream error; + error << "ActionEquip can't find item " << object.getCellRef().getRefId(); + throw std::runtime_error(error.str()); + } // equip the item in the first free slot std::vector::const_iterator slot=slots_.first.begin(); diff -Nru openmw-0.37.0/apps/openmw/mwworld/actionteleport.cpp openmw-0.38.0/apps/openmw/mwworld/actionteleport.cpp --- openmw-0.37.0/apps/openmw/mwworld/actionteleport.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/actionteleport.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,6 +3,9 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" + +#include "../mwworld/class.hpp" + #include "player.hpp" namespace @@ -40,6 +43,11 @@ for(std::set::iterator it = followers.begin();it != followers.end();++it) { MWWorld::Ptr follower = *it; + + std::string script = follower.getClass().getScript(follower); + if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1) + continue; + if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() <= 800*800) teleport(*it); diff -Nru openmw-0.37.0/apps/openmw/mwworld/cellfunctors.hpp openmw-0.38.0/apps/openmw/mwworld/cellfunctors.hpp --- openmw-0.37.0/apps/openmw/mwworld/cellfunctors.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/cellfunctors.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -#ifndef GAME_MWWORLD_CELLFUNCTORS_H -#define GAME_MWWORLD_CELLFUNCTORS_H - -#include -#include - -#include "ptr.hpp" - - -namespace MWWorld -{ - struct ListAndResetObjects - { - std::vector mObjects; - - bool operator() (MWWorld::Ptr ptr) - { - if (ptr.getRefData().getBaseNode()) - { - ptr.getRefData().setBaseNode(NULL); - mObjects.push_back (ptr); - } - - return true; - } - }; -} - -#endif diff -Nru openmw-0.37.0/apps/openmw/mwworld/cellreflist.hpp openmw-0.38.0/apps/openmw/mwworld/cellreflist.hpp --- openmw-0.37.0/apps/openmw/mwworld/cellreflist.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/cellreflist.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -24,17 +24,6 @@ /// all methods are known. void load (ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore); - LiveRef *find (const std::string& name) - { - for (typename List::iterator iter (mList.begin()); iter!=mList.end(); ++iter) - if (!iter->mData.isDeletedByContentFile() - && (iter->mRef.hasContentFile() || iter->mData.getCount() > 0) - && iter->mRef.getRefId() == name) - return &*iter; - - return 0; - } - LiveRef &insert (const LiveRef &item) { mList.push_back(item); diff -Nru openmw-0.37.0/apps/openmw/mwworld/cells.cpp openmw-0.38.0/apps/openmw/mwworld/cells.cpp --- openmw-0.37.0/apps/openmw/mwworld/cells.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/cells.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,5 +1,7 @@ #include "cells.hpp" +#include + #include #include #include @@ -23,7 +25,7 @@ if (result==mInteriors.end()) { - result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first; } return &result->second; @@ -36,7 +38,7 @@ if (result==mExteriors.end()) { result = mExteriors.insert (std::make_pair ( - std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell))).first; + std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell, mStore, mReader))).first; } @@ -70,7 +72,7 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const { if (cell.getState()!=CellStore::State_Loaded) - cell.load (mStore, mReader); + cell.load (); ESM::CellState cellState; @@ -114,13 +116,12 @@ } result = mExteriors.insert (std::make_pair ( - std::make_pair (x, y), CellStore (cell))).first; + std::make_pair (x, y), CellStore (cell, mStore, mReader))).first; } if (result->second.getState()!=CellStore::State_Loaded) { - // Multiple plugin support for landscape data is much easier than for references. The last plugin wins. - result->second.load (mStore, mReader); + result->second.load (); } return &result->second; @@ -135,12 +136,12 @@ { const ESM::Cell *cell = mStore.get().find(lowerName); - result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first; } if (result->second.getState()!=CellStore::State_Loaded) { - result->second.load (mStore, mReader); + result->second.load (); } return &result->second; @@ -158,13 +159,13 @@ bool searchInContainers) { if (cell.getState()==CellStore::State_Unloaded) - cell.preload (mStore, mReader); + cell.preload (); if (cell.getState()==CellStore::State_Preloaded) { if (cell.hasId (name)) { - cell.load (mStore, mReader); + cell.load (); } else return Ptr(); @@ -172,7 +173,7 @@ Ptr ptr = cell.search (name); - if (!ptr.isEmpty()) + if (!ptr.isEmpty() && MWWorld::CellStore::isAccessible(ptr.getRefData(), ptr.getCellRef())) return ptr; if (searchInContainers) @@ -304,6 +305,29 @@ } } +struct GetCellStoreCallback : public MWWorld::CellStore::GetCellStoreCallback +{ +public: + GetCellStoreCallback(MWWorld::Cells& cells) + : mCells(cells) + { + } + + MWWorld::Cells& mCells; + + virtual MWWorld::CellStore* getCellStore(const ESM::CellId& cellId) + { + try + { + return mCells.getCell(cellId); + } + catch (...) + { + return NULL; + } + } +}; + bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) { @@ -321,9 +345,9 @@ catch (...) { // silently drop cells that don't exist anymore + std::cerr << "Dropping state for cell " << state.mId.mWorldspace << " (cell no longer exists)" << std::endl; reader.skipRecord(); return true; - /// \todo log } state.load (reader); @@ -333,9 +357,11 @@ cellStore->readFog(reader); if (cellStore->getState()!=CellStore::State_Loaded) - cellStore->load (mStore, mReader); + cellStore->load (); + + GetCellStoreCallback callback(*this); - cellStore->readReferences (reader, contentFileMap); + cellStore->readReferences (reader, contentFileMap, &callback); return true; } diff -Nru openmw-0.37.0/apps/openmw/mwworld/cellstore.cpp openmw-0.38.0/apps/openmw/mwworld/cellstore.cpp --- openmw-0.37.0/apps/openmw/mwworld/cellstore.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/cellstore.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -47,13 +47,16 @@ template MWWorld::Ptr searchViaActorId (MWWorld::CellRefList& actorList, int actorId, - MWWorld::CellStore *cell) + MWWorld::CellStore *cell, const std::map& toIgnore) { for (typename MWWorld::CellRefList::List::iterator iter (actorList.mList.begin()); iter!=actorList.mList.end(); ++iter) { MWWorld::Ptr actor (&*iter, cell); + if (toIgnore.find(&*iter) != toIgnore.end()) + continue; + if (actor.getClass().getCreatureStats (actor).matchesActorId (actorId) && actor.getRefData().getCount() > 0) return actor; } @@ -141,6 +144,28 @@ ref.load (state); collection.mList.push_back (ref); } + + struct SearchByRefNumVisitor + { + MWWorld::LiveCellRefBase* mFound; + ESM::RefNum mRefNumToFind; + + SearchByRefNumVisitor(const ESM::RefNum& toFind) + : mFound(NULL) + , mRefNumToFind(toFind) + { + } + + bool operator()(const MWWorld::Ptr& ptr) + { + if (ptr.getCellRef().getRefNum() == mRefNumToFind) + { + mFound = ptr.getBase(); + return false; + } + return true; + } + }; } namespace MWWorld @@ -159,7 +184,7 @@ LiveRef liveCellRef (ref, ptr); if (deleted) - liveCellRef.mData.setDeleted(true); + liveCellRef.mData.setDeletedByContentFile(true); if (iter != mList.end()) *iter = liveCellRef; @@ -179,8 +204,121 @@ return (ref.mRef.mRefnum == pRefnum); } - CellStore::CellStore (const ESM::Cell *cell) - : mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) + void CellStore::moveFrom(const Ptr &object, CellStore *from) + { + if (mState != State_Loaded) + load(); + + mHasState = true; + MovedRefTracker::iterator found = mMovedToAnotherCell.find(object.getBase()); + if (found != mMovedToAnotherCell.end()) + { + // A cell we had previously moved an object to is returning it to us. + assert (found->second == from); + mMovedToAnotherCell.erase(found); + } + else + { + mMovedHere.insert(std::make_pair(object.getBase(), from)); + } + updateMergedRefs(); + } + + MWWorld::Ptr CellStore::moveTo(const Ptr &object, CellStore *cellToMoveTo) + { + if (cellToMoveTo == this) + throw std::runtime_error("moveTo: object is already in this cell"); + + // We assume that *this is in State_Loaded since we could hardly have reference to a live object otherwise. + if (mState != State_Loaded) + throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); + + // Ensure that the object actually exists in the cell + SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); + forEach(searchVisitor); + if (!searchVisitor.mFound) + throw std::runtime_error("moveTo: object is not in this cell"); + + + // Objects with no refnum can't be handled correctly in the merging process that happens + // on a save/load, so do a simple copy & delete for these objects. + if (!object.getCellRef().getRefNum().hasContentFile()) + { + MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo, object.getRefData().getCount()); + object.getRefData().setCount(0); + object.getRefData().setBaseNode(NULL); + return copied; + } + + MovedRefTracker::iterator found = mMovedHere.find(object.getBase()); + if (found != mMovedHere.end()) + { + // Special case - object didn't originate in this cell + // Move it back to its original cell first + CellStore* originalCell = found->second; + assert (originalCell != this); + originalCell->moveFrom(object, this); + + mMovedHere.erase(found); + + // Now that object is back to its rightful owner, we can move it + if (cellToMoveTo != originalCell) + { + originalCell->moveTo(object, cellToMoveTo); + } + + updateMergedRefs(); + return MWWorld::Ptr(object.getBase(), cellToMoveTo); + } + + cellToMoveTo->moveFrom(object, this); + mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo)); + + updateMergedRefs(); + return MWWorld::Ptr(object.getBase(), cellToMoveTo); + } + + struct MergeVisitor + { + MergeVisitor(std::vector& mergeTo, const std::map& movedHere, + const std::map& movedToAnotherCell) + : mMergeTo(mergeTo) + , mMovedHere(movedHere) + , mMovedToAnotherCell(movedToAnotherCell) + { + } + + bool operator() (const MWWorld::Ptr& ptr) + { + if (mMovedToAnotherCell.find(ptr.getBase()) != mMovedToAnotherCell.end()) + return true; + mMergeTo.push_back(ptr.getBase()); + return true; + } + + void merge() + { + for (std::map::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) + mMergeTo.push_back(it->first); + } + + private: + std::vector& mMergeTo; + + const std::map& mMovedHere; + const std::map& mMovedToAnotherCell; + }; + + void CellStore::updateMergedRefs() + { + mMergedRefs.clear(); + MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell); + forEachInternal(visitor); + visitor.merge(); + } + + CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector& readerList) + : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) { mWaterLevel = cell->mWater; } @@ -208,94 +346,65 @@ if (mState==State_Preloaded) return std::binary_search (mIds.begin(), mIds.end(), id); - /// \todo address const-issues - return const_cast (this)->search (id).isEmpty(); + return searchConst (id).isEmpty(); } - Ptr CellStore::search (const std::string& id) + template + struct SearchVisitor { - bool oldState = mHasState; - - mHasState = true; - - if (LiveCellRef *ref = mActivators.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mPotions.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mAppas.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mArmors.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mBooks.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mClothes.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mContainers.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mCreatures.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mDoors.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mIngreds.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mCreatureLists.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mItemLists.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mLights.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mLockpicks.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mMiscItems.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mNpcs.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mProbes.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mRepairs.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mStatics.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mWeapons.find (id)) - return Ptr (ref, this); + PtrType mFound; + std::string mIdToFind; + bool operator()(const PtrType& ptr) + { + if (ptr.getCellRef().getRefId() == mIdToFind) + { + mFound = ptr; + return false; + } + return true; + } + }; - mHasState = oldState; + Ptr CellStore::search (const std::string& id) + { + SearchVisitor searchVisitor; + searchVisitor.mIdToFind = id; + forEach(searchVisitor); + return searchVisitor.mFound; + } - return Ptr(); + ConstPtr CellStore::searchConst (const std::string& id) const + { + SearchVisitor searchVisitor; + searchVisitor.mIdToFind = id; + forEachConst(searchVisitor); + return searchVisitor.mFound; } Ptr CellStore::searchViaActorId (int id) { - if (Ptr ptr = ::searchViaActorId (mNpcs, id, this)) + if (Ptr ptr = ::searchViaActorId (mNpcs, id, this, mMovedToAnotherCell)) return ptr; - if (Ptr ptr = ::searchViaActorId (mCreatures, id, this)) + if (Ptr ptr = ::searchViaActorId (mCreatures, id, this, mMovedToAnotherCell)) return ptr; + for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) + { + MWWorld::Ptr actor (it->first, this); + if (!actor.getClass().isActor()) + continue; + if (actor.getClass().getCreatureStats (actor).matchesActorId (id) && actor.getRefData().getCount() > 0) + return actor; + } + return Ptr(); } float CellStore::getWaterLevel() const { + if (isExterior()) + return -1; return mWaterLevel; } @@ -307,37 +416,17 @@ int CellStore::count() const { - return - mActivators.mList.size() - + mPotions.mList.size() - + mAppas.mList.size() - + mArmors.mList.size() - + mBooks.mList.size() - + mClothes.mList.size() - + mContainers.mList.size() - + mDoors.mList.size() - + mIngreds.mList.size() - + mCreatureLists.mList.size() - + mItemLists.mList.size() - + mLights.mList.size() - + mLockpicks.mList.size() - + mMiscItems.mList.size() - + mProbes.mList.size() - + mRepairs.mList.size() - + mStatics.mList.size() - + mWeapons.mList.size() - + mCreatures.mList.size() - + mNpcs.mList.size(); + return mMergedRefs.size(); } - void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::load () { if (mState!=State_Loaded) { if (mState==State_Preloaded) mIds.clear(); - loadRefs (store, esm); + loadRefs (); mState = State_Loaded; @@ -347,18 +436,20 @@ } } - void CellStore::preload (const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::preload () { if (mState==State_Unloaded) { - listRefs (store, esm); + listRefs (); mState = State_Preloaded; } } - void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::listRefs() { + std::vector& esm = mReader; + assert (mCell); if (mCell->mContextList.empty()) @@ -402,8 +493,10 @@ std::sort (mIds.begin(), mIds.end()); } - void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::loadRefs() { + std::vector& esm = mReader; + assert (mCell); if (mCell->mContextList.empty()) @@ -430,7 +523,7 @@ continue; } - loadRef (ref, deleted, store); + loadRef (ref, deleted); } } @@ -439,8 +532,10 @@ { ESM::CellRef &ref = const_cast(*it); - loadRef (ref, false, store); + loadRef (ref, false); } + + updateMergedRefs(); } bool CellStore::isExterior() const @@ -468,14 +563,16 @@ return Ptr(); } - void CellStore::loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store) + void CellStore::loadRef (ESM::CellRef& ref, bool deleted) { - Misc::StringUtils::toLower (ref.mRefID); + Misc::StringUtils::lowerCaseInPlace (ref.mRefID); + + const MWWorld::ESMStore& store = mStore; switch (store.find (ref.mRefID)) { case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break; - case ESM::REC_ALCH: mPotions.load(ref, deleted, store); break; + case ESM::REC_ALCH: mPotions.load(ref, deleted,store); break; case ESM::REC_APPA: mAppas.load(ref, deleted, store); break; case ESM::REC_ARMO: mArmors.load(ref, deleted, store); break; case ESM::REC_BOOK: mBooks.load(ref, deleted, store); break; @@ -494,6 +591,7 @@ case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break; case ESM::REC_STAT: mStatics.load(ref, deleted, store); break; case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break; + case ESM::REC_BODY: mBodyParts.load(ref, deleted, store); break; case 0: std::cerr << "Cell reference " + ref.mRefID + " not found!\n"; break; @@ -562,10 +660,20 @@ writeReferenceCollection (writer, mRepairs); writeReferenceCollection (writer, mStatics); writeReferenceCollection (writer, mWeapons); + writeReferenceCollection (writer, mBodyParts); + + for (MovedRefTracker::const_iterator it = mMovedToAnotherCell.begin(); it != mMovedToAnotherCell.end(); ++it) + { + LiveCellRefBase* base = it->first; + ESM::RefNum refNum = base->mRef.getRefNum(); + ESM::CellId movedTo = it->second->getCell()->getCellId(); + + refNum.save(writer, true, "MVRF"); + movedTo.save(writer); + } } - void CellStore::readReferences (ESM::ESMReader& reader, - const std::map& contentFileMap) + void CellStore::readReferences (ESM::ESMReader& reader, const std::map& contentFileMap, GetCellStoreCallback* callback) { mHasState = true; @@ -688,11 +796,60 @@ readReferenceCollection (reader, mWeapons, cref, contentFileMap); break; + case ESM::REC_BODY: + + readReferenceCollection (reader, mBodyParts, cref, contentFileMap); + break; + default: throw std::runtime_error ("unknown type in cell reference section"); } } + + while (reader.isNextSub("MVRF")) + { + reader.cacheSubName(); + ESM::RefNum refnum; + ESM::CellId movedTo; + refnum.load(reader, true, "MVRF"); + movedTo.load(reader); + + // Search for the reference. It might no longer exist if its content file was removed. + SearchByRefNumVisitor visitor(refnum); + forEachInternal(visitor); + + if (!visitor.mFound) + { + std::cerr << "Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)" << std::endl; + continue; + } + + MWWorld::LiveCellRefBase* movedRef = visitor.mFound; + + CellStore* otherCell = callback->getCellStore(movedTo); + + if (otherCell == NULL) + { + std::cerr << "Dropping moved ref tag for " << movedRef->mRef.getRefId() + << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location." << std::endl; + // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. + // Restore original coordinates: + movedRef->mData.setPosition(movedRef->mRef.getPosition()); + continue; + } + + if (otherCell == this) + { + // Should never happen unless someone's tampering with files. + std::cerr << "Found invalid moved ref, ignoring" << std::endl; + continue; + } + + moveTo(MWWorld::Ptr(movedRef, this), otherCell); + } + + updateMergedRefs(); } bool operator== (const CellStore& left, const CellStore& right) diff -Nru openmw-0.37.0/apps/openmw/mwworld/cellstore.hpp openmw-0.38.0/apps/openmw/mwworld/cellstore.hpp --- openmw-0.37.0/apps/openmw/mwworld/cellstore.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/cellstore.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -31,23 +31,24 @@ #include #include #include +#include #include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld #include "timestamp.hpp" +#include "ptr.hpp" namespace ESM { struct CellState; struct FogState; + struct CellId; } namespace MWWorld { - class Ptr; class ESMStore; - /// \brief Mutable state of a cell class CellStore { @@ -60,6 +61,9 @@ private: + const MWWorld::ESMStore& mStore; + std::vector& mReader; + // Even though fog actually belongs to the player and not cells, // it makes sense to store it here since we need it once for each cell. // Note this is NULL until the cell is explored to save some memory @@ -73,6 +77,7 @@ MWWorld::TimeStamp mLastRespawn; + // List of refs owned by this cell CellRefList mActivators; CellRefList mPotions; CellRefList mAppas; @@ -93,10 +98,107 @@ CellRefList mRepairs; CellRefList mStatics; CellRefList mWeapons; + CellRefList mBodyParts; + + typedef std::map MovedRefTracker; + // References owned by a different cell that have been moved here. + // + MovedRefTracker mMovedHere; + // References owned by this cell that have been moved to another cell. + // + MovedRefTracker mMovedToAnotherCell; + + // Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell + std::vector mMergedRefs; + + /// Moves object from the given cell to this cell. + void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from); + + /// Repopulate mMergedRefs. + void updateMergedRefs(); + + // helper function for forEachInternal + template + bool forEachImp (Visitor& visitor, List& list) + { + for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); + ++iter) + { + if (!isAccessible(iter->mData, iter->mRef)) + continue; + if (!visitor (MWWorld::Ptr(&*iter, this))) + return false; + } + return true; + } + + // listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved objects are accounted for. + template + bool forEachInternal (Visitor& visitor) + { + return + forEachImp (visitor, mActivators) && + forEachImp (visitor, mPotions) && + forEachImp (visitor, mAppas) && + forEachImp (visitor, mArmors) && + forEachImp (visitor, mBooks) && + forEachImp (visitor, mClothes) && + forEachImp (visitor, mContainers) && + forEachImp (visitor, mDoors) && + forEachImp (visitor, mIngreds) && + forEachImp (visitor, mItemLists) && + forEachImp (visitor, mLights) && + forEachImp (visitor, mLockpicks) && + forEachImp (visitor, mMiscItems) && + forEachImp (visitor, mProbes) && + forEachImp (visitor, mRepairs) && + forEachImp (visitor, mStatics) && + forEachImp (visitor, mWeapons) && + forEachImp (visitor, mBodyParts) && + forEachImp (visitor, mCreatures) && + forEachImp (visitor, mNpcs) && + forEachImp (visitor, mCreatureLists); + } + + /// @note If you get a linker error here, this means the given type can not be stored in a cell. The supported types are + /// defined at the bottom of this file. + template + CellRefList& get(); public: - CellStore (const ESM::Cell *cell_); + /// Should this reference be accessible to the outside world (i.e. to scripts / game logic)? + /// Determined based on the deletion flags. By default, objects deleted by content files are never accessible; + /// objects deleted by setCount(0) are still accessible *if* they came from a content file (needed for vanilla + /// scripting compatibility, and the fact that objects may be "un-deleted" in the original game). + static bool isAccessible(const MWWorld::RefData& refdata, const MWWorld::CellRef& cref) + { + return !refdata.isDeletedByContentFile() && (cref.hasContentFile() || refdata.getCount() > 0); + } + + /// Moves object from this cell to the given cell. + /// @note automatically updates given cell by calling cellToMoveTo->moveFrom(...) + /// @note throws exception if cellToMoveTo == this + /// @return updated MWWorld::Ptr with the new CellStore pointer set. + MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); + + /// Make a copy of the given object and insert it into this cell. + /// @note If you get a linker error here, this means the given type can not be inserted into a cell. + /// The supported types are defined at the bottom of this file. + template + LiveCellRefBase* insert(const LiveCellRef* ref) + { + mHasState = true; + CellRefList& list = get(); + LiveCellRefBase* ret = &list.insert(*ref); + updateMergedRefs(); + return ret; + } + + /// @param readerList The readers to use for loading of the cell on-demand. + CellStore (const ESM::Cell *cell_, + const MWWorld::ESMStore& store, + std::vector& readerList); const ESM::Cell *getCell() const; @@ -108,10 +210,17 @@ bool hasId (const std::string& id) const; ///< May return true for deleted IDs when in preload state. Will return false, if cell is /// unloaded. + /// @note Will not account for moved references which may exist in Loaded state. Use search() instead if the cell is loaded. Ptr search (const std::string& id); ///< Will return an empty Ptr if cell is not loaded. Does not check references in /// containers. + /// @note Triggers CellStore hasState flag. + + ConstPtr searchConst (const std::string& id) const; + ///< Will return an empty Ptr if cell is not loaded. Does not check references in + /// containers. + /// @note Does not trigger CellStore hasState flag. Ptr searchViaActorId (int id); ///< Will return an empty Ptr if cell is not loaded. @@ -128,55 +237,102 @@ int count() const; ///< Return total number of references, including deleted ones. - void load (const MWWorld::ESMStore &store, std::vector &esm); + void load (); ///< Load references from content file. - void preload (const MWWorld::ESMStore &store, std::vector &esm); + void preload (); ///< Build ID list from content file. - /// Call functor (ref) for each reference. functor must return a bool. Returning + /// Call visitor (MWWorld::Ptr) for each reference. visitor must return a bool. Returning /// false will abort the iteration. + /// \note Prefer using forEachConst when possible. /// \attention This function also lists deleted (count 0) objects! /// \return Iteration completed? - /// - /// \note Creatures and NPCs are handled last. - template - bool forEach (Functor& functor) + template + bool forEach (Visitor& visitor) { + if (mState != State_Loaded) + return false; + mHasState = true; - return - forEachImp (functor, mActivators) && - forEachImp (functor, mPotions) && - forEachImp (functor, mAppas) && - forEachImp (functor, mArmors) && - forEachImp (functor, mBooks) && - forEachImp (functor, mClothes) && - forEachImp (functor, mContainers) && - forEachImp (functor, mDoors) && - forEachImp (functor, mIngreds) && - forEachImp (functor, mItemLists) && - forEachImp (functor, mLights) && - forEachImp (functor, mLockpicks) && - forEachImp (functor, mMiscItems) && - forEachImp (functor, mProbes) && - forEachImp (functor, mRepairs) && - forEachImp (functor, mStatics) && - forEachImp (functor, mWeapons) && - forEachImp (functor, mCreatures) && - forEachImp (functor, mNpcs) && - forEachImp (functor, mCreatureLists); + for (unsigned int i=0; imData, mMergedRefs[i]->mRef)) + continue; + + if (!visitor(MWWorld::Ptr(mMergedRefs[i], this))) + return false; + } + return true; + } + + /// Call visitor (MWWorld::ConstPtr) for each reference. visitor must return a bool. Returning + /// false will abort the iteration. + /// \attention This function also lists deleted (count 0) objects! + /// \return Iteration completed? + template + bool forEachConst (Visitor& visitor) const + { + if (mState != State_Loaded) + return false; + + for (unsigned int i=0; imData, mMergedRefs[i]->mRef)) + continue; + + if (!visitor(MWWorld::ConstPtr(mMergedRefs[i], this))) + return false; + } + return true; } - template - bool forEachContainer (Functor& functor) + + /// Call visitor (ref) for each reference of given type. visitor must return a bool. Returning + /// false will abort the iteration. + /// \attention This function also lists deleted (count 0) objects! + /// \return Iteration completed? + template + bool forEachType(Visitor& visitor) { + if (mState != State_Loaded) + return false; + mHasState = true; - return - forEachImp (functor, mContainers) && - forEachImp (functor, mCreatures) && - forEachImp (functor, mNpcs); + CellRefList& list = get(); + + for (typename CellRefList::List::iterator it (list.mList.begin()); it!=list.mList.end(); ++it) + { + LiveCellRefBase* base = &*it; + if (mMovedToAnotherCell.find(base) != mMovedToAnotherCell.end()) + continue; + if (!isAccessible(base->mData, base->mRef)) + continue; + if (!visitor(MWWorld::Ptr(base, this))) + return false; + } + + for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) + { + LiveCellRefBase* base = it->first; + if (dynamic_cast*>(base)) + if (!visitor(MWWorld::Ptr(base, this))) + return false; + } + return true; + } + + // NOTE: does not account for moved references + // Should be phased out when we have const version of forEach + inline const CellRefList& getReadOnlyDoors() const + { + return mDoors; + } + inline const CellRefList& getReadOnlyStatics() const + { + return mStatics; } bool isExterior() const; @@ -193,47 +349,31 @@ void writeReferences (ESM::ESMWriter& writer) const; - void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + struct GetCellStoreCallback + { + public: + ///@note must return NULL if the cell is not found + virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0; + }; + + /// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved references) + void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap, GetCellStoreCallback* callback); void respawn (); ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. - template - CellRefList& get() { - throw std::runtime_error ("Storage for type " + std::string(typeid(T).name())+ " does not exist in cells"); - } - - template - const CellRefList& getReadOnly() { - throw std::runtime_error ("Read Only CellRefList access not available for type " + std::string(typeid(T).name()) ); - } - bool isPointConnected(const int start, const int end) const; std::list aStarSearch(const int start, const int end) const; private: - template - bool forEachImp (Functor& functor, List& list) - { - for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); - ++iter) - { - if (iter->mData.isDeletedByContentFile()) - continue; - if (!functor (MWWorld::Ptr(&*iter, this))) - return false; - } - return true; - } - /// Run through references and store IDs - void listRefs(const MWWorld::ESMStore &store, std::vector &esm); + void listRefs(); - void loadRefs(const MWWorld::ESMStore &store, std::vector &esm); + void loadRefs(); - void loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store); + void loadRef (ESM::CellRef& ref, bool deleted); ///< Make case-adjustments to \a ref and insert it into the respective container. /// /// Invalid \a ref objects are silently dropped. @@ -382,9 +522,10 @@ } template<> - inline const CellRefList& CellStore::getReadOnly() + inline CellRefList& CellStore::get() { - return mDoors; + mHasState = true; + return mBodyParts; } bool operator== (const CellStore& left, const CellStore& right); diff -Nru openmw-0.37.0/apps/openmw/mwworld/cellvisitors.hpp openmw-0.38.0/apps/openmw/mwworld/cellvisitors.hpp --- openmw-0.37.0/apps/openmw/mwworld/cellvisitors.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/cellvisitors.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,29 @@ +#ifndef GAME_MWWORLD_CELLVISITORS_H +#define GAME_MWWORLD_CELLVISITORS_H + +#include +#include + +#include "ptr.hpp" + + +namespace MWWorld +{ + struct ListAndResetObjectsVisitor + { + std::vector mObjects; + + bool operator() (MWWorld::Ptr ptr) + { + if (ptr.getRefData().getBaseNode()) + { + ptr.getRefData().setBaseNode(NULL); + mObjects.push_back (ptr); + } + + return true; + } + }; +} + +#endif diff -Nru openmw-0.37.0/apps/openmw/mwworld/class.cpp openmw-0.38.0/apps/openmw/mwworld/class.cpp --- openmw-0.37.0/apps/openmw/mwworld/class.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/class.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -30,11 +30,6 @@ Class::~Class() {} - std::string Class::getId (const Ptr& ptr) const - { - throw std::runtime_error ("class does not support ID retrieval"); - } - void Class::insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const { @@ -55,12 +50,12 @@ throw std::runtime_error ("class does not represent an actor"); } - bool Class::canSell (const MWWorld::Ptr& item, int npcServices) const + bool Class::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return false; } - int Class::getServices(const Ptr &actor) const + int Class::getServices(const ConstPtr &actor) const { throw std::runtime_error ("class does not have services"); } @@ -75,12 +70,12 @@ throw std::runtime_error ("class does not have NPC stats"); } - bool Class::hasItemHealth (const Ptr& ptr) const + bool Class::hasItemHealth (const ConstPtr& ptr) const { return false; } - int Class::getItemHealth(const Ptr &ptr) const + int Class::getItemHealth(const ConstPtr &ptr) const { if (ptr.getCellRef().getCharge() == -1) return getItemMaxHealth(ptr); @@ -88,7 +83,7 @@ return ptr.getCellRef().getCharge(); } - int Class::getItemMaxHealth (const Ptr& ptr) const + int Class::getItemMaxHealth (const ConstPtr& ptr) const { throw std::runtime_error ("class does not have item health"); } @@ -143,7 +138,7 @@ throw std::runtime_error ("class does not support unlocking"); } - bool Class::canLock(const Ptr &ptr) const + bool Class::canLock(const ConstPtr &ptr) const { return false; } @@ -153,12 +148,12 @@ throw std::runtime_error ("class does not support time-based uses"); } - float Class::getRemainingUsageTime (const Ptr& ptr) const + float Class::getRemainingUsageTime (const ConstPtr& ptr) const { return -1; } - std::string Class::getScript (const Ptr& ptr) const + std::string Class::getScript (const ConstPtr& ptr) const { return ""; } @@ -173,7 +168,7 @@ return 0; } - int Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Class::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { throw std::runtime_error ("class does not support enchanting"); } @@ -188,17 +183,17 @@ return osg::Vec3f (0, 0, 0); } - std::pair, bool> Class::getEquipmentSlots (const Ptr& ptr) const + std::pair, bool> Class::getEquipmentSlots (const ConstPtr& ptr) const { return std::make_pair (std::vector(), false); } - int Class::getEquipmentSkill (const Ptr& ptr) const + int Class::getEquipmentSkill (const ConstPtr& ptr) const { return -1; } - int Class::getValue (const Ptr& ptr) const + int Class::getValue (const ConstPtr& ptr) const { throw std::logic_error ("value not supported by this class"); } @@ -208,7 +203,7 @@ throw std::runtime_error ("capacity not supported by this class"); } - float Class::getWeight(const Ptr &ptr) const + float Class::getWeight(const ConstPtr &ptr) const { throw std::runtime_error ("weight not supported by this class"); } @@ -218,7 +213,7 @@ throw std::runtime_error ("encumbrance not supported by class"); } - bool Class::isEssential (const MWWorld::Ptr& ptr) const + bool Class::isEssential (const MWWorld::ConstPtr& ptr) const { return false; } @@ -241,7 +236,7 @@ return *iter->second; } - bool Class::isPersistent(const Ptr &ptr) const + bool Class::isPersistent(const ConstPtr &ptr) const { throw std::runtime_error ("class does not support persistence"); } @@ -252,12 +247,12 @@ sClasses.insert(std::make_pair(key, instance)); } - std::string Class::getUpSoundId (const Ptr& ptr) const + std::string Class::getUpSoundId (const ConstPtr& ptr) const { throw std::runtime_error ("class does not have an up sound"); } - std::string Class::getDownSoundId (const Ptr& ptr) const + std::string Class::getDownSoundId (const ConstPtr& ptr) const { throw std::runtime_error ("class does not have an down sound"); } @@ -267,41 +262,41 @@ throw std::runtime_error("class does not support soundgen look up"); } - std::string Class::getInventoryIcon (const MWWorld::Ptr& ptr) const + std::string Class::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { throw std::runtime_error ("class does not have any inventory icon"); } - MWGui::ToolTipInfo Class::getToolTipInfo (const Ptr& ptr) const + MWGui::ToolTipInfo Class::getToolTipInfo (const ConstPtr& ptr, int count) const { throw std::runtime_error ("class does not have a tool tip"); } - bool Class::hasToolTip (const Ptr& ptr) const + bool Class::hasToolTip (const ConstPtr& ptr) const { return false; } - std::string Class::getEnchantment (const Ptr& ptr) const + std::string Class::getEnchantment (const ConstPtr& ptr) const { return ""; } - void Class::adjustScale(const MWWorld::Ptr& ptr, osg::Vec3f& scale, bool rendering) const + void Class::adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const { } - std::string Class::getModel(const MWWorld::Ptr &ptr) const + std::string Class::getModel(const MWWorld::ConstPtr &ptr) const { return ""; } - std::string Class::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + std::string Class::applyEnchantment(const MWWorld::ConstPtr &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 + std::pair Class::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { return std::make_pair (1, ""); } @@ -333,44 +328,45 @@ } MWWorld::Ptr - Class::copyToCellImpl(const Ptr &ptr, CellStore &cell) const + Class::copyToCellImpl(const ConstPtr &ptr, CellStore &cell) const { - throw std::runtime_error("unable to move class to cell"); + throw std::runtime_error("unable to copy class to cell"); } MWWorld::Ptr - Class::copyToCell(const Ptr &ptr, CellStore &cell) const + Class::copyToCell(const ConstPtr &ptr, CellStore &cell, int count) const { Ptr newPtr = copyToCellImpl(ptr, cell); newPtr.getCellRef().unsetRefNum(); // This RefNum is only valid within the original cell of the reference + newPtr.getRefData().setCount(count); return newPtr; } MWWorld::Ptr - Class::copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const + Class::copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const { - Ptr newPtr = copyToCell(ptr, cell); + Ptr newPtr = copyToCell(ptr, cell, count); newPtr.getRefData().setPosition(pos); return newPtr; } - bool Class::isBipedal(const Ptr &ptr) const + bool Class::isBipedal(const ConstPtr &ptr) const { return false; } - bool Class::canFly(const Ptr &ptr) const + bool Class::canFly(const ConstPtr &ptr) const { return false; } - bool Class::canSwim(const Ptr &ptr) const + bool Class::canSwim(const ConstPtr &ptr) const { return false; } - bool Class::canWalk(const Ptr &ptr) const + bool Class::canWalk(const ConstPtr &ptr) const { return false; } @@ -390,26 +386,26 @@ throw std::runtime_error("class does not support skills"); } - int Class::getBloodTexture (const MWWorld::Ptr& ptr) const + int Class::getBloodTexture (const MWWorld::ConstPtr& ptr) const { throw std::runtime_error("class does not support gore"); } void Class::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const {} - void Class::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const {} + void Class::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const {} - int Class::getBaseGold(const MWWorld::Ptr& ptr) const + int Class::getBaseGold(const MWWorld::ConstPtr& ptr) const { throw std::runtime_error("class does not support base gold"); } - bool Class::isClass(const MWWorld::Ptr& ptr, const std::string &className) const + bool Class::isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const { return false; } - int Class::getDoorState (const MWWorld::Ptr &ptr) const + int Class::getDoorState (const MWWorld::ConstPtr &ptr) const { throw std::runtime_error("this is not a door"); } @@ -428,26 +424,26 @@ return getEncumbrance(ptr) / capacity; } - std::string Class::getSound(const MWWorld::Ptr&) const + std::string Class::getSound(const MWWorld::ConstPtr&) const { return std::string(); } - int Class::getBaseFightRating(const Ptr &ptr) const + int Class::getBaseFightRating(const ConstPtr &ptr) const { throw std::runtime_error("class does not support fight rating"); } - std::string Class::getPrimaryFaction (const MWWorld::Ptr& ptr) const + std::string Class::getPrimaryFaction (const MWWorld::ConstPtr& ptr) const { return std::string(); } - int Class::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const + int Class::getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const { return -1; } - int Class::getEffectiveArmorRating(const Ptr &ptr, const Ptr &actor) const + int Class::getEffectiveArmorRating(const ConstPtr &armor, const Ptr &actor) const { throw std::runtime_error("class does not support armor ratings"); } diff -Nru openmw-0.37.0/apps/openmw/mwworld/class.hpp openmw-0.38.0/apps/openmw/mwworld/class.hpp --- openmw-0.37.0/apps/openmw/mwworld/class.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/class.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -66,7 +66,7 @@ boost::shared_ptr defaultItemActivate(const Ptr &ptr, const Ptr &actor) const; ///< Generate default action for activating inventory items - virtual Ptr copyToCellImpl(const Ptr &ptr, CellStore &cell) const; + virtual Ptr copyToCellImpl(const ConstPtr &ptr, CellStore &cell) const; public: @@ -76,17 +76,11 @@ return mTypeName; } - virtual std::string getId (const Ptr& ptr) const; - ///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval - /// (default implementation: throw an exception) - /// @note This function is currently redundant; the same ID can be retrieved by CellRef::getRefId. - /// Leaving it here for now in case we want to optimize later. - virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObject(const Ptr& ptr, const std::string& mesh, MWPhysics::PhysicsSystem& physics) const; ///< Add reference into a cell for rendering (default implementation: don't render anything). - virtual std::string getName (const Ptr& ptr) const = 0; + virtual std::string getName (const ConstPtr& ptr) const = 0; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -98,23 +92,23 @@ ///< Return creature stats or throw an exception, if class does not have creature stats /// (default implementation: throw an exception) - virtual bool hasToolTip (const Ptr& ptr) const; + virtual bool hasToolTip (const ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) - virtual MWGui::ToolTipInfo getToolTipInfo (const Ptr& ptr) const; + virtual MWGui::ToolTipInfo getToolTipInfo (const ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual MWMechanics::NpcStats& getNpcStats (const Ptr& ptr) const; ///< Return NPC stats or throw an exception, if class does not have NPC stats /// (default implementation: throw an exception) - virtual bool hasItemHealth (const Ptr& ptr) const; + virtual bool hasItemHealth (const ConstPtr& ptr) const; ///< \return Item health data available? (default implementation: false) - virtual int getItemHealth (const Ptr& ptr) const; + virtual int getItemHealth (const ConstPtr& ptr) const; ///< Return current item health or throw an exception if class does not have item health - virtual int getItemMaxHealth (const Ptr& ptr) const; + virtual int getItemMaxHealth (const ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exception) @@ -161,17 +155,17 @@ virtual void unlock (const Ptr& ptr) const; ///< Unlock object (default implementation: throw an exception) - virtual bool canLock (const Ptr& ptr) const; + virtual bool canLock (const ConstPtr& ptr) const; virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const; ///< Sets the remaining duration of the object, such as an equippable light /// source. (default implementation: throw an exception) - virtual float getRemainingUsageTime (const Ptr& ptr) const; + virtual float getRemainingUsageTime (const ConstPtr& ptr) const; ///< Returns the remaining duration of the object, such as an equippable light /// source. (default implementation: -1, i.e. infinite) - virtual std::string getScript (const Ptr& ptr) const; + virtual std::string getScript (const ConstPtr& ptr) const; ///< Return name of the script attached to ptr (default implementation: return an empty /// string). @@ -187,19 +181,19 @@ virtual osg::Vec3f getRotationVector (const Ptr& ptr) const; ///< Return desired rotations, as euler angles. - virtual std::pair, bool> getEquipmentSlots (const Ptr& ptr) const; + virtual std::pair, bool> getEquipmentSlots (const ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? /// /// Default implementation: return (empty vector, false). - virtual int getEquipmentSkill (const Ptr& ptr) + virtual int getEquipmentSkill (const ConstPtr& ptr) const; - /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// Return the index of the skill this item corresponds to when equipped or -1, if there is /// no such skill. /// (default implementation: return -1) - virtual int getValue (const Ptr& ptr) const; + virtual int getValue (const ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. /// (default implementation: throws an exception) @@ -229,16 +223,16 @@ /// /// (default implementations: throws an exception) - virtual bool isEssential (const MWWorld::Ptr& ptr) const; + virtual bool isEssential (const MWWorld::ConstPtr& ptr) const; ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) /// /// (default implementation: return false) - virtual std::string getUpSoundId (const Ptr& ptr) const; + virtual std::string getUpSoundId (const ConstPtr& ptr) const; ///< Return the up sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) - virtual std::string getDownSoundId (const Ptr& ptr) const; + virtual std::string getDownSoundId (const ConstPtr& ptr) const; ///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) @@ -248,49 +242,47 @@ virtual float getArmorRating (const MWWorld::Ptr& ptr) const; ///< @return combined armor rating of this actor - virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; + virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. - virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string /// (default implementation: return empty string) - virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; ///< @return the number of enchantment points available for possible enchanting - virtual void adjustScale(const MWWorld::Ptr& ptr, osg::Vec3f& scale, bool rendering) const; + virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh - virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + virtual bool canSell (const MWWorld::ConstPtr& 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 int getServices (const MWWorld::ConstPtr& actor) const; - virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual std::string applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. - virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + virtual std::pair canBeEquipped(const MWWorld::ConstPtr &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 float getWeight (const MWWorld::ConstPtr& ptr) const; - virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual bool isPersistent (const MWWorld::ConstPtr& ptr) const; - virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; } + virtual bool isKey (const MWWorld::ConstPtr& ptr) const { return false; } - virtual bool isGold(const MWWorld::Ptr& ptr) const { return false; }; + virtual bool isGold(const MWWorld::ConstPtr& ptr) const { return false; } /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) - virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; + virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; - virtual Ptr - copyToCell(const Ptr &ptr, CellStore &cell) const; + virtual Ptr copyToCell(const ConstPtr &ptr, CellStore &cell, int count) const; - virtual Ptr - copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const; + virtual Ptr copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const; virtual bool isActor() const { return false; @@ -300,10 +292,10 @@ return false; } - virtual bool isBipedal(const MWWorld::Ptr& ptr) const; - virtual bool canFly(const MWWorld::Ptr& ptr) const; - virtual bool canSwim(const MWWorld::Ptr& ptr) const; - virtual bool canWalk(const MWWorld::Ptr& ptr) const; + virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const; + virtual bool canFly(const MWWorld::ConstPtr& ptr) const; + virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; + virtual bool canWalk(const MWWorld::ConstPtr& ptr) const; bool isPureWaterCreature(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const; @@ -313,7 +305,7 @@ const; ///< Read additional state from \a state into \a ptr. - virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. @@ -322,12 +314,12 @@ static void registerClass (const std::string& key, boost::shared_ptr instance); - virtual int getBaseGold(const MWWorld::Ptr& ptr) const; + virtual int getBaseGold(const MWWorld::ConstPtr& ptr) const; - virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const; + virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const; /// 0 = nothing, 1 = opening, 2 = closing - virtual int getDoorState (const MWWorld::Ptr &ptr) const; + virtual int getDoorState (const MWWorld::ConstPtr &ptr) const; /// This does not actually cause the door to move. Use World::activateDoor instead. virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; @@ -336,15 +328,15 @@ virtual void restock (const MWWorld::Ptr& ptr) const {} /// Returns sound id - virtual std::string getSound(const MWWorld::Ptr& ptr) const; + virtual std::string getSound(const MWWorld::ConstPtr& ptr) const; - virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const; + virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const; - virtual std::string getPrimaryFaction (const MWWorld::Ptr& ptr) const; - virtual int getPrimaryFactionRank (const MWWorld::Ptr& ptr) const; + virtual std::string getPrimaryFaction (const MWWorld::ConstPtr& ptr) const; + virtual int getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const; /// Get the effective armor rating, factoring in the actor's skills, for the given armor. - virtual int getEffectiveArmorRating(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; + virtual int getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwworld/containerstore.cpp openmw-0.38.0/apps/openmw/mwworld/containerstore.cpp --- openmw-0.37.0/apps/openmw/mwworld/containerstore.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/containerstore.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -94,7 +94,7 @@ } template -void MWWorld::ContainerStore::storeStates (CellRefList& collection, +void MWWorld::ContainerStore::storeStates (const CellRefList& collection, ESM::InventoryState& inventory, int& index, bool equipable) const { for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); @@ -176,7 +176,7 @@ return retval; } -bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) +bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) { const MWWorld::Class& cls1 = ptr1.getClass(); const MWWorld::Class& cls2 = ptr2.getClass(); @@ -336,7 +336,7 @@ return addNewStack(ptr, count); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& ptr, int count) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const ConstPtr& ptr, int count) { ContainerStoreIterator it = begin(); @@ -415,6 +415,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel, const std::string& levItem) { + if (count == 0) return; //Don't restock with nothing. try { ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); @@ -442,9 +443,11 @@ // For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks if (!levItem.empty() && count < 0) { - if (mLevelledItemMap.find(id) == mLevelledItemMap.end()) - mLevelledItemMap[id] = 0; - mLevelledItemMap[id] += std::abs(count); + //If there is no item in map, insert it + std::map, int>::iterator itemInMap = + mLevelledItemMap.insert(std::make_pair(std::make_pair(id, levItem), 0)).first; + //Update spawned count + itemInMap->second += std::abs(count); } count = std::abs(count); @@ -461,30 +464,84 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner) { - // Remove the items already spawned by levelled items that will restock - for (std::map::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) - { - if (count(it->first) >= it->second) - remove(it->first, it->second, ptr); + //allowedForReplace - Holds information about how many items from the list were not sold; + // Hence, tells us how many items we don't need to restock. + //allowedForReplace[list] <- How many items we should generate(how many of these were sold) + std::map allowedForReplace; + + //Check which lists need restocking: + for (std::map, int>::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end();) + { + int spawnedCount = it->second; //How many items should be in shop originally + int itemCount = count(it->first.first); //How many items are there in shop now + //If something was not sold + if(itemCount >= spawnedCount) + { + const std::string& parent = it->first.second; + // Security check for old saves: + //If item is imported from old save(doesn't have an parent) and wasn't sold + if(parent == "") + { + //Remove it, from shop, + remove(it->first.first, itemCount, ptr);//ptr is the NPC + //And remove it from map, so that when we restock, the new item will have proper parent. + mLevelledItemMap.erase(it++); + continue; + } + //Create the entry if it does not exist yet + std::map::iterator listInMap = allowedForReplace.insert( + std::make_pair(it->first.second, 0)).first; + //And signal that we don't need to restock item from this list + listInMap->second += std::abs(itemCount); + } + //If every of the item was sold + else if (itemCount == 0) + { + mLevelledItemMap.erase(it++); + continue; + } + //If some was sold, but some remain + else + { + //Create entry if it does not exist yet + std::map::iterator listInMap = allowedForReplace.insert( + std::make_pair(it->first.second, 0)).first; + //And signal that we don't need to restock all items from this list + listInMap->second += std::abs(itemCount); + //And update itemCount so we don't mistake it next time. + it->second = itemCount; + } + ++it; } - mLevelledItemMap.clear(); + //Restock: + //For every item that NPC could have for (std::vector::const_iterator it = items.mList.begin(); it != items.mList.end(); ++it) { + //If he shouldn't have it restocked, don't restock it. if (it->mCount >= 0) continue; - std::string item = Misc::StringUtils::lowerCase(it->mItem.toString()); + std::string itemOrList = Misc::StringUtils::lowerCase(it->mItem.toString()); + //If it's levelled list, restock if there's need to do so. if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mItem.toString())) { - addInitialItem(item, owner, it->mCount, true); + std::map::iterator listInMap = allowedForReplace.find(itemOrList); + + int restockNum = std::abs(it->mCount); + //If we know we must restock less, take it into account + if(listInMap != allowedForReplace.end()) + restockNum -= std::min(restockNum, listInMap->second); + //restock + addInitialItem(itemOrList, owner, restockNum, true); } else { - int currentCount = count(item); + //Restocking static item - just restock to the max count + int currentCount = count(itemOrList); if (currentCount < std::abs(it->mCount)) - addInitialItem(item, owner, std::abs(it->mCount) - currentCount, true); + addInitialItem(itemOrList, owner, std::abs(it->mCount) - currentCount, true); } } flagAsModified(); @@ -528,7 +585,7 @@ return mCachedWeight; } -int MWWorld::ContainerStore::getType (const Ptr& ptr) +int MWWorld::ContainerStore::getType (const ConstPtr& ptr) { if (ptr.isEmpty()) throw std::runtime_error ("can't put a non-existent object into a container"); @@ -650,7 +707,7 @@ return Ptr(); } -void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) +void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const { state.mItems.clear(); diff -Nru openmw-0.37.0/apps/openmw/mwworld/containerstore.hpp openmw-0.38.0/apps/openmw/mwworld/containerstore.hpp --- openmw-0.37.0/apps/openmw/mwworld/containerstore.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/containerstore.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -68,9 +69,9 @@ MWWorld::CellRefList repairs; MWWorld::CellRefList weapons; - std::map mLevelledItemMap; - ///< Stores result of levelled item spawns. - /// This is used to remove the spawned item(s) if the levelled item is restocked. + std::map, int> mLevelledItemMap; + ///< Stores result of levelled item spawns. <(refId, spawningGroup), count> + /// This is used to restock levelled items(s) if the old item was sold. mutable float mCachedWeight; mutable bool mWeightUpToDate; @@ -85,7 +86,7 @@ void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; template - void storeStates (CellRefList& collection, + void storeStates (const CellRefList& collection, ESM::InventoryState& inventory, int& index, bool equipable = false) const; @@ -141,14 +142,14 @@ int count (const std::string& id); protected: - ContainerStoreIterator addNewStack (const Ptr& ptr, int count); + ContainerStoreIterator addNewStack (const ConstPtr& ptr, int count); ///< Add the item to this container (do not try to stack it onto existing items) virtual void flagAsModified(); public: - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); + virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2); ///< @return true if the two specified objects can stack with each other void fill (const ESM::InventoryList& items, const std::string& owner); @@ -162,14 +163,13 @@ float getWeight() const; ///< Return total weight of the items contained in *this. - static int getType (const Ptr& ptr); + static int getType (const ConstPtr& ptr); ///< This function throws an exception, if ptr does not point to an object, that can be /// put into a container. Ptr search (const std::string& id); - /// \todo make this method const once const-correct ContainerStoreIterators are available - virtual void writeState (ESM::InventoryState& state); + virtual void writeState (ESM::InventoryState& state) const; virtual void readState (const ESM::InventoryState& state); diff -Nru openmw-0.37.0/apps/openmw/mwworld/customdata.cpp openmw-0.38.0/apps/openmw/mwworld/customdata.cpp --- openmw-0.37.0/apps/openmw/mwworld/customdata.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/customdata.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,74 @@ +#include "customdata.hpp" + +#include +#include +#include + +namespace MWWorld +{ + +MWClass::CreatureCustomData &CustomData::asCreatureCustomData() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to CreatureCustomData"; + throw std::logic_error(error.str()); +} + +const MWClass::CreatureCustomData &CustomData::asCreatureCustomData() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to CreatureCustomData"; + throw std::logic_error(error.str()); +} + +MWClass::NpcCustomData &CustomData::asNpcCustomData() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to NpcCustomData"; + throw std::logic_error(error.str()); +} + +const MWClass::NpcCustomData &CustomData::asNpcCustomData() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to NpcCustomData"; + throw std::logic_error(error.str()); +} + +MWClass::ContainerCustomData &CustomData::asContainerCustomData() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to ContainerCustomData"; + throw std::logic_error(error.str()); +} + +MWClass::DoorCustomData &CustomData::asDoorCustomData() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to DoorCustomData"; + throw std::logic_error(error.str()); +} + +const MWClass::DoorCustomData &CustomData::asDoorCustomData() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to DoorCustomData"; + throw std::logic_error(error.str()); +} + +MWClass::CreatureLevListCustomData &CustomData::asCreatureLevListCustomData() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to CreatureLevListCustomData"; + throw std::logic_error(error.str()); +} + +const MWClass::CreatureLevListCustomData &CustomData::asCreatureLevListCustomData() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to CreatureLevListCustomData"; + throw std::logic_error(error.str()); +} + + +} diff -Nru openmw-0.37.0/apps/openmw/mwworld/customdata.hpp openmw-0.38.0/apps/openmw/mwworld/customdata.hpp --- openmw-0.37.0/apps/openmw/mwworld/customdata.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/customdata.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,6 +1,15 @@ #ifndef GAME_MWWORLD_CUSTOMDATA_H #define GAME_MWWORLD_CUSTOMDATA_H +namespace MWClass +{ + class CreatureCustomData; + class NpcCustomData; + class ContainerCustomData; + class DoorCustomData; + class CreatureLevListCustomData; +} + namespace MWWorld { /// \brief Base class for the MW-class-specific part of RefData @@ -11,6 +20,22 @@ virtual ~CustomData() {} virtual CustomData *clone() const = 0; + + // Fast version of dynamic_cast. Needs to be overridden in the respective class. + + virtual MWClass::CreatureCustomData& asCreatureCustomData(); + virtual const MWClass::CreatureCustomData& asCreatureCustomData() const; + + virtual MWClass::NpcCustomData& asNpcCustomData(); + virtual const MWClass::NpcCustomData& asNpcCustomData() const; + + virtual MWClass::ContainerCustomData& asContainerCustomData(); + + virtual MWClass::DoorCustomData& asDoorCustomData(); + virtual const MWClass::DoorCustomData& asDoorCustomData() const; + + virtual MWClass::CreatureLevListCustomData& asCreatureLevListCustomData(); + virtual const MWClass::CreatureLevListCustomData& asCreatureLevListCustomData() const; }; } diff -Nru openmw-0.37.0/apps/openmw/mwworld/esmstore.cpp openmw-0.38.0/apps/openmw/mwworld/esmstore.cpp --- openmw-0.37.0/apps/openmw/mwworld/esmstore.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/esmstore.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,7 +19,8 @@ id == ESM::REC_BOOK || id == ESM::REC_CLOT || id == ESM::REC_CONT || id == ESM::REC_CREA || id == ESM::REC_DOOR || id == ESM::REC_INGR || id == ESM::REC_LEVC || id == ESM::REC_LEVI || id == ESM::REC_LIGH || id == ESM::REC_LOCK || id == ESM::REC_MISC || id == ESM::REC_NPC_ || - id == ESM::REC_PROB || id == ESM::REC_REPA || id == ESM::REC_STAT || id == ESM::REC_WEAP) + id == ESM::REC_PROB || id == ESM::REC_REPA || id == ESM::REC_STAT || id == ESM::REC_WEAP || + id == ESM::REC_BODY) { return true; } @@ -32,6 +33,12 @@ ESM::Dialogue *dialogue = 0; + // Land texture loading needs to use a separate internal store for each plugin. + // We set the number of plugins here to avoid continual resizes during loading, + // and so we can properly verify if valid plugin indices are being passed to the + // LandTexture Store retrieval methods. + mLandTextures.resize(esm.getGlobalReaderList()->size()); + /// \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 @@ -96,34 +103,18 @@ throw std::runtime_error(error.str()); } } else { - // Load it - std::string id = esm.getHNOString("NAME"); - // ... unless it got deleted! This means that the following record - // has been deleted, and trying to load it using standard assumptions - // on the structure will (probably) fail. - if (esm.isNextSub("DELE")) { - esm.skipRecord(); - it->second->eraseStatic(id); - continue; - } - it->second->load(esm, id); - - // DELE can also occur after the usual subrecords - if (esm.isNextSub("DELE")) { - esm.skipRecord(); - it->second->eraseStatic(id); - continue; + RecordId id = it->second->load(esm); + if (id.mIsDeleted) + { + it->second->eraseStatic(id.mId); + continue; } if (n.val==ESM::REC_DIAL) { - dialogue = const_cast(mDialogs.find(id)); + dialogue = const_cast(mDialogs.find(id.mId)); } else { dialogue = 0; } - // Insert the reference into the global lookup - if (!id.empty() && isCacheableRecord(n.val)) { - mIds[Misc::StringUtils::lowerCase (id)] = n.val; - } } listener->setProgress(static_cast(esm.getFileOffset() / (float)esm.getFileSize() * 1000)); } @@ -131,9 +122,20 @@ void ESMStore::setUp() { - std::map::iterator it = mStores.begin(); - for (; it != mStores.end(); ++it) { - it->second->setUp(); + mIds.clear(); + + std::map::iterator storeIt = mStores.begin(); + for (; storeIt != mStores.end(); ++storeIt) { + storeIt->second->setUp(); + + if (isCacheableRecord(storeIt->first)) + { + std::vector identifiers; + storeIt->second->listIdentifier(identifiers); + + for (std::vector::const_iterator record = identifiers.begin(); record != identifiers.end(); ++record) + mIds[*record] = storeIt->first; + } } mSkills.setUp(); mMagicEffects.setUp(); @@ -195,19 +197,13 @@ case ESM::REC_LEVC: { - std::string id = reader.getHNString ("NAME"); - mStores[type]->read (reader, id); - - // FIXME: there might be stale dynamic IDs in mIds from an earlier savegame - // that really should be cleared instead of just overwritten - - mIds[id] = type; + mStores[type]->read (reader); } if (type==ESM::REC_NPC_) { // NPC record will always be last and we know that there can be only one - // dynamic NPC record (player) -> We are done here with dynamic record laoding + // dynamic NPC record (player) -> We are done here with dynamic record loading setUp(); const ESM::NPC *player = mNpcs.find ("player"); diff -Nru openmw-0.37.0/apps/openmw/mwworld/globals.cpp openmw-0.38.0/apps/openmw/mwworld/globals.cpp --- openmw-0.37.0/apps/openmw/mwworld/globals.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/globals.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -13,7 +13,7 @@ { Globals::Collection::const_iterator Globals::find (const std::string& name) const { - Collection::const_iterator iter = mVariables.find (name); + Collection::const_iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name)); if (iter==mVariables.end()) throw std::runtime_error ("unknown global variable: " + name); @@ -23,7 +23,7 @@ Globals::Collection::iterator Globals::find (const std::string& name) { - Collection::iterator iter = mVariables.find (name); + Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name)); if (iter==mVariables.end()) throw std::runtime_error ("unknown global variable: " + name); @@ -40,28 +40,28 @@ for (MWWorld::Store::iterator iter = globals.begin(); iter!=globals.end(); ++iter) { - mVariables.insert (std::make_pair (iter->mId, iter->mValue)); + mVariables.insert (std::make_pair (Misc::StringUtils::lowerCase (iter->mId), *iter)); } } const ESM::Variant& Globals::operator[] (const std::string& name) const { - return find (name)->second; + return find (Misc::StringUtils::lowerCase (name))->second.mValue; } ESM::Variant& Globals::operator[] (const std::string& name) { - return find (name)->second; + return find (Misc::StringUtils::lowerCase (name))->second.mValue; } char Globals::getType (const std::string& name) const { - Collection::const_iterator iter = mVariables.find (name); + Collection::const_iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name)); if (iter==mVariables.end()) return ' '; - switch (iter->second.getType()) + switch (iter->second.mValue.getType()) { case ESM::VT_Short: return 's'; case ESM::VT_Long: return 'l'; @@ -81,8 +81,7 @@ for (Collection::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter) { writer.startRecord (ESM::REC_GLOB); - writer.writeHNString ("NAME", iter->first); - iter->second.write (writer, ESM::Variant::Format_Global); + iter->second.save (writer); writer.endRecord (ESM::REC_GLOB); } } @@ -91,14 +90,17 @@ { if (type==ESM::REC_GLOB) { - std::string id = reader.getHNString ("NAME"); + ESM::Global global; + bool isDeleted = false; - Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (id)); + // This readRecord() method is used when reading a saved game. + // Deleted globals can't appear there, so isDeleted will be ignored here. + global.load(reader, isDeleted); + Misc::StringUtils::lowerCaseInPlace(global.mId); + Collection::iterator iter = mVariables.find (global.mId); if (iter!=mVariables.end()) - iter->second.read (reader, ESM::Variant::Format_Global); - else - reader.skipRecord(); + iter->second = global; return true; } diff -Nru openmw-0.37.0/apps/openmw/mwworld/globals.hpp openmw-0.38.0/apps/openmw/mwworld/globals.hpp --- openmw-0.37.0/apps/openmw/mwworld/globals.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/globals.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,7 +8,7 @@ #include #include -#include +#include namespace ESM { @@ -29,7 +29,7 @@ { private: - typedef std::map Collection; + typedef std::map Collection; Collection mVariables; // type, value diff -Nru openmw-0.37.0/apps/openmw/mwworld/inventorystore.cpp openmw-0.38.0/apps/openmw/mwworld/inventorystore.cpp --- openmw-0.37.0/apps/openmw/mwworld/inventorystore.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/inventorystore.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -410,11 +410,6 @@ // the items should appear as if they'd always been equipped. mListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip, !mFirstAutoEquip && effectIt == enchantment.mEffects.mList.begin()); - - // Apply instant effects - MWMechanics::CastSpell cast(actor, actor); - if (magnitude) - cast.applyInstantEffect(actor, actor, effectIt->mEffectID, magnitude); } if (magnitude) @@ -455,7 +450,7 @@ mRechargingItemsUpToDate = false; } -bool MWWorld::InventoryStore::stacks(const Ptr& ptr1, const Ptr& ptr2) +bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) { bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2); if (!canStack) @@ -705,7 +700,7 @@ if (*iter==end()) continue; - if ((*iter)->getClass().getId(**iter) != sourceId) + if ((*iter)->getCellRef().getRefId() != sourceId) continue; std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter); @@ -747,7 +742,7 @@ ContainerStore::clear(); } -bool MWWorld::InventoryStore::isEquipped(const MWWorld::Ptr &item) +bool MWWorld::InventoryStore::isEquipped(const MWWorld::ConstPtr &item) { for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) { @@ -757,7 +752,7 @@ return false; } -void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) +void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const { MWWorld::ContainerStore::writeState(state); diff -Nru openmw-0.37.0/apps/openmw/mwworld/inventorystore.hpp openmw-0.38.0/apps/openmw/mwworld/inventorystore.hpp --- openmw-0.37.0/apps/openmw/mwworld/inventorystore.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/inventorystore.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -141,7 +141,7 @@ void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor); ///< \warning \a iterator can not be an end()-iterator, use unequip function instead - bool isEquipped(const MWWorld::Ptr& item); + bool isEquipped(const MWWorld::ConstPtr& item); ///< Utility function, returns true if the given item is equipped in any slot void setSelectedEnchantItem(const ContainerStoreIterator& iterator); @@ -167,7 +167,7 @@ ///< \attention This function is internal to the world model and should not be called from /// outside. - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); + virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2); ///< @return true if the two specified objects can stack with each other virtual int remove(const Ptr& item, int count, const Ptr& actor); @@ -207,7 +207,7 @@ virtual void clear(); ///< Empty container. - virtual void writeState (ESM::InventoryState& state); + virtual void writeState (ESM::InventoryState& state) const; virtual void readState (const ESM::InventoryState& state); }; diff -Nru openmw-0.37.0/apps/openmw/mwworld/livecellref.cpp openmw-0.38.0/apps/openmw/mwworld/livecellref.cpp --- openmw-0.37.0/apps/openmw/mwworld/livecellref.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/livecellref.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,7 +11,7 @@ #include "class.hpp" #include "esmstore.hpp" -MWWorld::LiveCellRefBase::LiveCellRefBase(std::string type, const ESM::CellRef &cref) +MWWorld::LiveCellRefBase::LiveCellRefBase(const std::string& type, const ESM::CellRef &cref) : mClass(&Class::get(type)), mRef(cref), mData(cref) { } @@ -19,7 +19,7 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) { mRef = state.mRef; - mData = RefData (state); + mData = RefData (state, mData.isDeletedByContentFile()); Ptr ptr (this); @@ -54,8 +54,7 @@ { mRef.writeState(state); - /// \todo get rid of this cast once const-correct Ptr are available - Ptr ptr (const_cast (this)); + ConstPtr ptr (this); mData.write (state, mClass->getScript (ptr)); diff -Nru openmw-0.37.0/apps/openmw/mwworld/livecellref.hpp openmw-0.38.0/apps/openmw/mwworld/livecellref.hpp --- openmw-0.37.0/apps/openmw/mwworld/livecellref.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/livecellref.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -31,7 +31,7 @@ /** runtime-data */ RefData mData; - LiveCellRefBase(std::string type, const ESM::CellRef &cref=ESM::CellRef()); + LiveCellRefBase(const std::string& type, const ESM::CellRef &cref=ESM::CellRef()); /* Need this for the class to be recognized as polymorphic */ virtual ~LiveCellRefBase() { } diff -Nru openmw-0.37.0/apps/openmw/mwworld/localscripts.cpp openmw-0.38.0/apps/openmw/mwworld/localscripts.cpp --- openmw-0.37.0/apps/openmw/mwworld/localscripts.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/localscripts.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,51 +11,62 @@ namespace { - template - void listCellScripts (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::CellStore *cell) + + struct AddScriptsVisitor { - for (typename MWWorld::CellRefList::List::iterator iter ( - cellRefList.mList.begin()); - iter!=cellRefList.mList.end(); ++iter) + AddScriptsVisitor(MWWorld::LocalScripts& scripts) + : mScripts(scripts) { - if (!iter->mBase->mScript.empty() && !iter->mData.isDeleted()) - { - localScripts.add (iter->mBase->mScript, MWWorld::Ptr (&*iter, cell)); - } } - } + MWWorld::LocalScripts& mScripts; - // Adds scripts for items in containers (containers/npcs/creatures) - template - void listCellScriptsCont (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::CellStore *cell) - { - for (typename MWWorld::CellRefList::List::iterator iter ( - cellRefList.mList.begin()); - iter!=cellRefList.mList.end(); ++iter) + bool operator()(const MWWorld::Ptr& ptr) { + if (ptr.getRefData().isDeleted()) + return true; + + std::string script = ptr.getClass().getScript(ptr); + + if (!script.empty()) + mScripts.add(script, ptr); + + return true; + } + }; - MWWorld::Ptr containerPtr (&*iter, cell); + struct AddContainerItemScriptsVisitor + { + AddContainerItemScriptsVisitor(MWWorld::LocalScripts& scripts) + : mScripts(scripts) + { + } + MWWorld::LocalScripts& mScripts; + bool operator()(const MWWorld::Ptr& containerPtr) + { MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr); - for(MWWorld::ContainerStoreIterator it3 = container.begin(); it3 != container.end(); ++it3) + for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) { - std::string script = it3->getClass().getScript(*it3); + std::string script = it->getClass().getScript(*it); if(script != "") { - MWWorld::Ptr item = *it3; - item.mCell = cell; - localScripts.add (script, item); + MWWorld::Ptr item = *it; + item.mCell = containerPtr.getCell(); + mScripts.add (script, item); } } + return true; } - } + }; + } -MWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (store) {} +MWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (store) +{ + mIter = mScripts.end(); +} -void MWWorld::LocalScripts::setIgnore (const Ptr& ptr) +void MWWorld::LocalScripts::setIgnore (const ConstPtr& ptr) { mIgnore = ptr; } @@ -116,26 +127,13 @@ void MWWorld::LocalScripts::addCell (CellStore *cell) { - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScriptsCont (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScriptsCont (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScriptsCont (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); + AddScriptsVisitor addScriptsVisitor(*this); + cell->forEach(addScriptsVisitor); + + AddContainerItemScriptsVisitor addContainerItemScriptsVisitor(*this); + cell->forEachType(addContainerItemScriptsVisitor); + cell->forEachType(addContainerItemScriptsVisitor); + cell->forEachType(addContainerItemScriptsVisitor); } void MWWorld::LocalScripts::clear() diff -Nru openmw-0.37.0/apps/openmw/mwworld/localscripts.hpp openmw-0.38.0/apps/openmw/mwworld/localscripts.hpp --- openmw-0.37.0/apps/openmw/mwworld/localscripts.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/localscripts.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -17,14 +17,14 @@ { std::list > mScripts; std::list >::iterator mIter; - MWWorld::Ptr mIgnore; + MWWorld::ConstPtr mIgnore; const MWWorld::ESMStore& mStore; public: LocalScripts (const MWWorld::ESMStore& store); - void setIgnore (const Ptr& ptr); + void setIgnore (const ConstPtr& ptr); ///< Mark a single reference for ignoring during iteration over local scripts (will revoke /// previous ignores). @@ -52,7 +52,7 @@ void remove (RefData *ref); void remove (const Ptr& ptr); - ///< Remove script for given reference (ignored if reference does not have a scirpt listed). + ///< Remove script for given reference (ignored if reference does not have a script listed). }; } diff -Nru openmw-0.37.0/apps/openmw/mwworld/manualref.cpp openmw-0.38.0/apps/openmw/mwworld/manualref.cpp --- openmw-0.37.0/apps/openmw/mwworld/manualref.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/manualref.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -54,6 +54,7 @@ case ESM::REC_REPA: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_STAT: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_WEAP: create(store.get(), lowerName, mRef, mPtr); break; + case ESM::REC_BODY: create(store.get(), lowerName, mRef, mPtr); break; case 0: throw std::logic_error("failed to create manual cell ref for " + lowerName + " (unknown ID)"); diff -Nru openmw-0.37.0/apps/openmw/mwworld/player.cpp openmw-0.38.0/apps/openmw/mwworld/player.cpp --- openmw-0.37.0/apps/openmw/mwworld/player.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/player.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -205,6 +205,35 @@ return ptr.getClass().getNpcStats(ptr).getDrawState(); } + void Player::activate() + { + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + return; + + MWWorld::Ptr player = getPlayer(); + const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player); + if (playerStats.isParalyzed() || playerStats.getKnockedDown()) + return; + + MWWorld::Ptr toActivate = MWBase::Environment::get().getWorld()->getFacedObject(); + + if (toActivate.isEmpty()) + return; + + if (toActivate.getClass().getName(toActivate) == "") // objects without name presented to user can never be activated + return; + + if (toActivate.getClass().isActor()) + { + MWMechanics::CreatureStats &stats = toActivate.getClass().getCreatureStats(toActivate); + + if (stats.getAiSequence().isInCombat() && !stats.isDead()) + return; + } + + MWBase::Environment::get().getWorld()->activate(toActivate, player); + } + bool Player::wasTeleported() const { return mTeleported; diff -Nru openmw-0.37.0/apps/openmw/mwworld/player.hpp openmw-0.38.0/apps/openmw/mwworld/player.hpp --- openmw-0.37.0/apps/openmw/mwworld/player.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/player.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -82,6 +82,9 @@ void setDrawState (MWMechanics::DrawState_ state); MWMechanics::DrawState_ getDrawState(); /// \todo constness + /// Activate the object under the crosshair, if any + void activate(); + bool getAutoMove() const; void setAutoMove (bool enable); diff -Nru openmw-0.37.0/apps/openmw/mwworld/projectilemanager.cpp openmw-0.38.0/apps/openmw/mwworld/projectilemanager.cpp --- openmw-0.37.0/apps/openmw/mwworld/projectilemanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/projectilemanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" @@ -25,6 +27,7 @@ #include "../mwrender/effectmanager.hpp" #include "../mwrender/animation.hpp" #include "../mwrender/vismask.hpp" +#include "../mwrender/renderingmanager.hpp" #include "../mwsound/sound.hpp" @@ -34,23 +37,69 @@ namespace MWWorld { - ProjectileManager::ProjectileManager(osg::Group* parent, Resource::ResourceSystem* resourceSystem, MWPhysics::PhysicsSystem* physics) + ProjectileManager::ProjectileManager(osg::Group* parent, Resource::ResourceSystem* resourceSystem, + MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics) : mParent(parent) , mResourceSystem(resourceSystem) + , mRendering(rendering) , mPhysics(physics) { } - void ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient) + /// Rotates an osg::PositionAttitudeTransform over time. + class RotateCallback : public osg::NodeCallback + { + public: + RotateCallback(const osg::Vec3f& axis = osg::Vec3f(0,-1,0), float rotateSpeed = osg::PI*2) + : mAxis(axis) + , mRotateSpeed(rotateSpeed) + { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osg::PositionAttitudeTransform* transform = static_cast(node); + + double time = nv->getFrameStamp()->getSimulationTime(); + + osg::Quat orient = osg::Quat(time * mRotateSpeed, mAxis); + transform->setAttitude(orient); + + traverse(node, nv); + } + + private: + osg::Vec3f mAxis; + float mRotateSpeed; + }; + + + void ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate) { state.mNode = new osg::PositionAttitudeTransform; state.mNode->setNodeMask(MWRender::Mask_Effect); state.mNode->setPosition(pos); state.mNode->setAttitude(orient); - mParent->addChild(state.mNode); - mResourceSystem->getSceneManager()->createInstance(model, state.mNode); + osg::Group* attachTo = state.mNode; + + if (rotate) + { + osg::ref_ptr rotateNode (new osg::PositionAttitudeTransform); + rotateNode->addUpdateCallback(new RotateCallback()); + state.mNode->addChild(rotateNode); + attachTo = rotateNode; + } + + mResourceSystem->getSceneManager()->createInstance(model, attachTo); + + SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; + state.mNode->accept(disableFreezeOnCullVisitor); + + state.mNode->addCullCallback(new SceneUtil::LightListCallback); + + mParent->addChild(state.mNode); state.mEffectAnimationTime.reset(new MWRender::EffectAnimationTime); @@ -104,15 +153,15 @@ MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), model); MWWorld::Ptr ptr = ref.getPtr(); - createModel(state, ptr.getClass().getModel(ptr), pos, orient); + createModel(state, ptr.getClass().getModel(ptr), pos, orient, true); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - state.mSound = sndMgr->playManualSound3D(pos, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + state.mSound = sndMgr->playSound3D(pos, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); mMagicBolts.push_back(state); } - void ProjectileManager::launchProjectile(Ptr actor, Ptr projectile, const osg::Vec3f &pos, const osg::Quat &orient, Ptr bow, float speed, float attackStrength) + void ProjectileManager::launchProjectile(Ptr actor, ConstPtr projectile, const osg::Vec3f &pos, const osg::Quat &orient, Ptr bow, float speed, float attackStrength) { ProjectileState state; state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); @@ -125,7 +174,7 @@ MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().getRefId()); MWWorld::Ptr ptr = ref.getPtr(); - createModel(state, ptr.getClass().getModel(ptr), pos, orient); + createModel(state, ptr.getClass().getModel(ptr), pos, orient, false); mProjectiles.push_back(state); } @@ -191,7 +240,6 @@ MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, ESM::RT_Target, it->mSpellId, it->mSourceName); MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); - mParent->removeChild(it->mNode); it = mMagicBolts.erase(it); @@ -226,32 +274,38 @@ // TODO: use a proper btRigidBody / btGhostObject? MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, 0xff, MWPhysics::CollisionType_Projectile); - if (result.mHit) + bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos); + if (result.mHit || underwater) { - MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId); - - // Try to get a Ptr to the bow that was used. It might no longer exist. - MWWorld::Ptr bow = projectileRef.getPtr(); - if (!caster.isEmpty()) + if (result.mHit) { - MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster); - MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId)) - bow = *invIt; - } + MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId); - if (caster.isEmpty()) - caster = result.mHitObject; + // Try to get a Ptr to the bow that was used. It might no longer exist. + MWWorld::Ptr bow = projectileRef.getPtr(); + if (!caster.isEmpty()) + { + MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster); + MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId)) + bow = *invIt; + } - MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHitPos, it->mAttackStrength); + if (caster.isEmpty()) + caster = result.mHitObject; - mParent->removeChild(it->mNode); + MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHitPos, it->mAttackStrength); + } + + if (underwater) + mRendering->emitWaterRipple(newPos); + mParent->removeChild(it->mNode); it = mProjectiles.erase(it); continue; } - else - ++it; + + ++it; } } @@ -340,7 +394,7 @@ return true; } - createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation)); + createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), false); mProjectiles.push_back(state); return true; @@ -371,11 +425,11 @@ return true; } - createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation)); + createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - state.mSound = sndMgr->playManualSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f, - MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + state.mSound = sndMgr->playSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f, + MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); state.mSoundId = esm.mSound; mMagicBolts.push_back(state); diff -Nru openmw-0.37.0/apps/openmw/mwworld/projectilemanager.hpp openmw-0.38.0/apps/openmw/mwworld/projectilemanager.hpp --- openmw-0.37.0/apps/openmw/mwworld/projectilemanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/projectilemanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,6 +4,7 @@ #include #include +#include #include @@ -35,6 +36,7 @@ namespace MWRender { class EffectAnimationTime; + class RenderingManager; } namespace MWWorld @@ -44,14 +46,14 @@ { public: ProjectileManager (osg::Group* parent, Resource::ResourceSystem* resourceSystem, - MWPhysics::PhysicsSystem* physics); + MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics); /// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used. void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection); - void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& pos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength); void update(float dt); @@ -66,6 +68,7 @@ private: osg::ref_ptr mParent; Resource::ResourceSystem* mResourceSystem; + MWRender::RenderingManager* mRendering; MWPhysics::PhysicsSystem* mPhysics; struct State @@ -117,8 +120,11 @@ void moveProjectiles(float dt); void moveMagicBolts(float dt); - void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient); + void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate); void update (State& state, float duration); + + void operator=(const ProjectileManager&); + ProjectileManager(const ProjectileManager&); }; } diff -Nru openmw-0.37.0/apps/openmw/mwworld/ptr.cpp openmw-0.38.0/apps/openmw/mwworld/ptr.cpp --- openmw-0.37.0/apps/openmw/mwworld/ptr.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/ptr.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -52,3 +52,30 @@ { return mRef; } + +// ------------------------------------------------------------------------------- + +const std::string &MWWorld::ConstPtr::getTypeName() const +{ + if(mRef != 0) + return mRef->mClass->getTypeName(); + throw std::runtime_error("Can't get type name from an empty object."); +} + +const MWWorld::LiveCellRefBase *MWWorld::ConstPtr::getBase() const +{ + if (!mRef) + throw std::runtime_error ("Can't access cell ref pointed to by null Ptr"); + + return mRef; +} + +const MWWorld::ContainerStore *MWWorld::ConstPtr::getContainerStore() const +{ + return mContainerStore; +} + +MWWorld::ConstPtr::operator const void *() +{ + return mRef; +} diff -Nru openmw-0.37.0/apps/openmw/mwworld/ptr.hpp openmw-0.38.0/apps/openmw/mwworld/ptr.hpp --- openmw-0.37.0/apps/openmw/mwworld/ptr.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/ptr.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -85,6 +85,87 @@ ///< Return a 0-pointer, if Ptr is empty; return a non-0-pointer, if Ptr is not empty }; + /// \brief Pointer to a const LiveCellRef + /// @note a Ptr can be implicitely converted to a ConstPtr, but you can not convert a ConstPtr to a Ptr. + class ConstPtr + { + public: + + const MWWorld::LiveCellRefBase *mRef; + const CellStore *mCell; + const ContainerStore *mContainerStore; + + public: + ConstPtr(const MWWorld::LiveCellRefBase *liveCellRef=0, const CellStore *cell=0) + : mRef(liveCellRef), mCell(cell), mContainerStore(0) + { + } + + ConstPtr(const MWWorld::Ptr& ptr) + : mRef(ptr.mRef), mCell(ptr.mCell), mContainerStore(ptr.mContainerStore) + { + } + + bool isEmpty() const + { + return mRef == 0; + } + + const std::string& getTypeName() const; + + const Class& getClass() const + { + if(mRef != 0) + return *(mRef->mClass); + throw std::runtime_error("Cannot get class of an empty object"); + } + + template + const MWWorld::LiveCellRef *get() const + { + const MWWorld::LiveCellRef *ref = dynamic_cast*>(mRef); + if(ref) return ref; + + std::stringstream str; + str<< "Bad LiveCellRef cast to "<mRef; + } + + const RefData& getRefData() const + { + assert(mRef); + return mRef->mData; + } + + const CellStore *getCell() const + { + assert(mCell); + return mCell; + } + + bool isInCell() const + { + return (mContainerStore == 0) && (mCell != 0); + } + + const ContainerStore *getContainerStore() const; + ///< May return a 0-pointer, if reference is not in a container. + + operator const void *(); + ///< Return a 0-pointer, if Ptr is empty; return a non-0-pointer, if Ptr is not empty + }; + inline bool operator== (const Ptr& left, const Ptr& right) { return left.mRef==right.mRef; @@ -114,6 +195,36 @@ { return !(left>right); } + + inline bool operator== (const ConstPtr& left, const ConstPtr& right) + { + return left.mRef==right.mRef; + } + + inline bool operator!= (const ConstPtr& left, const ConstPtr& right) + { + return !(left==right); + } + + inline bool operator< (const ConstPtr& left, const ConstPtr& right) + { + return left.mRef= (const ConstPtr& left, const ConstPtr& right) + { + return !(left (const ConstPtr& left, const ConstPtr& right) + { + return rightright); + } } #endif diff -Nru openmw-0.37.0/apps/openmw/mwworld/refdata.cpp openmw-0.38.0/apps/openmw/mwworld/refdata.cpp --- openmw-0.37.0/apps/openmw/mwworld/refdata.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/refdata.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -18,7 +18,7 @@ mCount = refData.mCount; mPosition = refData.mPosition; mChanged = refData.mChanged; - mDeleted = refData.mDeleted; + mDeletedByContentFile = refData.mDeletedByContentFile; mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; } @@ -32,7 +32,7 @@ } RefData::RefData() - : mBaseNode(0), mDeleted(false), mEnabled (true), mCount (1), mCustomData (0), mChanged(false) + : mBaseNode(0), mDeletedByContentFile(false), mEnabled (true), mCount (1), mCustomData (0), mChanged(false) { for (int i=0; i<3; ++i) { @@ -42,15 +42,15 @@ } RefData::RefData (const ESM::CellRef& cellRef) - : mBaseNode(0), mDeleted(false), mEnabled (true), + : mBaseNode(0), mDeletedByContentFile(false), mEnabled (true), mCount (1), mPosition (cellRef.mPos), mCustomData (0), mChanged(false) // Loading from ESM/ESP files -> assume unchanged { } - RefData::RefData (const ESM::ObjectState& objectState) - : mBaseNode(0), mDeleted(false), + RefData::RefData (const ESM::ObjectState& objectState, bool deletedByContentFile) + : mBaseNode(0), mDeletedByContentFile(deletedByContentFile), mEnabled (objectState.mEnabled != 0), mCount (objectState.mCount), mPosition (objectState.mPosition), @@ -108,12 +108,17 @@ {} } - void RefData::setBaseNode(osg::PositionAttitudeTransform *base) + void RefData::setBaseNode(SceneUtil::PositionAttitudeTransform *base) { mBaseNode = base; } - osg::PositionAttitudeTransform* RefData::getBaseNode() + SceneUtil::PositionAttitudeTransform* RefData::getBaseNode() + { + return mBaseNode; + } + + const SceneUtil::PositionAttitudeTransform* RefData::getBaseNode() const { return mBaseNode; } @@ -139,19 +144,19 @@ mCount = count; } - void RefData::setDeleted(bool deleted) + void RefData::setDeletedByContentFile(bool deleted) { - mDeleted = deleted; + mDeletedByContentFile = deleted; } bool RefData::isDeleted() const { - return mDeleted || mCount == 0; + return mDeletedByContentFile || mCount == 0; } bool RefData::isDeletedByContentFile() const { - return mDeleted; + return mDeletedByContentFile; } MWScript::Locals& RefData::getLocals() @@ -166,14 +171,20 @@ void RefData::enable() { - mChanged = !mEnabled; - mEnabled = true; + if (!mEnabled) + { + mChanged = true; + mEnabled = true; + } } void RefData::disable() { - mChanged = mEnabled; - mEnabled = false; + if (mEnabled) + { + mChanged = true; + mEnabled = false; + } } void RefData::setPosition(const ESM::Position& pos) @@ -182,7 +193,7 @@ mPosition = pos; } - const ESM::Position& RefData::getPosition() + const ESM::Position& RefData::getPosition() const { return mPosition; } @@ -198,6 +209,11 @@ { return mCustomData; } + + const CustomData *RefData::getCustomData() const + { + return mCustomData; + } bool RefData::hasChanged() const { diff -Nru openmw-0.37.0/apps/openmw/mwworld/refdata.hpp openmw-0.38.0/apps/openmw/mwworld/refdata.hpp --- openmw-0.37.0/apps/openmw/mwworld/refdata.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/refdata.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,7 +7,7 @@ #include -namespace osg +namespace SceneUtil { class PositionAttitudeTransform; } @@ -26,14 +26,18 @@ class RefData { - osg::PositionAttitudeTransform* mBaseNode; + SceneUtil::PositionAttitudeTransform* mBaseNode; MWScript::Locals mLocals; - bool mDeleted; // separate delete flag used for deletion by a content file + /// separate delete flag used for deletion by a content file + /// @note not stored in the save game file. + bool mDeletedByContentFile; + bool mEnabled; - int mCount; // 0: deleted + /// 0: deleted + int mCount; ESM::Position mPosition; @@ -51,10 +55,10 @@ /// @param cellRef Used to copy constant data such as position into this class where it can /// be altered without affecting the original data. This makes it possible - /// to reset the position as the orignal data is still held in the CellRef + /// to reset the position as the original data is still held in the CellRef RefData (const ESM::CellRef& cellRef); - RefData (const ESM::ObjectState& objectState); + RefData (const ESM::ObjectState& objectState, bool deletedByContentFile); ///< Ignores local variables and custom data (not enough context available here to /// perform these operations). @@ -69,10 +73,13 @@ RefData& operator= (const RefData& refData); /// Return base node (can be a null pointer). - osg::PositionAttitudeTransform* getBaseNode(); + SceneUtil::PositionAttitudeTransform* getBaseNode(); + + /// Return base node (can be a null pointer). + const SceneUtil::PositionAttitudeTransform* getBaseNode() const; /// Set base node (can be a null pointer). - void setBaseNode (osg::PositionAttitudeTransform* base); + void setBaseNode (SceneUtil::PositionAttitudeTransform* base); int getCount() const; @@ -87,7 +94,7 @@ /// This flag is only used for content stack loading and will not be stored in the savegame. /// If the object was deleted by gameplay, then use setCount(0) instead. - void setDeleted(bool deleted); + void setDeletedByContentFile(bool deleted); /// Returns true if the object was either deleted by the content file or by gameplay. bool isDeleted() const; @@ -103,7 +110,7 @@ void disable(); void setPosition (const ESM::Position& pos); - const ESM::Position& getPosition(); + const ESM::Position& getPosition() const; void setCustomData (CustomData *data); ///< Set custom data (potentially replacing old custom data). The ownership of \a data is @@ -112,6 +119,8 @@ CustomData *getCustomData(); ///< May return a 0-pointer. The ownership of the return data object is not transferred. + const CustomData *getCustomData() const; + bool hasChanged() const; ///< Has this RefData changed since it was originally loaded? }; diff -Nru openmw-0.37.0/apps/openmw/mwworld/scene.cpp openmw-0.38.0/apps/openmw/mwworld/scene.cpp --- openmw-0.37.0/apps/openmw/mwworld/scene.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/scene.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -22,7 +22,7 @@ #include "localscripts.hpp" #include "esmstore.hpp" #include "class.hpp" -#include "cellfunctors.hpp" +#include "cellvisitors.hpp" #include "cellstore.hpp" namespace @@ -32,7 +32,7 @@ MWRender::RenderingManager& rendering) { std::string model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr), rendering.getResourceSystem()->getVFS()); - std::string id = ptr.getClass().getId(ptr); + std::string id = ptr.getCellRef().getRefId(); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player ptr.getClass().insertObjectRendering(ptr, model, rendering); @@ -78,7 +78,7 @@ } } - struct InsertFunctor + struct InsertVisitor { MWWorld::CellStore& mCell; bool mRescale; @@ -86,13 +86,13 @@ MWPhysics::PhysicsSystem& mPhysics; MWRender::RenderingManager& mRendering; - InsertFunctor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, + InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering); bool operator() (const MWWorld::Ptr& ptr); }; - InsertFunctor::InsertFunctor (MWWorld::CellStore& cell, bool rescale, + InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) : mCell (cell), mRescale (rescale), mLoadingListener (loadingListener), @@ -100,7 +100,7 @@ mRendering (rendering) {} - bool InsertFunctor::operator() (const MWWorld::Ptr& ptr) + bool InsertVisitor::operator() (const MWWorld::Ptr& ptr) { if (mRescale) { @@ -116,7 +116,6 @@ { addObject(ptr, mPhysics, mRendering); updateObjectRotation(ptr, mPhysics, mRendering, false); - ptr.getClass().adjustPosition (ptr, false); } catch (const std::exception& e) { @@ -129,6 +128,17 @@ return true; } + + struct AdjustPositionVisitor + { + bool operator() (const MWWorld::Ptr& ptr) + { + if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) + ptr.getClass().adjustPosition (ptr, false); + return true; + } + }; + } @@ -196,11 +206,11 @@ void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; - ListAndResetObjects functor; + ListAndResetObjectsVisitor visitor; - (*iter)->forEach(functor); - for (std::vector::const_iterator iter2 (functor.mObjects.begin()); - iter2!=functor.mObjects.end(); ++iter2) + (*iter)->forEach(visitor); + for (std::vector::const_iterator iter2 (visitor.mObjects.begin()); + iter2!=visitor.mObjects.end(); ++iter2) { mPhysics->remove(*iter2); } @@ -265,7 +275,7 @@ mRendering.addCell(cell); bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior(); - float waterLevel = cell->isExterior() ? -1.f : cell->getWaterLevel(); + float waterLevel = cell->getWaterLevel(); mRendering.setWaterEnabled(waterEnabled); if (waterEnabled) { @@ -403,6 +413,8 @@ // Delay the map update until scripts have been given a chance to run. // If we don't do this, objects that should be disabled will still appear on the map. mNeedMapUpdate = true; + + mRendering.getResourceSystem()->clearCache(); } void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) @@ -518,6 +530,8 @@ // Delay the map update until scripts have been given a chance to run. // If we don't do this, objects that should be disabled will still appear on the map. mNeedMapUpdate = true; + + mRendering.getResourceSystem()->clearCache(); } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos) @@ -547,8 +561,12 @@ void Scene::insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener) { - InsertFunctor functor (cell, rescale, *loadingListener, *mPhysics, mRendering); - cell.forEach (functor); + InsertVisitor insertVisitor (cell, rescale, *loadingListener, *mPhysics, mRendering); + cell.forEach (insertVisitor); + + // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order + AdjustPositionVisitor adjustPosVisitor; + cell.forEach (adjustPosVisitor); } void Scene::addObjectToScene (const Ptr& ptr) diff -Nru openmw-0.37.0/apps/openmw/mwworld/store.cpp openmw-0.38.0/apps/openmw/mwworld/store.cpp --- openmw-0.37.0/apps/openmw/mwworld/store.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/store.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -42,6 +42,10 @@ namespace MWWorld { + RecordId::RecordId(const std::string &id, bool isDeleted) + : mId(id), mIsDeleted(isDeleted) + {} + template IndexedStore::IndexedStore() { @@ -60,7 +64,9 @@ void IndexedStore::load(ESM::ESMReader &esm) { T record; - record.load(esm); + bool isDeleted = false; + + record.load(esm, isDeleted); // Try to overwrite existing record std::pair ret = mStatic.insert(std::make_pair(record.mIndex, record)); @@ -178,16 +184,21 @@ return ptr; } template - void Store::load(ESM::ESMReader &esm, const std::string &id) + RecordId Store::load(ESM::ESMReader &esm) { - std::string idLower = Misc::StringUtils::lowerCase(id); + T record; + bool isDeleted = false; - std::pair inserted = mStatic.insert(std::make_pair(idLower, T())); + record.load(esm, isDeleted); + Misc::StringUtils::lowerCaseInPlace(record.mId); + + std::pair inserted = mStatic.insert(std::make_pair(record.mId, record)); if (inserted.second) mShared.push_back(&inserted.first->second); + else + inserted.first->second = record; - inserted.first->second.mId = idLower; - inserted.first->second.load(esm); + return RecordId(record.mId, isDeleted); } template void Store::setUp() @@ -309,20 +320,21 @@ ++iter) { writer.startRecord (T::sRecordId); - writer.writeHNString ("NAME", iter->second.mId); iter->second.save (writer); writer.endRecord (T::sRecordId); } } template - void Store::read(ESM::ESMReader& reader, const std::string& id) + RecordId Store::read(ESM::ESMReader& reader) { T record; - record.mId = id; - record.load (reader); + bool isDeleted = false; + + record.load (reader, isDeleted); insert (record); - } + return RecordId(record.mId, isDeleted); + } // LandTexture //========================================================================= @@ -339,8 +351,9 @@ assert(plugin < mStatic.size()); const LandTextureList <exl = mStatic[plugin]; - assert(index < ltexl.size()); - return <exl.at(index); + if (index >= ltexl.size()) + return NULL; + return <exl[index]; } const ESM::LandTexture *Store::find(size_t index, size_t plugin) const { @@ -361,26 +374,27 @@ assert(plugin < mStatic.size()); return mStatic[plugin].size(); } - void Store::load(ESM::ESMReader &esm, const std::string &id, size_t plugin) + RecordId Store::load(ESM::ESMReader &esm, size_t plugin) { ESM::LandTexture lt; - lt.load(esm); - lt.mId = id; + bool isDeleted = false; + + lt.load(esm, isDeleted); + + assert(plugin < mStatic.size()); - // Make sure we have room for the structure - if (plugin >= mStatic.size()) { - mStatic.resize(plugin+1); - } LandTextureList <exl = mStatic[plugin]; if(lt.mIndex + 1 > (int)ltexl.size()) ltexl.resize(lt.mIndex+1); // Store it ltexl[lt.mIndex] = lt; + + return RecordId(lt.mId, isDeleted); } - void Store::load(ESM::ESMReader &esm, const std::string &id) + RecordId Store::load(ESM::ESMReader &esm) { - load(esm, id, esm.getIndex()); + return load(esm, esm.getIndex()); } Store::iterator Store::begin(size_t plugin) const { @@ -392,7 +406,11 @@ assert(plugin < mStatic.size()); return mStatic[plugin].end(); } - + void Store::resize(size_t num) + { + if (mStatic.size() < num) + mStatic.resize(num); + } // Land //========================================================================= @@ -440,10 +458,12 @@ } return ptr; } - void Store::load(ESM::ESMReader &esm, const std::string &id) + RecordId Store::load(ESM::ESMReader &esm) { ESM::Land *ptr = new ESM::Land(); - ptr->load(esm); + bool isDeleted = false; + + ptr->load(esm, isDeleted); // Same area defined in multiple plugins? -> last plugin wins // Can't use search() because we aren't sorted yet - is there any other way to speed this up? @@ -458,6 +478,8 @@ } mStatic.push_back(ptr); + + return RecordId("", isDeleted); } void Store::setUp() { @@ -600,7 +622,7 @@ mSharedExt.push_back(&(it->second)); } } - void Store::load(ESM::ESMReader &esm, const std::string &id) + RecordId Store::load(ESM::ESMReader &esm) { // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, // and we merge all this data into one Cell object. However, we can't simply search for the cell id, @@ -608,13 +630,13 @@ // are not available until both cells have been loaded at least partially! // All cells have a name record, even nameless exterior cells. - std::string idLower = Misc::StringUtils::lowerCase(id); ESM::Cell cell; - cell.mName = id; + bool isDeleted = false; - // Load the (x,y) coordinates of the cell, if it is an exterior cell, + // Load the (x,y) coordinates of the cell, if it is an exterior cell, // so we can find the cell we need to merge with - cell.loadData(esm); + cell.loadNameAndData(esm, isDeleted); + std::string idLower = Misc::StringUtils::lowerCase(cell.mName); if(cell.mData.mFlags & ESM::Cell::Interior) { @@ -682,6 +704,8 @@ mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell; } } + + return RecordId(cell.mName, isDeleted); } Store::iterator Store::intBegin() const { @@ -837,10 +861,12 @@ { mCells = &cells; } - void Store::load(ESM::ESMReader &esm, const std::string &id) + RecordId Store::load(ESM::ESMReader &esm) { ESM::Pathgrid pathgrid; - pathgrid.load(esm); + bool isDeleted = false; + + pathgrid.load(esm, isDeleted); // Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell. // For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name. @@ -862,6 +888,8 @@ if (!ret.second) ret.first->second = pathgrid; } + + return RecordId("", isDeleted); } size_t Store::getSize() const { @@ -1013,51 +1041,29 @@ } template <> - void Store::load(ESM::ESMReader &esm, const std::string &id) { - std::string idLower = Misc::StringUtils::lowerCase(id); - - std::map::iterator it = mStatic.find(idLower); - if (it == mStatic.end()) { - it = mStatic.insert( std::make_pair( idLower, ESM::Dialogue() ) ).first; - it->second.mId = id; // don't smash case here, as this line is printed + inline RecordId Store::load(ESM::ESMReader &esm) { + // The original letter case of a dialogue ID is saved, because it's printed + ESM::Dialogue dialogue; + bool isDeleted = false; + + dialogue.loadId(esm); + + std::string idLower = Misc::StringUtils::lowerCase(dialogue.mId); + std::map::iterator found = mStatic.find(idLower); + if (found == mStatic.end()) + { + dialogue.loadData(esm, isDeleted); + mStatic.insert(std::make_pair(idLower, dialogue)); } - - it->second.load(esm); - } - - - // Script - //========================================================================= - - template <> - void Store::load(ESM::ESMReader &esm, const std::string &id) { - ESM::Script scpt; - scpt.load(esm); - Misc::StringUtils::toLower(scpt.mId); - - std::pair inserted = mStatic.insert(std::make_pair(scpt.mId, scpt)); - if (inserted.second) - mShared.push_back(&inserted.first->second); else - inserted.first->second = scpt; - } - - - // StartScript - //========================================================================= + { + found->second.loadData(esm, isDeleted); + dialogue = found->second; + } - template <> - void Store::load(ESM::ESMReader &esm, const std::string &id) - { - ESM::StartScript s; - s.load(esm); - s.mId = Misc::StringUtils::toLower(s.mId); - std::pair inserted = mStatic.insert(std::make_pair(s.mId, s)); - if (inserted.second) - mShared.push_back(&inserted.first->second); - else - inserted.first->second = s; + return RecordId(dialogue.mId, isDeleted); } + } template class MWWorld::Store; @@ -1082,7 +1088,7 @@ template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; -template class MWWorld::Store; +//template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; diff -Nru openmw-0.37.0/apps/openmw/mwworld/store.hpp openmw-0.38.0/apps/openmw/mwworld/store.hpp --- openmw-0.37.0/apps/openmw/mwworld/store.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/store.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,23 +19,34 @@ namespace MWWorld { - struct StoreBase + struct RecordId { + std::string mId; + bool mIsDeleted; + + RecordId(const std::string &id = "", bool isDeleted = false); + }; + + class StoreBase + { + public: virtual ~StoreBase() {} virtual void setUp() {} + + /// List identifiers of records contained in this Store (case-smashed). No-op for Stores that don't use string IDs. virtual void listIdentifier(std::vector &list) const {} virtual size_t getSize() const = 0; virtual int getDynamicSize() const { return 0; } - virtual void load(ESM::ESMReader &esm, const std::string &id) = 0; + virtual RecordId load(ESM::ESMReader &esm) = 0; virtual bool eraseStatic(const std::string &id) {return false;} virtual void clearDynamic() {} virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const {} - virtual void read (ESM::ESMReader& reader, const std::string& id) {} + virtual RecordId read (ESM::ESMReader& reader) { return RecordId(); } ///< Read into dynamic storage }; @@ -180,9 +191,9 @@ bool erase(const std::string &id); bool erase(const T &item); - void load(ESM::ESMReader &esm, const std::string &id); + RecordId load(ESM::ESMReader &esm); void write(ESM::ESMWriter& writer, Loading::Listener& progress) const; - void read(ESM::ESMReader& reader, const std::string& id); + RecordId read(ESM::ESMReader& reader); }; template <> @@ -202,11 +213,14 @@ const ESM::LandTexture *search(size_t index, size_t plugin) const; const ESM::LandTexture *find(size_t index, size_t plugin) const; + /// Resize the internal store to hold at least \a num plugins. + void resize(size_t num); + size_t getSize() const; size_t getSize(size_t plugin) const; - void load(ESM::ESMReader &esm, const std::string &id, size_t plugin); - void load(ESM::ESMReader &esm, const std::string &id); + RecordId load(ESM::ESMReader &esm, size_t plugin); + RecordId load(ESM::ESMReader &esm); iterator begin(size_t plugin) const; iterator end(size_t plugin) const; @@ -231,7 +245,7 @@ ESM::Land *search(int x, int y) const; ESM::Land *find(int x, int y) const; - void load(ESM::ESMReader &esm, const std::string &id); + RecordId load(ESM::ESMReader &esm); void setUp(); }; @@ -281,7 +295,7 @@ void setUp(); - void load(ESM::ESMReader &esm, const std::string &id); + RecordId load(ESM::ESMReader &esm); iterator intBegin() const; iterator intEnd() const; @@ -323,7 +337,7 @@ Store(); void setCells(Store& cells); - void load(ESM::ESMReader &esm, const std::string &id); + RecordId load(ESM::ESMReader &esm); size_t getSize() const; void setUp(); @@ -338,14 +352,16 @@ template <> - struct Store : public IndexedStore + class Store : public IndexedStore { + public: Store(); }; template <> - struct Store : public IndexedStore + class Store : public IndexedStore { + public: Store(); }; diff -Nru openmw-0.37.0/apps/openmw/mwworld/timestamp.cpp openmw-0.38.0/apps/openmw/mwworld/timestamp.cpp --- openmw-0.37.0/apps/openmw/mwworld/timestamp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/timestamp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -96,7 +96,7 @@ if (left.getHour() stormWindSpeed) , mRainSpeed(rainSpeed) , mRainFrequency(fallback.getFallbackFloat("Weather_" + name + "_Rain_Entrance_Speed")) @@ -148,6 +146,21 @@ mThunderSoundID[1] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_1"); mThunderSoundID[2] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_2"); mThunderSoundID[3] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_3"); + + // TODO: support weathers that have both "Ambient Loop Sound ID" and "Rain Loop Sound ID", need to play both sounds at the same time. + + if (!mRainEffect.empty()) // NOTE: in vanilla, the weathers with rain seem to be hardcoded; changing Using_Precip has no effect + { + mAmbientLoopSoundID = fallback.getFallbackString("Weather_" + name + "_Rain_Loop_Sound_ID"); + if (mAmbientLoopSoundID.empty()) // default to "rain" if not set + mAmbientLoopSoundID = "rain"; + } + else + mAmbientLoopSoundID = fallback.getFallbackString("Weather_" + name + "_Ambient_Loop_Sound_ID"); + + if (Misc::StringUtils::ciEqual(mAmbientLoopSoundID, "None")) + mAmbientLoopSoundID.clear(); + /* Unhandled: Rain Diameter=600 ? @@ -528,12 +541,12 @@ addWeather("Cloudy", fallback); // 1 addWeather("Foggy", fallback); // 2 addWeather("Overcast", fallback); // 3 - addWeather("Rain", fallback, "rain"); // 4 - addWeather("Thunderstorm", fallback, "rain heavy"); // 5 - addWeather("Ashstorm", fallback, "ashstorm", "meshes\\ashcloud.nif"); // 6 - addWeather("Blight", fallback, "blight", "meshes\\blightcloud.nif"); // 7 - addWeather("Snow", fallback, "", "meshes\\snow.nif"); // 8 - addWeather("Blizzard", fallback, "BM Blizzard", "meshes\\blizzard.nif"); // 9 + addWeather("Rain", fallback); // 4 + addWeather("Thunderstorm", fallback); // 5 + addWeather("Ashstorm", fallback, "meshes\\ashcloud.nif"); // 6 + addWeather("Blight", fallback, "meshes\\blightcloud.nif"); // 7 + addWeather("Snow", fallback, "meshes\\snow.nif"); // 8 + addWeather("Blizzard", fallback, "meshes\\blizzard.nif"); // 9 Store::iterator it = store.get().begin(); for(; it != store.get().end(); ++it) @@ -610,11 +623,11 @@ void WeatherManager::update(float duration, bool paused) { - MWWorld::Ptr player = MWMechanics::getPlayer(); + MWWorld::ConstPtr player = MWMechanics::getPlayer(); MWBase::World& world = *MWBase::Environment::get().getWorld(); TimeStamp time = world.getTimeStamp(); - if(!paused) + if(!paused || mFastForward) { // Add new transitions when either the player's current external region changes. std::string playerRegion = Misc::StringUtils::lowerCase(player.getCell()->getCell()->mRegion); @@ -725,11 +738,9 @@ void WeatherManager::stopSounds() { if (mAmbientSound.get()) - { MWBase::Environment::get().getSoundManager()->stopSound(mAmbientSound); - mAmbientSound.reset(); - mPlayingSoundID.clear(); - } + mAmbientSound.reset(); + mPlayingSoundID.clear(); } float WeatherManager::getWindSpeed() const @@ -852,12 +863,11 @@ inline void WeatherManager::addWeather(const std::string& name, const MWWorld::Fallback& fallback, - const std::string& ambientLoopSoundID, const std::string& particleEffect) { static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->getFloat(); - Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, ambientLoopSoundID, particleEffect); + Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, particleEffect); mWeatherSettings.push_back(weather); } @@ -875,7 +885,7 @@ inline void WeatherManager::regionalWeatherChanged(const std::string& regionID, RegionWeather& region) { // If the region is current, then add a weather transition for it. - MWWorld::Ptr player = MWMechanics::getPlayer(); + MWWorld::ConstPtr player = MWMechanics::getPlayer(); if(player.isInCell()) { std::string playerRegion = Misc::StringUtils::lowerCase(player.getCell()->getCell()->mRegion); diff -Nru openmw-0.37.0/apps/openmw/mwworld/weather.hpp openmw-0.38.0/apps/openmw/mwworld/weather.hpp --- openmw-0.37.0/apps/openmw/mwworld/weather.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/weather.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -69,7 +69,6 @@ const MWWorld::Fallback& fallback, float stormWindSpeed, float rainSpeed, - const std::string& ambientLoopSoundID, const std::string& particleEffect); std::string mCloudTexture; @@ -290,7 +289,6 @@ void addWeather(const std::string& name, const MWWorld::Fallback& fallback, - const std::string& ambientLoopSoundID = "", const std::string& particleEffect = ""); void importRegions(); diff -Nru openmw-0.37.0/apps/openmw/mwworld/worldimp.cpp openmw-0.38.0/apps/openmw/mwworld/worldimp.cpp --- openmw-0.37.0/apps/openmw/mwworld/worldimp.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/worldimp.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -22,6 +21,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -53,7 +54,6 @@ #include "player.hpp" #include "manualref.hpp" #include "cellstore.hpp" -#include "cellfunctors.hpp" #include "containerstore.hpp" #include "inventorystore.hpp" #include "actionteleport.hpp" @@ -137,8 +137,7 @@ { if (mSky && (isCellExterior() || isCellQuasiExterior())) { - mRendering->skySetDate (mGlobalVariables["day"].getInteger(), - mGlobalVariables["month"].getInteger()); + mRendering->skySetDate (mDay->getInteger(), mMonth->getInteger()); mRendering->setSkyEnabled(true); } @@ -163,8 +162,8 @@ mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0) { mPhysics = new MWPhysics::PhysicsSystem(resourceSystem, rootNode); - mProjectileManager.reset(new ProjectileManager(rootNode, resourceSystem, mPhysics)); mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, &mFallback, resourcePath); + mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering, mPhysics)); mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); @@ -187,18 +186,30 @@ if (mEsm[0].getFormat() == 0) ensureNeededRecords(); + fillGlobalVariables(); + mStore.setUp(); mStore.movePlayerRecord(); mSwimHeightScale = mStore.get().find("fSwimHeightScale")->getFloat(); - mGlobalVariables.fill (mStore); - mWeatherManager = new MWWorld::WeatherManager(*mRendering, mFallback, mStore); mWorldScene = new Scene(*mRendering, mPhysics); } + void World::fillGlobalVariables() + { + mGlobalVariables.fill (mStore); + + mGameHour = &mGlobalVariables["gamehour"]; + mDaysPassed = &mGlobalVariables["dayspassed"]; + mDay = &mGlobalVariables["day"]; + mMonth = &mGlobalVariables["month"]; + mYear = &mGlobalVariables["year"]; + mTimeScale = &mGlobalVariables["timescale"]; + } + void World::startNewGame (bool bypass) { mGoToJail = false; @@ -305,7 +316,7 @@ mTeleportEnabled = true; mLevitationEnabled = true; - mGlobalVariables.fill (mStore); + fillGlobalVariables(); } int World::countSavedGameRecords() const @@ -678,12 +689,12 @@ return mWorldScene->searchPtrViaActorId (actorId); } - struct FindContainerFunctor + struct FindContainerVisitor { - Ptr mContainedPtr; + ConstPtr mContainedPtr; Ptr mResult; - FindContainerFunctor(const Ptr& containedPtr) : mContainedPtr(containedPtr) {} + FindContainerVisitor(const ConstPtr& containedPtr) : mContainedPtr(containedPtr) {} bool operator() (Ptr ptr) { @@ -697,7 +708,7 @@ } }; - Ptr World::findContainer(const Ptr& ptr) + Ptr World::findContainer(const ConstPtr& ptr) { if (ptr.isInCell()) return Ptr(); @@ -709,11 +720,15 @@ const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { - FindContainerFunctor functor(ptr); - (*cellIt)->forEachContainer(functor); + FindContainerVisitor visitor(ptr); + (*cellIt)->forEachType(visitor); + if (visitor.mResult.isEmpty()) + (*cellIt)->forEachType(visitor); + if (visitor.mResult.isEmpty()) + (*cellIt)->forEachType(visitor); - if (!functor.mResult.isEmpty()) - return functor.mResult; + if (!visitor.mResult.isEmpty()) + return visitor.mResult; } return Ptr(); @@ -797,15 +812,15 @@ mWeatherManager->advanceTime (hours, incremental); - hours += mGlobalVariables["gamehour"].getFloat(); + hours += mGameHour->getFloat(); setHour (hours); int days = static_cast(hours / 24); if (days>0) - mGlobalVariables["dayspassed"].setInteger ( - days + mGlobalVariables["dayspassed"].getInteger()); + mDaysPassed->setInteger ( + days + mDaysPassed->getInteger()); } void World::setHour (double hour) @@ -817,10 +832,10 @@ hour = std::fmod (hour, 24); - mGlobalVariables["gamehour"].setFloat(static_cast(hour)); + mGameHour->setFloat(static_cast(hour)); if (days>0) - setDay (days + mGlobalVariables["day"].getInteger()); + setDay (days + mDay->getInteger()); } void World::setDay (int day) @@ -828,7 +843,7 @@ if (day<1) day = 1; - int month = mGlobalVariables["month"].getInteger(); + int month = mMonth->getInteger(); while (true) { @@ -843,14 +858,14 @@ else { month = 0; - mGlobalVariables["year"].setInteger (mGlobalVariables["year"].getInteger()+1); + mYear->setInteger(mYear->getInteger()+1); } day -= days; } - mGlobalVariables["day"].setInteger (day); - mGlobalVariables["month"].setInteger (month); + mDay->setInteger(day); + mMonth->setInteger(month); mRendering->skySetDate(day, month); } @@ -865,30 +880,30 @@ int days = getDaysPerMonth (month); - if (mGlobalVariables["day"].getInteger()>days) - mGlobalVariables["day"].setInteger (days); + if (mDay->getInteger()>days) + mDay->setInteger (days); - mGlobalVariables["month"].setInteger (month); + mMonth->setInteger (month); if (years>0) - mGlobalVariables["year"].setInteger (years+mGlobalVariables["year"].getInteger()); + mYear->setInteger (years+mYear->getInteger()); - mRendering->skySetDate (mGlobalVariables["day"].getInteger(), month); + mRendering->skySetDate (mDay->getInteger(), month); } int World::getDay() const { - return mGlobalVariables["day"].getInteger(); + return mDay->getInteger(); } int World::getMonth() const { - return mGlobalVariables["month"].getInteger(); + return mMonth->getInteger(); } int World::getYear() const { - return mGlobalVariables["year"].getInteger(); + return mYear->getInteger(); } std::string World::getMonthName (int month) const @@ -913,8 +928,7 @@ TimeStamp World::getTimeStamp() const { - return TimeStamp (mGlobalVariables["gamehour"].getFloat(), - mGlobalVariables["dayspassed"].getInteger()); + return TimeStamp (mGameHour->getFloat(), mDaysPassed->getInteger()); } bool World::toggleSky() @@ -941,7 +955,7 @@ float World::getTimeScaleFactor() const { - return mGlobalVariables["timescale"].getFloat(); + return mTimeScale->getFloat(); } void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) @@ -998,7 +1012,8 @@ if (mActivationDistanceOverride >= 0) return static_cast(mActivationDistanceOverride); - return getStore().get().find("iMaxActivateDist")->getFloat() * 5 / 4; + static const int iMaxActivateDist = getStore().get().find("iMaxActivateDist")->getInt(); + return iMaxActivateDist * 5.f / 4.f; } MWWorld::Ptr World::getFacedObject() @@ -1023,32 +1038,30 @@ return facedObject; } - osg::Vec3f getActorHeadPosition(const MWWorld::Ptr& actor, MWRender::RenderingManager* rendering) + osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const { - osg::Vec3f origin(actor.getRefData().getPosition().asVec3()); - - MWRender::Animation* anim = rendering->getAnimation(actor); - if (anim != NULL) + const MWRender::Animation *anim = mRendering->getAnimation(actor); + if(anim) { - const osg::Node* node = anim->getNode("Head"); - if (node == NULL) - node = anim->getNode("Bip01 Head"); - if (node != NULL) + const osg::Node *node = anim->getNode("Head"); + if(!node) node = anim->getNode("Bip01 Head"); + if(node) { osg::MatrixList mats = node->getWorldMatrices(); - if (mats.size()) - origin = mats[0].getTrans(); + if(!mats.empty()) + return mats[0]; } } - return origin; + return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3()); } - std::pair World::getHitContact(const MWWorld::Ptr &ptr, float distance) + + std::pair World::getHitContact(const MWWorld::ConstPtr &ptr, float distance) { const ESM::Position &posdata = ptr.getRefData().getPosition(); osg::Quat rot = osg::Quat(posdata.rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0,0,-1)); - osg::Vec3f pos = getActorHeadPosition(ptr, mRendering); + osg::Vec3f pos = getActorHeadTransform(ptr).getTrans(); std::pair result = mPhysics->getHitContact(ptr, pos, rot, distance); if(result.first.isEmpty()) @@ -1061,6 +1074,9 @@ { if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == NULL) { + if (ptr == getPlayerPtr()) + throw std::runtime_error("can not delete player object"); + ptr.getRefData().setCount(0); if (ptr.isInCell() @@ -1134,7 +1150,7 @@ bool newCellActive = mWorldScene->isCellActive(*newCell); if (!currCellActive && newCellActive) { - newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); + newPtr = currCell->moveTo(ptr, newCell); mWorldScene->addObjectToScene(newPtr); std::string script = newPtr.getClass().getScript(newPtr); @@ -1150,17 +1166,16 @@ removeContainerScripts (ptr); haveToMove = false; - newPtr = ptr.getClass().copyToCell(ptr, *newCell); + newPtr = currCell->moveTo(ptr, newCell); newPtr.getRefData().setBaseNode(0); } else if (!currCellActive && !newCellActive) - newPtr = ptr.getClass().copyToCell(ptr, *newCell); + newPtr = currCell->moveTo(ptr, newCell); else // both cells active { - newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); + newPtr = currCell->moveTo(ptr, newCell); mRendering->updatePtr(ptr, newPtr); - ptr.getRefData().setBaseNode(NULL); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr); mPhysics->updatePtr(ptr, newPtr); @@ -1177,7 +1192,6 @@ addContainerScripts (newPtr, newCell); } } - ptr.getRefData().setCount(0); } } if (haveToMove && newPtr.getRefData().getBaseNode()) @@ -1305,9 +1319,9 @@ rotateObjectImp(ptr, osg::Vec3f(x, y, z), adjust); } - MWWorld::Ptr World::safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos) + MWWorld::Ptr World::safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) { - return copyObjectToCell(ptr,cell,pos,false); + return copyObjectToCell(ptr,cell,pos,ptr.getRefData().getCount(),false); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const @@ -1366,7 +1380,7 @@ { osg::Vec3f a(x1,y1,z1); osg::Vec3f b(x2,y2,z2); - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), MWPhysics::CollisionType_World); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); return result.mHit; } @@ -1399,7 +1413,7 @@ bool reached = (targetRot == maxRot && it->second) || targetRot == minRot; /// \todo should use convexSweepTest here - std::vector collisions = mPhysics->getCollisions(it->first, MWPhysics::CollisionType_Actor, MWPhysics::CollisionType_Actor); + std::vector collisions = mPhysics->getCollisions(it->first, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor); for (std::vector::iterator cit = collisions.begin(); cit != collisions.end(); ++cit) { MWWorld::Ptr ptr = *cit; @@ -1553,8 +1567,20 @@ mPlayer->setLastKnownExteriorPosition(pos.asVec3()); } - if (player.getClass().getNpcStats(player).isWerewolf()) - MWBase::Environment::get().getWindowManager()->setWerewolfOverlay(mRendering->getCamera()->isFirstPerson()); + bool isWerewolf = player.getClass().getNpcStats(player).isWerewolf(); + bool isFirstPerson = mRendering->getCamera()->isFirstPerson(); + if (isWerewolf && isFirstPerson) + { + float werewolfFov = mFallback.getFallbackFloat("General_Werewolf_FOV"); + if (werewolfFov != 0) + mRendering->overrideFieldOfView(werewolfFov); + MWBase::Environment::get().getWindowManager()->setWerewolfOverlay(true); + } + else + { + mRendering->resetFieldOfView(); + MWBase::Environment::get().getWindowManager()->setWerewolfOverlay(false); + } // Sink the camera while sneaking bool sneaking = player.getClass().getCreatureStats(getPlayerPtr()).getStance(MWMechanics::CreatureStats::Stance_Sneak); @@ -1589,18 +1615,23 @@ void World::updateSoundListener() { const ESM::Position& refpos = getPlayerPtr().getRefData().getPosition(); - osg::Vec3f playerPos = refpos.asVec3(); + osg::Vec3f listenerPos; - playerPos.z() += 1.85f * mPhysics->getHalfExtents(getPlayerPtr()).z(); + if (isFirstPerson()) + listenerPos = mRendering->getCameraPosition(); + else + listenerPos = refpos.asVec3() + osg::Vec3f(0, 0, 1.85f * mPhysics->getHalfExtents(getPlayerPtr()).z()); - osg::Quat playerOrient = osg::Quat(refpos.rot[1], osg::Vec3f(0,-1,0)) * + osg::Quat listenerOrient = osg::Quat(refpos.rot[1], osg::Vec3f(0,-1,0)) * osg::Quat(refpos.rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0,0,-1)); - osg::Vec3f forward = playerOrient * osg::Vec3f(0,1,0); - osg::Vec3f up = playerOrient * osg::Vec3f(0,0,1); + osg::Vec3f forward = listenerOrient * osg::Vec3f(0,1,0); + osg::Vec3f up = listenerOrient * osg::Vec3f(0,0,1); + + bool underwater = isUnderwater(getPlayerPtr().getCell(), listenerPos); - MWBase::Environment::get().getSoundManager()->setListenerPosDir(playerPos, forward, up); + MWBase::Environment::get().getSoundManager()->setListenerPosDir(listenerPos, forward, up, underwater); } void World::updateWindowManager () @@ -1676,27 +1707,32 @@ osg::Vec2f World::getNorthVector (CellStore* cell) { - MWWorld::CellRefList& statics = cell->get(); - MWWorld::LiveCellRef* ref = statics.find("northmarker"); - if (!ref) + MWWorld::Ptr northmarker = cell->search("northmarker"); + + if (northmarker.isEmpty()) return osg::Vec2f(0, 1); - osg::Quat orient (-ref->mData.getPosition().rot[2], osg::Vec3f(0,0,1)); + osg::Quat orient (-northmarker.getRefData().getPosition().rot[2], osg::Vec3f(0,0,1)); osg::Vec3f dir = orient * osg::Vec3f(0,1,0); osg::Vec2f d (dir.x(), dir.y()); return d; } - void World::getDoorMarkers (CellStore* cell, std::vector& out) + struct GetDoorMarkerVisitor { - MWWorld::CellRefList& doors = cell->get(); - CellRefList::List& refList = doors.mList; - for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) + GetDoorMarkerVisitor(std::vector& out) + : mOut(out) { - MWWorld::LiveCellRef& ref = *it; + } - if (!ref.mData.isEnabled()) - continue; + std::vector& mOut; + + bool operator()(const MWWorld::Ptr& ptr) + { + MWWorld::LiveCellRef& ref = *static_cast* >(ptr.getBase()); + + if (!ref.mData.isEnabled() || ref.mData.isDeleted()) + return true; if (ref.mRef.getTeleport()) { @@ -1712,7 +1748,7 @@ else { cellid.mPaged = true; - positionToIndex( + MWBase::Environment::get().getWorld()->positionToIndex( ref.mRef.getDoorDest().pos[0], ref.mRef.getDoorDest().pos[1], cellid.mIndex.mX, @@ -1724,9 +1760,16 @@ newMarker.x = pos.pos[0]; newMarker.y = pos.pos[1]; - out.push_back(newMarker); + mOut.push_back(newMarker); } + return true; } + }; + + void World::getDoorMarkers (CellStore* cell, std::vector& out) + { + GetDoorMarkerVisitor visitor(out); + cell->forEachType(visitor); } void World::setWaterHeight(const float height) @@ -1754,7 +1797,7 @@ item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1); } - MWWorld::Ptr World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) + MWWorld::Ptr World::placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) { const float maxDist = 200.f; @@ -1774,10 +1817,7 @@ pos.rot[1] = 0; // copy the object and set its count - int origCount = object.getRefData().getCount(); - object.getRefData().setCount(amount); - Ptr dropped = copyObjectToCell(object, cell, pos, true); - object.getRefData().setCount(origCount); + Ptr dropped = copyObjectToCell(object, cell, pos, amount, true); // only the player place items in the world, so no need to check actor PCDropped(dropped); @@ -1803,7 +1843,7 @@ } - Ptr World::copyObjectToCell(const Ptr &object, CellStore* cell, ESM::Position pos, bool adjustPos) + Ptr World::copyObjectToCell(const ConstPtr &object, CellStore* cell, ESM::Position pos, int count, bool adjustPos) { if (cell->isExterior()) { @@ -1813,7 +1853,7 @@ } MWWorld::Ptr dropped = - object.getClass().copyToCell(object, *cell, pos); + object.getClass().copyToCell(object, *cell, pos, count); // Reset some position values that could be uninitialized if this item came from a container dropped.getCellRef().setPosition(pos); @@ -1856,7 +1896,7 @@ return dropped; } - MWWorld::Ptr World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) + MWWorld::Ptr World::dropObjectOnGround (const Ptr& actor, const ConstPtr& object, int amount) { MWWorld::CellStore* cell = actor.getCell(); @@ -1870,17 +1910,14 @@ orig.z() += 20; osg::Vec3f dir (0, 0, -1); - float len = 100.0; + float len = 1000000.0; MWRender::RenderingManager::RayResult result = mRendering->castRay(orig, orig+dir*len, true, true); if (result.mHit) pos.pos[2] = result.mHitPointWorld.z(); // copy the object and set its count - int origCount = object.getRefData().getCount(); - object.getRefData().setCount(amount); - Ptr dropped = copyObjectToCell(object, cell, pos); - object.getRefData().setCount(origCount); + Ptr dropped = copyObjectToCell(object, cell, pos, amount, true); if(actor == mPlayer->getPlayer()) // Only call if dropped by player PCDropped(dropped); @@ -1928,23 +1965,23 @@ return false; } - bool World::isSubmerged(const MWWorld::Ptr &object) const + bool World::isSubmerged(const MWWorld::ConstPtr &object) const { return isUnderwater(object, 1.0f/mSwimHeightScale); } - bool World::isSwimming(const MWWorld::Ptr &object) const + bool World::isSwimming(const MWWorld::ConstPtr &object) const { return isUnderwater(object, mSwimHeightScale); } - bool World::isWading(const MWWorld::Ptr &object) const + bool World::isWading(const MWWorld::ConstPtr &object) const { const float kneeDeep = 0.25f; return isUnderwater(object, kneeDeep); } - bool World::isUnderwater(const MWWorld::Ptr &object, const float heightRatio) const + bool World::isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const { osg::Vec3f pos (object.getRefData().getPosition().asVec3()); @@ -2059,11 +2096,10 @@ if (!actor) throw std::runtime_error("can't find player"); - if((!actor->getOnGround()&&actor->getCollisionMode()) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) + if ((actor->getCollisionMode() && !mPhysics->isOnSolidGround(player)) || isUnderwater(currentCell, playerPos)) return 2; - if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || - player.getClass().getNpcStats(player).isWerewolf()) + if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || player.getClass().getNpcStats(player).isWerewolf()) return 1; return 0; @@ -2071,8 +2107,11 @@ MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr) { - if (ptr == getPlayerPtr()) - return mRendering->getPlayerAnimation(); + return mRendering->getAnimation(ptr); + } + + const MWRender::Animation* World::getAnimation(const MWWorld::ConstPtr &ptr) const + { return mRendering->getAnimation(ptr); } @@ -2112,33 +2151,33 @@ mDoorStates.erase(door); } - bool World::getPlayerStandingOn (const MWWorld::Ptr& object) + bool World::getPlayerStandingOn (const MWWorld::ConstPtr& object) { MWWorld::Ptr player = getPlayerPtr(); return mPhysics->isActorStandingOn(player, object); } - bool World::getActorStandingOn (const MWWorld::Ptr& object) + bool World::getActorStandingOn (const MWWorld::ConstPtr& object) { std::vector actors; mPhysics->getActorsStandingOn(object, actors); return !actors.empty(); } - bool World::getPlayerCollidingWith (const MWWorld::Ptr& object) + bool World::getPlayerCollidingWith (const MWWorld::ConstPtr& object) { MWWorld::Ptr player = getPlayerPtr(); return mPhysics->isActorCollidingWith(player, object); } - bool World::getActorCollidingWith (const MWWorld::Ptr& object) + bool World::getActorCollidingWith (const MWWorld::ConstPtr& object) { std::vector actors; mPhysics->getActorsCollidingWith(object, actors); return !actors.empty(); } - void World::hurtStandingActors(const Ptr &object, float healthPerSecond) + void World::hurtStandingActors(const ConstPtr &object, float healthPerSecond) { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; @@ -2155,6 +2194,8 @@ health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration()); stats.setHealth(health); + mPhysics->markAsNonSolid (object); + if (healthPerSecond > 0.0f) { if (actor == getPlayerPtr()) @@ -2166,7 +2207,7 @@ } } - void World::hurtCollidingActors(const Ptr &object, float healthPerSecond) + void World::hurtCollidingActors(const ConstPtr &object, float healthPerSecond) { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; @@ -2183,6 +2224,8 @@ health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration()); stats.setHealth(health); + mPhysics->markAsNonSolid (object); + if (healthPerSecond > 0.0f) { if (actor == getPlayerPtr()) @@ -2218,25 +2261,40 @@ return osg::Vec3f(0,1,0); } - void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) + struct GetContainersOwnedByVisitor + { + GetContainersOwnedByVisitor(const MWWorld::ConstPtr& owner, std::vector& out) + : mOwner(owner) + , mOut(out) + { + } + + MWWorld::ConstPtr mOwner; + std::vector& mOut; + + bool operator()(const MWWorld::Ptr& ptr) + { + if (ptr.getRefData().isDeleted()) + return true; + + if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), mOwner.getCellRef().getRefId())) + mOut.push_back(ptr); + + return true; + } + }; + + void World::getContainersOwnedBy (const MWWorld::ConstPtr& owner, std::vector& out) { const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { - MWWorld::CellRefList& containers = (*cellIt)->get(); - CellRefList::List& refList = containers.mList; - for (CellRefList::List::iterator container = refList.begin(); container != refList.end(); ++container) - { - MWWorld::Ptr ptr (&*container, *cellIt); - if (ptr.getRefData().isDeleted()) - continue; - if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), npc.getCellRef().getRefId())) - out.push_back(ptr); - } + GetContainersOwnedByVisitor visitor (owner, out); + (*cellIt)->forEachType(visitor); } } - struct ListObjectsFunctor + struct ListObjectsVisitor { std::vector mObjects; @@ -2248,25 +2306,25 @@ } }; - void World::getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) + void World::getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out) { const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { - ListObjectsFunctor functor; - (*cellIt)->forEach(functor); + ListObjectsVisitor visitor; + (*cellIt)->forEach(visitor); - for (std::vector::iterator it = functor.mObjects.begin(); it != functor.mObjects.end(); ++it) + for (std::vector::iterator it = visitor.mObjects.begin(); it != visitor.mObjects.end(); ++it) if (Misc::StringUtils::ciEqual(it->getCellRef().getOwner(), npc.getCellRef().getRefId())) out.push_back(*it); } } - bool World::getLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor) + bool World::getLOS(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& targetActor) { if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled()) return false; // cannot get LOS unless both NPC's are enabled - if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode()) + if (!targetActor.getRefData().getBaseNode() || !actor.getRefData().getBaseNode()) return false; // not in active cell return mPhysics->getLineOfSight(actor, targetActor); @@ -2279,7 +2337,7 @@ to = from + (to * maxDist); MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(from, to, MWWorld::Ptr(), - MWPhysics::CollisionType_World|MWPhysics::CollisionType_HeightMap); + MWPhysics::CollisionType_World|MWPhysics::CollisionType_HeightMap|MWPhysics::CollisionType_Door); if (!result.mHit) return maxDist; @@ -2307,7 +2365,8 @@ if (0 == cellStore) { return false; } - const DoorList &doors = cellStore->get().mList; + + const DoorList &doors = cellStore->getReadOnlyDoors().mList; for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) { if (!it->mRef.getTeleport()) { continue; @@ -2329,7 +2388,7 @@ if (0 != source) { // Find door leading to our current teleport door // and use it destination to position inside cell. - const DoorList &doors = source->get().mList; + const DoorList &doors = source->getReadOnlyDoors().mList; for (DoorList::const_iterator jt = doors.begin(); jt != doors.end(); ++jt) { if (it->mRef.getTeleport() && Misc::StringUtils::ciEqual(name, jt->mRef.getDestCell())) @@ -2343,7 +2402,7 @@ } } // Fall back to the first static location. - const StaticList &statics = cellStore->get().mList; + const StaticList &statics = cellStore->getReadOnlyStatics().mList; if ( statics.begin() != statics.end() ) { pos = statics.begin()->mRef.getPosition(); return true; @@ -2394,111 +2453,6 @@ mRendering->rebuildPtr(getPlayerPtr()); } - void World::setWerewolf(const MWWorld::Ptr& actor, bool werewolf) - { - MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor); - - // The actor does not have to change state - if (npcStats.isWerewolf() == werewolf) - return; - - if (actor == getPlayerPtr()) - { - if (werewolf) - { - mPlayer->saveSkillsAttributes(); - mPlayer->setWerewolfSkillsAttributes(); - } - else - mPlayer->restoreSkillsAttributes(); - } - - npcStats.setWerewolf(werewolf); - - // This is a bit dangerous. Equipped items other than WerewolfRobe may reference - // bones that do not even exist with the werewolf object root. - // Therefore, make sure to unequip everything at once, and only fire the change event - // (which will rebuild the animation parts) afterwards. unequipAll will do this for us. - MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); - invStore.unequipAll(actor); - - if(werewolf) - { - InventoryStore &inv = actor.getClass().getInventoryStore(actor); - - inv.equip(InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor); - } - else - { - actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); - } - - // NpcAnimation::updateParts will already rebuild the animation when it detects change of Npc type. - // the following is just for reattaching the camera properly. - mRendering->rebuildPtr(actor); - - if(actor == getPlayerPtr()) - { - // Update the GUI only when called on the player - MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); - - if (werewolf) - { - windowManager->forceHide(MWGui::GW_Inventory); - windowManager->forceHide(MWGui::GW_Magic); - } - else - { - windowManager->unsetForceHide(MWGui::GW_Inventory); - windowManager->unsetForceHide(MWGui::GW_Magic); - } - - windowManager->setWerewolfOverlay(werewolf); - - // Witnesses of the player's transformation will make them a globally known werewolf - std::vector closeActors; - MWBase::Environment::get().getMechanicsManager()->getActorsInRange(actor.getRefData().getPosition().asVec3(), - getStore().get().search("fAlarmRadius")->getFloat(), - closeActors); - - bool detected = false, reported = false; - for (std::vector::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it) - { - if (*it == actor) - continue; - - if (!it->getClass().isNpc()) - continue; - - if (getLOS(*it, actor) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, *it)) - detected = true; - if (it->getClass().getCreatureStats(*it).getAiSetting(MWMechanics::CreatureStats::AI_Alarm).getModified() > 0) - reported = true; - } - - if (detected) - { - windowManager->messageBox("#{sWerewolfAlarmMessage}"); - setGlobalInt("pcknownwerewolf", 1); - - if (reported) - { - npcStats.setBounty(npcStats.getBounty()+ - mStore.get().find("iWereWolfBounty")->getInt()); - windowManager->messageBox("#{sCrimeMessage}"); - } - } - } - } - - void World::applyWerewolfAcrobatics(const Ptr &actor) - { - const Store &gmst = getStore().get(); - MWMechanics::NpcStats &stats = actor.getClass().getNpcStats(actor); - - stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getInt()); - } - bool World::getGodModeState() { return mGodMode; @@ -2567,7 +2521,7 @@ } // If this is a power, check if it was already used in the last 24h - if (!fail && spell->mData.mType == ESM::Spell::ST_Power && !stats.getSpells().canUsePower(spell->mId)) + if (!fail && spell->mData.mType == ESM::Spell::ST_Power && !stats.getSpells().canUsePower(spell)) { message = "#{sPowerAlreadyUsed}"; fail = true; @@ -2595,7 +2549,7 @@ MWWorld::Ptr target; float distance = 192.f; // ?? osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3(); - osg::Vec3f origin = getActorHeadPosition(actor, mRendering); + osg::Vec3f origin = getActorHeadTransform(actor).getTrans(); osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1)); @@ -2650,7 +2604,7 @@ } } - void World::launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + void World::launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) { mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); @@ -2712,7 +2666,7 @@ MWWorld::CellStore *next = getInterior( *i ); if ( !next ) continue; - const MWWorld::CellRefList& doors = next->getReadOnly(); + const MWWorld::CellRefList& doors = next->getReadOnlyDoors(); const CellRefList::List& refList = doors.mList; // Check if any door in the cell leads to an exterior directly @@ -2743,7 +2697,7 @@ return false; } - MWWorld::Ptr World::getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ) + MWWorld::ConstPtr World::getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ) { if ( ptr.getCell()->isExterior() ) { return getClosestMarkerFromExteriorPosition(mPlayer->getLastKnownExteriorPosition(), id); @@ -2755,7 +2709,7 @@ std::set< std::string >checkedCells; std::set< std::string >currentCells; std::set< std::string >nextCells; - MWWorld::Ptr closestMarker; + MWWorld::ConstPtr closestMarker; nextCells.insert( ptr.getCell()->getCell()->mName ); while ( !nextCells.empty() ) { @@ -2766,13 +2720,13 @@ checkedCells.insert( *i ); if ( !next ) continue; - closestMarker = next->search( id ); + closestMarker = next->searchConst( id ); if ( !closestMarker.isEmpty() ) { return closestMarker; } - const MWWorld::CellRefList& doors = next->getReadOnly(); + const MWWorld::CellRefList& doors = next->getReadOnlyDoors(); const CellRefList::List& doorList = doors.mList; // Check if any door in the cell leads to an exterior directly @@ -2796,12 +2750,11 @@ } } } - return MWWorld::Ptr(); } - MWWorld::Ptr World::getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ) { - MWWorld::Ptr closestMarker; + MWWorld::ConstPtr World::getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ) { + MWWorld::ConstPtr closestMarker; float closestDistance = std::numeric_limits::max(); std::vector markers; @@ -2826,7 +2779,7 @@ void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id) { - MWWorld::Ptr closestMarker = getClosestMarker( ptr, id ); + MWWorld::ConstPtr closestMarker = getClosestMarker( ptr, id ); if ( closestMarker.isEmpty() ) { @@ -2853,9 +2806,9 @@ mWeatherManager->update(duration, paused); } - struct AddDetectedReference + struct AddDetectedReferenceVisitor { - AddDetectedReference(std::vector& out, Ptr detector, World::DetectionType type, float squaredDist) + AddDetectedReferenceVisitor(std::vector& out, Ptr detector, World::DetectionType type, float squaredDist) : mOut(out), mDetector(detector), mSquaredDist(squaredDist), mType(type) { } @@ -2864,7 +2817,7 @@ Ptr mDetector; float mSquaredDist; World::DetectionType mType; - bool operator() (MWWorld::Ptr ptr) + bool operator() (const MWWorld::Ptr& ptr) { if ((ptr.getRefData().getPosition().asVec3() - mDetector.getRefData().getPosition().asVec3()).length2() >= mSquaredDist) return true; @@ -2895,7 +2848,7 @@ return true; } - bool needToAdd (MWWorld::Ptr ptr, MWWorld::Ptr detector) + bool needToAdd (const MWWorld::Ptr& ptr, const MWWorld::Ptr& detector) { if (mType == World::Detect_Creature) { @@ -2935,13 +2888,13 @@ dist = feetToGameUnits(dist); - AddDetectedReference functor (out, ptr, type, dist*dist); + AddDetectedReferenceVisitor visitor (out, ptr, type, dist*dist); const Scene::CellStoreCollection& active = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator it = active.begin(); it != active.end(); ++it) { MWWorld::CellStore* cellStore = *it; - cellStore->forEach(functor); + cellStore->forEach(visitor); } } @@ -2986,13 +2939,13 @@ void World::confiscateStolenItems(const Ptr &ptr) { - MWWorld::Ptr prisonMarker = getClosestMarker( ptr, "prisonmarker" ); + MWWorld::ConstPtr prisonMarker = getClosestMarker( ptr, "prisonmarker" ); if ( prisonMarker.isEmpty() ) { std::cerr << "Failed to confiscate items: no closest prison marker found." << std::endl; return; } - std::string prisonName = prisonMarker.mRef->mRef.getDestCell(); + std::string prisonName = prisonMarker.getCellRef().getDestCell(); if ( prisonName.empty() ) { std::cerr << "Failed to confiscate items: prison marker not linked to prison interior" << std::endl; @@ -3139,9 +3092,9 @@ { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(!effect->mAreaSound.empty()) - sndMgr->playManualSound3D(origin, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + sndMgr->playSound3D(origin, effect->mAreaSound, 1.0f, 1.0f); else - sndMgr->playManualSound3D(origin, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + sndMgr->playSound3D(origin, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f); } // Get the actors in range of the effect std::vector objects; @@ -3197,7 +3150,7 @@ interpreterContext.executeActivation(object, actor); } - struct ResetActorsFunctor + struct ResetActorsVisitor { bool operator() (Ptr ptr) { @@ -3219,23 +3172,30 @@ iter!=mWorldScene->getActiveCells().end(); ++iter) { CellStore* cellstore = *iter; - ResetActorsFunctor functor; - cellstore->forEach(functor); + ResetActorsVisitor visitor; + cellstore->forEach(visitor); } } - bool World::isWalkingOnWater(const Ptr &actor) + bool World::isWalkingOnWater(const ConstPtr &actor) { - MWPhysics::Actor* physicActor = mPhysics->getActor(actor); + const MWPhysics::Actor* physicActor = mPhysics->getActor(actor); if (physicActor && physicActor->isWalkingOnWater()) return true; return false; } - osg::Vec3f World::aimToTarget(const Ptr &actor, const MWWorld::Ptr& target) + osg::Vec3f World::aimToTarget(const ConstPtr &actor, const MWWorld::ConstPtr& target) { - osg::Vec3f weaponPos = getActorHeadPosition(actor, mRendering); + osg::Vec3f weaponPos = getActorHeadTransform(actor).getTrans(); osg::Vec3f targetPos = mPhysics->getPosition(target); return (targetPos - weaponPos); } + + float World::getHitDistance(const ConstPtr &actor, const ConstPtr &target) + { + osg::Vec3f weaponPos = getActorHeadTransform(actor).getTrans(); + return mPhysics->getHitDistance(weaponPos, target); + } + } diff -Nru openmw-0.37.0/apps/openmw/mwworld/worldimp.hpp openmw-0.38.0/apps/openmw/mwworld/worldimp.hpp --- openmw-0.37.0/apps/openmw/mwworld/worldimp.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw/mwworld/worldimp.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -85,6 +85,13 @@ MWPhysics::PhysicsSystem *mPhysics; bool mSky; + ESM::Variant* mGameHour; + ESM::Variant* mDaysPassed; + ESM::Variant* mDay; + ESM::Variant* mMonth; + ESM::Variant* mYear; + ESM::Variant* mTimeScale; + Cells mCells; std::string mCurrentWorldSpace; @@ -116,14 +123,15 @@ Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return an updated Ptr in case the Ptr's cell changes - Ptr copyObjectToCell(const Ptr &ptr, CellStore* cell, ESM::Position pos, bool adjustPos=true); + Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); void updateSoundListener(); void updateWindowManager (); void updatePlayer(bool paused); MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true); - + public: // FIXME void removeContainerScripts(const Ptr& reference); + private: void addContainerScripts(const Ptr& reference, CellStore* cell); void PCDropped (const Ptr& item); @@ -135,6 +143,8 @@ void ensureNeededRecords(); + void fillGlobalVariables(); + /** * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) * @param fileCollections- Container which holds content file names and their paths @@ -145,7 +155,7 @@ const std::vector& content, ContentLoader& contentLoader); float mSwimHeightScale; - bool isUnderwater(const MWWorld::Ptr &object, const float heightRatio) const; + bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const; ///< helper function for implementing isSwimming(), isSubmerged(), isWading() bool mTeleportEnabled; @@ -155,8 +165,8 @@ float feetToGameUnits(float feet); - MWWorld::Ptr getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ); - MWWorld::Ptr getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ); + MWWorld::ConstPtr getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ); + MWWorld::ConstPtr getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ); public: @@ -259,7 +269,7 @@ virtual Ptr searchPtrViaActorId (int actorId); ///< Search is limited to the active cells. - virtual MWWorld::Ptr findContainer (const MWWorld::Ptr& ptr); + virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr); ///< Return a pointer to a liveCellRef which contains \a ptr. /// \note Search is limited to the active cells. @@ -334,7 +344,7 @@ /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to /// use the "Head" node as a basis. - virtual std::pair getHitContact(const MWWorld::Ptr &ptr, float distance); + virtual std::pair getHitContact(const MWWorld::ConstPtr &ptr, float distance); /// @note No-op for items in containers. Use ContainerStore::removeItem instead. virtual void deleteObject (const Ptr& ptr); @@ -355,7 +365,7 @@ /// \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 MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos); + virtual MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& 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. virtual float getMaxActivationDistance(); @@ -433,14 +443,14 @@ virtual void update (float duration, bool paused); - virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); + virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount); ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place - virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount); + virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount); ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object @@ -454,12 +464,14 @@ virtual bool isFlying(const MWWorld::Ptr &ptr) const; virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const; ///Is the head of the creature underwater? - virtual bool isSubmerged(const MWWorld::Ptr &object) const; - virtual bool isSwimming(const MWWorld::Ptr &object) const; + virtual bool isSubmerged(const MWWorld::ConstPtr &object) const; + virtual bool isSwimming(const MWWorld::ConstPtr &object) const; virtual bool isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const; - virtual bool isWading(const MWWorld::Ptr &object) const; + virtual bool isWading(const MWWorld::ConstPtr &object) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; + virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const; + virtual void togglePOV(); virtual bool isFirstPerson() const; @@ -488,25 +500,25 @@ /// @note throws an exception when invoked on a teleport door virtual void activateDoor(const MWWorld::Ptr& door, int state); - 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 bool getPlayerCollidingWith(const MWWorld::Ptr& object); ///< @return true if the player is colliding with \a object - virtual bool getActorCollidingWith (const MWWorld::Ptr& object); ///< @return true if any actor is colliding with \a object - virtual void hurtStandingActors (const MWWorld::Ptr& object, float dmgPerSecond); + virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object); ///< @return true if the player is standing on \a object + virtual bool getActorStandingOn (const MWWorld::ConstPtr& object); ///< @return true if any actor is standing on \a object + virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object); ///< @return true if the player is colliding with \a object + virtual bool getActorCollidingWith (const MWWorld::ConstPtr& object); ///< @return true if any actor is colliding with \a object + virtual void hurtStandingActors (const MWWorld::ConstPtr& object, float dmgPerSecond); ///< Apply a health difference to any actors standing on \a object. /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. - virtual void hurtCollidingActors (const MWWorld::Ptr& object, float dmgPerSecond); + virtual void hurtCollidingActors (const MWWorld::ConstPtr& object, float dmgPerSecond); ///< Apply a health difference to any actors colliding with \a object. /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. virtual float getWindSpeed(); - virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out); + virtual void getContainersOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out); ///< get all containers in active cells owned by this Npc - virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out); + virtual void getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out); ///< get all items in active cells owned by this Npc - virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor); + virtual bool getLOS(const MWWorld::ConstPtr& actor,const MWWorld::ConstPtr& targetActor); ///< get Line of Sight (morrowind stupid implementation) virtual float getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist); @@ -522,6 +534,7 @@ /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr); + virtual const MWRender::Animation* getAnimation(const MWWorld::ConstPtr &ptr) const; virtual void reattachPlayerCamera(); /// \todo this does not belong here @@ -547,10 +560,6 @@ /// Returns true if levitation spell effect is allowed. virtual bool isLevitationEnabled() const; - virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf); - - virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor); - virtual bool getGodModeState(); virtual bool toggleGodMode(); @@ -574,7 +583,7 @@ virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection); - virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength); @@ -628,11 +637,14 @@ /// Resets all actors in the current active cells to their original location within that cell. virtual void resetActors(); - virtual bool isWalkingOnWater (const MWWorld::Ptr& actor); + virtual bool isWalkingOnWater (const MWWorld::ConstPtr& actor); /// Return a vector aiming the actor's weapon towards a target. /// @note The length of the vector is the distance between actor and target. - virtual osg::Vec3f aimToTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); + virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target); + + /// Return the distance between actor's weapon and target's collision box. + virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target); }; } diff -Nru openmw-0.37.0/apps/openmw_test_suite/CMakeLists.txt openmw-0.38.0/apps/openmw_test_suite/CMakeLists.txt --- openmw-0.37.0/apps/openmw_test_suite/CMakeLists.txt 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw_test_suite/CMakeLists.txt 2016-01-12 16:11:28.000000000 +0000 @@ -4,8 +4,11 @@ include_directories(${GTEST_INCLUDE_DIRS}) file(GLOB UNITTEST_SRC_FILES - components/misc/test_*.cpp - mwdialogue/test_*.cpp + ../openmw/mwworld/store.cpp + ../openmw/mwworld/esmstore.cpp + mwworld/test_store.cpp + + mwdialogue/test_keywordsearch.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff -Nru openmw-0.37.0/apps/openmw_test_suite/components/misc/test_stringops.cpp openmw-0.38.0/apps/openmw_test_suite/components/misc/test_stringops.cpp --- openmw-0.37.0/apps/openmw_test_suite/components/misc/test_stringops.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/openmw_test_suite/components/misc/test_stringops.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -#include -#include "components/misc/stringops.hpp" - -struct StringOpsTest : public ::testing::Test -{ - protected: - virtual void SetUp() - { - } - - virtual void TearDown() - { - } -}; diff -Nru openmw-0.37.0/apps/openmw_test_suite/mwworld/test_store.cpp openmw-0.38.0/apps/openmw_test_suite/mwworld/test_store.cpp --- openmw-0.37.0/apps/openmw_test_suite/mwworld/test_store.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/apps/openmw_test_suite/mwworld/test_store.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,315 @@ +#include + +#include + +#include +#include +#include +#include + +#include "apps/openmw/mwworld/esmstore.hpp" + +static Loading::Listener dummyListener; + +/// Base class for tests of ESMStore that rely on external content files to produce the test results +struct ContentFileTest : public ::testing::Test +{ + protected: + + virtual void SetUp() + { + readContentFiles(); + + // load the content files + std::vector readerList; + readerList.resize(mContentFiles.size()); + + int index=0; + for (std::vector::const_iterator it = mContentFiles.begin(); it != mContentFiles.end(); ++it) + { + ESM::ESMReader lEsm; + lEsm.setEncoder(NULL); + lEsm.setIndex(index); + lEsm.setGlobalReaderList(&readerList); + lEsm.open(it->string()); + readerList[index] = lEsm; + mEsmStore.load(readerList[index], &dummyListener); + + ++index; + } + + mEsmStore.setUp(); + } + + virtual void TearDown() + { + } + + // read absolute path to content files from openmw.cfg + void readContentFiles() + { + boost::program_options::variables_map variables; + + boost::program_options::options_description desc("Allowed options"); + desc.add_options() + ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()->composing()) + ("content", boost::program_options::value >()->default_value(std::vector(), "") + ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") + ("data-local", boost::program_options::value()->default_value("")); + + boost::program_options::notify(variables); + + mConfigurationManager.readConfiguration(variables, desc, true); + + Files::PathContainer dataDirs, dataLocal; + if (!variables["data"].empty()) { + dataDirs = Files::PathContainer(variables["data"].as()); + } + + std::string local = variables["data-local"].as(); + if (!local.empty()) { + dataLocal.push_back(Files::PathContainer::value_type(local)); + } + + mConfigurationManager.processPaths (dataDirs); + mConfigurationManager.processPaths (dataLocal, true); + + if (!dataLocal.empty()) + dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); + + Files::Collections collections (dataDirs, true); + + std::vector contentFiles = variables["content"].as >(); + for (std::vector::iterator it = contentFiles.begin(); it != contentFiles.end(); ++it) + mContentFiles.push_back(collections.getPath(*it)); + } + +protected: + Files::ConfigurationManager mConfigurationManager; + MWWorld::ESMStore mEsmStore; + std::vector mContentFiles; +}; + +/// Print results of the dialogue merging process, i.e. the resulting linked list. +TEST_F(ContentFileTest, dialogue_merging_test) +{ + if (mContentFiles.empty()) + { + std::cout << "No content files found, skipping test" << std::endl; + return; + } + + const std::string file = "test_dialogue_merging.txt"; + + boost::filesystem::ofstream stream; + stream.open(file); + + const MWWorld::Store& dialStore = mEsmStore.get(); + for (MWWorld::Store::iterator it = dialStore.begin(); it != dialStore.end(); ++it) + { + const ESM::Dialogue& dial = *it; + stream << "Dialogue: " << dial.mId << std::endl; + + for (ESM::Dialogue::InfoContainer::const_iterator infoIt = dial.mInfo.begin(); infoIt != dial.mInfo.end(); ++infoIt) + { + const ESM::DialInfo& info = *infoIt; + stream << info.mId << std::endl; + } + stream << std::endl; + } + + std::cout << "dialogue_merging_test successful, results printed to " << file << std::endl; +} + +// Note: here we don't test records that don't use string names (e.g. Land, Pathgrid, Cell) +#define RUN_TEST_FOR_TYPES(func, arg1, arg2) \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); \ + func(arg1, arg2); + +template +void printRecords(MWWorld::ESMStore& esmStore, std::ostream& outStream) +{ + const MWWorld::Store& store = esmStore.get(); + outStream << store.getSize() << " " << T::getRecordType() << " records" << std::endl; + + for (typename MWWorld::Store::iterator it = store.begin(); it != store.end(); ++it) + { + const T& record = *it; + outStream << record.mId << std::endl; + } + + outStream << std::endl; +} + +/// Print some basic diagnostics about the loaded content files, e.g. number of records and names of those records +/// Also used to test the iteration order of records +TEST_F(ContentFileTest, content_diagnostics_test) +{ + if (mContentFiles.empty()) + { + std::cout << "No content files found, skipping test" << std::endl; + return; + } + + const std::string file = "test_content_diagnostics.txt"; + + boost::filesystem::ofstream stream; + stream.open(file); + + RUN_TEST_FOR_TYPES(printRecords, mEsmStore, stream); + + std::cout << "diagnostics_test successful, results printed to " << file << std::endl; +} + +// TODO: +/// Print results of autocalculated NPC spell lists. Also serves as test for attribute/skill autocalculation which the spell autocalculation heavily relies on +/// - even incorrect rounding modes can completely change the resulting spell lists. +/* +TEST_F(ContentFileTest, autocalc_test) +{ + if (mContentFiles.empty()) + { + std::cout << "No content files found, skipping test" << std::endl; + return; + } + + +} +*/ + +/// Base class for tests of ESMStore that do not rely on external content files +struct StoreTest : public ::testing::Test +{ +protected: + MWWorld::ESMStore mEsmStore; +}; + + +/// Create an ESM file in-memory containing the specified record. +/// @param deleted Write record with deleted flag? +template +Files::IStreamPtr getEsmFile(T record, bool deleted) +{ + ESM::ESMWriter writer; + std::stringstream* stream = new std::stringstream; + writer.setFormat(0); + writer.save(*stream); + writer.startRecord(T::sRecordId); + record.save(writer, deleted); + writer.endRecord(T::sRecordId); + + return Files::IStreamPtr(stream); +} + +/// Tests deletion of records. +TEST_F(StoreTest, delete_test) +{ + const std::string recordId = "foobar"; + + typedef ESM::Apparatus RecordType; + + RecordType record; + record.blank(); + record.mId = recordId; + + ESM::ESMReader reader; + std::vector readerList; + readerList.push_back(reader); + reader.setGlobalReaderList(&readerList); + + // master file inserts a record + Files::IStreamPtr file = getEsmFile(record, false); + reader.open(file, "filename"); + mEsmStore.load(reader, &dummyListener); + mEsmStore.setUp(); + + ASSERT_TRUE (mEsmStore.get().getSize() == 1); + + // now a plugin deletes it + file = getEsmFile(record, true); + reader.open(file, "filename"); + mEsmStore.load(reader, &dummyListener); + mEsmStore.setUp(); + + ASSERT_TRUE (mEsmStore.get().getSize() == 0); + + // now another plugin inserts it again + // expected behaviour is the record to reappear rather than staying deleted + file = getEsmFile(record, false); + reader.open(file, "filename"); + mEsmStore.load(reader, &dummyListener); + mEsmStore.setUp(); + + ASSERT_TRUE (mEsmStore.get().getSize() == 1); +} + +/// Tests overwriting of records. +TEST_F(StoreTest, overwrite_test) +{ + const std::string recordId = "foobar"; + const std::string recordIdUpper = "Foobar"; + + typedef ESM::Apparatus RecordType; + + RecordType record; + record.blank(); + record.mId = recordId; + + ESM::ESMReader reader; + std::vector readerList; + readerList.push_back(reader); + reader.setGlobalReaderList(&readerList); + + // master file inserts a record + Files::IStreamPtr file = getEsmFile(record, false); + reader.open(file, "filename"); + mEsmStore.load(reader, &dummyListener); + mEsmStore.setUp(); + + // now a plugin overwrites it with changed data + record.mId = recordIdUpper; // change id to uppercase, to test case smashing while we're at it + record.mModel = "the_new_model"; + file = getEsmFile(record, false); + reader.open(file, "filename"); + mEsmStore.load(reader, &dummyListener); + mEsmStore.setUp(); + + // verify that changes were actually applied + const RecordType* overwrittenRec = mEsmStore.get().search(recordId); + + ASSERT_TRUE (overwrittenRec != NULL); + + ASSERT_TRUE (overwrittenRec && overwrittenRec->mModel == "the_new_model"); +} diff -Nru openmw-0.37.0/apps/wizard/installationpage.cpp openmw-0.38.0/apps/wizard/installationpage.cpp --- openmw-0.37.0/apps/wizard/installationpage.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/wizard/installationpage.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -29,12 +29,6 @@ mThread, SLOT(quit())); connect(mUnshield, SIGNAL(finished()), - mUnshield, SLOT(deleteLater())); - - connect(mUnshield, SIGNAL(finished()), - mThread, SLOT(deleteLater()));; - - connect(mUnshield, SIGNAL(finished()), this, SLOT(installationFinished()), Qt::QueuedConnection); connect(mUnshield, SIGNAL(error(QString, QString)), @@ -60,6 +54,7 @@ { if (mThread->isRunning()) { mUnshield->stopWorker(); + mThread->quit(); mThread->wait(); } diff -Nru openmw-0.37.0/apps/wizard/mainwizard.cpp openmw-0.38.0/apps/wizard/mainwizard.cpp --- openmw-0.37.0/apps/wizard/mainwizard.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/wizard/mainwizard.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -138,7 +138,7 @@ QString path(userPath + QLatin1String("openmw.cfg")); QFile file(path); - qDebug() << "Loading config file:" << qPrintable(path); + qDebug() << "Loading config file:" << path.toUtf8().constData(); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -163,7 +163,7 @@ paths.append(globalPath + QLatin1String("openmw.cfg")); foreach (const QString &path, paths) { - qDebug() << "Loading config file:" << qPrintable(path); + qDebug() << "Loading config file:" << path.toUtf8().constData(); QFile file(path); if (file.exists()) { @@ -197,7 +197,7 @@ QFile file(path); - qDebug() << "Loading config file:" << qPrintable(path); + qDebug() << "Loading config file:" << path.toUtf8().constData(); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { diff -Nru openmw-0.37.0/apps/wizard/unshield/unshieldworker.cpp openmw-0.38.0/apps/wizard/unshield/unshieldworker.cpp --- openmw-0.37.0/apps/wizard/unshield/unshieldworker.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/wizard/unshield/unshieldworker.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -45,9 +45,7 @@ void Wizard::UnshieldWorker::stopWorker() { - mMutex.lock(); mStopped = true; - mMutex.unlock(); } void Wizard::UnshieldWorker::setInstallComponent(Wizard::Component component, bool install) diff -Nru openmw-0.37.0/apps/wizard/unshield/unshieldworker.hpp openmw-0.38.0/apps/wizard/unshield/unshieldworker.hpp --- openmw-0.37.0/apps/wizard/unshield/unshieldworker.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/apps/wizard/unshield/unshieldworker.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -104,7 +104,6 @@ QTextCodec *mIniCodec; QWaitCondition mWait; - QMutex mMutex; QReadWriteLock mLock; diff -Nru openmw-0.37.0/AUTHORS.md openmw-0.38.0/AUTHORS.md --- openmw-0.37.0/AUTHORS.md 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/AUTHORS.md 2016-01-12 16:11:28.000000000 +0000 @@ -13,6 +13,7 @@ Marc Zinnschlag (Zini) - Lead Programmer/Project Manager Adam Hogan (aurix) + Aesylwinn Aleksandar Jovanov Alex Haddad (rainChu) Alex McKibben (WeirdSexy) @@ -55,6 +56,7 @@ Jeffrey Haines (Jyby) Jengerer Jiří KuneÅ¡ (kunesj) + Joe Wilkerson (neuralroberts) Joel Graff (graffy) John Blomberg (fstp) Jordan Ayers @@ -74,6 +76,7 @@ Marco Melletti (mellotanica) Marco Schulze Mateusz KoÅ‚aczek (PL_kolek) + Mateusz Malisz (malice) megaton Michael Hogan (Xethik) Michael Mc Donnell @@ -88,6 +91,7 @@ Nikolay Kasyanov (corristo) nobrakal Nolan Poe (nopoe) + Paul Cercueil (pcercuei) Paul McElroy (Greendogo) Pieter van der Kloet (pvdk) pkubik diff -Nru openmw-0.37.0/CHANGELOG.md openmw-0.38.0/CHANGELOG.md --- openmw-0.37.0/CHANGELOG.md 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/CHANGELOG.md 2016-01-12 16:11:28.000000000 +0000 @@ -1,3 +1,77 @@ +0.38.0 +------ + + Bug #1699: Guard will continuously run into mudcrab + Bug #1934: Saw in Dome of Kasia doesnt harm the player + Bug #1962: Rat floats when killed near the door + Bug #1963: Kwama eggsacks pulse too fast + Bug #2198: NPC voice sound source should be placed at their head + Bug #2210: OpenMW installation wizard crashes... + Bug #2211: Editor: handle DELE subrecord at the end of a record + Bug #2413: ESM error Unknown subrecord in Grandmaster of Hlaalu + Bug #2537: Bloodmoon quest Ristaag: Sattir not consistently dying, plot fails to advance; same with Grerid + Bug #2697: "The Swimmer" moves away after leading you to underwater cave + Bug #2724: Loading previous save duplicates containers and harvestables + Bug #2769: Inventory doll - Cursor not respecting order of clothes + Bug #2865: Scripts silently fail when moving NPCs between cells. + Bug #2873: Starting a new game leads to CTD / Fatal Error + Bug #2918: Editor: it's not possible to create an omwaddon containing a dot in the file name + Bug #2933: Dialog box can't disable a npc if it is in another cell. (Rescue Madura Seran). + Bug #2942: atronach sign behavior (spell absorption) changes when trying to receive a blessing at "shrine of tribunal" + Bug #2952: Enchantment Merchant Items reshuffled EVERY time 'barter' is clicked + Bug #2961: ESM Error: Unknown subrecord if Deus Ex Machina mod is loaded + Bug #2972: Resurrecting the player via console does not work when health was 0 + Bug #2986: Projectile weapons work underwater + Bug #2988: "Expected subrecord" bugs showing up. + Bug #2991: Can't use keywords in strings for MessageBox + Bug #2993: Tribunal:The Shrine of the Dead – Urvel Dulni can't stop to follow the player. + Bug #3008: NIFFile Error while loading meshes with a NiLODNode + Bug #3010: Engine: items should sink to the ground when dropped under water + Bug #3011: NIFFile Error while loading meshes with a NiPointLight + Bug #3016: Engine: something wrong with scripting - crash / fatal error + Bug #3020: Editor: verify does not check if given "item ID" (as content) for a "container" exists + Bug #3026: [MOD: Julan Ashlander Companion] Dialogue not triggering correctly + Bug #3028: Tooltips for Health, Magicka and Fatigue show in Options menu even when bars aren't visible + Bug #3034: Item count check dialogue option doesn't work (Guards accept gold even if you don't have enough) + Bug #3036: Owned tooltip color affects spell tooltips incorrrectly + Bug #3037: Fatal error loading old ES_Landscape.esp in Store::search + Bug #3038: Player sounds come from underneath + Bug #3040: Execution of script failed: There is a message box already + Bug #3047: [MOD: Julan Ashlander Companion] Scripts KS_Bedscript or KS_JulanNight not working as intended + Bug #3048: Fatal Error + Bug #3051: High field of view results in first person rendering glitches + Bug #3053: Crash on new game at character class selection + Bug #3058: Physiched sleeves aren't rendered correctly. + Bug #3060: NPCs use wrong landing sound + Bug #3062: Mod support regression: Andromeda's fast travel. + Bug #3063: Missing Journal Textures without Tribunal and Bloodmoon installed + Bug #3077: repeated aifollow causes the distance to stack + Bug #3078: Creature Dialogues not showing when certain Function/Conditions are required. + Bug #3082: Crash when entering Holamayan Monastery with mesh replacer installed + Bug #3086: Party at Boro's House – Creature with Class don't talk under OpenMW + Bug #3089: Dreamers spawn too soon + Bug #3100: Certain controls erroneously work as a werewolf + Bug #3102: Multiple unique soultrap spell sources clone souls. + Bug #3105: Summoned creatures and objects disappear at midnight + Bug #3112: gamecontrollerdb file creation with wrong extension + Bug #3116: Dialogue Function "Same Race" is avoided + Bug #3117: Dialogue Bug: Choice conditions are tested when not in a choice + Bug #3118: Body Parts are not rendered when used in a pose. + Bug #3122: NPC direction is reversed during sneak awareness check + Feature #776: Sound effects from one direction don't necessarily affect both speakers in stereo + Feature #858: Different fov settings for hands and the game world + Feature #1176: Handle movement of objects between cells + Feature #2507: Editor: choosing colors for syntax highlighting + Feature #2867: Editor: hide script error list when there are no errors + Feature #2885: Accept a file format other than nif + Feature #2982: player->SetDelete 1 results in: PC can't move, menu can be opened + Feature #2996: Editor: make it possible to preset the height of the script check area in a script view + Feature #3014: Editor: Tooltips in 3D scene + Feature #3064: Werewolf field of view + Feature #3074: Quicksave indicator + Task #287: const version of Ptr + Task #2542: Editor: redo user settings system + 0.37.0 ------ diff -Nru openmw-0.37.0/cmake/FindOpenGLES.cmake openmw-0.38.0/cmake/FindOpenGLES.cmake --- openmw-0.37.0/cmake/FindOpenGLES.cmake 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/cmake/FindOpenGLES.cmake 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,94 @@ +#------------------------------------------------------------------- +# This file is part of the CMake build system for OGRE +# (Object-oriented Graphics Rendering Engine) +# For the latest info, see http://www.ogre3d.org/ +# +# The contents of this file are placed in the public domain. Feel +# free to make use of it in any way you like. +#------------------------------------------------------------------- + +# - Try to find OpenGLES +# Once done this will define +# +# OPENGLES_FOUND - system has OpenGLES +# OPENGLES_INCLUDE_DIR - the GL include directory +# OPENGLES_LIBRARIES - Link these to use OpenGLES + +IF (WIN32) + IF (CYGWIN) + + FIND_PATH(OPENGLES_INCLUDE_DIR GLES/gl.h ) + + FIND_LIBRARY(OPENGLES_gl_LIBRARY libgles_cm ) + + ELSE (CYGWIN) + + IF(BORLAND) + SET (OPENGLES_gl_LIBRARY import32 CACHE STRING "OpenGL ES 1.x library for win32") + ELSE(BORLAND) + #MS compiler - todo - fix the following line: + SET (OPENGLES_gl_LIBRARY ${OGRE_SOURCE_DIR}/Dependencies/lib/release/libgles_cm.lib CACHE STRING "OpenGL ES 1.x library for win32") + ENDIF(BORLAND) + + ENDIF (CYGWIN) + +ELSE (WIN32) + + IF (APPLE) + + #create_search_paths(/Developer/Platforms) + #findpkg_framework(OpenGLES) + #set(OPENGLES_gl_LIBRARY "-framework OpenGLES") + + ELSE(APPLE) + + FIND_PATH(OPENGLES_INCLUDE_DIR GLES/gl.h + /opt/vc/include + /opt/graphics/OpenGL/include + /usr/openwin/share/include + /usr/X11R6/include + /usr/include + ) + + FIND_LIBRARY(OPENGLES_gl_LIBRARY + NAMES GLES_CM GLESv1_CM + PATHS /opt/vc/lib + /opt/graphics/OpenGL/lib + /usr/openwin/lib + /usr/shlib /usr/X11R6/lib + /usr/lib + ) + + # On Unix OpenGL most certainly always requires X11. + # Feel free to tighten up these conditions if you don't + # think this is always true. + + #IF (OPENGLES_gl_LIBRARY) + # IF(NOT X11_FOUND) + # INCLUDE(FindX11) + # ENDIF(NOT X11_FOUND) + # IF (X11_FOUND) + # SET (OPENGLES_LIBRARIES ${X11_LIBRARIES}) + # ENDIF (X11_FOUND) + #ENDIF (OPENGLES_gl_LIBRARY) + + ENDIF(APPLE) +ENDIF (WIN32) + +SET( OPENGLES_FOUND "NO" ) +IF(OPENGLES_gl_LIBRARY) + + SET( OPENGLES_LIBRARIES ${OPENGLES_gl_LIBRARY} ${OPENGLES_LIBRARIES}) + + SET( OPENGLES_FOUND "YES" ) + +ENDIF(OPENGLES_gl_LIBRARY) + +MARK_AS_ADVANCED( + OPENGLES_INCLUDE_DIR + OPENGLES_gl_LIBRARY +) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(OPENGLES REQUIRED_VARS OPENGLES_LIBRARIES OPENGLES_INCLUDE_DIR) diff -Nru openmw-0.37.0/CMakeLists.txt openmw-0.38.0/CMakeLists.txt --- openmw-0.37.0/CMakeLists.txt 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/CMakeLists.txt 2016-01-12 16:11:28.000000000 +0000 @@ -3,8 +3,9 @@ # If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them. IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING - "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." + "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS None Debug Release RelWithDebInfo MinSizeRel) ENDIF() if (APPLE) @@ -19,7 +20,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 37) +set(OPENMW_VERSION_MINOR 38) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") @@ -45,10 +46,6 @@ # Macros include(OpenMWMacros) -if (ANDROID) - set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}") -endif (ANDROID) - # doxygen main page configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_BINARY_DIR}/docs/mainpage.hpp") @@ -83,13 +80,28 @@ option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF) endif() -# Location of morrowind data files +# Set up common paths if (APPLE) set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") set(OPENMW_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") elseif(UNIX) - set(MORROWIND_DATA_FILES "${CMAKE_INSTALL_PREFIX}/share/games/openmw/data/" CACHE PATH "location of Morrowind data files") - set(OPENMW_RESOURCE_FILES "${CMAKE_INSTALL_PREFIX}/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files") + # Paths + SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") + SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Where to install libraries") + SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") + SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix") + SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") + SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") + SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.") + IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr") + SET(GLOBAL_CONFIG_PATH "/etc/" CACHE PATH "Set config dir prefix") + ELSE() + SET(GLOBAL_CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/etc/" CACHE PATH "Set config dir prefix") + ENDIF() + SET(SYSCONFDIR "${GLOBAL_CONFIG_PATH}/openmw" CACHE PATH "Set config dir") + + set(MORROWIND_DATA_FILES "${DATADIR}/data" CACHE PATH "location of Morrowind data files") + set(OPENMW_RESOURCE_FILES "${DATADIR}/resources" CACHE PATH "location of OpenMW resources files") else() set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files") set(OPENMW_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files") @@ -115,8 +127,6 @@ # Required for building the FFmpeg headers add_definitions(-D__STDC_CONSTANT_MACROS) -set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES}) - # TinyXML option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) if(USE_SYSTEM_TINYXML) @@ -146,20 +156,39 @@ add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) endif() -# Dependencies +if (ANDROID) + set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}") + set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE) +endif (ANDROID) + +option(OPENGL_ES "enable opengl es support" FALSE ) -set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)") -message(STATUS "Using Qt${DESIRED_QT_VERSION}") +if (OPENGL_ES) + add_definitions(-DOPENGL_ES) +endif(OPENGL_ES) -if (DESIRED_QT_VERSION MATCHES 4) - find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL) +if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD) + set(USE_QT FALSE) else() - find_package(Qt5Widgets REQUIRED) - find_package(Qt5Core REQUIRED) - find_package(Qt5Network REQUIRED) - find_package(Qt5OpenGL REQUIRED) + set(USE_QT TRUE) +endif() + +# Dependencies +if (USE_QT) + set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)") + set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5) + message(STATUS "Using Qt${DESIRED_QT_VERSION}") + + if (DESIRED_QT_VERSION MATCHES 4) + find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL) + else() + find_package(Qt5Widgets REQUIRED) + find_package(Qt5Core REQUIRED) + find_package(Qt5Network REQUIRED) + find_package(Qt5OpenGL REQUIRED) # Instruct CMake to run moc automatically when needed. #set(CMAKE_AUTOMOC ON) + endif() endif() # Fix for not visible pthreads functions for linker with glibc 2.15 @@ -191,7 +220,12 @@ set(Boost_USE_STATIC_LIBS ON) endif() -find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgQt osgUtil osgFX) +if (USE_QT) + set (OSG_QT osgQt) +endif() + +find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle ${OSG_QT} osgUtil osgFX) + include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) if(OSG_STATIC) @@ -315,8 +349,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg "${OpenMW_BINARY_DIR}/openmw.cfg.install") -configure_file(${OpenMW_SOURCE_DIR}/files/opencs.ini - "${OpenMW_BINARY_DIR}/opencs.ini") +configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg + "${OpenMW_BINARY_DIR}/openmw-cs.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) @@ -363,21 +397,7 @@ endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) IF(NOT WIN32 AND NOT APPLE) - # Linux building - # Paths - SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") - SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Where to install libraries") - SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") - SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix") - SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") - SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") - SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.") - IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr") - SET(GLOBAL_CONFIG_PATH "/etc/" CACHE PATH "Set config dir prefix") - ELSE() - SET(GLOBAL_CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/etc/" CACHE PATH "Set config dir prefix") - ENDIF() - SET(SYSCONFDIR "${GLOBAL_CONFIG_PATH}/openmw" CACHE PATH "Set config dir") + # Linux installation # Install binaries IF(BUILD_OPENMW) @@ -430,7 +450,7 @@ INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") IF(BUILD_OPENCS) - INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs") + INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs") ENDIF(BUILD_OPENCS) # Install resources @@ -463,7 +483,7 @@ ENDIF(BUILD_ESSIMPORTER) IF(BUILD_OPENCS) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".") - INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".") + INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".") ENDIF(BUILD_OPENCS) IF(BUILD_WIZARD) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".") @@ -728,7 +748,7 @@ install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) - install(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + install(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) @@ -748,9 +768,10 @@ set(ABSOLUTE_PLUGINS "") set(USED_OSG_PLUGINS - osgdb_tga osgdb_dds - osgdb_imageio + osgdb_jpeg + osgdb_png + osgdb_tga ) foreach (PLUGIN_NAME ${USED_OSG_PLUGINS}) diff -Nru openmw-0.37.0/components/bsa/bsa_file.cpp openmw-0.38.0/components/bsa/bsa_file.cpp --- openmw-0.37.0/components/bsa/bsa_file.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/bsa/bsa_file.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -28,8 +28,6 @@ #include #include -#include "../files/constrainedfilestream.hpp" - using namespace std; using namespace Bsa; diff -Nru openmw-0.37.0/components/CMakeLists.txt openmw-0.38.0/components/CMakeLists.txt --- openmw-0.37.0/components/CMakeLists.txt 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/CMakeLists.txt 2016-01-12 16:11:28.000000000 +0000 @@ -20,7 +20,11 @@ configure_file(${VERSION_IN_FILE} ${VERSION_FILE}) endif (GIT_CHECKOUT) -find_package(OpenGL REQUIRED) +if (OPENGL_ES) + find_package(OpenGLES REQUIRED) +else() + find_package(OpenGL REQUIRED) +endif() # source files @@ -37,11 +41,11 @@ ) add_component_dir (resource - scenemanager texturemanager resourcesystem + scenemanager keyframemanager texturemanager resourcesystem bulletshapemanager bulletshape niffilemanager objectcache ) add_component_dir (sceneutil - clone attach lightmanager visitor util statesetupdater controller skeleton riggeometry lightcontroller + clone attach lightmanager visitor util statesetupdater controller skeleton riggeometry lightcontroller positionattitudetransform # not used yet #workqueue ) @@ -55,7 +59,7 @@ ) add_component_dir (nifbullet - bulletnifloader bulletshapemanager + bulletnifloader ) add_component_dir (to_utf8 @@ -137,29 +141,31 @@ set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) -add_component_qt_dir (contentselector - model/modelitem model/esmfile - model/naturalsort model/contentmodel - model/loadordererror - view/combobox view/contentselector - ) -add_component_qt_dir (config - gamesettings - launchersettings - settingsbase - ) - -add_component_qt_dir (process - processinvoker -) - -if (DESIRED_QT_VERSION MATCHES 4) - include(${QT_USE_FILE}) - QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI}) - QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) -else() - QT5_WRAP_UI(ESM_UI_HDR ${ESM_UI}) - QT5_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) +if (USE_QT) + add_component_qt_dir (contentselector + model/modelitem model/esmfile + model/naturalsort model/contentmodel + model/loadordererror + view/combobox view/contentselector + ) + add_component_qt_dir (config + gamesettings + launchersettings + settingsbase + ) + + add_component_qt_dir (process + processinvoker + ) + + if (DESIRED_QT_VERSION MATCHES 4) + include(${QT_USE_FILE}) + QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI}) + QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) + else() + QT5_WRAP_UI(ESM_UI_HDR ${ESM_UI}) + QT5_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) + endif() endif() if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -172,30 +178,46 @@ add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR}) +if (OPENGL_ES) + set(GL_LIB ${OPENGLES_gl_LIBRARY}) +else() + set(GL_LIB ${OPENGL_gl_LIBRARY}) +endif() + target_link_libraries(components ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${OPENSCENEGRAPH_LIBRARIES} + ${OSG_LIBRARIES} + ${OPENTHREADS_LIBRARIES} + ${OSGPARTICLE_LIBRARIES} + ${OSGUTIL_LIBRARIES} + ${OSGDB_LIBRARIES} + ${OSGVIEWER_LIBRARIES} + ${OSGGA_LIBRARIES} + ${OSGFX_LIBRARIES} + ${OSGANIMATION_LIBRARIES} ${BULLET_LIBRARIES} ${SDL2_LIBRARY} # For MyGUI platform - ${OPENGL_gl_LIBRARY} + ${GL_LIB} ${MYGUI_LIBRARIES} -) + ) if (WIN32) target_link_libraries(components ${Boost_LOCALE_LIBRARY}) endif() -if (DESIRED_QT_VERSION MATCHES 4) - target_link_libraries(components - ${QT_QTCORE_LIBRARY} - ${QT_QTGUI_LIBRARY}) -else() - qt5_use_modules(components Widgets Core) +if (USE_QT) + if (DESIRED_QT_VERSION MATCHES 4) + target_link_libraries(components + ${QT_QTCORE_LIBRARY} + ${QT_QTGUI_LIBRARY}) + else() + qt5_use_modules(components Widgets Core) + endif() endif() if (GIT_CHECKOUT) diff -Nru openmw-0.37.0/components/compiler/exprparser.cpp openmw-0.38.0/components/compiler/exprparser.cpp --- openmw-0.37.0/components/compiler/exprparser.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/compiler/exprparser.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -804,6 +804,8 @@ if (optional) ++optionalCount; } + else + getErrorHandler().warning("Ignoring extra argument", mTokenLoc); } else if (*iter=='X') { @@ -815,6 +817,8 @@ if (parser.isEmpty()) break; + else + getErrorHandler().warning("Ignoring extra argument", mTokenLoc); } else if (*iter=='z') { @@ -825,6 +829,8 @@ if (discardParser.isEmpty()) break; + else + getErrorHandler().warning("Ignoring extra argument", mTokenLoc); } else if (*iter=='j') { diff -Nru openmw-0.37.0/components/compiler/extensions0.cpp openmw-0.38.0/components/compiler/extensions0.cpp --- openmw-0.37.0/components/compiler/extensions0.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/compiler/extensions0.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -310,7 +310,7 @@ extensions.registerFunction ("getpctraveling", 'l', "", opcodeGetPcTraveling); extensions.registerInstruction ("betacomment", "/S", opcodeBetaComment, opcodeBetaCommentExplicit); extensions.registerInstruction ("bc", "/S", opcodeBetaComment, opcodeBetaCommentExplicit); - extensions.registerInstruction ("ori", "/S", opcodeBetaComment, opcodeBetaCommentExplicit); + extensions.registerInstruction ("ori", "/S", opcodeBetaComment, opcodeBetaCommentExplicit); // 'ori' stands for 'ObjectReferenceInfo' extensions.registerInstruction ("addtolevcreature", "ccl", opcodeAddToLevCreature); extensions.registerInstruction ("removefromlevcreature", "ccl", opcodeRemoveFromLevCreature); extensions.registerInstruction ("addtolevitem", "ccl", opcodeAddToLevItem); @@ -341,9 +341,9 @@ extensions.registerInstruction ("say", "SS", opcodeSay, opcodeSayExplicit); extensions.registerFunction ("saydone", 'l', "", opcodeSayDone, opcodeSayDoneExplicit); extensions.registerInstruction ("streammusic", "S", opcodeStreamMusic); - extensions.registerInstruction ("playsound", "c", opcodePlaySound); + extensions.registerInstruction ("playsound", "cXX", opcodePlaySound); extensions.registerInstruction ("playsoundvp", "cff", opcodePlaySoundVP); - extensions.registerInstruction ("playsound3d", "c", opcodePlaySound3D, + extensions.registerInstruction ("playsound3d", "cXX", opcodePlaySound3D, opcodePlaySound3DExplicit); extensions.registerInstruction ("playsound3dvp", "cff", opcodePlaySound3DVP, opcodePlaySound3DVPExplicit); diff -Nru openmw-0.37.0/components/compiler/extensions.hpp openmw-0.38.0/components/compiler/extensions.hpp --- openmw-0.37.0/components/compiler/extensions.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/compiler/extensions.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,9 +20,9 @@ l - Integer
s - Short
S - String, case preserved
- x - Optional, ignored string argument - X - Optional, ignored numeric expression - z - Optional, ignored string or numeric argument + x - Optional, ignored string argument. Emits a parser warning when this argument is supplied.
+ X - Optional, ignored numeric expression. Emits a parser warning when this argument is supplied.
+ z - Optional, ignored string or numeric argument. Emits a parser warning when this argument is supplied.
j - A piece of junk (either . or a specific keyword) **/ typedef std::string ScriptArgs; diff -Nru openmw-0.37.0/components/compiler/lineparser.cpp openmw-0.38.0/components/compiler/lineparser.cpp --- openmw-0.37.0/components/compiler/lineparser.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/compiler/lineparser.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -411,7 +411,12 @@ } case Scanner::K_set: mState = SetState; return true; - case Scanner::K_messagebox: mState = MessageState; return true; + + case Scanner::K_messagebox: + + mState = MessageState; + scanner.enableStrictKeywords(); + return true; case Scanner::K_return: diff -Nru openmw-0.37.0/components/compiler/scanner.cpp openmw-0.38.0/components/compiler/scanner.cpp --- openmw-0.37.0/components/compiler/scanner.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/compiler/scanner.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -26,6 +26,7 @@ if (c=='\n') { + mStrictKeywords = false; mLoc.mColumn = 0; ++mLoc.mLine; mLoc.mLiteral.clear(); @@ -294,8 +295,11 @@ name = name.substr (1, name.size()-2); // allow keywords enclosed in "" /// \todo optionally disable -// cont = parser.parseName (name, loc, *this); -// return true; + if (mStrictKeywords) + { + cont = parser.parseName (name, loc, *this); + return true; + } } int i = 0; @@ -567,7 +571,8 @@ Scanner::Scanner (ErrorHandler& errorHandler, std::istream& inputStream, const Extensions *extensions) : mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions), - mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0) + mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0), + mStrictKeywords (false) { } @@ -619,4 +624,9 @@ if (mExtensions) mExtensions->listKeywords (keywords); } + + void Scanner::enableStrictKeywords() + { + mStrictKeywords = true; + } } diff -Nru openmw-0.37.0/components/compiler/scanner.hpp openmw-0.38.0/components/compiler/scanner.hpp --- openmw-0.37.0/components/compiler/scanner.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/compiler/scanner.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -37,6 +37,7 @@ float mPutbackFloat; std::string mPutbackName; TokenLoc mPutbackLoc; + bool mStrictKeywords; public: @@ -116,13 +117,18 @@ ///< put back a float token void putbackName (const std::string& name, const TokenLoc& loc); - ///< put back a name toekn + ///< put back a name token void putbackKeyword (int keyword, const TokenLoc& loc); ///< put back a keyword token void listKeywords (std::vector& keywords); - ///< Append all known keywords to \a kaywords. + ///< Append all known keywords to \a keywords. + + /// Do not accept keywords in quotation marks anymore. + /// + /// \attention This mode lasts only until the next newline is reached. + void enableStrictKeywords(); }; } diff -Nru openmw-0.37.0/components/config/gamesettings.cpp openmw-0.38.0/components/config/gamesettings.cpp --- openmw-0.37.0/components/config/gamesettings.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/config/gamesettings.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -454,3 +454,11 @@ return Config::LauncherSettings::reverse(values(sContentKey)); } +void Config::GameSettings::clear() +{ + mSettings.clear(); + mUserSettings.clear(); + mDataDirs.clear(); + mDataLocal.clear(); +} + diff -Nru openmw-0.37.0/components/config/gamesettings.hpp openmw-0.38.0/components/config/gamesettings.hpp --- openmw-0.37.0/components/config/gamesettings.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/config/gamesettings.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -72,6 +72,8 @@ void setContentList(const QStringList& fileNames); QStringList getContentList() const; + void clear(); + private: Files::ConfigurationManager &mCfgMgr; diff -Nru openmw-0.37.0/components/config/launchersettings.cpp openmw-0.38.0/components/config/launchersettings.cpp --- openmw-0.37.0/components/config/launchersettings.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/config/launchersettings.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -25,8 +25,6 @@ QMap settings = SettingsBase::getSettings(); QStringList keys = settings.uniqueKeys(); - qDebug() << keys; - QRegExp keyRe("(.+)/"); QStringList result; diff -Nru openmw-0.37.0/components/config/settingsbase.hpp openmw-0.38.0/components/config/settingsbase.hpp --- openmw-0.37.0/components/config/settingsbase.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/config/settingsbase.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -101,6 +101,11 @@ return true; } + void clear() + { + mSettings.clear(); + } + private: Map mSettings; diff -Nru openmw-0.37.0/components/contentselector/model/contentmodel.cpp openmw-0.38.0/components/contentselector/model/contentmodel.cpp --- openmw-0.37.0/components/contentselector/model/contentmodel.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/contentselector/model/contentmodel.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,7 +7,7 @@ #include #include -#include "components/esm/esmreader.hpp" +#include ContentSelectorModel::ContentModel::ContentModel(QObject *parent, QIcon warningIcon) : QAbstractTableModel(parent), @@ -110,12 +110,11 @@ bool gamefileChecked = (file->gameFiles().count() == 0); foreach (const QString &fileName, file->gameFiles()) { - bool depFound = false; foreach (EsmFile *dependency, mFiles) { //compare filenames only. Multiple instances //of the filename (with different paths) is not relevant here. - depFound = (dependency->fileName().compare(fileName, Qt::CaseInsensitive) == 0); + bool depFound = (dependency->fileName().compare(fileName, Qt::CaseInsensitive) == 0); if (!depFound) continue; diff -Nru openmw-0.37.0/components/contentselector/model/loadordererror.hpp openmw-0.38.0/components/contentselector/model/loadordererror.hpp --- openmw-0.37.0/components/contentselector/model/loadordererror.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/contentselector/model/loadordererror.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -17,12 +17,9 @@ ErrorCode_LoadOrder = 3 }; - inline LoadOrderError() : mErrorCode(ErrorCode_None) {}; + inline LoadOrderError() : mErrorCode(ErrorCode_None) {} inline LoadOrderError(ErrorCode errorCode, QString fileName) - { - mErrorCode = errorCode; - mFileName = fileName; - } + : mErrorCode(errorCode), mFileName(fileName) {} inline ErrorCode errorCode() const { return mErrorCode; } inline QString fileName() const { return mFileName; } QString toolTip() const; diff -Nru openmw-0.37.0/components/contentselector/view/contentselector.cpp openmw-0.38.0/components/contentselector/view/contentselector.cpp --- openmw-0.37.0/components/contentselector/view/contentselector.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/contentselector/view/contentselector.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,6 +1,6 @@ #include "contentselector.hpp" -#include "../model/esmfile.hpp" +#include #include diff -Nru openmw-0.37.0/components/contentselector/view/contentselector.hpp openmw-0.38.0/components/contentselector/view/contentselector.hpp --- openmw-0.37.0/components/contentselector/view/contentselector.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/contentselector/view/contentselector.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,7 +4,7 @@ #include #include "ui_contentselector.h" -#include "../model/contentmodel.hpp" +#include class QSortFilterProxyModel; diff -Nru openmw-0.37.0/components/esm/cellref.cpp openmw-0.38.0/components/esm/cellref.cpp --- openmw-0.37.0/components/esm/cellref.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/cellref.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,12 +3,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -void ESM::RefNum::load (ESMReader& esm, bool wide) +void ESM::RefNum::load (ESMReader& esm, bool wide, const std::string& tag) { if (wide) - esm.getHNT (*this, "FRMR", 8); + esm.getHNT (*this, tag.c_str(), 8); else - esm.getHNT (mIndex, "FRMR"); + esm.getHNT (mIndex, tag.c_str()); } void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const @@ -24,10 +24,10 @@ } -void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) +void ESM::CellRef::load (ESMReader& esm, bool &isDeleted, bool wideRefNum) { loadId(esm, wideRefNum); - loadData(esm); + loadData(esm, isDeleted); } void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) @@ -39,71 +39,98 @@ if (esm.isNextSub ("NAM0")) esm.skipHSub(); + blank(); + mRefNum.load (esm, wideRefNum); mRefID = esm.getHNString ("NAME"); } -void ESM::CellRef::loadData(ESMReader &esm) +void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted) { - // Again, UNAM sometimes appears after NAME and sometimes later. - // Or perhaps this UNAM means something different? - mReferenceBlocked = -1; - esm.getHNOT (mReferenceBlocked, "UNAM"); - - mScale = 1.0; - esm.getHNOT (mScale, "XSCL"); - - mOwner = esm.getHNOString ("ANAM"); - mGlobalVariable = esm.getHNOString ("BNAM"); - mSoul = esm.getHNOString ("XSOL"); - - mFaction = esm.getHNOString ("CNAM"); - mFactionRank = -2; - esm.getHNOT (mFactionRank, "INDX"); - - mGoldValue = 1; - mChargeInt = -1; - mEnchantmentCharge = -1; - - esm.getHNOT (mEnchantmentCharge, "XCHG"); - - esm.getHNOT (mChargeInt, "INTV"); - - esm.getHNOT (mGoldValue, "NAM9"); + isDeleted = false; - // Present for doors that teleport you to another cell. - if (esm.isNextSub ("DODT")) + bool isLoaded = false; + while (!isLoaded && esm.hasMoreSubs()) { - mTeleport = true; - esm.getHT (mDoorDest); - mDestCell = esm.getHNOString ("DNAM"); + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::FourCC<'U','N','A','M'>::value: + esm.getHT(mReferenceBlocked); + break; + case ESM::FourCC<'X','S','C','L'>::value: + esm.getHT(mScale); + break; + case ESM::FourCC<'A','N','A','M'>::value: + mOwner = esm.getHString(); + break; + case ESM::FourCC<'B','N','A','M'>::value: + mGlobalVariable = esm.getHString(); + break; + case ESM::FourCC<'X','S','O','L'>::value: + mSoul = esm.getHString(); + break; + case ESM::FourCC<'C','N','A','M'>::value: + mFaction = esm.getHString(); + break; + case ESM::FourCC<'I','N','D','X'>::value: + esm.getHT(mFactionRank); + break; + case ESM::FourCC<'X','C','H','G'>::value: + esm.getHT(mEnchantmentCharge); + break; + case ESM::FourCC<'I','N','T','V'>::value: + esm.getHT(mChargeInt); + break; + case ESM::FourCC<'N','A','M','9'>::value: + esm.getHT(mGoldValue); + break; + case ESM::FourCC<'D','O','D','T'>::value: + esm.getHT(mDoorDest); + mTeleport = true; + break; + case ESM::FourCC<'D','N','A','M'>::value: + mDestCell = esm.getHString(); + break; + case ESM::FourCC<'F','L','T','V'>::value: + esm.getHT(mLockLevel); + break; + case ESM::FourCC<'K','N','A','M'>::value: + mKey = esm.getHString(); + break; + case ESM::FourCC<'T','N','A','M'>::value: + mTrap = esm.getHString(); + break; + case ESM::FourCC<'D','A','T','A'>::value: + esm.getHT(mPos, 24); + break; + case ESM::FourCC<'N','A','M','0'>::value: + esm.skipHSub(); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.cacheSubName(); + isLoaded = true; + break; + } } - else - mTeleport = false; - - mLockLevel = 0; //Set to 0 to indicate no lock - esm.getHNOT (mLockLevel, "FLTV"); - - mKey = esm.getHNOString ("KNAM"); - mTrap = esm.getHNOString ("TNAM"); - - esm.getHNOT (mReferenceBlocked, "UNAM"); - if (esm.isNextSub("FLTV")) // no longer used - esm.skipHSub(); - - esm.getHNOT(mPos, "DATA", 24); - - if (esm.isNextSub("NAM0")) - esm.skipHSub(); } -void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const +void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool isDeleted) const { mRefNum.save (esm, wideRefNum); esm.writeHNCString("NAME", mRefID); + if (isDeleted) { + esm.writeHNCString("DELE", ""); + return; + } + if (mScale != 1.0) { esm.writeHNT("XSCL", mScale); } @@ -134,7 +161,7 @@ } if (!inInventory && mLockLevel != 0) { - esm.writeHNT("FLTV", mLockLevel); + esm.writeHNT("FLTV", mLockLevel); } if (!inInventory) @@ -153,7 +180,7 @@ void ESM::CellRef::blank() { mRefNum.unset(); - mRefID.clear(); + mRefID.clear(); mScale = 1; mOwner.clear(); mGlobalVariable.clear(); @@ -169,7 +196,7 @@ mTrap.clear(); mReferenceBlocked = -1; mTeleport = false; - + for (int i=0; i<3; ++i) { mDoorDest.pos[i] = 0; diff -Nru openmw-0.37.0/components/esm/cellref.hpp openmw-0.38.0/components/esm/cellref.hpp --- openmw-0.37.0/components/esm/cellref.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/cellref.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -16,7 +16,7 @@ unsigned int mIndex; int mContentFile; - void load (ESMReader& esm, bool wide = false); + void load (ESMReader& esm, bool wide = false, const std::string& tag = "FRMR"); void save (ESMWriter &esm, bool wide = false, const std::string& tag = "FRMR") const; @@ -34,7 +34,6 @@ class CellRef { public: - // Reference number // Note: Currently unused for items in containers RefNum mRefNum; @@ -100,14 +99,14 @@ Position mPos; /// Calls loadId and loadData - void load (ESMReader& esm, bool wideRefNum = false); + void load (ESMReader& esm, bool &isDeleted, bool wideRefNum = false); void loadId (ESMReader& esm, bool wideRefNum = false); /// Implicitly called by load - void loadData (ESMReader& esm); + void loadData (ESMReader& esm, bool &isDeleted); - void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false) const; + void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const; void blank(); }; diff -Nru openmw-0.37.0/components/esm/debugprofile.cpp openmw-0.38.0/components/esm/debugprofile.cpp --- openmw-0.37.0/components/esm/debugprofile.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/debugprofile.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,15 +6,48 @@ unsigned int ESM::DebugProfile::sRecordId = REC_DBGP; -void ESM::DebugProfile::load (ESMReader& esm) +void ESM::DebugProfile::load (ESMReader& esm, bool &isDeleted) { - mDescription = esm.getHNString ("DESC"); - mScriptText = esm.getHNString ("SCRP"); - esm.getHNT (mFlags, "FLAG"); + isDeleted = false; + + while (esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + break; + case ESM::FourCC<'D','E','S','C'>::value: + mDescription = esm.getHString(); + break; + case ESM::FourCC<'S','C','R','P'>::value: + mScriptText = esm.getHString(); + break; + case ESM::FourCC<'F','L','A','G'>::value: + esm.getHT(mFlags); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } + } } -void ESM::DebugProfile::save (ESMWriter& esm) const +void ESM::DebugProfile::save (ESMWriter& esm, bool isDeleted) const { + esm.writeHNCString ("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString ("DESC", mDescription); esm.writeHNCString ("SCRP", mScriptText); esm.writeHNT ("FLAG", mFlags); diff -Nru openmw-0.37.0/components/esm/debugprofile.hpp openmw-0.38.0/components/esm/debugprofile.hpp --- openmw-0.37.0/components/esm/debugprofile.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/debugprofile.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,8 +27,8 @@ unsigned int mFlags; - void load (ESMReader& esm); - void save (ESMWriter& esm) const; + void load (ESMReader& esm, bool &isDeleted); + void save (ESMWriter& esm, bool isDeleted = false) const; /// Set record to default state (does not touch the ID). void blank(); diff -Nru openmw-0.37.0/components/esm/defs.hpp openmw-0.38.0/components/esm/defs.hpp --- openmw-0.37.0/components/esm/defs.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/defs.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -133,5 +133,12 @@ REC_DBGP = FourCC<'D','B','G','P'>::value ///< only used in project files }; +/// Common subrecords +enum SubRecNameInts +{ + SREC_DELE = ESM::FourCC<'D','E','L','E'>::value, + SREC_NAME = ESM::FourCC<'N','A','M','E'>::value +}; + } #endif diff -Nru openmw-0.37.0/components/esm/esmreader.cpp openmw-0.38.0/components/esm/esmreader.cpp --- openmw-0.37.0/components/esm/esmreader.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/esmreader.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -187,6 +187,11 @@ return mCtx.subName == name; } +void ESMReader::cacheSubName() +{ + mCtx.subCached = true; +} + // Read subrecord name. This gets called a LOT, so I've optimized it // slightly. void ESMReader::getSubName() @@ -276,6 +281,7 @@ { skip(mCtx.leftRec); mCtx.leftRec = 0; + mCtx.subCached = false; } void ESMReader::getRecHeader(uint32_t &flags) diff -Nru openmw-0.37.0/components/esm/esmreader.hpp openmw-0.38.0/components/esm/esmreader.hpp --- openmw-0.37.0/components/esm/esmreader.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/esmreader.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -185,6 +185,9 @@ bool peekNextSub(const char* name); + // Store the current subrecord name for the next call of getSubName() + void cacheSubName(); + // Read subrecord name. This gets called a LOT, so I've optimized it // slightly. void getSubName(); diff -Nru openmw-0.37.0/components/esm/filter.cpp openmw-0.38.0/components/esm/filter.cpp --- openmw-0.37.0/components/esm/filter.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/filter.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,14 +6,46 @@ unsigned int ESM::Filter::sRecordId = REC_FILT; -void ESM::Filter::load (ESMReader& esm) +void ESM::Filter::load (ESMReader& esm, bool &isDeleted) { - mFilter = esm.getHNString ("FILT"); - mDescription = esm.getHNString ("DESC"); + isDeleted = false; + + while (esm.hasMoreSubs()) + { + esm.getSubName(); + uint32_t name = esm.retSubName().val; + switch (name) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + break; + case ESM::FourCC<'F','I','L','T'>::value: + mFilter = esm.getHString(); + break; + case ESM::FourCC<'D','E','S','C'>::value: + mDescription = esm.getHString(); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } + } } -void ESM::Filter::save (ESMWriter& esm) const +void ESM::Filter::save (ESMWriter& esm, bool isDeleted) const { + esm.writeHNCString ("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString ("FILT", mFilter); esm.writeHNCString ("DESC", mDescription); } diff -Nru openmw-0.37.0/components/esm/filter.hpp openmw-0.38.0/components/esm/filter.hpp --- openmw-0.37.0/components/esm/filter.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/filter.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -18,8 +18,8 @@ std::string mFilter; - void load (ESMReader& esm); - void save (ESMWriter& esm) const; + void load (ESMReader& esm, bool &isDeleted); + void save (ESMWriter& esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/inventorystate.cpp openmw-0.38.0/components/esm/inventorystate.cpp --- openmw-0.37.0/components/esm/inventorystate.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/inventorystate.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -31,13 +31,20 @@ ++index; } - + //Next item is Levelled item while (esm.isNextSub("LEVM")) { + //Get its name std::string id = esm.getHString(); int count; + std::string parentGroup = ""; + //Then get its count esm.getHNT (count, "COUN"); - mLevelledItemMap[id] = count; + //Old save formats don't have information about parent group; check for that + if(esm.isNextSub("LGRP")) + //Newest saves contain parent group + parentGroup = esm.getHString(); + mLevelledItemMap[std::make_pair(id, parentGroup)] = count; } while (esm.isNextSub("MAGI")) @@ -79,10 +86,11 @@ iter->save (esm, true); } - for (std::map::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) + for (std::map, int>::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) { - esm.writeHNString ("LEVM", it->first); + esm.writeHNString ("LEVM", it->first.first); esm.writeHNT ("COUN", it->second); + esm.writeHNString("LGRP", it->first.second); } for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it) diff -Nru openmw-0.37.0/components/esm/inventorystate.hpp openmw-0.38.0/components/esm/inventorystate.hpp --- openmw-0.37.0/components/esm/inventorystate.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/inventorystate.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,7 +20,7 @@ // std::map mEquipmentSlots; - std::map mLevelledItemMap; + std::map, int> mLevelledItemMap; typedef std::map > > TEffectMagnitudes; TEffectMagnitudes mPermanentMagicEffectMagnitudes; diff -Nru openmw-0.37.0/components/esm/loadacti.cpp openmw-0.38.0/components/esm/loadacti.cpp --- openmw-0.37.0/components/esm/loadacti.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadacti.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,14 +8,20 @@ { unsigned int Activator::sRecordId = REC_ACTI; - void Activator::load(ESMReader &esm) + void Activator::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -25,13 +31,29 @@ case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); } - void Activator::save(ESMWriter &esm) const + void Activator::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); diff -Nru openmw-0.37.0/components/esm/loadacti.hpp openmw-0.38.0/components/esm/loadacti.hpp --- openmw-0.37.0/components/esm/loadacti.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadacti.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -17,8 +17,8 @@ std::string mId, mName, mScript, mModel; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadalch.cpp openmw-0.38.0/components/esm/loadalch.cpp --- openmw-0.37.0/components/esm/loadalch.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadalch.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,16 +8,23 @@ { unsigned int Potion::sRecordId = REC_ALCH; - void Potion::load(ESMReader &esm) + void Potion::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + mEffects.mList.clear(); + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -37,15 +44,31 @@ case ESM::FourCC<'E','N','A','M'>::value: mEffects.add(esm); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) - esm.fail("Missing ALDT"); + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing ALDT subrecord"); } - void Potion::save(ESMWriter &esm) const + void Potion::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("TEXT", mIcon); esm.writeHNOCString("SCRI", mScript); diff -Nru openmw-0.37.0/components/esm/loadalch.hpp openmw-0.38.0/components/esm/loadalch.hpp --- openmw-0.37.0/components/esm/loadalch.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadalch.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -33,8 +33,8 @@ std::string mId, mName, mModel, mIcon, mScript; EffectList mEffects; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadappa.cpp openmw-0.38.0/components/esm/loadappa.cpp --- openmw-0.37.0/components/esm/loadappa.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadappa.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,47 +8,69 @@ { unsigned int Apparatus::sRecordId = REC_APPA; -void Apparatus::load(ESMReader &esm) -{ - bool hasData = false; - while (esm.hasMoreSubs()) + void Apparatus::load(ESMReader &esm, bool &isDeleted) { - esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + isDeleted = false; + + bool hasName = false; + bool hasData = false; + while (esm.hasMoreSubs()) { - case ESM::FourCC<'M','O','D','L'>::value: - mModel = esm.getHString(); - break; - case ESM::FourCC<'F','N','A','M'>::value: - mName = esm.getHString(); - break; - case ESM::FourCC<'A','A','D','T'>::value: - esm.getHT(mData); - hasData = true; - break; - case ESM::FourCC<'S','C','R','I'>::value: - mScript = esm.getHString(); - break; - case ESM::FourCC<'I','T','E','X'>::value: - mIcon = esm.getHString(); - break; - default: - esm.fail("Unknown subrecord"); + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'M','O','D','L'>::value: + mModel = esm.getHString(); + break; + case ESM::FourCC<'F','N','A','M'>::value: + mName = esm.getHString(); + break; + case ESM::FourCC<'A','A','D','T'>::value: + esm.getHT(mData); + hasData = true; + break; + case ESM::FourCC<'S','C','R','I'>::value: + mScript = esm.getHString(); + break; + case ESM::FourCC<'I','T','E','X'>::value: + mIcon = esm.getHString(); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing AADT subrecord"); } - if (!hasData) - esm.fail("Missing AADT"); -} -void Apparatus::save(ESMWriter &esm) const -{ - esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); - esm.writeHNT("AADT", mData, 16); - esm.writeHNOCString("SCRI", mScript); - esm.writeHNCString("ITEX", mIcon); -} + void Apparatus::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + + esm.writeHNCString("MODL", mModel); + esm.writeHNCString("FNAM", mName); + esm.writeHNT("AADT", mData, 16); + esm.writeHNOCString("SCRI", mScript); + esm.writeHNCString("ITEX", mIcon); + } void Apparatus::blank() { diff -Nru openmw-0.37.0/components/esm/loadappa.hpp openmw-0.38.0/components/esm/loadappa.hpp --- openmw-0.37.0/components/esm/loadappa.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadappa.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -38,8 +38,8 @@ AADTstruct mData; std::string mId, mModel, mIcon, mScript, mName; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadarmo.cpp openmw-0.38.0/components/esm/loadarmo.cpp --- openmw-0.37.0/components/esm/loadarmo.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadarmo.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -38,16 +38,23 @@ unsigned int Armor::sRecordId = REC_ARMO; - void Armor::load(ESMReader &esm) + void Armor::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + mParts.mParts.clear(); + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -70,16 +77,32 @@ case ESM::FourCC<'I','N','D','X'>::value: mParts.add(esm); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing CTDT subrecord"); } - void Armor::save(ESMWriter &esm) const + void Armor::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); diff -Nru openmw-0.37.0/components/esm/loadarmo.hpp openmw-0.38.0/components/esm/loadarmo.hpp --- openmw-0.37.0/components/esm/loadarmo.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadarmo.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -96,8 +96,8 @@ std::string mId, mName, mModel, mIcon, mScript, mEnchant; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadbody.cpp openmw-0.38.0/components/esm/loadbody.cpp --- openmw-0.37.0/components/esm/loadbody.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadbody.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,40 +8,61 @@ { unsigned int BodyPart::sRecordId = REC_BODY; - -void BodyPart::load(ESMReader &esm) -{ - bool hasData = false; - while (esm.hasMoreSubs()) + void BodyPart::load(ESMReader &esm, bool &isDeleted) { - esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + isDeleted = false; + + bool hasName = false; + bool hasData = false; + while (esm.hasMoreSubs()) { - case ESM::FourCC<'M','O','D','L'>::value: - mModel = esm.getHString(); - break; - case ESM::FourCC<'F','N','A','M'>::value: - mRace = esm.getHString(); - break; - case ESM::FourCC<'B','Y','D','T'>::value: - esm.getHT(mData, 4); - hasData = true; - break; - default: - esm.fail("Unknown subrecord"); + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'M','O','D','L'>::value: + mModel = esm.getHString(); + break; + case ESM::FourCC<'F','N','A','M'>::value: + mRace = esm.getHString(); + break; + case ESM::FourCC<'B','Y','D','T'>::value: + esm.getHT(mData, 4); + hasData = true; + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing BYDT subrecord"); } - if (!hasData) - esm.fail("Missing BYDT subrecord"); -} -void BodyPart::save(ESMWriter &esm) const -{ - esm.writeHNCString("MODL", mModel); - esm.writeHNOCString("FNAM", mRace); - esm.writeHNT("BYDT", mData, 4); -} + void BodyPart::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + + esm.writeHNCString("MODL", mModel); + esm.writeHNOCString("FNAM", mRace); + esm.writeHNT("BYDT", mData, 4); + } void BodyPart::blank() { diff -Nru openmw-0.37.0/components/esm/loadbody.hpp openmw-0.38.0/components/esm/loadbody.hpp --- openmw-0.37.0/components/esm/loadbody.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadbody.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -60,8 +60,8 @@ BYDTstruct mData; std::string mId, mModel, mRace; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadbook.cpp openmw-0.38.0/components/esm/loadbook.cpp --- openmw-0.37.0/components/esm/loadbook.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadbook.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int Book::sRecordId = REC_BOOK; - void Book::load(ESMReader &esm) + void Book::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -39,15 +45,31 @@ case ESM::FourCC<'T','E','X','T'>::value: mText = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing BKDT subrecord"); } - void Book::save(ESMWriter &esm) const + void Book::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("BKDT", mData, 20); diff -Nru openmw-0.37.0/components/esm/loadbook.hpp openmw-0.38.0/components/esm/loadbook.hpp --- openmw-0.37.0/components/esm/loadbook.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadbook.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -28,8 +28,8 @@ std::string mName, mModel, mIcon, mScript, mEnchant, mText; std::string mId; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadbsgn.cpp openmw-0.38.0/components/esm/loadbsgn.cpp --- openmw-0.37.0/components/esm/loadbsgn.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadbsgn.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,41 +8,63 @@ { unsigned int BirthSign::sRecordId = REC_BSGN; -void BirthSign::load(ESMReader &esm) -{ - mPowers.mList.clear(); - while (esm.hasMoreSubs()) + void BirthSign::load(ESMReader &esm, bool &isDeleted) { - esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + isDeleted = false; + + mPowers.mList.clear(); + + bool hasName = false; + while (esm.hasMoreSubs()) { - case ESM::FourCC<'F','N','A','M'>::value: - mName = esm.getHString(); - break; - case ESM::FourCC<'T','N','A','M'>::value: - mTexture = esm.getHString(); - break; - case ESM::FourCC<'D','E','S','C'>::value: - mDescription = esm.getHString(); - break; - case ESM::FourCC<'N','P','C','S'>::value: - mPowers.add(esm); - break; - default: - esm.fail("Unknown subrecord"); + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'F','N','A','M'>::value: + mName = esm.getHString(); + break; + case ESM::FourCC<'T','N','A','M'>::value: + mTexture = esm.getHString(); + break; + case ESM::FourCC<'D','E','S','C'>::value: + mDescription = esm.getHString(); + break; + case ESM::FourCC<'N','P','C','S'>::value: + mPowers.add(esm); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); } -} -void BirthSign::save(ESMWriter &esm) const -{ - esm.writeHNOCString("FNAM", mName); - esm.writeHNOCString("TNAM", mTexture); - esm.writeHNOCString("DESC", mDescription); + void BirthSign::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("NAME", mId); - mPowers.save(esm); -} + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNOCString("FNAM", mName); + esm.writeHNOCString("TNAM", mTexture); + esm.writeHNOCString("DESC", mDescription); + + mPowers.save(esm); + } void BirthSign::blank() { diff -Nru openmw-0.37.0/components/esm/loadbsgn.hpp openmw-0.38.0/components/esm/loadbsgn.hpp --- openmw-0.37.0/components/esm/loadbsgn.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadbsgn.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -22,8 +22,8 @@ // List of powers and abilities that come with this birth sign. SpellList mPowers; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadcell.cpp openmw-0.38.0/components/esm/loadcell.cpp --- openmw-0.37.0/components/esm/loadcell.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadcell.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -52,173 +52,215 @@ return ref.mRefNum == refNum; } -void Cell::load(ESMReader &esm, bool saveContext) -{ - loadData(esm); - loadCell(esm, saveContext); -} - -void Cell::loadCell(ESMReader &esm, bool saveContext) -{ - mRefNumCounter = 0; + void Cell::load(ESMReader &esm, bool &isDeleted, bool saveContext) + { + loadNameAndData(esm, isDeleted); + loadCell(esm, saveContext); + } - if (mData.mFlags & Interior) + void Cell::loadNameAndData(ESMReader &esm, bool &isDeleted) { - // Interior cells - if (esm.isNextSub("INTV")) + isDeleted = false; + + blank(); + + bool hasData = false; + bool isLoaded = false; + while (!isLoaded && esm.hasMoreSubs()) { - int waterl; - esm.getHT(waterl); - mWater = (float) waterl; - mWaterInt = true; + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mName = esm.getHString(); + break; + case ESM::FourCC<'D','A','T','A'>::value: + esm.getHT(mData, 12); + hasData = true; + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.cacheSubName(); + isLoaded = true; + break; + } } - else if (esm.isNextSub("WHGT")) + + if (!hasData) + esm.fail("Missing DATA subrecord"); + + mCellId.mPaged = !(mData.mFlags & Interior); + + if (mCellId.mPaged) { - esm.getHT(mWater); + mCellId.mWorldspace = "sys::default"; + mCellId.mIndex.mX = mData.mX; + mCellId.mIndex.mY = mData.mY; + } + else + { + mCellId.mWorldspace = Misc::StringUtils::lowerCase (mName); + mCellId.mIndex.mX = 0; + mCellId.mIndex.mY = 0; } - - // Quasi-exterior cells have a region (which determines the - // weather), pure interior cells have ambient lighting - // instead. - if (mData.mFlags & QuasiEx) - mRegion = esm.getHNOString("RGNN"); - else if (esm.isNextSub("AMBI")) - esm.getHT(mAmbi); } - else - { - // Exterior cells - mRegion = esm.getHNOString("RGNN"); - mMapColor = 0; - esm.getHNOT(mMapColor, "NAM5"); - } - if (esm.isNextSub("NAM0")) { - esm.getHT(mRefNumCounter); + void Cell::loadCell(ESMReader &esm, bool saveContext) + { + bool isLoaded = false; + while (!isLoaded && esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::FourCC<'I','N','T','V'>::value: + int waterl; + esm.getHT(waterl); + mWater = static_cast(waterl); + mWaterInt = true; + break; + case ESM::FourCC<'W','H','G','T'>::value: + esm.getHT(mWater); + mWaterInt = false; + break; + case ESM::FourCC<'A','M','B','I'>::value: + esm.getHT(mAmbi); + break; + case ESM::FourCC<'R','G','N','N'>::value: + mRegion = esm.getHString(); + break; + case ESM::FourCC<'N','A','M','5'>::value: + esm.getHT(mMapColor); + break; + case ESM::FourCC<'N','A','M','0'>::value: + esm.getHT(mRefNumCounter); + break; + default: + esm.cacheSubName(); + isLoaded = true; + break; + } + } + + if (saveContext) + { + mContextList.push_back(esm.getContext()); + esm.skipRecord(); + } } - if (saveContext) { + void Cell::postLoad(ESMReader &esm) + { + // Save position of the cell references and move on mContextList.push_back(esm.getContext()); esm.skipRecord(); } -} -void Cell::loadData(ESMReader &esm) -{ - // Ignore this for now, it might mean we should delete the entire - // cell? - // TODO: treat the special case "another plugin moved this ref, but we want to delete it"! - if (esm.isNextSub("DELE")) { - esm.skipHSub(); - } + void Cell::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNOCString("NAME", mName); + esm.writeHNT("DATA", mData, 12); - esm.getHNT(mData, "DATA", 12); -} + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } -void Cell::postLoad(ESMReader &esm) -{ - // Save position of the cell references and move on - mContextList.push_back(esm.getContext()); - esm.skipRecord(); -} + if (mData.mFlags & Interior) + { + if (mWaterInt) { + int water = + (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); + esm.writeHNT("INTV", water); + } else { + esm.writeHNT("WHGT", mWater); + } -void Cell::save(ESMWriter &esm) const -{ - esm.writeHNT("DATA", mData, 12); - if (mData.mFlags & Interior) - { - if (mWaterInt) { - int water = - (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); - esm.writeHNT("INTV", water); - } else { - esm.writeHNT("WHGT", mWater); + if (mData.mFlags & QuasiEx) + esm.writeHNOCString("RGNN", mRegion); + else + esm.writeHNT("AMBI", mAmbi, 16); } - - if (mData.mFlags & QuasiEx) - esm.writeHNOCString("RGNN", mRegion); else - esm.writeHNT("AMBI", mAmbi, 16); - } - else - { - esm.writeHNOCString("RGNN", mRegion); - if (mMapColor != 0) - esm.writeHNT("NAM5", mMapColor); - } - - if (mRefNumCounter != 0) - esm.writeHNT("NAM0", mRefNumCounter); -} - -void Cell::restore(ESMReader &esm, int iCtx) const -{ - esm.restoreContext(mContextList.at (iCtx)); -} + { + esm.writeHNOCString("RGNN", mRegion); + if (mMapColor != 0) + esm.writeHNT("NAM5", mMapColor); + } -std::string Cell::getDescription() const -{ - if (mData.mFlags & Interior) - { - return mName; + if (mRefNumCounter != 0) + esm.writeHNT("NAM0", mRefNumCounter); } - else + + void Cell::restore(ESMReader &esm, int iCtx) const { - std::ostringstream stream; - stream << mData.mX << ", " << mData.mY; - return stream.str(); + esm.restoreContext(mContextList.at (iCtx)); } -} - -bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted, bool ignoreMoves, MovedCellRef *mref) -{ - // TODO: Try and document reference numbering, I don't think this has been done anywhere else. - if (!esm.hasMoreSubs()) - return false; - // NOTE: We should not need this check. It is a safety check until we have checked - // more plugins, and how they treat these moved references. - if (esm.isNextSub("MVRF")) - { - if (ignoreMoves) - { - esm.getHT (mref->mRefNum.mIndex); - esm.getHNOT (mref->mTarget, "CNDT"); - adjustRefNum (mref->mRefNum, esm); + std::string Cell::getDescription() const + { + if (mData.mFlags & Interior) + { + return mName; } else { - // skip rest of cell record (moved references), they are handled elsewhere - esm.skipRecord(); // skip MVRF, CNDT - return false; + std::ostringstream stream; + stream << mData.mX << ", " << mData.mY; + return stream.str(); } } - ref.load (esm); + bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, bool ignoreMoves, MovedCellRef *mref) + { + isDeleted = false; - // Identify references belonging to a parent file and adapt the ID accordingly. - adjustRefNum (ref.mRefNum, esm); + // TODO: Try and document reference numbering, I don't think this has been done anywhere else. + if (!esm.hasMoreSubs()) + return false; - if (esm.isNextSub("DELE")) - { - esm.skipHSub(); - deleted = true; + // NOTE: We should not need this check. It is a safety check until we have checked + // more plugins, and how they treat these moved references. + if (esm.isNextSub("MVRF")) + { + if (ignoreMoves) + { + esm.getHT (mref->mRefNum.mIndex); + esm.getHNOT (mref->mTarget, "CNDT"); + adjustRefNum (mref->mRefNum, esm); + } + else + { + // skip rest of cell record (moved references), they are handled elsewhere + esm.skipRecord(); // skip MVRF, CNDT + return false; + } + } + + if (esm.peekNextSub("FRMR")) + { + ref.load (esm, isDeleted); + + // Identify references belonging to a parent file and adapt the ID accordingly. + adjustRefNum (ref.mRefNum, esm); + return true; + } + return false; } - else - deleted = false; - - return true; -} -bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) -{ - esm.getHT(mref.mRefNum.mIndex); - esm.getHNOT(mref.mTarget, "CNDT"); + bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) + { + esm.getHT(mref.mRefNum.mIndex); + esm.getHNOT(mref.mTarget, "CNDT"); - adjustRefNum (mref.mRefNum, esm); + adjustRefNum (mref.mRefNum, esm); - return true; -} + return true; + } void Cell::blank() { @@ -239,25 +281,8 @@ mAmbi.mFogDensity = 0; } - CellId Cell::getCellId() const + const CellId& Cell::getCellId() const { - CellId id; - - id.mPaged = !(mData.mFlags & Interior); - - if (id.mPaged) - { - id.mWorldspace = "sys::default"; - id.mIndex.mX = mData.mX; - id.mIndex.mY = mData.mY; - } - else - { - id.mWorldspace = Misc::StringUtils::lowerCase (mName); - id.mIndex.mX = 0; - id.mIndex.mY = 0; - } - - return id; + return mCellId; } } diff -Nru openmw-0.37.0/components/esm/loadcell.hpp openmw-0.38.0/components/esm/loadcell.hpp --- openmw-0.37.0/components/esm/loadcell.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadcell.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,6 +8,7 @@ #include "esmcommon.hpp" #include "defs.hpp" #include "cellref.hpp" +#include "cellid.hpp" namespace MWWorld { @@ -18,7 +19,6 @@ { class ESMReader; class ESMWriter; -struct CellId; /* 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 @@ -97,6 +97,8 @@ std::vector mContextList; // File position; multiple positions for multiple plugin support DATAstruct mData; + CellId mCellId; + AMBIstruct mAmbi; float mWater; // Water level @@ -116,11 +118,11 @@ // 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. - void load(ESMReader &esm, bool saveContext = true); // Load everything (except references) - void loadData(ESMReader &esm); // Load DATAstruct only - void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except DATAstruct and references + void load(ESMReader &esm, bool &isDeleted, bool saveContext = true); // Load everything (except references) + void loadNameAndData(ESMReader &esm, bool &isDeleted); // Load NAME and DATAstruct + void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except NAME, DATAstruct and references - void save(ESMWriter &esm) const; + void save(ESMWriter &esm, bool isDeleted = false) const; bool isExterior() const { @@ -159,8 +161,11 @@ reuse one memory location without blanking it between calls. */ /// \param ignoreMoves ignore MVRF record and read reference like a regular CellRef. - static bool getNextRef(ESMReader &esm, - CellRef &ref, bool& deleted, bool ignoreMoves = false, MovedCellRef *mref = 0); + static bool getNextRef(ESMReader &esm, + CellRef &ref, + bool &isDeleted, + bool ignoreMoves = false, + MovedCellRef *mref = 0); /* This fetches an MVRF record, which is used to track moved references. * Since they are comparably rare, we use a separate method for this. @@ -170,7 +175,7 @@ void blank(); ///< Set record to default state (does not touch the ID/index). - CellId getCellId() const; + const CellId& getCellId() const; }; } #endif diff -Nru openmw-0.37.0/components/esm/loadclas.cpp openmw-0.38.0/components/esm/loadclas.cpp --- openmw-0.37.0/components/esm/loadclas.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadclas.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -22,7 +22,6 @@ "sSpecializationStealth" }; - int& Class::CLDTstruct::getSkill (int index, bool major) { if (index<0 || index>=5) @@ -39,15 +38,21 @@ return mSkills[index][major ? 1 : 0]; } - void Class::load(ESMReader &esm) + void Class::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; @@ -60,15 +65,31 @@ case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing CLDT subrecord"); } - void Class::save(ESMWriter &esm) const + void Class::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNOCString("FNAM", mName); esm.writeHNT("CLDT", mData, 60); esm.writeHNOString("DESC", mDescription); diff -Nru openmw-0.37.0/components/esm/loadclas.hpp openmw-0.38.0/components/esm/loadclas.hpp --- openmw-0.37.0/components/esm/loadclas.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadclas.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -73,8 +73,8 @@ std::string mId, mName, mDescription; CLDTstruct mData; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadclot.cpp openmw-0.38.0/components/esm/loadclot.cpp --- openmw-0.37.0/components/esm/loadclot.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadclot.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,16 +8,23 @@ { unsigned int Clothing::sRecordId = REC_CLOT; - void Clothing::load(ESMReader &esm) + void Clothing::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + mParts.mParts.clear(); + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -40,16 +47,32 @@ case ESM::FourCC<'I','N','D','X'>::value: mParts.add(esm); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing CTDT subrecord"); } - void Clothing::save(ESMWriter &esm) const + void Clothing::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("CTDT", mData, 12); diff -Nru openmw-0.37.0/components/esm/loadclot.hpp openmw-0.38.0/components/esm/loadclot.hpp --- openmw-0.37.0/components/esm/loadclot.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadclot.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -48,8 +48,8 @@ std::string mId, mName, mModel, mIcon, mEnchant, mScript; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadcont.cpp openmw-0.38.0/components/esm/loadcont.cpp --- openmw-0.37.0/components/esm/loadcont.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadcont.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -24,17 +24,24 @@ unsigned int Container::sRecordId = REC_CONT; - void Container::load(ESMReader &esm) + void Container::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + mInventory.mList.clear(); + + bool hasName = false; bool hasWeight = false; bool hasFlags = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -59,18 +66,34 @@ case ESM::FourCC<'N','P','C','O'>::value: mInventory.add(esm); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasWeight) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasWeight && !isDeleted) esm.fail("Missing CNDT subrecord"); - if (!hasFlags) + if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); } - void Container::save(ESMWriter &esm) const + void Container::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("CNDT", mWeight, 4); diff -Nru openmw-0.37.0/components/esm/loadcont.hpp openmw-0.38.0/components/esm/loadcont.hpp --- openmw-0.37.0/components/esm/loadcont.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadcont.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -52,8 +52,8 @@ int mFlags; InventoryList mInventory; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadcrea.cpp openmw-0.38.0/components/esm/loadcrea.cpp --- openmw-0.37.0/components/esm/loadcrea.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadcrea.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,5 +1,7 @@ #include "loadcrea.hpp" +#include + #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" @@ -8,8 +10,10 @@ unsigned int Creature::sRecordId = REC_CREA; - void Creature::load(ESMReader &esm) + void Creature::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + mPersistent = (esm.getRecordFlags() & 0x0400) != 0; mAiPackage.mList.clear(); @@ -19,14 +23,19 @@ mScale = 1.f; mHasAI = false; + + bool hasName = false; bool hasNpdt = false; bool hasFlags = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -72,18 +81,40 @@ case AI_CNDT: mAiPackage.add(esm); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + case ESM::FourCC<'I','N','D','X'>::value: + // seems to occur only in .ESS files, unsure of purpose + int index; + esm.getHT(index); + std::cerr << "Creature::load: Unhandled INDX " << index << std::endl; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasNpdt) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasNpdt && !isDeleted) esm.fail("Missing NPDT subrecord"); - if (!hasFlags) + if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); } - void Creature::save(ESMWriter &esm) const + void Creature::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("CNAM", mOriginal); esm.writeHNOCString("FNAM", mName); diff -Nru openmw-0.37.0/components/esm/loadcrea.hpp openmw-0.38.0/components/esm/loadcrea.hpp --- openmw-0.37.0/components/esm/loadcrea.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadcrea.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -91,7 +91,6 @@ InventoryList mInventory; SpellList mSpells; - bool mHasAI; AIData mAiData; AIPackageList mAiPackage; @@ -99,8 +98,8 @@ const std::vector& getTransport() const; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loaddial.cpp openmw-0.38.0/components/esm/loaddial.cpp --- openmw-0.37.0/components/esm/loaddial.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loaddial.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,121 +10,150 @@ { unsigned int Dialogue::sRecordId = REC_DIAL; -void Dialogue::load(ESMReader &esm) -{ - esm.getSubNameIs("DATA"); - esm.getSubHeader(); - int si = esm.getSubSize(); - if (si == 1) - esm.getT(mType); - else if (si == 4) - { - // These are just markers, their values are not used. - int i; - esm.getT(i); - esm.getHNT(i, "DELE"); - mType = Deleted; - } - else - esm.fail("Unknown sub record size"); -} - -void Dialogue::save(ESMWriter &esm) const -{ - if (mType != Deleted) - esm.writeHNT("DATA", mType); - else - { - esm.writeHNT("DATA", (int)1); - esm.writeHNT("DELE", (int)1); - } -} - -void Dialogue::blank() -{ - mInfo.clear(); -} - -void Dialogue::readInfo(ESMReader &esm, bool merge) -{ - const std::string& id = esm.getHNOString("INAM"); - - if (!merge || mInfo.empty()) - { - ESM::DialInfo info; - info.mId = id; - info.load(esm); - mLookup[id] = mInfo.insert(mInfo.end(), info); - return; - } - - ESM::Dialogue::InfoContainer::iterator it = mInfo.end(); - - std::map::iterator lookup; - - lookup = mLookup.find(id); - - ESM::DialInfo info; - if (lookup != mLookup.end()) - { - it = lookup->second; - - // Merge with existing record. Only the subrecords that are present in - // the new record will be overwritten. - it->load(esm); - info = *it; - - // Since the record merging may have changed the next/prev linked list connection, we need to re-insert the record - mInfo.erase(it); - mLookup.erase(lookup); - } - else + void Dialogue::load(ESMReader &esm, bool &isDeleted) { - info.mId = id; - info.load(esm); + loadId(esm); + loadData(esm, isDeleted); } - if (info.mNext.empty()) - { - mLookup[id] = mInfo.insert(mInfo.end(), info); - return; - } - if (info.mPrev.empty()) + void Dialogue::loadId(ESMReader &esm) { - mLookup[id] = mInfo.insert(mInfo.begin(), info); - return; + mId = esm.getHNString("NAME"); + } + + void Dialogue::loadData(ESMReader &esm, bool &isDeleted) + { + isDeleted = false; + + while (esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::FourCC<'D','A','T','A'>::value: + { + esm.getSubHeader(); + int size = esm.getSubSize(); + if (size == 1) + { + esm.getT(mType); + } + else + { + esm.skip(size); + } + break; + } + case ESM::SREC_DELE: + esm.skipHSub(); + mType = Unknown; + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } + } + } + + void Dialogue::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("NAME", mId); + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + } + else + { + esm.writeHNT("DATA", mType); + } } - lookup = mLookup.find(info.mPrev); - if (lookup != mLookup.end()) + void Dialogue::blank() { - it = lookup->second; - - mLookup[id] = mInfo.insert(++it, info); - return; + mInfo.clear(); } - lookup = mLookup.find(info.mNext); - if (lookup != mLookup.end()) + void Dialogue::readInfo(ESMReader &esm, bool merge) { - it = lookup->second; - - mLookup[id] = mInfo.insert(it, info); - return; - } - - std::cerr << "Failed to insert info " << id << std::endl; -} + ESM::DialInfo info; + info.loadId(esm); -void Dialogue::clearDeletedInfos() -{ - for (InfoContainer::iterator it = mInfo.begin(); it != mInfo.end(); ) - { - if (it->mQuestStatus == DialInfo::QS_Deleted) - it = mInfo.erase(it); + bool isDeleted = false; + if (!merge || mInfo.empty()) + { + info.loadData(esm, isDeleted); + mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.end(), info), isDeleted); + + return; + } + + InfoContainer::iterator it = mInfo.end(); + + LookupMap::iterator lookup; + lookup = mLookup.find(info.mId); + + if (lookup != mLookup.end()) + { + it = lookup->second.first; + + // Merge with existing record. Only the subrecords that are present in + // the new record will be overwritten. + it->loadData(esm, isDeleted); + info = *it; + + // Since the record merging may have changed the next/prev linked list connection, we need to re-insert the record + mInfo.erase(it); + mLookup.erase(lookup); + } else - ++it; + { + info.loadData(esm, isDeleted); + } + + if (info.mNext.empty()) + { + mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.end(), info), isDeleted); + return; + } + if (info.mPrev.empty()) + { + mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.begin(), info), isDeleted); + return; + } + + lookup = mLookup.find(info.mPrev); + if (lookup != mLookup.end()) + { + it = lookup->second.first; + + mLookup[info.mId] = std::make_pair(mInfo.insert(++it, info), isDeleted); + return; + } + + lookup = mLookup.find(info.mNext); + if (lookup != mLookup.end()) + { + it = lookup->second.first; + + mLookup[info.mId] = std::make_pair(mInfo.insert(it, info), isDeleted); + return; + } + + std::cerr << "Failed to insert info " << info.mId << std::endl; + } + + void Dialogue::clearDeletedInfos() + { + LookupMap::const_iterator current = mLookup.begin(); + LookupMap::const_iterator end = mLookup.end(); + for (; current != end; ++current) + { + if (current->second.second) + { + mInfo.erase(current->second.first); + } + } + mLookup.clear(); } } - -} diff -Nru openmw-0.37.0/components/esm/loaddial.hpp openmw-0.38.0/components/esm/loaddial.hpp --- openmw-0.37.0/components/esm/loaddial.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loaddial.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -31,7 +31,7 @@ Greeting = 2, Persuasion = 3, Journal = 4, - Deleted = -1 + Unknown = -1 // Used for deleted dialogues }; std::string mId; @@ -39,17 +39,24 @@ typedef std::list InfoContainer; - typedef std::map LookupMap; + // Parameters: Info ID, (Info iterator, Deleted flag) + typedef std::map > LookupMap; InfoContainer mInfo; // This is only used during the loading phase to speed up DialInfo merging. LookupMap mLookup; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + ///< Loads all sub-records of Dialogue record + void loadId(ESMReader &esm); + ///< Loads NAME sub-record of Dialogue record + void loadData(ESMReader &esm, bool &isDeleted); + ///< Loads all sub-records of Dialogue record, except NAME sub-record - /// Remove all INFOs marked as QS_Deleted from mInfos. + void save(ESMWriter &esm, bool isDeleted = false) const; + + /// Remove all INFOs that are deleted void clearDeletedInfos(); /// Read the next info record diff -Nru openmw-0.37.0/components/esm/loaddoor.cpp openmw-0.38.0/components/esm/loaddoor.cpp --- openmw-0.37.0/components/esm/loaddoor.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loaddoor.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,14 +8,20 @@ { unsigned int Door::sRecordId = REC_DOOR; - void Door::load(ESMReader &esm) + void Door::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -31,14 +37,30 @@ case ESM::FourCC<'A','N','A','M'>::value: mCloseSound = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); } - void Door::save(ESMWriter &esm) const + void Door::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); diff -Nru openmw-0.37.0/components/esm/loaddoor.hpp openmw-0.38.0/components/esm/loaddoor.hpp --- openmw-0.37.0/components/esm/loaddoor.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loaddoor.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -17,8 +17,8 @@ std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadench.cpp openmw-0.38.0/components/esm/loadench.cpp --- openmw-0.37.0/components/esm/loadench.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadench.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,37 +8,58 @@ { unsigned int Enchantment::sRecordId = REC_ENCH; -void Enchantment::load(ESMReader &esm) -{ - mEffects.mList.clear(); - bool hasData = false; - while (esm.hasMoreSubs()) + void Enchantment::load(ESMReader &esm, bool &isDeleted) { - esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + isDeleted = false; + mEffects.mList.clear(); + + bool hasName = false; + bool hasData = false; + while (esm.hasMoreSubs()) { - case ESM::FourCC<'E','N','D','T'>::value: - esm.getHT(mData, 16); - hasData = true; - break; - case ESM::FourCC<'E','N','A','M'>::value: - mEffects.add(esm); - break; - default: - esm.fail("Unknown subrecord"); - break; + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'E','N','D','T'>::value: + esm.getHT(mData, 16); + hasData = true; + break; + case ESM::FourCC<'E','N','A','M'>::value: + mEffects.add(esm); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing ENDT subrecord"); } - if (!hasData) - esm.fail("Missing ENDT subrecord"); -} -void Enchantment::save(ESMWriter &esm) const -{ - esm.writeHNT("ENDT", mData, 16); - mEffects.save(esm); -} + void Enchantment::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + + esm.writeHNT("ENDT", mData, 16); + mEffects.save(esm); + } void Enchantment::blank() { diff -Nru openmw-0.37.0/components/esm/loadench.hpp openmw-0.38.0/components/esm/loadench.hpp --- openmw-0.37.0/components/esm/loadench.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadench.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -42,8 +42,8 @@ ENDTstruct mData; EffectList mEffects; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadfact.cpp openmw-0.38.0/components/esm/loadfact.cpp --- openmw-0.37.0/components/esm/loadfact.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadfact.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -26,69 +26,92 @@ return mSkills[index]; } -void Faction::load(ESMReader &esm) -{ - mReactions.clear(); - for (int i=0;i<10;++i) - mRanks[i].clear(); - - int rankCounter=0; - bool hasData = false; - while (esm.hasMoreSubs()) + void Faction::load(ESMReader &esm, bool &isDeleted) { - esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + isDeleted = false; + + mReactions.clear(); + for (int i=0;i<10;++i) + mRanks[i].clear(); + + int rankCounter = 0; + bool hasName = false; + bool hasData = false; + while (esm.hasMoreSubs()) { - case ESM::FourCC<'F','N','A','M'>::value: - mName = esm.getHString(); - break; - case ESM::FourCC<'R','N','A','M'>::value: - if (rankCounter >= 10) - esm.fail("Rank out of range"); - mRanks[rankCounter++] = esm.getHString(); - break; - case ESM::FourCC<'F','A','D','T'>::value: - esm.getHT(mData, 240); - if (mData.mIsHidden > 1) - esm.fail("Unknown flag!"); - hasData = true; - break; - case ESM::FourCC<'A','N','A','M'>::value: + esm.getSubName(); + switch (esm.retSubName().val) { - std::string faction = esm.getHString(); - int reaction; - esm.getHNT(reaction, "INTV"); - mReactions[faction] = reaction; - break; + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'F','N','A','M'>::value: + mName = esm.getHString(); + break; + case ESM::FourCC<'R','N','A','M'>::value: + if (rankCounter >= 10) + esm.fail("Rank out of range"); + mRanks[rankCounter++] = esm.getHString(); + break; + case ESM::FourCC<'F','A','D','T'>::value: + esm.getHT(mData, 240); + if (mData.mIsHidden > 1) + esm.fail("Unknown flag!"); + hasData = true; + break; + case ESM::FourCC<'A','N','A','M'>::value: + { + std::string faction = esm.getHString(); + int reaction; + esm.getHNT(reaction, "INTV"); + mReactions[faction] = reaction; + break; + } + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; } - default: - esm.fail("Unknown subrecord"); } + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing FADT subrecord"); } - if (!hasData) - esm.fail("Missing FADT subrecord"); -} -void Faction::save(ESMWriter &esm) const -{ - esm.writeHNOCString("FNAM", mName); - for (int i = 0; i < 10; i++) + void Faction::save(ESMWriter &esm, bool isDeleted) const { - if (mRanks[i].empty()) - break; + esm.writeHNCString("NAME", mId); - esm.writeHNString("RNAM", mRanks[i], 32); - } + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } - esm.writeHNT("FADT", mData, 240); + esm.writeHNOCString("FNAM", mName); - for (std::map::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it) - { - esm.writeHNString("ANAM", it->first); - esm.writeHNT("INTV", it->second); + for (int i = 0; i < 10; i++) + { + if (mRanks[i].empty()) + break; + + esm.writeHNString("RNAM", mRanks[i], 32); + } + + esm.writeHNT("FADT", mData, 240); + + for (std::map::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it) + { + esm.writeHNString("ANAM", it->first); + esm.writeHNT("INTV", it->second); + } } -} void Faction::blank() { diff -Nru openmw-0.37.0/components/esm/loadfact.hpp openmw-0.38.0/components/esm/loadfact.hpp --- openmw-0.37.0/components/esm/loadfact.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadfact.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -62,8 +62,8 @@ // Name of faction ranks (may be empty for NPC factions) std::string mRanks[10]; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadglob.cpp openmw-0.38.0/components/esm/loadglob.cpp --- openmw-0.37.0/components/esm/loadglob.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadglob.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,19 +1,42 @@ #include "loadglob.hpp" +#include "esmreader.hpp" +#include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Global::sRecordId = REC_GLOB; - void Global::load (ESMReader &esm) + void Global::load (ESMReader &esm, bool &isDeleted) { - mValue.read (esm, ESM::Variant::Format_Global); + isDeleted = false; + + mId = esm.getHNString ("NAME"); + + if (esm.isNextSub ("DELE")) + { + esm.skipHSub(); + isDeleted = true; + } + else + { + mValue.read (esm, ESM::Variant::Format_Global); + } } - void Global::save (ESMWriter &esm) const + void Global::save (ESMWriter &esm, bool isDeleted) const { - mValue.write (esm, ESM::Variant::Format_Global); + esm.writeHNCString ("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString ("DELE", ""); + } + else + { + mValue.write (esm, ESM::Variant::Format_Global); + } } void Global::blank() diff -Nru openmw-0.37.0/components/esm/loadglob.hpp openmw-0.38.0/components/esm/loadglob.hpp --- openmw-0.37.0/components/esm/loadglob.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadglob.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -24,8 +24,8 @@ std::string mId; Variant mValue; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadgmst.cpp openmw-0.38.0/components/esm/loadgmst.cpp --- openmw-0.37.0/components/esm/loadgmst.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadgmst.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,18 +1,24 @@ #include "loadgmst.hpp" +#include "esmreader.hpp" +#include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int GameSetting::sRecordId = REC_GMST; - void GameSetting::load (ESMReader &esm) + void GameSetting::load (ESMReader &esm, bool &isDeleted) { + isDeleted = false; // GameSetting record can't be deleted now (may be changed in the future) + + mId = esm.getHNString("NAME"); mValue.read (esm, ESM::Variant::Format_Gmst); } - void GameSetting::save (ESMWriter &esm) const + void GameSetting::save (ESMWriter &esm, bool /*isDeleted*/) const { + esm.writeHNCString("NAME", mId); mValue.write (esm, ESM::Variant::Format_Gmst); } diff -Nru openmw-0.37.0/components/esm/loadgmst.hpp openmw-0.38.0/components/esm/loadgmst.hpp --- openmw-0.37.0/components/esm/loadgmst.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadgmst.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -26,7 +26,7 @@ Variant mValue; - void load(ESMReader &esm); + void load(ESMReader &esm, bool &isDeleted); /// \todo remove the get* functions (redundant, since mValue has equivalent functions now). @@ -39,7 +39,7 @@ std::string getString() const; ///< Throwns an exception if GMST is not of type string. - void save(ESMWriter &esm) const; + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadinfo.cpp openmw-0.38.0/components/esm/loadinfo.cpp --- openmw-0.37.0/components/esm/loadinfo.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadinfo.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,156 +8,140 @@ { unsigned int DialInfo::sRecordId = REC_INFO; -void DialInfo::load(ESMReader &esm) -{ - mQuestStatus = QS_None; - mFactionLess = false; - - mPrev = esm.getHNString("PNAM"); - mNext = esm.getHNString("NNAM"); - - // Since there's no way to mark selects as "deleted", we have to clear the SelectStructs from all previous loadings - mSelects.clear(); - - // Not present if deleted - if (esm.isNextSub("DATA")) { - esm.getHT(mData, 12); - } - - if (!esm.hasMoreSubs()) - return; - - // What follows is somewhat spaghetti-ish, but it's worth if for - // an extra speedup. INFO is by far the most common record type. - - // subName is a reference to the original, so it changes whenever - // a new sub name is read. esm.isEmptyOrGetName() will get the - // next name for us, or return true if there are no more records. - esm.getSubName(); - const NAME &subName = esm.retSubName(); - - if (subName.val == REC_ONAM) - { - mActor = esm.getHString(); - if (esm.isEmptyOrGetName()) - return; - } - if (subName.val == REC_RNAM) - { - mRace = esm.getHString(); - if (esm.isEmptyOrGetName()) - return; - } - if (subName.val == REC_CNAM) + void DialInfo::load(ESMReader &esm, bool &isDeleted) { - mClass = esm.getHString(); - if (esm.isEmptyOrGetName()) - return; - } - - if (subName.val == REC_FNAM) - { - mFaction = esm.getHString(); - if (mFaction == "FFFF") - mFactionLess = true; - if (esm.isEmptyOrGetName()) - return; - } - if (subName.val == REC_ANAM) - { - mCell = esm.getHString(); - if (esm.isEmptyOrGetName()) - return; - } - if (subName.val == REC_DNAM) - { - mPcFaction = esm.getHString(); - if (esm.isEmptyOrGetName()) - return; + loadId(esm); + loadData(esm, isDeleted); } - if (subName.val == REC_SNAM) - { - mSound = esm.getHString(); - if (esm.isEmptyOrGetName()) - return; - } - if (subName.val == REC_NAME) - { - mResponse = esm.getHString(); - if (esm.isEmptyOrGetName()) - return; - } - - while (subName.val == REC_SCVR) - { - SelectStruct ss; - - ss.mSelectRule = esm.getHString(); - - ss.mValue.read (esm, Variant::Format_Info); - - mSelects.push_back(ss); - - if (esm.isEmptyOrGetName()) - return; - } - - if (subName.val == REC_BNAM) - { - mResultScript = esm.getHString(); - if (esm.isEmptyOrGetName()) - return; - } - - if (subName.val == REC_QSTN) - mQuestStatus = QS_Name; - else if (subName.val == REC_QSTF) - mQuestStatus = QS_Finished; - else if (subName.val == REC_QSTR) - mQuestStatus = QS_Restart; - else if (subName.val == REC_DELE) - mQuestStatus = QS_Deleted; - else - esm.fail( - "Don't know what to do with " + subName.toString() - + " in INFO " + mId); - - if (mQuestStatus != QS_None) - // Skip rest of record - esm.skipRecord(); -} - -void DialInfo::save(ESMWriter &esm) const -{ - esm.writeHNCString("PNAM", mPrev); - esm.writeHNCString("NNAM", mNext); - esm.writeHNT("DATA", mData, 12); - esm.writeHNOCString("ONAM", mActor); - esm.writeHNOCString("RNAM", mRace); - esm.writeHNOCString("CNAM", mClass); - esm.writeHNOCString("FNAM", mFaction); - esm.writeHNOCString("ANAM", mCell); - esm.writeHNOCString("DNAM", mPcFaction); - esm.writeHNOCString("SNAM", mSound); - esm.writeHNOString("NAME", mResponse); - - for (std::vector::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) - { - esm.writeHNString("SCVR", it->mSelectRule); - it->mValue.write (esm, Variant::Format_Info); - } - - esm.writeHNOString("BNAM", mResultScript); - switch(mQuestStatus) + void DialInfo::loadId(ESMReader &esm) { - case QS_Name: esm.writeHNT("QSTN",'\1'); break; - case QS_Finished: esm.writeHNT("QSTF", '\1'); break; - case QS_Restart: esm.writeHNT("QSTR", '\1'); break; - case QS_Deleted: esm.writeHNT("DELE", '\1'); break; - default: break; + mId = esm.getHNString("INAM"); + } + + void DialInfo::loadData(ESMReader &esm, bool &isDeleted) + { + isDeleted = false; + + mQuestStatus = QS_None; + mFactionLess = false; + + mPrev = esm.getHNString("PNAM"); + mNext = esm.getHNString("NNAM"); + + // Since there's no way to mark selects as "deleted", we have to clear the SelectStructs from all previous loadings + mSelects.clear(); + + while (esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::FourCC<'D','A','T','A'>::value: + esm.getHT(mData, 12); + break; + case ESM::FourCC<'O','N','A','M'>::value: + mActor = esm.getHString(); + break; + case ESM::FourCC<'R','N','A','M'>::value: + mRace = esm.getHString(); + break; + case ESM::FourCC<'C','N','A','M'>::value: + mClass = esm.getHString(); + break; + case ESM::FourCC<'F','N','A','M'>::value: + { + mFaction = esm.getHString(); + if (mFaction == "FFFF") + { + mFactionLess = true; + } + break; + } + case ESM::FourCC<'A','N','A','M'>::value: + mCell = esm.getHString(); + break; + case ESM::FourCC<'D','N','A','M'>::value: + mPcFaction = esm.getHString(); + break; + case ESM::FourCC<'S','N','A','M'>::value: + mSound = esm.getHString(); + break; + case ESM::SREC_NAME: + mResponse = esm.getHString(); + break; + case ESM::FourCC<'S','C','V','R'>::value: + { + SelectStruct ss; + ss.mSelectRule = esm.getHString(); + ss.mValue.read(esm, Variant::Format_Info); + mSelects.push_back(ss); + break; + } + case ESM::FourCC<'B','N','A','M'>::value: + mResultScript = esm.getHString(); + break; + case ESM::FourCC<'Q','S','T','N'>::value: + mQuestStatus = QS_Name; + esm.skipRecord(); + break; + case ESM::FourCC<'Q','S','T','F'>::value: + mQuestStatus = QS_Finished; + esm.skipRecord(); + break; + case ESM::FourCC<'Q','S','T','R'>::value: + mQuestStatus = QS_Restart; + esm.skipRecord(); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } + } + } + + void DialInfo::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("INAM", mId); + esm.writeHNCString("PNAM", mPrev); + esm.writeHNCString("NNAM", mNext); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + + esm.writeHNT("DATA", mData, 12); + esm.writeHNOCString("ONAM", mActor); + esm.writeHNOCString("RNAM", mRace); + esm.writeHNOCString("CNAM", mClass); + esm.writeHNOCString("FNAM", mFaction); + esm.writeHNOCString("ANAM", mCell); + esm.writeHNOCString("DNAM", mPcFaction); + esm.writeHNOCString("SNAM", mSound); + esm.writeHNOString("NAME", mResponse); + + for (std::vector::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) + { + esm.writeHNString("SCVR", it->mSelectRule); + it->mValue.write (esm, Variant::Format_Info); + } + + esm.writeHNOString("BNAM", mResultScript); + + switch(mQuestStatus) + { + case QS_Name: esm.writeHNT("QSTN",'\1'); break; + case QS_Finished: esm.writeHNT("QSTF", '\1'); break; + case QS_Restart: esm.writeHNT("QSTR", '\1'); break; + default: break; + } } -} void DialInfo::blank() { diff -Nru openmw-0.37.0/components/esm/loadinfo.hpp openmw-0.38.0/components/esm/loadinfo.hpp --- openmw-0.37.0/components/esm/loadinfo.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadinfo.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -59,8 +59,7 @@ QS_None = 0, QS_Name = 1, QS_Finished = 2, - QS_Restart = 3, - QS_Deleted + QS_Restart = 3 }; // Rules for when to include this item in the final list of options @@ -106,8 +105,14 @@ REC_DELE = 0x454c4544 }; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + ///< Loads all sub-records of Info record + void loadId(ESMReader &esm); + ///< Loads only Id of Info record (INAM sub-record) + void loadData(ESMReader &esm, bool &isDeleted); + ///< Loads all sub-records of Info record, except INAM sub-record + + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadingr.cpp openmw-0.38.0/components/esm/loadingr.cpp --- openmw-0.37.0/components/esm/loadingr.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadingr.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int Ingredient::sRecordId = REC_INGR; - void Ingredient::load(ESMReader &esm) + void Ingredient::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -33,12 +39,19 @@ case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing IRDT subrecord"); // horrible hack to fix broken data in records @@ -65,8 +78,16 @@ } } - void Ingredient::save(ESMWriter &esm) const + void Ingredient::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("IRDT", mData, 56); diff -Nru openmw-0.37.0/components/esm/loadingr.hpp openmw-0.38.0/components/esm/loadingr.hpp --- openmw-0.37.0/components/esm/loadingr.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadingr.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -31,8 +31,8 @@ IRDTstruct mData; std::string mId, mName, mModel, mIcon, mScript; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadland.cpp openmw-0.38.0/components/esm/loadland.cpp --- openmw-0.37.0/components/esm/loadland.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadland.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,212 +10,251 @@ { unsigned int Land::sRecordId = REC_LAND; -void Land::LandData::save(ESMWriter &esm) const -{ - if (mDataTypes & Land::DATA_VNML) { - esm.writeHNT("VNML", mNormals, sizeof(mNormals)); - } - if (mDataTypes & Land::DATA_VHGT) { - VHGT offsets; - offsets.mHeightOffset = mHeights[0] / HEIGHT_SCALE; - offsets.mUnk1 = mUnk1; - offsets.mUnk2 = mUnk2; - - float prevY = mHeights[0]; - int number = 0; // avoid multiplication - for (int i = 0; i < LAND_SIZE; ++i) { - float diff = (mHeights[number] - prevY) / HEIGHT_SCALE; - offsets.mHeightData[number] = - (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5); - - float prevX = prevY = mHeights[number]; - ++number; - - for (int j = 1; j < LAND_SIZE; ++j) { - diff = (mHeights[number] - prevX) / HEIGHT_SCALE; + void Land::LandData::save(ESMWriter &esm) const + { + if (mDataTypes & Land::DATA_VNML) { + esm.writeHNT("VNML", mNormals, sizeof(mNormals)); + } + if (mDataTypes & Land::DATA_VHGT) { + VHGT offsets; + offsets.mHeightOffset = mHeights[0] / HEIGHT_SCALE; + offsets.mUnk1 = mUnk1; + offsets.mUnk2 = mUnk2; + + float prevY = mHeights[0]; + int number = 0; // avoid multiplication + for (int i = 0; i < LAND_SIZE; ++i) { + float diff = (mHeights[number] - prevY) / HEIGHT_SCALE; offsets.mHeightData[number] = (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5); - prevX = mHeights[number]; + float prevX = prevY = mHeights[number]; ++number; + + for (int j = 1; j < LAND_SIZE; ++j) { + diff = (mHeights[number] - prevX) / HEIGHT_SCALE; + offsets.mHeightData[number] = + (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5); + + prevX = mHeights[number]; + ++number; + } } + esm.writeHNT("VHGT", offsets, sizeof(VHGT)); + } + if (mDataTypes & Land::DATA_WNAM) { + esm.writeHNT("WNAM", mWnam, 81); + } + if (mDataTypes & Land::DATA_VCLR) { + esm.writeHNT("VCLR", mColours, 3*LAND_NUM_VERTS); + } + if (mDataTypes & Land::DATA_VTEX) { + static uint16_t vtex[LAND_NUM_TEXTURES]; + transposeTextureData(mTextures, vtex); + esm.writeHNT("VTEX", vtex, sizeof(vtex)); } - esm.writeHNT("VHGT", offsets, sizeof(VHGT)); } - if (mDataTypes & Land::DATA_WNAM) { - esm.writeHNT("WNAM", mWnam, 81); + + Land::Land() + : mFlags(0) + , mX(0) + , mY(0) + , mPlugin(0) + , mEsm(NULL) + , mDataTypes(0) + , mDataLoaded(false) + , mLandData(NULL) + { } - if (mDataTypes & Land::DATA_VCLR) { - esm.writeHNT("VCLR", mColours, 3*LAND_NUM_VERTS); + + void Land::LandData::transposeTextureData(const uint16_t *in, uint16_t *out) + { + int readPos = 0; //bit ugly, but it works + for ( int y1 = 0; y1 < 4; y1++ ) + for ( int x1 = 0; x1 < 4; x1++ ) + for ( int y2 = 0; y2 < 4; y2++) + for ( int x2 = 0; x2 < 4; x2++ ) + out[(y1*4+y2)*16+(x1*4+x2)] = in[readPos++]; } - if (mDataTypes & Land::DATA_VTEX) { - static uint16_t vtex[LAND_NUM_TEXTURES]; - transposeTextureData(mTextures, vtex); - esm.writeHNT("VTEX", vtex, sizeof(vtex)); + + Land::~Land() + { + delete mLandData; } -} -void Land::LandData::transposeTextureData(const uint16_t *in, uint16_t *out) -{ - int readPos = 0; //bit ugly, but it works - for ( int y1 = 0; y1 < 4; y1++ ) - for ( int x1 = 0; x1 < 4; x1++ ) - for ( int y2 = 0; y2 < 4; y2++) - for ( int x2 = 0; x2 < 4; x2++ ) - out[(y1*4+y2)*16+(x1*4+x2)] = in[readPos++]; -} - -Land::Land() - : mFlags(0) - , mX(0) - , mY(0) - , mPlugin(0) - , mEsm(NULL) - , mDataTypes(0) - , mDataLoaded(false) - , mLandData(NULL) -{ -} + void Land::load(ESMReader &esm, bool &isDeleted) + { + isDeleted = false; -Land::~Land() -{ - delete mLandData; -} + mEsm = &esm; + mPlugin = mEsm->getIndex(); -void Land::load(ESMReader &esm) -{ - mEsm = &esm; - mPlugin = mEsm->getIndex(); + bool hasLocation = false; + bool isLoaded = false; + while (!isLoaded && esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::FourCC<'I','N','T','V'>::value: + esm.getSubHeaderIs(8); + esm.getT(mX); + esm.getT(mY); + hasLocation = true; + break; + case ESM::FourCC<'D','A','T','A'>::value: + esm.getHT(mFlags); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.cacheSubName(); + isLoaded = true; + break; + } + } - // Get the grid location - esm.getSubNameIs("INTV"); - esm.getSubHeaderIs(8); - esm.getT(mX); - esm.getT(mY); + if (!hasLocation) + esm.fail("Missing INTV subrecord"); - esm.getHNT(mFlags, "DATA"); + mContext = esm.getContext(); - // Store the file position - mContext = esm.getContext(); + // Skip the land data here. Load it when the cell is loaded. + while (esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::FourCC<'V','N','M','L'>::value: + esm.skipHSub(); + mDataTypes |= DATA_VNML; + break; + case ESM::FourCC<'V','H','G','T'>::value: + esm.skipHSub(); + mDataTypes |= DATA_VHGT; + break; + case ESM::FourCC<'W','N','A','M'>::value: + esm.skipHSub(); + mDataTypes |= DATA_WNAM; + break; + case ESM::FourCC<'V','C','L','R'>::value: + esm.skipHSub(); + mDataTypes |= DATA_VCLR; + break; + case ESM::FourCC<'V','T','E','X'>::value: + esm.skipHSub(); + mDataTypes |= DATA_VTEX; + break; + default: + esm.fail("Unknown subrecord"); + break; + } + } - // Skip these here. Load the actual data when the cell is loaded. - if (esm.isNextSub("VNML")) - { - esm.skipHSubSize(12675); - mDataTypes |= DATA_VNML; - } - if (esm.isNextSub("VHGT")) - { - esm.skipHSubSize(4232); - mDataTypes |= DATA_VHGT; - } - if (esm.isNextSub("WNAM")) - { - esm.skipHSubSize(81); - mDataTypes |= DATA_WNAM; - } - if (esm.isNextSub("VCLR")) - { - esm.skipHSubSize(12675); - mDataTypes |= DATA_VCLR; + mDataLoaded = 0; + mLandData = NULL; } - if (esm.isNextSub("VTEX")) + + void Land::save(ESMWriter &esm, bool isDeleted) const { - esm.skipHSubSize(512); - mDataTypes |= DATA_VTEX; - } + esm.startSubRecord("INTV"); + esm.writeT(mX); + esm.writeT(mY); + esm.endRecord("INTV"); + + esm.writeHNT("DATA", mFlags); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } - mDataLoaded = 0; - mLandData = NULL; -} + if (mLandData) + { + mLandData->save(esm); + } + } -void Land::save(ESMWriter &esm) const -{ - esm.startSubRecord("INTV"); - esm.writeT(mX); - esm.writeT(mY); - esm.endRecord("INTV"); + void Land::loadData(int flags) const + { + // Try to load only available data + flags = flags & mDataTypes; + // Return if all required data is loaded + if ((mDataLoaded & flags) == flags) { + return; + } + // Create storage if nothing is loaded + if (mLandData == NULL) { + mLandData = new LandData; + mLandData->mDataTypes = mDataTypes; + } + mEsm->restoreContext(mContext); - esm.writeHNT("DATA", mFlags); -} + if (mEsm->isNextSub("VNML")) { + condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); + } -void Land::loadData(int flags) const -{ - // Try to load only available data - flags = flags & mDataTypes; - // Return if all required data is loaded - if ((mDataLoaded & flags) == flags) { - return; - } - // Create storage if nothing is loaded - if (mLandData == NULL) { - mLandData = new LandData; - mLandData->mDataTypes = mDataTypes; - } - mEsm->restoreContext(mContext); - - if (mEsm->isNextSub("VNML")) { - condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); - } - - if (mEsm->isNextSub("VHGT")) { - static VHGT vhgt; - if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { - float rowOffset = vhgt.mHeightOffset; - for (int y = 0; y < LAND_SIZE; y++) { - rowOffset += vhgt.mHeightData[y * LAND_SIZE]; - - mLandData->mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE; - - float colOffset = rowOffset; - for (int x = 1; x < LAND_SIZE; x++) { - colOffset += vhgt.mHeightData[y * LAND_SIZE + x]; - mLandData->mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE; + if (mEsm->isNextSub("VHGT")) { + static VHGT vhgt; + if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { + float rowOffset = vhgt.mHeightOffset; + for (int y = 0; y < LAND_SIZE; y++) { + rowOffset += vhgt.mHeightData[y * LAND_SIZE]; + + mLandData->mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE; + + float colOffset = rowOffset; + for (int x = 1; x < LAND_SIZE; x++) { + colOffset += vhgt.mHeightData[y * LAND_SIZE + x]; + mLandData->mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE; + } } + mLandData->mUnk1 = vhgt.mUnk1; + mLandData->mUnk2 = vhgt.mUnk2; } - mLandData->mUnk1 = vhgt.mUnk1; - mLandData->mUnk2 = vhgt.mUnk2; } - } - if (mEsm->isNextSub("WNAM")) { - condLoad(flags, DATA_WNAM, mLandData->mWnam, 81); - } - if (mEsm->isNextSub("VCLR")) - condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); - if (mEsm->isNextSub("VTEX")) { - static uint16_t vtex[LAND_NUM_TEXTURES]; - if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) { - LandData::transposeTextureData(vtex, mLandData->mTextures); + if (mEsm->isNextSub("WNAM")) { + condLoad(flags, DATA_WNAM, mLandData->mWnam, 81); + } + if (mEsm->isNextSub("VCLR")) + condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); + if (mEsm->isNextSub("VTEX")) { + static uint16_t vtex[LAND_NUM_TEXTURES]; + if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) { + LandData::transposeTextureData(vtex, mLandData->mTextures); + } } } -} -void Land::unloadData() -{ - if (mDataLoaded) + void Land::unloadData() { - delete mLandData; - mLandData = NULL; - mDataLoaded = 0; + if (mDataLoaded) + { + delete mLandData; + mLandData = NULL; + mDataLoaded = 0; + } } -} -bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const -{ - if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) { - mEsm->getHExact(ptr, size); - mDataLoaded |= dataFlag; - return true; - } - mEsm->skipHSubSize(size); - return false; -} + bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const + { + if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) { + mEsm->getHExact(ptr, size); + mDataLoaded |= dataFlag; + return true; + } + mEsm->skipHSubSize(size); + return false; + } -bool Land::isDataLoaded(int flags) const -{ - return (mDataLoaded & flags) == (flags & mDataTypes); -} + bool Land::isDataLoaded(int flags) const + { + return (mDataLoaded & flags) == (flags & mDataTypes); + } Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), diff -Nru openmw-0.37.0/components/esm/loadland.hpp openmw-0.38.0/components/esm/loadland.hpp --- openmw-0.37.0/components/esm/loadland.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadland.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -106,8 +106,8 @@ static void transposeTextureData(const uint16_t *in, uint16_t *out); }; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank() {} diff -Nru openmw-0.37.0/components/esm/loadlevlist.cpp openmw-0.38.0/components/esm/loadlevlist.cpp --- openmw-0.37.0/components/esm/loadlevlist.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadlevlist.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,42 +6,84 @@ namespace ESM { - - void LevelledListBase::load(ESMReader &esm) + void LevelledListBase::load(ESMReader &esm, bool &isDeleted) { - esm.getHNT(mFlags, "DATA"); - esm.getHNT(mChanceNone, "NNAM"); + isDeleted = false; - if (esm.isNextSub("INDX")) - { - int len; - esm.getHT(len); - mList.resize(len); - } - else + bool hasName = false; + bool hasList = false; + while (esm.hasMoreSubs()) { - // Original engine ignores rest of the record, even if there are items following - mList.clear(); - esm.skipRecord(); - return; - } + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'D','A','T','A'>::value: + esm.getHT(mFlags); + break; + case ESM::FourCC<'N','N','A','M'>::value: + esm.getHT(mChanceNone); + break; + case ESM::FourCC<'I','N','D','X'>::value: + { + int length = 0; + esm.getHT(length); + mList.resize(length); - // If this levelled list was already loaded by a previous content file, - // we overwrite the list. Merging lists should probably be left to external tools, - // with the limited amount of information there is in the records, all merging methods - // will be flawed in some way. For a proper fix the ESM format would have to be changed - // to actually track list changes instead of including the whole list for every file - // that does something with that list. + // If this levelled list was already loaded by a previous content file, + // we overwrite the list. Merging lists should probably be left to external tools, + // with the limited amount of information there is in the records, all merging methods + // will be flawed in some way. For a proper fix the ESM format would have to be changed + // to actually track list changes instead of including the whole list for every file + // that does something with that list. + for (size_t i = 0; i < mList.size(); i++) + { + LevelItem &li = mList[i]; + li.mId = esm.getHNString(mRecName); + esm.getHNT(li.mLevel, "INTV"); + } - for (size_t i = 0; i < mList.size(); i++) - { - LevelItem &li = mList[i]; - li.mId = esm.getHNString(mRecName); - esm.getHNT(li.mLevel, "INTV"); + hasList = true; + break; + } + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + { + if (!hasList) + { + // Original engine ignores rest of the record, even if there are items following + mList.clear(); + esm.skipRecord(); + } + else + { + esm.fail("Unknown subrecord"); + } + break; + } + } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); } - void LevelledListBase::save(ESMWriter &esm) const + + void LevelledListBase::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNT("DATA", mFlags); esm.writeHNT("NNAM", mChanceNone); esm.writeHNT("INDX", mList.size()); diff -Nru openmw-0.37.0/components/esm/loadlevlist.hpp openmw-0.38.0/components/esm/loadlevlist.hpp --- openmw-0.37.0/components/esm/loadlevlist.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadlevlist.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -36,8 +36,8 @@ std::vector mList; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadligh.cpp openmw-0.38.0/components/esm/loadligh.cpp --- openmw-0.37.0/components/esm/loadligh.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadligh.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int Light::sRecordId = REC_LIGH; - void Light::load(ESMReader &esm) + void Light::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -36,15 +42,31 @@ case ESM::FourCC<'S','N','A','M'>::value: mSound = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing LHDT subrecord"); } - void Light::save(ESMWriter &esm) const + void Light::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("ITEX", mIcon); diff -Nru openmw-0.37.0/components/esm/loadligh.hpp openmw-0.38.0/components/esm/loadligh.hpp --- openmw-0.37.0/components/esm/loadligh.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadligh.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -47,8 +47,8 @@ std::string mSound, mScript, mModel, mIcon, mName, mId; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadlock.cpp openmw-0.38.0/components/esm/loadlock.cpp --- openmw-0.37.0/components/esm/loadlock.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadlock.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int Lockpick::sRecordId = REC_LOCK; - void Lockpick::load(ESMReader &esm) + void Lockpick::load(ESMReader &esm, bool &isDeleted) { - bool hasData = true; + isDeleted = false; + + bool hasName = false; + bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -33,16 +39,32 @@ case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing LKDT subrecord"); } - void Lockpick::save(ESMWriter &esm) const + void Lockpick::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff -Nru openmw-0.37.0/components/esm/loadlock.hpp openmw-0.38.0/components/esm/loadlock.hpp --- openmw-0.37.0/components/esm/loadlock.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadlock.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,8 +27,8 @@ Data mData; std::string mId, mName, mModel, mIcon, mScript; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadltex.cpp openmw-0.38.0/components/esm/loadltex.cpp --- openmw-0.37.0/components/esm/loadltex.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadltex.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,21 +8,58 @@ { unsigned int LandTexture::sRecordId = REC_LTEX; -void LandTexture::load(ESMReader &esm) -{ - esm.getHNT(mIndex, "INTV"); - mTexture = esm.getHNString("DATA"); -} -void LandTexture::save(ESMWriter &esm) const -{ - esm.writeHNT("INTV", mIndex); - esm.writeHNCString("DATA", mTexture); -} + void LandTexture::load(ESMReader &esm, bool &isDeleted) + { + isDeleted = false; -void LandTexture::blank() -{ - mTexture.clear(); - mIndex = -1; -} + bool hasName = false; + bool hasIndex = false; + while (esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'I','N','T','V'>::value: + esm.getHT(mIndex); + hasIndex = true; + break; + case ESM::FourCC<'D','A','T','A'>::value: + mTexture = esm.getHString(); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } + } + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasIndex) + esm.fail("Missing INTV subrecord"); + } + void LandTexture::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("NAME", mId); + esm.writeHNT("INTV", mIndex); + esm.writeHNCString("DATA", mTexture); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + } + } + void LandTexture::blank() + { + mTexture.clear(); + mIndex = -1; + } } diff -Nru openmw-0.37.0/components/esm/loadltex.hpp openmw-0.38.0/components/esm/loadltex.hpp --- openmw-0.37.0/components/esm/loadltex.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadltex.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -34,11 +34,11 @@ std::string mId, mTexture; int mIndex; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; + void blank(); ///< Set record to default state (does not touch the ID). - - void load(ESMReader &esm); - void save(ESMWriter &esm) const; }; } #endif diff -Nru openmw-0.37.0/components/esm/loadmgef.cpp openmw-0.38.0/components/esm/loadmgef.cpp --- openmw-0.37.0/components/esm/loadmgef.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadmgef.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -189,8 +189,10 @@ { unsigned int MagicEffect::sRecordId = REC_MGEF; -void MagicEffect::load(ESMReader &esm) +void MagicEffect::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future) + esm.getHNT(mIndex, "INDX"); mId = indexToId (mIndex); @@ -209,8 +211,7 @@ while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); @@ -250,7 +251,7 @@ } } } -void MagicEffect::save(ESMWriter &esm) const +void MagicEffect::save(ESMWriter &esm, bool /*isDeleted*/) const { esm.writeHNT("INDX", mIndex); diff -Nru openmw-0.37.0/components/esm/loadmgef.hpp openmw-0.38.0/components/esm/loadmgef.hpp --- openmw-0.37.0/components/esm/loadmgef.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadmgef.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -96,8 +96,8 @@ // sMagicCreature04ID/05ID. int mIndex; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; /// Set record to default state (does not touch the ID/index). void blank(); diff -Nru openmw-0.37.0/components/esm/loadmisc.cpp openmw-0.38.0/components/esm/loadmisc.cpp --- openmw-0.37.0/components/esm/loadmisc.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadmisc.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int Miscellaneous::sRecordId = REC_MISC; - void Miscellaneous::load(ESMReader &esm) + void Miscellaneous::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -33,14 +39,32 @@ case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing MCDT subrecord"); } - void Miscellaneous::save(ESMWriter &esm) const + void Miscellaneous::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("MCDT", mData, 12); diff -Nru openmw-0.37.0/components/esm/loadmisc.hpp openmw-0.38.0/components/esm/loadmisc.hpp --- openmw-0.37.0/components/esm/loadmisc.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadmisc.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -32,8 +32,8 @@ std::string mId, mName, mModel, mIcon, mScript; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadnpc.cpp openmw-0.38.0/components/esm/loadnpc.cpp --- openmw-0.37.0/components/esm/loadnpc.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadnpc.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,24 +8,30 @@ { unsigned int NPC::sRecordId = REC_NPC_; - void NPC::load(ESMReader &esm) + void NPC::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + mPersistent = (esm.getRecordFlags() & 0x0400) != 0; mSpells.mList.clear(); mInventory.mList.clear(); mTransport.mList.clear(); mAiPackage.mList.clear(); + mHasAI = false; + bool hasName = false; bool hasNpdt = false; bool hasFlags = false; - mHasAI = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -92,17 +98,33 @@ case AI_CNDT: mAiPackage.add(esm); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasNpdt) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasNpdt && !isDeleted) esm.fail("Missing NPDT subrecord"); - if (!hasFlags) + if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); } - void NPC::save(ESMWriter &esm) const + void NPC::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNOCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNCString("RNAM", mRace); diff -Nru openmw-0.37.0/components/esm/loadnpc.hpp openmw-0.38.0/components/esm/loadnpc.hpp --- openmw-0.37.0/components/esm/loadnpc.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadnpc.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -130,8 +130,8 @@ // body parts std::string mHair, mHead; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; bool isMale() const; diff -Nru openmw-0.37.0/components/esm/loadpgrd.cpp openmw-0.38.0/components/esm/loadpgrd.cpp --- openmw-0.37.0/components/esm/loadpgrd.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadpgrd.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -32,98 +32,130 @@ { } -void Pathgrid::load(ESMReader &esm) -{ - esm.getHNT(mData, "DATA", 12); - mCell = esm.getHNString("NAME"); + void Pathgrid::load(ESMReader &esm, bool &isDeleted) + { + isDeleted = false; - mPoints.clear(); - mEdges.clear(); + mPoints.clear(); + mEdges.clear(); - // keep track of total connections so we can reserve edge vector size - int edgeCount = 0; + // keep track of total connections so we can reserve edge vector size + int edgeCount = 0; - if (esm.isNextSub("PGRP")) - { - esm.getSubHeader(); - int size = esm.getSubSize(); - // Check that the sizes match up. Size = 16 * s2 (path points) - if (size != static_cast (sizeof(Point) * mData.mS2)) - esm.fail("Path point subrecord size mismatch"); - else + bool hasData = false; + while (esm.hasMoreSubs()) { - int pointCount = mData.mS2; - mPoints.reserve(pointCount); - for (int i = 0; i < pointCount; ++i) + esm.getSubName(); + switch (esm.retSubName().val) { - Point p; - esm.getExact(&p, sizeof(Point)); - mPoints.push_back(p); - edgeCount += p.mConnectionNum; + case ESM::SREC_NAME: + mCell = esm.getHString(); + break; + case ESM::FourCC<'D','A','T','A'>::value: + esm.getHT(mData, 12); + hasData = true; + break; + case ESM::FourCC<'P','G','R','P'>::value: + { + esm.getSubHeader(); + int size = esm.getSubSize(); + // Check that the sizes match up. Size = 16 * s2 (path points) + if (size != static_cast (sizeof(Point) * mData.mS2)) + esm.fail("Path point subrecord size mismatch"); + else + { + int pointCount = mData.mS2; + mPoints.reserve(pointCount); + for (int i = 0; i < pointCount; ++i) + { + Point p; + esm.getExact(&p, sizeof(Point)); + mPoints.push_back(p); + edgeCount += p.mConnectionNum; + } + } + break; + } + case ESM::FourCC<'P','G','R','C'>::value: + { + esm.getSubHeader(); + int size = esm.getSubSize(); + if (size % sizeof(int) != 0) + esm.fail("PGRC size not a multiple of 4"); + else + { + int rawConnNum = size / sizeof(int); + std::vector rawConnections; + rawConnections.reserve(rawConnNum); + for (int i = 0; i < rawConnNum; ++i) + { + int currentValue; + esm.getT(currentValue); + rawConnections.push_back(currentValue); + } + + std::vector::const_iterator rawIt = rawConnections.begin(); + int pointIndex = 0; + mEdges.reserve(edgeCount); + for(PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it, ++pointIndex) + { + unsigned char connectionNum = (*it).mConnectionNum; + for (int i = 0; i < connectionNum; ++i) { + Edge edge; + edge.mV0 = pointIndex; + edge.mV1 = *rawIt; + ++rawIt; + mEdges.push_back(edge); + } + } + } + break; + } + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; } } + + if (!hasData) + esm.fail("Missing DATA subrecord"); } - if (esm.isNextSub("PGRC")) + void Pathgrid::save(ESMWriter &esm, bool isDeleted) const { - esm.getSubHeader(); - int size = esm.getSubSize(); - if (size % sizeof(int) != 0) - esm.fail("PGRC size not a multiple of 4"); - else - { - int rawConnNum = size / sizeof(int); - std::vector rawConnections; - rawConnections.reserve(rawConnNum); - for (int i = 0; i < rawConnNum; ++i) - { - int currentValue; - esm.getT(currentValue); - rawConnections.push_back(currentValue); - } + esm.writeHNCString("NAME", mCell); + esm.writeHNT("DATA", mData, 12); - std::vector::const_iterator rawIt = rawConnections.begin(); - int pointIndex = 0; - mEdges.reserve(edgeCount); - for(PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it, ++pointIndex) - { - unsigned char connectionNum = (*it).mConnectionNum; - for (int i = 0; i < connectionNum; ++i) { - Edge edge; - edge.mV0 = pointIndex; - edge.mV1 = *rawIt; - ++rawIt; - mEdges.push_back(edge); - } - } + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; } - } -} -void Pathgrid::save(ESMWriter &esm) const -{ - esm.writeHNT("DATA", mData, 12); - esm.writeHNCString("NAME", mCell); - if (!mPoints.empty()) - { - esm.startSubRecord("PGRP"); - for (PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it) + if (!mPoints.empty()) { - esm.writeT(*it); + esm.startSubRecord("PGRP"); + for (PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it) + { + esm.writeT(*it); + } + esm.endRecord("PGRP"); } - esm.endRecord("PGRP"); - } - if (!mEdges.empty()) - { - esm.startSubRecord("PGRC"); - for (std::vector::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) + if (!mEdges.empty()) { - esm.writeT(it->mV1); + esm.startSubRecord("PGRC"); + for (std::vector::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) + { + esm.writeT(it->mV1); + } + esm.endRecord("PGRC"); } - esm.endRecord("PGRC"); } -} void Pathgrid::blank() { diff -Nru openmw-0.37.0/components/esm/loadpgrd.hpp openmw-0.38.0/components/esm/loadpgrd.hpp --- openmw-0.37.0/components/esm/loadpgrd.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadpgrd.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -53,8 +53,8 @@ typedef std::vector EdgeList; EdgeList mEdges; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); }; diff -Nru openmw-0.37.0/components/esm/loadprob.cpp openmw-0.38.0/components/esm/loadprob.cpp --- openmw-0.37.0/components/esm/loadprob.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadprob.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int Probe::sRecordId = REC_PROB; - void Probe::load(ESMReader &esm) + void Probe::load(ESMReader &esm, bool &isDeleted) { - bool hasData = true; + isDeleted = false; + + bool hasName = false; + bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -33,16 +39,32 @@ case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing PBDT subrecord"); } - void Probe::save(ESMWriter &esm) const + void Probe::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff -Nru openmw-0.37.0/components/esm/loadprob.hpp openmw-0.38.0/components/esm/loadprob.hpp --- openmw-0.37.0/components/esm/loadprob.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadprob.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,8 +27,8 @@ Data mData; std::string mId, mName, mModel, mIcon, mScript; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadrace.cpp openmw-0.38.0/components/esm/loadrace.cpp --- openmw-0.37.0/components/esm/loadrace.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadrace.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -18,44 +18,65 @@ return static_cast(male ? mMale : mFemale); } -void Race::load(ESMReader &esm) -{ - mPowers.mList.clear(); + void Race::load(ESMReader &esm, bool &isDeleted) + { + isDeleted = false; + + mPowers.mList.clear(); + + bool hasName = false; + bool hasData = false; + while (esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'F','N','A','M'>::value: + mName = esm.getHString(); + break; + case ESM::FourCC<'R','A','D','T'>::value: + esm.getHT(mData, 140); + hasData = true; + break; + case ESM::FourCC<'D','E','S','C'>::value: + mDescription = esm.getHString(); + break; + case ESM::FourCC<'N','P','C','S'>::value: + mPowers.add(esm); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + } + } - bool hasData = false; - while (esm.hasMoreSubs()) + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing RADT subrecord"); + } + void Race::save(ESMWriter &esm, bool isDeleted) const { - esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + esm.writeHNCString("NAME", mId); + + if (isDeleted) { - case ESM::FourCC<'F','N','A','M'>::value: - mName = esm.getHString(); - break; - case ESM::FourCC<'R','A','D','T'>::value: - esm.getHT(mData, 140); - hasData = true; - break; - case ESM::FourCC<'D','E','S','C'>::value: - mDescription = esm.getHString(); - break; - case ESM::FourCC<'N','P','C','S'>::value: - mPowers.add(esm); - break; - default: - esm.fail("Unknown subrecord"); + esm.writeHNCString("DELE", ""); + return; } + + esm.writeHNOCString("FNAM", mName); + esm.writeHNT("RADT", mData, 140); + mPowers.save(esm); + esm.writeHNOString("DESC", mDescription); } - if (!hasData) - esm.fail("Missing RADT subrecord"); -} -void Race::save(ESMWriter &esm) const -{ - esm.writeHNOCString("FNAM", mName); - esm.writeHNT("RADT", mData, 140); - mPowers.save(esm); - esm.writeHNOString("DESC", mDescription); -} void Race::blank() { diff -Nru openmw-0.37.0/components/esm/loadrace.hpp openmw-0.38.0/components/esm/loadrace.hpp --- openmw-0.37.0/components/esm/loadrace.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadrace.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -68,8 +68,8 @@ std::string mId, mName, mDescription; SpellList mPowers; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadregn.cpp openmw-0.38.0/components/esm/loadregn.cpp --- openmw-0.37.0/components/esm/loadregn.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadregn.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,62 +8,102 @@ { unsigned int Region::sRecordId = REC_REGN; -void Region::load(ESMReader &esm) -{ - mName = esm.getHNOString("FNAM"); - - esm.getSubNameIs("WEAT"); - esm.getSubHeader(); - if (esm.getVer() == VER_12) + void Region::load(ESMReader &esm, bool &isDeleted) { - mData.mA = 0; - mData.mB = 0; - esm.getExact(&mData, sizeof(mData) - 2); - } - else if (esm.getVer() == VER_13) - { - // May include the additional two bytes (but not necessarily) - if (esm.getSubSize() == sizeof(mData)) - esm.getExact(&mData, sizeof(mData)); - else + isDeleted = false; + + bool hasName = false; + while (esm.hasMoreSubs()) { - mData.mA = 0; - mData.mB = 0; - esm.getExact(&mData, sizeof(mData)-2); + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'F','N','A','M'>::value: + mName = esm.getHString(); + break; + case ESM::FourCC<'W','E','A','T'>::value: + { + esm.getSubHeader(); + if (esm.getVer() == VER_12) + { + mData.mA = 0; + mData.mB = 0; + esm.getExact(&mData, sizeof(mData) - 2); + } + else if (esm.getVer() == VER_13) + { + // May include the additional two bytes (but not necessarily) + if (esm.getSubSize() == sizeof(mData)) + { + esm.getExact(&mData, sizeof(mData)); + } + else + { + mData.mA = 0; + mData.mB = 0; + esm.getExact(&mData, sizeof(mData)-2); + } + } + else + { + esm.fail("Don't know what to do in this version"); + } + break; + } + case ESM::FourCC<'B','N','A','M'>::value: + mSleepList = esm.getHString(); + break; + case ESM::FourCC<'C','N','A','M'>::value: + esm.getHT(mMapColor); + break; + case ESM::FourCC<'S','N','A','M'>::value: + SoundRef sr; + esm.getHT(sr, 33); + mSoundList.push_back(sr); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); } - else - esm.fail("Don't know what to do in this version"); - mSleepList = esm.getHNOString("BNAM"); + void Region::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("NAME", mId); - esm.getHNT(mMapColor, "CNAM"); + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } - mSoundList.clear(); - while (esm.hasMoreSubs()) - { - SoundRef sr; - esm.getHNT(sr, "SNAM", 33); - mSoundList.push_back(sr); - } -} -void Region::save(ESMWriter &esm) const -{ - esm.writeHNOCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); - if (esm.getVersion() == VER_12) - esm.writeHNT("WEAT", mData, sizeof(mData) - 2); - else - esm.writeHNT("WEAT", mData); + if (esm.getVersion() == VER_12) + esm.writeHNT("WEAT", mData, sizeof(mData) - 2); + else + esm.writeHNT("WEAT", mData); - esm.writeHNOCString("BNAM", mSleepList); + esm.writeHNOCString("BNAM", mSleepList); - esm.writeHNT("CNAM", mMapColor); - for (std::vector::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) - { - esm.writeHNT("SNAM", *it); + esm.writeHNT("CNAM", mMapColor); + for (std::vector::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) + { + esm.writeHNT("SNAM", *it); + } } -} void Region::blank() { diff -Nru openmw-0.37.0/components/esm/loadregn.hpp openmw-0.38.0/components/esm/loadregn.hpp --- openmw-0.37.0/components/esm/loadregn.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadregn.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -45,14 +45,14 @@ WEATstruct mData; int mMapColor; // RGBA - // sleepList refers to a eveled list of creatures you can meet if + // sleepList refers to a leveled list of creatures you can meet if // you sleep outside in this region. std::string mId, mName, mSleepList; std::vector mSoundList; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadrepa.cpp openmw-0.38.0/components/esm/loadrepa.cpp --- openmw-0.37.0/components/esm/loadrepa.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadrepa.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,48 +8,70 @@ { unsigned int Repair::sRecordId = REC_REPA; -void Repair::load(ESMReader &esm) -{ - bool hasData = true; - while (esm.hasMoreSubs()) + void Repair::load(ESMReader &esm, bool &isDeleted) { - esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + isDeleted = false; + + bool hasName = false; + bool hasData = false; + while (esm.hasMoreSubs()) { - case ESM::FourCC<'M','O','D','L'>::value: - mModel = esm.getHString(); - break; - case ESM::FourCC<'F','N','A','M'>::value: - mName = esm.getHString(); - break; - case ESM::FourCC<'R','I','D','T'>::value: - esm.getHT(mData, 16); - hasData = true; - break; - case ESM::FourCC<'S','C','R','I'>::value: - mScript = esm.getHString(); - break; - case ESM::FourCC<'I','T','E','X'>::value: - mIcon = esm.getHString(); - break; - default: - esm.fail("Unknown subrecord"); + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'M','O','D','L'>::value: + mModel = esm.getHString(); + break; + case ESM::FourCC<'F','N','A','M'>::value: + mName = esm.getHString(); + break; + case ESM::FourCC<'R','I','D','T'>::value: + esm.getHT(mData, 16); + hasData = true; + break; + case ESM::FourCC<'S','C','R','I'>::value: + mScript = esm.getHString(); + break; + case ESM::FourCC<'I','T','E','X'>::value: + mIcon = esm.getHString(); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } } + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing RIDT subrecord"); } - if (!hasData) - esm.fail("Missing RIDT subrecord"); -} -void Repair::save(ESMWriter &esm) const -{ - esm.writeHNCString("MODL", mModel); - esm.writeHNOCString("FNAM", mName); + void Repair::save(ESMWriter &esm, bool isDeleted) const + { + esm.writeHNCString("NAME", mId); - esm.writeHNT("RIDT", mData, 16); - esm.writeHNOString("SCRI", mScript); - esm.writeHNOCString("ITEX", mIcon); -} + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + + esm.writeHNCString("MODL", mModel); + esm.writeHNOCString("FNAM", mName); + + esm.writeHNT("RIDT", mData, 16); + esm.writeHNOString("SCRI", mScript); + esm.writeHNOCString("ITEX", mIcon); + } void Repair::blank() { diff -Nru openmw-0.37.0/components/esm/loadrepa.hpp openmw-0.38.0/components/esm/loadrepa.hpp --- openmw-0.37.0/components/esm/loadrepa.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadrepa.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,8 +27,8 @@ Data mData; std::string mId, mName, mModel, mIcon, mScript; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadscpt.cpp openmw-0.38.0/components/esm/loadscpt.cpp --- openmw-0.37.0/components/esm/loadscpt.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadscpt.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,7 +8,6 @@ namespace ESM { - unsigned int Script::sRecordId = REC_SCPT; void Script::loadSCVR(ESMReader &esm) @@ -58,21 +57,25 @@ } } - void Script::load(ESMReader &esm) + void Script::load(ESMReader &esm, bool &isDeleted) { - SCHD data; - esm.getHNT(data, "SCHD", 52); - mData = data.mData; - mId = data.mName.toString(); + isDeleted = false; mVarNames.clear(); + bool hasHeader = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::FourCC<'S','C','H','D'>::value: + SCHD data; + esm.getHT(data, 52); + mData = data.mData; + mId = data.mName.toString(); + hasHeader = true; + break; case ESM::FourCC<'S','C','V','R'>::value: // list of local variables loadSCVR(esm); @@ -85,13 +88,21 @@ case ESM::FourCC<'S','C','T','X'>::value: mScriptText = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } + + if (!hasHeader) + esm.fail("Missing SCHD subrecord"); } - void Script::save(ESMWriter &esm) const + void Script::save(ESMWriter &esm, bool isDeleted) const { std::string varNameString; if (!mVarNames.empty()) @@ -106,6 +117,12 @@ esm.writeHNT("SCHD", data, 52); + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + if (!mVarNames.empty()) { esm.startSubRecord("SCVR"); diff -Nru openmw-0.37.0/components/esm/loadscpt.hpp openmw-0.38.0/components/esm/loadscpt.hpp --- openmw-0.37.0/components/esm/loadscpt.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadscpt.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -50,8 +50,8 @@ /// Script source code std::string mScriptText; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadskil.cpp openmw-0.38.0/components/esm/loadskil.cpp --- openmw-0.37.0/components/esm/loadskil.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadskil.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -129,15 +129,16 @@ unsigned int Skill::sRecordId = REC_SKIL; - void Skill::load(ESMReader &esm) + void Skill::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; // Skill record can't be deleted now (may be changed in the future) + bool hasIndex = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { case ESM::FourCC<'I','N','D','X'>::value: esm.getHT(mIndex); @@ -164,7 +165,7 @@ mId = indexToId (mIndex); } - void Skill::save(ESMWriter &esm) const + void Skill::save(ESMWriter &esm, bool /*isDeleted*/) const { esm.writeHNT("INDX", mIndex); esm.writeHNT("SKDT", mData, 24); diff -Nru openmw-0.37.0/components/esm/loadskil.hpp openmw-0.38.0/components/esm/loadskil.hpp --- openmw-0.37.0/components/esm/loadskil.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadskil.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -78,8 +78,8 @@ static const std::string sIconNames[Length]; static const boost::array sSkillIds; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadsndg.cpp openmw-0.38.0/components/esm/loadsndg.cpp --- openmw-0.37.0/components/esm/loadsndg.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadsndg.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int SoundGenerator::sRecordId = REC_SNDG; - void SoundGenerator::load(ESMReader &esm) + void SoundGenerator::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mType, 4); hasData = true; @@ -27,18 +33,35 @@ case ESM::FourCC<'S','N','A','M'>::value: mSound = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) - esm.fail("Missing DATA"); + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing DATA subrecord"); } - void SoundGenerator::save(ESMWriter &esm) const + void SoundGenerator::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNT("DATA", mType, 4); esm.writeHNOCString("CNAM", mCreature); esm.writeHNOCString("SNAM", mSound); + } void SoundGenerator::blank() diff -Nru openmw-0.37.0/components/esm/loadsndg.hpp openmw-0.38.0/components/esm/loadsndg.hpp --- openmw-0.37.0/components/esm/loadsndg.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadsndg.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -36,8 +36,8 @@ std::string mId, mCreature, mSound; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); }; diff -Nru openmw-0.37.0/components/esm/loadsoun.cpp openmw-0.38.0/components/esm/loadsoun.cpp --- openmw-0.37.0/components/esm/loadsoun.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadsoun.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int Sound::sRecordId = REC_SOUN; - void Sound::load(ESMReader &esm) + void Sound::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'F','N','A','M'>::value: mSound = esm.getHString(); break; @@ -24,16 +30,32 @@ esm.getHT(mData, 3); hasData = true; break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) - esm.fail("Missing DATA"); + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) + esm.fail("Missing DATA subrecord"); } - void Sound::save(ESMWriter &esm) const + void Sound::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNOCString("FNAM", mSound); esm.writeHNT("DATA", mData, 3); } diff -Nru openmw-0.37.0/components/esm/loadsoun.hpp openmw-0.38.0/components/esm/loadsoun.hpp --- openmw-0.37.0/components/esm/loadsoun.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadsoun.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,8 +23,8 @@ SOUNstruct mData; std::string mId, mSound; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadspel.cpp openmw-0.38.0/components/esm/loadspel.cpp --- openmw-0.37.0/components/esm/loadspel.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadspel.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,17 +8,23 @@ { unsigned int Spell::sRecordId = REC_SPEL; - void Spell::load(ESMReader &esm) + void Spell::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + mEffects.mList.clear(); + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t val = esm.retSubName().val; - - switch (val) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; @@ -31,14 +37,32 @@ esm.getHT(s, 24); mEffects.mList.push_back(s); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing SPDT subrecord"); } - void Spell::save(ESMWriter &esm) const + void Spell::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNOCString("FNAM", mName); esm.writeHNT("SPDT", mData, 12); mEffects.save(esm); @@ -51,7 +75,6 @@ mData.mFlags = 0; mName.clear(); - mEffects.mList.clear(); } } diff -Nru openmw-0.37.0/components/esm/loadspel.hpp openmw-0.38.0/components/esm/loadspel.hpp --- openmw-0.37.0/components/esm/loadspel.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadspel.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -45,8 +45,8 @@ std::string mId, mName; EffectList mEffects; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff -Nru openmw-0.37.0/components/esm/loadsscr.cpp openmw-0.38.0/components/esm/loadsscr.cpp --- openmw-0.37.0/components/esm/loadsscr.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadsscr.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,37 +8,51 @@ { unsigned int StartScript::sRecordId = REC_SSCR; - void StartScript::load(ESMReader &esm) + void StartScript::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + bool hasData = false; bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'D','A','T','A'>::value: mData = esm.getHString(); hasData = true; break; - case ESM::FourCC<'N','A','M','E'>::value: - mId = esm.getHString(); - hasName = true; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; break; default: esm.fail("Unknown subrecord"); + break; } } - if (!hasData) - esm.fail("Missing DATA"); + if (!hasName) esm.fail("Missing NAME"); + if (!hasData && !isDeleted) + esm.fail("Missing DATA"); } - void StartScript::save(ESMWriter &esm) const + void StartScript::save(ESMWriter &esm, bool isDeleted) const { - esm.writeHNString("DATA", mData); - esm.writeHNString("NAME", mId); + esm.writeHNCString("NAME", mId); + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + } + else + { + esm.writeHNString("DATA", mData); + } } void StartScript::blank() diff -Nru openmw-0.37.0/components/esm/loadsscr.hpp openmw-0.38.0/components/esm/loadsscr.hpp --- openmw-0.37.0/components/esm/loadsscr.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadsscr.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -27,8 +27,8 @@ std::string mId; // Load a record and add it to the list - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); }; diff -Nru openmw-0.37.0/components/esm/loadstat.cpp openmw-0.38.0/components/esm/loadstat.cpp --- openmw-0.37.0/components/esm/loadstat.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadstat.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,13 +8,47 @@ { unsigned int Static::sRecordId = REC_STAT; - void Static::load(ESMReader &esm) + void Static::load(ESMReader &esm, bool &isDeleted) { - mModel = esm.getHNString("MODL"); + isDeleted = false; + + bool hasName = false; + while (esm.hasMoreSubs()) + { + esm.getSubName(); + switch (esm.retSubName().val) + { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; + case ESM::FourCC<'M','O','D','L'>::value: + mModel = esm.getHString(); + break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; + default: + esm.fail("Unknown subrecord"); + break; + } + } + + if (!hasName) + esm.fail("Missing NAME subrecord"); } - void Static::save(ESMWriter &esm) const + void Static::save(ESMWriter &esm, bool isDeleted) const { - esm.writeHNCString("MODL", mModel); + esm.writeHNCString("NAME", mId); + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + } + else + { + esm.writeHNCString("MODL", mModel); + } } void Static::blank() diff -Nru openmw-0.37.0/components/esm/loadstat.hpp openmw-0.38.0/components/esm/loadstat.hpp --- openmw-0.37.0/components/esm/loadstat.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadstat.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -28,8 +28,8 @@ std::string mId, mModel; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/loadweap.cpp openmw-0.38.0/components/esm/loadweap.cpp --- openmw-0.37.0/components/esm/loadweap.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadweap.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,15 +8,21 @@ { unsigned int Weapon::sRecordId = REC_WEAP; - void Weapon::load(ESMReader &esm) + void Weapon::load(ESMReader &esm, bool &isDeleted) { + isDeleted = false; + + bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); - uint32_t name = esm.retSubName().val; - switch (name) + switch (esm.retSubName().val) { + case ESM::SREC_NAME: + mId = esm.getHString(); + hasName = true; + break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; @@ -36,15 +42,30 @@ case ESM::FourCC<'E','N','A','M'>::value: mEnchant = esm.getHString(); break; + case ESM::SREC_DELE: + esm.skipHSub(); + isDeleted = true; + break; default: esm.fail("Unknown subrecord"); } } - if (!hasData) + + if (!hasName) + esm.fail("Missing NAME subrecord"); + if (!hasData && !isDeleted) esm.fail("Missing WPDT subrecord"); } - void Weapon::save(ESMWriter &esm) const + void Weapon::save(ESMWriter &esm, bool isDeleted) const { + esm.writeHNCString("NAME", mId); + + if (isDeleted) + { + esm.writeHNCString("DELE", ""); + return; + } + esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("WPDT", mData, 32); diff -Nru openmw-0.37.0/components/esm/loadweap.hpp openmw-0.38.0/components/esm/loadweap.hpp --- openmw-0.37.0/components/esm/loadweap.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/loadweap.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -69,8 +69,8 @@ std::string mId, mName, mModel, mIcon, mEnchant, mScript; - void load(ESMReader &esm); - void save(ESMWriter &esm) const; + void load(ESMReader &esm, bool &isDeleted); + void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). diff -Nru openmw-0.37.0/components/esm/objectstate.cpp openmw-0.38.0/components/esm/objectstate.cpp --- openmw-0.37.0/components/esm/objectstate.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esm/objectstate.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,7 +7,8 @@ { mVersion = esm.getFormat(); - mRef.loadData(esm); + bool isDeleted; + mRef.loadData(esm, isDeleted); mHasLocals = 0; esm.getHNOT (mHasLocals, "HLOC"); diff -Nru openmw-0.37.0/components/esmterrain/storage.cpp openmw-0.38.0/components/esmterrain/storage.cpp --- openmw-0.37.0/components/esmterrain/storage.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/esmterrain/storage.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,6 +1,7 @@ #include "storage.hpp" #include +#include #include #include @@ -299,11 +300,17 @@ std::string Storage::getTextureName(UniqueTextureId id) { + static const std::string defaultTexture = "textures\\_land_default.dds"; if (id.first == 0) - return "textures\\_land_default.dds"; // Not sure if the default texture really is hardcoded? + return defaultTexture; // Not sure if the default texture really is hardcoded? // NB: All vtex ids are +1 compared to the ltex ids const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); + if (!ltex) + { + std::cerr << "Unable to find land texture index " << id.first-1 << " in plugin " << id.second << ", using default texture instead" << std::endl; + return defaultTexture; + } // this is needed due to MWs messed up texture handling std::string texture = Misc::ResourceHelpers::correctTexturePath(ltex->mTexture, mVFS); diff -Nru openmw-0.37.0/components/files/configurationmanager.cpp openmw-0.38.0/components/files/configurationmanager.cpp --- openmw-0.37.0/components/files/configurationmanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/files/configurationmanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -60,10 +60,14 @@ loadConfig(mFixedPath.getUserConfigPath(), variables, description); boost::program_options::notify(variables); - loadConfig(mFixedPath.getLocalPath(), variables, description); - boost::program_options::notify(variables); - loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); + // read either local or global config depending on type of installation + bool loaded = loadConfig(mFixedPath.getLocalPath(), variables, description); boost::program_options::notify(variables); + if (!loaded) + { + loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); + boost::program_options::notify(variables); + } mSilent = silent; } @@ -126,7 +130,7 @@ boost::bind(&boost::filesystem::path::empty, _1)), dataDirs.end()); } -void ConfigurationManager::loadConfig(const boost::filesystem::path& path, +bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, boost::program_options::variables_map& variables, boost::program_options::options_description& description) { @@ -145,13 +149,16 @@ if (!mSilent) std::cout << "done." << std::endl; + return true; } else { if (!mSilent) std::cout << "failed." << std::endl; + return false; } } + return false; } const boost::filesystem::path& ConfigurationManager::getGlobalPath() const diff -Nru openmw-0.37.0/components/files/configurationmanager.hpp openmw-0.38.0/components/files/configurationmanager.hpp --- openmw-0.37.0/components/files/configurationmanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/files/configurationmanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -58,7 +58,7 @@ typedef std::tr1::unordered_map TokensMappingContainer; #endif - void loadConfig(const boost::filesystem::path& path, + bool loadConfig(const boost::filesystem::path& path, boost::program_options::variables_map& variables, boost::program_options::options_description& description); diff -Nru openmw-0.37.0/components/files/linuxpath.cpp openmw-0.38.0/components/files/linuxpath.cpp --- openmw-0.37.0/components/files/linuxpath.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/files/linuxpath.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,6 +8,8 @@ #include #include +#include + namespace { @@ -139,7 +141,7 @@ { // Change drive letter to lowercase, so we could use // ~/.wine/dosdevices symlinks - mwpath[0] = tolower(mwpath[0]); + mwpath[0] = Misc::StringUtils::toLower(mwpath[0]); installPath /= homePath; installPath /= ".wine/dosdevices/"; installPath /= mwpath; diff -Nru openmw-0.37.0/components/files/lowlevelfile.cpp openmw-0.38.0/components/files/lowlevelfile.cpp --- openmw-0.37.0/components/files/lowlevelfile.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/files/lowlevelfile.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,6 +8,8 @@ #include #include #include +#include +#include #endif #if FILE_API == FILE_API_STDIO @@ -139,7 +141,7 @@ if (mHandle == -1) { std::ostringstream os; - os << "Failed to open '" << filename << "' for reading."; + os << "Failed to open '" << filename << "' for reading: " << strerror(errno); throw std::runtime_error (os.str ()); } } @@ -160,15 +162,27 @@ size_t oldPosition = ::lseek (mHandle, 0, SEEK_CUR); if (oldPosition == size_t (-1)) - throw std::runtime_error ("A query operation on a file failed."); + { + std::ostringstream os; + os << "An lseek() call failed:" << strerror(errno); + throw std::runtime_error (os.str ()); + } size_t Size = ::lseek (mHandle, 0, SEEK_END); if (Size == size_t (-1)) - throw std::runtime_error ("A query operation on a file failed."); + { + std::ostringstream os; + os << "An lseek() call failed:" << strerror(errno); + throw std::runtime_error (os.str ()); + } if (lseek (mHandle, oldPosition, SEEK_SET) == -1) - throw std::runtime_error ("A query operation on a file failed."); + { + std::ostringstream os; + os << "An lseek() call failed:" << strerror(errno); + throw std::runtime_error (os.str ()); + } return Size; } @@ -178,7 +192,11 @@ assert (mHandle != -1); if (::lseek (mHandle, Position, SEEK_SET) == -1) - throw std::runtime_error ("A seek operation on a file failed."); + { + std::ostringstream os; + os << "An lseek() call failed:" << strerror(errno); + throw std::runtime_error (os.str ()); + } } size_t LowLevelFile::tell () @@ -188,7 +206,11 @@ size_t Position = ::lseek (mHandle, 0, SEEK_CUR); if (Position == size_t (-1)) - throw std::runtime_error ("A query operation on a file failed."); + { + std::ostringstream os; + os << "An lseek() call failed:" << strerror(errno); + throw std::runtime_error (os.str ()); + } return Position; } @@ -200,7 +222,11 @@ int amount = ::read (mHandle, data, size); if (amount == -1) - throw std::runtime_error ("A read operation on a file failed."); + { + std::ostringstream os; + os << "An attempt to read " << size << "bytes failed:" << strerror(errno); + throw std::runtime_error (os.str ()); + } return amount; } diff -Nru openmw-0.37.0/components/files/macospath.cpp openmw-0.38.0/components/files/macospath.cpp --- openmw-0.37.0/components/files/macospath.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/files/macospath.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -7,6 +7,8 @@ #include #include +#include + namespace { boost::filesystem::path getUserHome() @@ -129,7 +131,7 @@ if (!mwpath.empty()) { // Change drive letter to lowercase, so we could use ~/.wine/dosdevice symlinks - mwpath[0] = tolower(mwpath[0]); + mwpath[0] = Misc::StringUtils::toLower(mwpath[0]); installPath /= homePath; installPath /= ".wine/dosdevices/"; installPath /= mwpath; diff -Nru openmw-0.37.0/components/files/multidircollection.cpp openmw-0.38.0/components/files/multidircollection.cpp --- openmw-0.37.0/components/files/multidircollection.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/files/multidircollection.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include + namespace Files { struct NameEqual @@ -28,8 +30,8 @@ for (std::size_t i=0; i #include #include -#include #include #include +#include + namespace Files { typedef std::vector PathContainer; @@ -25,12 +26,11 @@ return left #include #include #include #include +#include + namespace Interpreter{ bool check(const std::string& str, const std::string& escword, unsigned int* i, unsigned int* start) @@ -34,8 +35,7 @@ if(text[i] == eschar) { retval << text.substr(start, i - start); - std::string temp = text.substr(i+1, 100); - transform(temp.begin(), temp.end(), temp.begin(), ::tolower); + std::string temp = Misc::StringUtils::lowerCase(text.substr(i+1, 100)); bool found = false; try diff -Nru openmw-0.37.0/components/loadinglistener/loadinglistener.hpp openmw-0.38.0/components/loadinglistener/loadinglistener.hpp --- openmw-0.37.0/components/loadinglistener/loadinglistener.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/loadinglistener/loadinglistener.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,26 +1,37 @@ #ifndef COMPONENTS_LOADINGLISTENER_H #define COMPONENTS_LOADINGLISTENER_H +#include + namespace Loading { class Listener { public: - virtual void setLabel (const std::string& label) = 0; - - // Use ScopedLoad instead of using these directly - virtual void loadingOn() = 0; - virtual void loadingOff() = 0; + /// Set a text label to show on the loading screen. + /// @param label The label + /// @param important Is the label considered important to show? + /// @note "non-important" labels may not show on screen if the loading process went so fast + /// that the implementation decided not to show a loading screen at all. "important" labels + /// will show in a separate message-box if the loading screen was not shown. + virtual void setLabel (const std::string& label, bool important=false) {} - /// Indicate that some progress has been made, without specifying how much - virtual void indicateProgress () = 0; + /// Start a loading sequence. Must call loadingOff() when done. + /// @note To get the loading screen to actually update, you must call setProgress / increaseProgress periodically. + /// @note It is best to use the ScopedLoad object instead of using loadingOn()/loadingOff() directly, + /// so that the loading is exception safe. + virtual void loadingOn() {} + virtual void loadingOff() {} - virtual void setProgressRange (size_t range) = 0; - virtual void setProgress (size_t value) = 0; - virtual void increaseProgress (size_t increase = 1) = 0; + /// Set the total range of progress (e.g. the number of objects to load). + virtual void setProgressRange (size_t range) {} + /// Set current progress. Valid range is [0, progressRange) + virtual void setProgress (size_t value) {} + /// Increase current progress, default by 1. + virtual void increaseProgress (size_t increase = 1) {} }; - // Used for stopping a loading sequence when the object goes out of scope + /// @brief Used for stopping a loading sequence when the object goes out of scope struct ScopedLoad { ScopedLoad(Listener* l) : mListener(l) { mListener->loadingOn(); } diff -Nru openmw-0.37.0/components/misc/resourcehelpers.cpp openmw-0.38.0/components/misc/resourcehelpers.cpp --- openmw-0.37.0/components/misc/resourcehelpers.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/misc/resourcehelpers.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -51,7 +51,7 @@ std::string prefix2 = topLevelDirectory + '/'; std::string correctedPath = resPath; - Misc::StringUtils::toLower(correctedPath); + Misc::StringUtils::lowerCaseInPlace(correctedPath); // Apparently, leading separators are allowed while (correctedPath.size() && (correctedPath[0] == '/' || correctedPath[0] == '\\')) diff -Nru openmw-0.37.0/components/misc/stringops.cpp openmw-0.38.0/components/misc/stringops.cpp --- openmw-0.37.0/components/misc/stringops.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/misc/stringops.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -#include "stringops.hpp" - -namespace Misc -{ - -std::locale StringUtils::mLocale = std::locale::classic(); - -} diff -Nru openmw-0.37.0/components/misc/stringops.hpp openmw-0.38.0/components/misc/stringops.hpp --- openmw-0.37.0/components/misc/stringops.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/misc/stringops.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -4,22 +4,57 @@ #include #include #include -#include namespace Misc { class StringUtils { - - static std::locale mLocale; struct ci { bool operator()(char x, char y) const { - return std::tolower(x, StringUtils::mLocale) < std::tolower(y, StringUtils::mLocale); + return toLower(x) < toLower(y); } }; public: + + /// Plain and simple locale-unaware toLower. Anything from A to Z is lower-cased, multibyte characters are unchanged. + /// Don't use std::tolower(char, locale&) because that is abysmally slow. + /// Don't use tolower(int) because that depends on global locale. + static char toLower(char c) + { + switch(c) + { + case 'A':return 'a'; + case 'B':return 'b'; + case 'C':return 'c'; + case 'D':return 'd'; + case 'E':return 'e'; + case 'F':return 'f'; + case 'G':return 'g'; + case 'H':return 'h'; + case 'I':return 'i'; + case 'J':return 'j'; + case 'K':return 'k'; + case 'L':return 'l'; + case 'M':return 'm'; + case 'N':return 'n'; + case 'O':return 'o'; + case 'P':return 'p'; + case 'Q':return 'q'; + case 'R':return 'r'; + case 'S':return 's'; + case 'T':return 't'; + case 'U':return 'u'; + case 'V':return 'v'; + case 'W':return 'w'; + case 'X':return 'x'; + case 'Y':return 'y'; + case 'Z':return 'z'; + default:return c; + }; + } + static bool ciLess(const std::string &x, const std::string &y) { return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci()); } @@ -31,7 +66,7 @@ std::string::const_iterator xit = x.begin(); std::string::const_iterator yit = y.begin(); for (; xit != x.end(); ++xit, ++yit) { - if (std::tolower(*xit, mLocale) != std::tolower(*yit, mLocale)) { + if (toLower(*xit) != toLower(*yit)) { return false; } } @@ -45,7 +80,7 @@ for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len) { int res = *xit - *yit; - if(res != 0 && std::tolower(*xit, mLocale) != std::tolower(*yit, mLocale)) + if(res != 0 && toLower(*xit) != toLower(*yit)) return (res > 0) ? 1 : -1; } if(len > 0) @@ -59,17 +94,17 @@ } /// Transforms input string to lower case w/o copy - static std::string &toLower(std::string &inout) { + static void lowerCaseInPlace(std::string &inout) { for (unsigned int i=0; igetFloat(); ambient = nif->getVector3(); diffuse = nif->getVector3(); specular = nif->getVector3(); } -void NiLight::read(NIFStream *nif) -{ - Effect::read(nif); - - nif->getInt(); // 1 - nif->getInt(); // 1? - light.read(nif); -} - void NiTextureEffect::read(NIFStream *nif) { - Effect::read(nif); - - int tmp = nif->getInt(); - if(tmp) nif->getInt(); // always 1? + NiDynamicEffect::read(nif); /* 3 x Vector4 = [1,0,0,0] @@ -52,10 +42,25 @@ void NiTextureEffect::post(NIFFile *nif) { - Effect::post(nif); + NiDynamicEffect::post(nif); texture.post(nif); } +void NiPointLight::read(NIFStream *nif) +{ + NiLight::read(nif); + + constantAttenuation = nif->getFloat(); + linearAttenuation = nif->getFloat(); + quadraticAttenuation = nif->getFloat(); +} +void NiSpotLight::read(NIFStream *nif) +{ + NiPointLight::read(nif); + + cutoff = nif->getFloat(); + exponent = nif->getFloat(); +} } diff -Nru openmw-0.37.0/components/nif/effect.hpp openmw-0.38.0/components/nif/effect.hpp --- openmw-0.37.0/components/nif/effect.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nif/effect.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -29,27 +29,45 @@ namespace Nif { -typedef Node Effect; - -// Used for NiAmbientLight and NiDirectionalLight. Might also work for -// NiPointLight and NiSpotLight? -struct NiLight : Effect +struct NiDynamicEffect : public Node { - struct SLight + void read(NIFStream *nif) { - float dimmer; - osg::Vec3f ambient; - osg::Vec3f diffuse; - osg::Vec3f specular; - - void read(NIFStream *nif); - }; - SLight light; + Node::read(nif); + unsigned int numAffectedNodes = nif->getUInt(); + for (unsigned int i=0; igetUInt(); // ref to another Node + } +}; + +// Used as base for NiAmbientLight, NiDirectionalLight, NiPointLight and NiSpotLight. +struct NiLight : NiDynamicEffect +{ + float dimmer; + osg::Vec3f ambient; + osg::Vec3f diffuse; + osg::Vec3f specular; + + void read(NIFStream *nif); +}; +struct NiPointLight : public NiLight +{ + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; + + void read(NIFStream *nif); +}; + +struct NiSpotLight : public NiPointLight +{ + float cutoff; + float exponent; void read(NIFStream *nif); }; -struct NiTextureEffect : Effect +struct NiTextureEffect : NiDynamicEffect { NiSourceTexturePtr texture; diff -Nru openmw-0.37.0/components/nif/niffile.cpp openmw-0.38.0/components/nif/niffile.cpp --- openmw-0.37.0/components/nif/niffile.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nif/niffile.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -12,9 +12,8 @@ : ver(0) , filename(name) , mUseSkinning(false) - , mStream(stream) { - parse(); + parse(stream); } NIFFile::~NIFFile() @@ -48,6 +47,8 @@ { std::map newFactory; newFactory.insert(makeEntry("NiNode", &construct , RC_NiNode )); + newFactory.insert(makeEntry("NiSwitchNode", &construct , RC_NiSwitchNode )); + newFactory.insert(makeEntry("NiLODNode", &construct , RC_NiLODNode )); newFactory.insert(makeEntry("AvoidNode", &construct , RC_AvoidNode )); newFactory.insert(makeEntry("NiBSParticleNode", &construct , RC_NiBSParticleNode )); newFactory.insert(makeEntry("NiBSAnimationNode", &construct , RC_NiBSAnimationNode )); @@ -80,6 +81,8 @@ newFactory.insert(makeEntry("NiFlipController", &construct , RC_NiFlipController )); newFactory.insert(makeEntry("NiAmbientLight", &construct , RC_NiLight )); newFactory.insert(makeEntry("NiDirectionalLight", &construct , RC_NiLight )); + newFactory.insert(makeEntry("NiPointLight", &construct , RC_NiLight )); + newFactory.insert(makeEntry("NiSpotLight", &construct , RC_NiLight )); newFactory.insert(makeEntry("NiTextureEffect", &construct , RC_NiTextureEffect )); newFactory.insert(makeEntry("NiVertWeightsExtraData", &construct , RC_NiVertWeightsExtraData )); newFactory.insert(makeEntry("NiTextKeyExtraData", &construct , RC_NiTextKeyExtraData )); @@ -111,7 +114,6 @@ ///Make the factory map used for parsing the file static const std::map factories = makeFactory(); -/// Get the file's version in a human readable form std::string NIFFile::printVersion(unsigned int version) { union ver_quad @@ -130,9 +132,9 @@ return stream.str(); } -void NIFFile::parse() +void NIFFile::parse(Files::IStreamPtr stream) { - NIFStream nif (this, mStream); + NIFStream nif (this, stream); // Check the header string std::string head = nif.getVersionString(); diff -Nru openmw-0.37.0/components/nif/niffile.hpp openmw-0.38.0/components/nif/niffile.hpp --- openmw-0.37.0/components/nif/niffile.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nif/niffile.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -35,7 +35,7 @@ bool mUseSkinning; /// Parse the file - void parse(); + void parse(Files::IStreamPtr stream); /// Get the file's version in a human readable form ///\returns A string containing a human readable NIF version number @@ -46,8 +46,6 @@ ///\overload void operator = (NIFFile const &); - Files::IStreamPtr mStream; - public: /// Used if file parsing fails void fail(const std::string &msg) diff -Nru openmw-0.37.0/components/nif/nifkey.hpp openmw-0.38.0/components/nif/nifkey.hpp --- openmw-0.37.0/components/nif/nifkey.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nif/nifkey.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -37,6 +37,9 @@ struct KeyMapT { typedef std::map< float, KeyT > MapType; + typedef T ValueType; + typedef KeyT KeyType; + static const unsigned int sLinearInterpolation = 1; static const unsigned int sQuadraticInterpolation = 2; static const unsigned int sTBCInterpolation = 3; @@ -135,6 +138,11 @@ /*key.mBackwardValue = */(nif.*getValue)(); } + static void readQuadratic(NIFStream &nif, KeyT &key) + { + readValue(nif, key); + } + static void readTBC(NIFStream &nif, KeyT &key) { readValue(nif, key); diff -Nru openmw-0.37.0/components/nif/node.hpp openmw-0.38.0/components/nif/node.hpp --- openmw-0.37.0/components/nif/node.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nif/node.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -9,6 +9,8 @@ #include "controller.hpp" #include "base.hpp" +#include + namespace Nif { @@ -118,10 +120,10 @@ children.read(nif); effects.read(nif); - // Discard tranformations for the root node, otherwise some meshes + // Discard transformations for the root node, otherwise some meshes // occasionally get wrong orientation. Only for NiNode-s for now, but // can be expanded if needed. - if (0 == recIndex) + if (0 == recIndex && !Misc::StringUtils::ciEqual(name, "bip01")) { static_cast(this)->trafo = Nif::Transformation::getIdentity(); } @@ -250,5 +252,41 @@ } }; +// A node used as the base to switch between child nodes, such as for LOD levels. +struct NiSwitchNode : public NiNode +{ + void read(NIFStream *nif) + { + NiNode::read(nif); + nif->getInt(); // unknown + } +}; + +struct NiLODNode : public NiSwitchNode +{ + osg::Vec3f lodCenter; + + struct LODRange + { + float minRange; + float maxRange; + }; + std::vector lodLevels; + + void read(NIFStream *nif) + { + NiSwitchNode::read(nif); + lodCenter = nif->getVector3(); + unsigned int numLodLevels = nif->getUInt(); + for (unsigned int i=0; igetFloat(); + r.maxRange = nif->getFloat(); + lodLevels.push_back(r); + } + } +}; + } // Namespace #endif diff -Nru openmw-0.37.0/components/nif/record.hpp openmw-0.38.0/components/nif/record.hpp --- openmw-0.37.0/components/nif/record.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nif/record.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -36,6 +36,8 @@ { RC_MISSING = 0, RC_NiNode, + RC_NiSwitchNode, + RC_NiLODNode, RC_NiBillboardNode, RC_AvoidNode, RC_NiTriShape, diff -Nru openmw-0.37.0/components/nifbullet/bulletnifloader.cpp openmw-0.38.0/components/nifbullet/bulletnifloader.cpp --- openmw-0.37.0/components/nifbullet/bulletnifloader.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifbullet/bulletnifloader.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -12,12 +12,11 @@ #include -#include "../nif/niffile.hpp" -#include "../nif/node.hpp" -#include "../nif/data.hpp" -#include "../nif/property.hpp" -#include "../nif/controller.hpp" -#include "../nif/extra.hpp" +#include +#include +#include +#include +#include namespace @@ -40,21 +39,6 @@ namespace NifBullet { -// Subclass btBhvTriangleMeshShape to auto-delete the meshInterface -struct TriangleMeshShape : public btBvhTriangleMeshShape -{ - TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression) - : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression) - { - } - - virtual ~TriangleMeshShape() - { - delete getTriangleInfoMap(); - delete m_meshInterface; - } -}; - BulletNifLoader::BulletNifLoader() : mCompoundShape(NULL) , mStaticMesh(NULL) @@ -65,9 +49,9 @@ { } -osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr nif) +osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr nif) { - mShape = new BulletShape; + mShape = new Resource::BulletShape; mCompoundShape = NULL; mStaticMesh = NULL; @@ -126,11 +110,11 @@ { btTransform trans; trans.setIdentity(); - mCompoundShape->addChildShape(trans, new TriangleMeshShape(mStaticMesh,true)); + mCompoundShape->addChildShape(trans, new Resource::TriangleMeshShape(mStaticMesh,true)); } } else if (mStaticMesh) - mShape->mCollisionShape = new TriangleMeshShape(mStaticMesh,true); + mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh,true); return mShape; } @@ -306,7 +290,7 @@ childMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } - TriangleMeshShape* childShape = new TriangleMeshShape(childMesh,true); + Resource::TriangleMeshShape* childShape = new Resource::TriangleMeshShape(childMesh,true); float scale = shape->trafo.scale; const Nif::Node* parent = shape; @@ -335,6 +319,9 @@ const osg::Vec3Array& vertices = *data->vertices; const osg::DrawElementsUShort& triangles = *data->triangles; + mStaticMesh->preallocateVertices(data->vertices->size()); + mStaticMesh->preallocateIndices(data->triangles->size()); + size_t numtris = data->triangles->size(); for(size_t i = 0;i < numtris;i+=3) { @@ -346,93 +333,4 @@ } } -BulletShape::BulletShape() - : mCollisionShape(NULL) -{ - -} - -BulletShape::~BulletShape() -{ - deleteShape(mCollisionShape); -} - -void BulletShape::deleteShape(btCollisionShape* shape) -{ - if(shape!=NULL) - { - if(shape->isCompound()) - { - btCompoundShape* ms = static_cast(shape); - int a = ms->getNumChildShapes(); - for(int i=0; i getChildShape(i)); - } - delete shape; - } -} - -btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape) const -{ - if(shape->isCompound()) - { - btCompoundShape *comp = static_cast(shape); - btCompoundShape *newShape = new btCompoundShape; - - int numShapes = comp->getNumChildShapes(); - for(int i = 0;i < numShapes;++i) - { - btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i)); - btTransform trans = comp->getChildTransform(i); - newShape->addChildShape(trans, child); - } - - return newShape; - } - - if(btBvhTriangleMeshShape* trishape = dynamic_cast(shape)) - { -#if BT_BULLET_VERSION >= 283 - btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(trishape, btVector3(1.f, 1.f, 1.f)); -#else - // work around btScaledBvhTriangleMeshShape bug ( https://code.google.com/p/bullet/issues/detail?id=371 ) in older bullet versions - btTriangleMesh* oldMesh = static_cast(trishape->getMeshInterface()); - btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh); - NifBullet::TriangleMeshShape* newShape = new NifBullet::TriangleMeshShape(newMesh, true); -#endif - return newShape; - } - - if (btBoxShape* boxshape = dynamic_cast(shape)) - { - return new btBoxShape(*boxshape); - } - - throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName()); -} - -btCollisionShape *BulletShape::getCollisionShape() -{ - return mCollisionShape; -} - -osg::ref_ptr BulletShape::makeInstance() -{ - osg::ref_ptr instance (new BulletShapeInstance(this)); - return instance; -} - -BulletShapeInstance::BulletShapeInstance(osg::ref_ptr source) - : BulletShape() - , mSource(source) -{ - mCollisionBoxHalfExtents = source->mCollisionBoxHalfExtents; - mCollisionBoxTranslate = source->mCollisionBoxTranslate; - - mAnimatedShapes = source->mAnimatedShapes; - - if (source->mCollisionShape) - mCollisionShape = duplicateCollisionShape(source->mCollisionShape); -} - } // namespace NifBullet diff -Nru openmw-0.37.0/components/nifbullet/bulletnifloader.hpp openmw-0.38.0/components/nifbullet/bulletnifloader.hpp --- openmw-0.37.0/components/nifbullet/bulletnifloader.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifbullet/bulletnifloader.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -13,6 +13,7 @@ #include #include +#include class btTriangleMesh; class btCompoundShape; @@ -28,48 +29,6 @@ namespace NifBullet { -class BulletShapeInstance; -class BulletShape : public osg::Referenced -{ -public: - BulletShape(); - virtual ~BulletShape(); - - btCollisionShape* mCollisionShape; - - // Used for actors. Note, ideally actors would use a separate loader - as it is - // we have to keep a redundant copy of the actor model around in mCollisionShape, which isn't used. - // For now, use one file <-> one resource for simplicity. - osg::Vec3f mCollisionBoxHalfExtents; - osg::Vec3f mCollisionBoxTranslate; - - // Stores animated collision shapes. If any collision nodes in the NIF are animated, then mCollisionShape - // will be a btCompoundShape (which consists of one or more child shapes). - // In this map, for each animated collision shape, - // we store the node's record index mapped to the child index of the shape in the btCompoundShape. - std::map mAnimatedShapes; - - osg::ref_ptr makeInstance(); - - btCollisionShape* duplicateCollisionShape(btCollisionShape* shape) const; - - btCollisionShape* getCollisionShape(); - -private: - void deleteShape(btCollisionShape* shape); -}; - -// An instance of a BulletShape that may have its own unique scaling set on the mCollisionShape. -// Vertex data is shallow-copied where possible. A ref_ptr to the original shape needs to be held to keep vertex pointers intact. -class BulletShapeInstance : public BulletShape -{ -public: - BulletShapeInstance(osg::ref_ptr source); - -private: - osg::ref_ptr mSource; -}; - /** *Load bulletShape from NIF files. */ @@ -91,7 +50,7 @@ abort(); } - osg::ref_ptr load(const Nif::NIFFilePtr file); + osg::ref_ptr load(const Nif::NIFFilePtr file); private: bool findBoundingBox(const Nif::Node* node, int flags = 0); @@ -106,7 +65,7 @@ btTriangleMesh* mStaticMesh; - osg::ref_ptr mShape; + osg::ref_ptr mShape; }; } diff -Nru openmw-0.37.0/components/nifbullet/bulletshapemanager.cpp openmw-0.38.0/components/nifbullet/bulletshapemanager.cpp --- openmw-0.37.0/components/nifbullet/bulletshapemanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifbullet/bulletshapemanager.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -#include "bulletshapemanager.hpp" - -#include - -#include - -namespace NifBullet -{ - -BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs) - : mVFS(vfs) -{ - -} - -BulletShapeManager::~BulletShapeManager() -{ - -} - -osg::ref_ptr BulletShapeManager::createInstance(const std::string &name) -{ - std::string normalized = name; - mVFS->normalizeFilename(normalized); - - osg::ref_ptr shape; - Index::iterator it = mIndex.find(normalized); - if (it == mIndex.end()) - { - Files::IStreamPtr file = mVFS->get(normalized); - - // TODO: add support for non-NIF formats - - BulletNifLoader loader; - // might be worth sharing NIFFiles with SceneManager in some way - shape = loader.load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized))); - - mIndex[normalized] = shape; - } - else - shape = it->second; - - osg::ref_ptr instance = shape->makeInstance(); - return instance; -} - -} diff -Nru openmw-0.37.0/components/nifbullet/bulletshapemanager.hpp openmw-0.38.0/components/nifbullet/bulletshapemanager.hpp --- openmw-0.37.0/components/nifbullet/bulletshapemanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifbullet/bulletshapemanager.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -#ifndef OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H -#define OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H - -#include -#include - -#include - -namespace VFS -{ - class Manager; -} - -namespace Resource -{ - class SceneManager; -} - -namespace NifBullet -{ - - class BulletShape; - class BulletShapeInstance; - - class BulletShapeManager - { - public: - BulletShapeManager(const VFS::Manager* vfs); - ~BulletShapeManager(); - - osg::ref_ptr createInstance(const std::string& name); - - private: - const VFS::Manager* mVFS; - - typedef std::map > Index; - Index mIndex; - }; - -} - -#endif diff -Nru openmw-0.37.0/components/nifosg/controller.cpp openmw-0.38.0/components/nifosg/controller.cpp --- openmw-0.37.0/components/nifosg/controller.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifosg/controller.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -86,56 +86,23 @@ KeyframeController::KeyframeController(const Nif::NiKeyframeData *data) : mRotations(data->mRotations) - , mXRotations(data->mXRotations) - , mYRotations(data->mYRotations) - , mZRotations(data->mZRotations) - , mTranslations(data->mTranslations) - , mScales(data->mScales) + , mXRotations(data->mXRotations, 0.f) + , mYRotations(data->mYRotations, 0.f) + , mZRotations(data->mZRotations, 0.f) + , mTranslations(data->mTranslations, osg::Vec3f()) + , mScales(data->mScales, 1.f) { } -osg::Quat KeyframeController::interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time) -{ - if(time <= keys.begin()->first) - return keys.begin()->second.mValue; - - Nif::QuaternionKeyMap::MapType::const_iterator it = keys.lower_bound(time); - if (it != keys.end()) - { - float aTime = it->first; - const Nif::QuaternionKey* aKey = &it->second; - - assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function - - Nif::QuaternionKeyMap::MapType::const_iterator last = --it; - float aLastTime = last->first; - const Nif::QuaternionKey* aLastKey = &last->second; - - float a = (time - aLastTime) / (aTime - aLastTime); - - osg::Quat v1 = aLastKey->mValue; - osg::Quat v2 = aKey->mValue; - // don't take the long path - if (v1.x()*v2.x() + v1.y()*v2.y() + v1.z()*v2.z() + v1.w()*v2.w() < 0) // dotProduct(v1,v2) - v1 = -v1; - - osg::Quat result; - result.slerp(a, v1, v2); - return result; - } - else - return keys.rbegin()->second.mValue; -} - osg::Quat KeyframeController::getXYZRotation(float time) const { float xrot = 0, yrot = 0, zrot = 0; - if (mXRotations.get()) - xrot = interpKey(mXRotations->mKeys, time); - if (mYRotations.get()) - yrot = interpKey(mYRotations->mKeys, time); - if (mZRotations.get()) - zrot = interpKey(mZRotations->mKeys, time); + if (!mXRotations.empty()) + xrot = mXRotations.interpKey(time); + if (!mYRotations.empty()) + yrot = mYRotations.interpKey(time); + if (!mZRotations.empty()) + zrot = mZRotations.interpKey(time); osg::Quat xr(xrot, osg::Vec3f(1,0,0)); osg::Quat yr(yrot, osg::Vec3f(0,1,0)); osg::Quat zr(zrot, osg::Vec3f(0,0,1)); @@ -144,8 +111,8 @@ osg::Vec3f KeyframeController::getTranslation(float time) const { - if(mTranslations.get() && mTranslations->mKeys.size() > 0) - return interpKey(mTranslations->mKeys, time); + if(!mTranslations.empty()) + return mTranslations.interpKey(time); return osg::Vec3f(); } @@ -162,12 +129,12 @@ Nif::Matrix3& rot = userdata->mRotationScale; bool setRot = false; - if(mRotations.get() && !mRotations->mKeys.empty()) + if(!mRotations.empty()) { - mat.setRotate(interpKey(mRotations->mKeys, time)); + mat.setRotate(mRotations.interpKey(time)); setRot = true; } - else if (mXRotations.get() || mYRotations.get() || mZRotations.get()) + else if (!mXRotations.empty() || !mYRotations.empty() || !mZRotations.empty()) { mat.setRotate(getXYZRotation(time)); setRot = true; @@ -186,15 +153,15 @@ rot.mValues[i][j] = mat(j,i); // NB column/row major difference float& scale = userdata->mScale; - if(mScales.get() && !mScales->mKeys.empty()) - scale = interpKey(mScales->mKeys, time); + if(!mScales.empty()) + scale = mScales.interpKey(time); for (int i=0;i<3;++i) for (int j=0;j<3;++j) mat(i,j) *= scale; - if(mTranslations.get() && !mTranslations->mKeys.empty()) - mat.setTrans(interpKey(mTranslations->mKeys, time)); + if(!mTranslations.empty()) + mat.setTrans(mTranslations.interpKey(time)); trans->setMatrix(mat); } @@ -216,33 +183,35 @@ GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data) { for (unsigned int i=0; imMorphs.size(); ++i) - mKeyFrames.push_back(data->mMorphs[i].mKeyFrames); + mKeyFrames.push_back(FloatInterpolator(data->mMorphs[i].mKeyFrames)); } void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable) { - osgAnimation::MorphGeometry* morphGeom = dynamic_cast(drawable); - if (morphGeom) + osgAnimation::MorphGeometry* morphGeom = static_cast(drawable); + if (hasInput()) { - if (hasInput()) + if (mKeyFrames.size() <= 1) + return; + float input = getInputValue(nv); + int i = 0; + for (std::vector::iterator it = mKeyFrames.begin()+1; it != mKeyFrames.end(); ++it,++i) { - if (mKeyFrames.size() <= 1) - return; - float input = getInputValue(nv); - int i = 0; - for (std::vector::iterator it = mKeyFrames.begin()+1; it != mKeyFrames.end(); ++it,++i) - { - float val = 0; - if (!(*it)->mKeys.empty()) - val = interpKey((*it)->mKeys, input); - val = std::max(0.f, std::min(1.f, val)); + float val = 0; + if (!(*it).empty()) + val = it->interpKey(input); + val = std::max(0.f, std::min(1.f, val)); - morphGeom->setWeight(i, val); + osgAnimation::MorphGeometry::MorphTarget& target = morphGeom->getMorphTarget(i); + if (target.getWeight() != val) + { + target.setWeight(val); + morphGeom->dirty(); } } - - morphGeom->transformSoftwareMethod(); } + + // morphGeometry::transformSoftwareMethod() done in cull callback i.e. only for visible morph geometries } UVController::UVController() @@ -250,10 +219,10 @@ } UVController::UVController(const Nif::NiUVData *data, std::set textureUnits) - : mUTrans(data->mKeyList[0]) - , mVTrans(data->mKeyList[1]) - , mUScale(data->mKeyList[2]) - , mVScale(data->mKeyList[3]) + : mUTrans(data->mKeyList[0], 0.f) + , mVTrans(data->mKeyList[1], 0.f) + , mUScale(data->mKeyList[2], 1.f) + , mVScale(data->mKeyList[3], 1.f) , mTextureUnits(textureUnits) { } @@ -280,16 +249,16 @@ if (hasInput()) { float value = getInputValue(nv); - float uTrans = interpKey(mUTrans->mKeys, value, 0.0f); - float vTrans = interpKey(mVTrans->mKeys, value, 0.0f); - float uScale = interpKey(mUScale->mKeys, value, 1.0f); - float vScale = interpKey(mVScale->mKeys, value, 1.0f); + float uTrans = mUTrans.interpKey(value); + float vTrans = mVTrans.interpKey(value); + float uScale = mUScale.interpKey(value); + float vScale = mVScale.interpKey(value); osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1); mat.setTrans(uTrans, vTrans, 0); // setting once is enough because all other texture units share the same TexMat (see setDefaults). - if (mTextureUnits.size()) + if (!mTextureUnits.empty()) { osg::TexMat* texMat = static_cast(stateset->getTextureAttribute(*mTextureUnits.begin(), osg::StateAttribute::TEXMAT)); texMat->setMatrix(mat); @@ -338,7 +307,7 @@ } AlphaController::AlphaController(const Nif::NiFloatData *data) - : mData(data->mKeyList) + : mData(data->mKeyList, 1.f) { } @@ -348,7 +317,7 @@ } AlphaController::AlphaController(const AlphaController ©, const osg::CopyOp ©op) - : StateSetUpdater(copy, copyop), Controller(copy), ValueInterpolator() + : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) { } @@ -364,7 +333,7 @@ { if (hasInput()) { - float value = interpKey(mData->mKeys, getInputValue(nv)); + float value = mData.interpKey(getInputValue(nv)); osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); diffuse.a() = value; @@ -373,7 +342,7 @@ } MaterialColorController::MaterialColorController(const Nif::NiPosData *data) - : mData(data->mKeyList) + : mData(data->mKeyList, osg::Vec3f(1,1,1)) { } @@ -398,7 +367,7 @@ { if (hasInput()) { - osg::Vec3f value = interpKey(mData->mKeys, getInputValue(nv)); + osg::Vec3f value = mData.interpKey(getInputValue(nv)); osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); diffuse.set(value.x(), value.y(), value.z(), diffuse.a()); @@ -466,10 +435,9 @@ { if (hasInput()) { - osgParticle::ParticleProcessor* emitter = dynamic_cast(node); + osgParticle::ParticleProcessor* emitter = static_cast(node); float time = getInputValue(nv); - if (emitter) - emitter->setEnabled(time >= mEmitStart && time < mEmitStop); + emitter->setEnabled(time >= mEmitStart && time < mEmitStop); } traverse(node, nv); } diff -Nru openmw-0.37.0/components/nifosg/controller.hpp openmw-0.38.0/components/nifosg/controller.hpp --- openmw-0.37.0/components/nifosg/controller.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifosg/controller.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -42,38 +42,118 @@ namespace NifOsg { + // interpolation of keyframes + template class ValueInterpolator { - protected: - template - T interpKey (const std::map< float, Nif::KeyT >& keys, float time, T defaultValue = T()) const + public: + typedef typename MapT::ValueType ValueT; + + ValueInterpolator() + : mDefaultVal(ValueT()) + { + } + + ValueInterpolator(boost::shared_ptr keys, ValueT defaultVal = ValueT()) + : mKeys(keys) + , mDefaultVal(defaultVal) + { + if (keys) + { + mLastLowKey = mKeys->mKeys.end(); + mLastHighKey = mKeys->mKeys.end(); + } + } + + ValueT interpKey(float time) const { - if (keys.size() == 0) - return defaultValue; + if (empty()) + return mDefaultVal; + + const typename MapT::MapType & keys = mKeys->mKeys; if(time <= keys.begin()->first) return keys.begin()->second.mValue; - typename std::map< float, Nif::KeyT >::const_iterator it = keys.lower_bound(time); + // retrieve the current position in the map, optimized for the most common case + // where time moves linearly along the keyframe track + typename MapT::MapType::const_iterator it = mLastHighKey; + if (mLastHighKey != keys.end()) + { + if (time > mLastHighKey->first) + { + // try if we're there by incrementing one + ++mLastLowKey; + ++mLastHighKey; + it = mLastHighKey; + } + if (mLastHighKey == keys.end() || (time < mLastLowKey->first || time > mLastHighKey->first)) + it = keys.lower_bound(time); // still not there, reorient by performing lower_bound check on the whole map + } + else + it = keys.lower_bound(time); + + // now do the actual interpolation if (it != keys.end()) { float aTime = it->first; - const Nif::KeyT* aKey = &it->second; + const typename MapT::KeyType* aKey = &it->second; + + // cache for next time + mLastHighKey = it; assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function - typename std::map< float, Nif::KeyT >::const_iterator last = --it; + typename MapT::MapType::const_iterator last = --it; + mLastLowKey = last; float aLastTime = last->first; - const Nif::KeyT* aLastKey = &last->second; + const typename MapT::KeyType* aLastKey = &last->second; float a = (time - aLastTime) / (aTime - aLastTime); - return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a); + + return InterpolationFunc()(aLastKey->mValue, aKey->mValue, a); } else return keys.rbegin()->second.mValue; } + + bool empty() const + { + return !mKeys || mKeys->mKeys.empty(); + } + + private: + mutable typename MapT::MapType::const_iterator mLastLowKey; + mutable typename MapT::MapType::const_iterator mLastHighKey; + + boost::shared_ptr mKeys; + + ValueT mDefaultVal; + }; + + struct LerpFunc + { + template + inline ValueType operator()(const ValueType& a, const ValueType& b, float fraction) + { + return a + ((b - a) * fraction); + } }; + struct QuaternionSlerpFunc + { + inline osg::Quat operator()(const osg::Quat& a, const osg::Quat& b, float fraction) + { + osg::Quat result; + result.slerp(fraction, a, b); + return result; + } + }; + + typedef ValueInterpolator QuaternionInterpolator; + typedef ValueInterpolator FloatInterpolator; + typedef ValueInterpolator Vec3Interpolator; + class ControllerFunction : public SceneUtil::ControllerFunction { private: @@ -97,7 +177,8 @@ virtual float getMaximum() const; }; - class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller, public ValueInterpolator + /// Must be set on an osgAnimation::MorphGeometry. + class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller { public: GeomMorpherController(const Nif::NiMorphData* data); @@ -109,10 +190,10 @@ virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable); private: - std::vector mKeyFrames; + std::vector mKeyFrames; }; - class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller, public ValueInterpolator + class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller { public: KeyframeController(const Nif::NiKeyframeData *data); @@ -126,23 +207,19 @@ virtual void operator() (osg::Node*, osg::NodeVisitor*); private: - Nif::QuaternionKeyMapPtr mRotations; - - Nif::FloatKeyMapPtr mXRotations; - Nif::FloatKeyMapPtr mYRotations; - Nif::FloatKeyMapPtr mZRotations; - - Nif::Vector3KeyMapPtr mTranslations; - Nif::FloatKeyMapPtr mScales; + QuaternionInterpolator mRotations; - using ValueInterpolator::interpKey; + FloatInterpolator mXRotations; + FloatInterpolator mYRotations; + FloatInterpolator mZRotations; - osg::Quat interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time); + Vec3Interpolator mTranslations; + FloatInterpolator mScales; osg::Quat getXYZRotation(float time) const; }; - class UVController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller, public ValueInterpolator + class UVController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller { public: UVController(); @@ -155,10 +232,10 @@ virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv); private: - Nif::FloatKeyMapPtr mUTrans; - Nif::FloatKeyMapPtr mVTrans; - Nif::FloatKeyMapPtr mUScale; - Nif::FloatKeyMapPtr mVScale; + FloatInterpolator mUTrans; + FloatInterpolator mVTrans; + FloatInterpolator mUScale; + FloatInterpolator mVScale; std::set mTextureUnits; }; @@ -179,10 +256,10 @@ virtual void operator() (osg::Node* node, osg::NodeVisitor* nv); }; - class AlphaController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller, public ValueInterpolator + class AlphaController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller { private: - Nif::FloatKeyMapPtr mData; + FloatInterpolator mData; public: AlphaController(const Nif::NiFloatData *data); @@ -196,10 +273,10 @@ META_Object(NifOsg, AlphaController) }; - class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller, public ValueInterpolator + class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller { private: - Nif::Vector3KeyMapPtr mData; + Vec3Interpolator mData; public: MaterialColorController(const Nif::NiPosData *data); diff -Nru openmw-0.37.0/components/nifosg/nifloader.cpp openmw-0.38.0/components/nifosg/nifloader.cpp --- openmw-0.37.0/components/nifosg/nifloader.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifosg/nifloader.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,6 +6,7 @@ #include #include #include +#include // resource #include @@ -99,21 +100,20 @@ virtual void traverse(osg::NodeVisitor& nv) { - const osg::FrameStamp* stamp = nv.getFrameStamp(); - if (!stamp || nv.getTraversalMode() != osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) + if (nv.getTraversalMode() != osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN && nv.getVisitorType() != osg::NodeVisitor::UPDATE_VISITOR) osg::Group::traverse(nv); else { for (unsigned int i=0; igetFrameNumber()%2) + if (i%2 == nv.getTraversalNumber()%2) getChild(i)->accept(nv); } } } }; - // NodeCallback used to have a transform always oriented towards the camera. Can have translation and scale + // NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale // set just like a regular MatrixTransform, but the rotation set will be overridden in order to face the camera. // Must be set as a cull callback. class BillboardCallback : public osg::NodeCallback @@ -132,8 +132,7 @@ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = dynamic_cast(nv); - osg::MatrixTransform* billboardNode = dynamic_cast(node); - if (billboardNode && cv) + if (node && cv) { osg::Matrix modelView = *cv->getModelViewMatrix(); @@ -181,9 +180,9 @@ if (!geom) return false; - if (mLastFrameNumber == nv->getFrameStamp()->getFrameNumber()) + if (mLastFrameNumber == nv->getTraversalNumber()) return false; - mLastFrameNumber = nv->getFrameStamp()->getFrameNumber(); + mLastFrameNumber = nv->getTraversalNumber(); geom->transformSoftwareMethod(); return false; @@ -255,7 +254,7 @@ nextpos = std::distance(str.begin(), ++last); } std::string result = str.substr(pos, nextpos-pos); - textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); + textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::lowerCase(result))); pos = nextpos; } @@ -424,6 +423,20 @@ // Need to make sure that won't break transparency sorting. Check what the original engine is doing? } + osg::ref_ptr handleLodNode(const Nif::NiLODNode* niLodNode) + { + osg::ref_ptr lod (new osg::LOD); + lod->setCenterMode(osg::LOD::USER_DEFINED_CENTER); + lod->setCenter(niLodNode->lodCenter); + for (unsigned int i=0; ilodLevels.size(); ++i) + { + const Nif::NiLODNode::LODRange& range = niLodNode->lodLevels[i]; + lod->setRange(i, range.minRange, range.maxRange); + } + lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); + return lod; + } + osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::TextureManager* textureManager, std::vector boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL) { @@ -555,6 +568,15 @@ // Optimization pass optimize(nifNode, node, skipMeshes); + + if (nifNode->recType == Nif::RC_NiLODNode) + { + const Nif::NiLODNode* niLodNode = static_cast(nifNode); + osg::ref_ptr lod = handleLodNode(niLodNode); + node->addChild(lod); // unsure if LOD should be above or below this node's transform + node = lod; + } + const Nif::NiNode *ninode = dynamic_cast(nifNode); if(ninode) { @@ -865,7 +887,7 @@ // This seems to be true for all NIF files in the game that I've checked, suggesting that NIFs work similar to OSG with regards to update order. // If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster. - FindRecIndexVisitor find (partctrl->emitter->recIndex); + FindGroupByRecIndex find (partctrl->emitter->recIndex); rootNode->accept(find); if (!find.mFound) { @@ -941,7 +963,9 @@ int uvSet = *it; if (uvSet >= (int)data->uvlist.size()) { - std::cerr << "Warning: using an undefined UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename << std::endl; + std::cerr << "Warning: out of bounds UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename << std::endl; + if (data->uvlist.size()) + geometry->setTexCoordArray(textureStage, data->uvlist[0]); continue; } @@ -1342,7 +1366,7 @@ int wrapT = (clamp) & 0x1; int wrapS = (clamp >> 1) & 0x1; - osg::Texture2D* texture2d = textureManager->getTexture2D(filename, + osg::ref_ptr texture2d = textureManager->getTexture2D(filename, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); diff -Nru openmw-0.37.0/components/nifosg/nifloader.hpp openmw-0.38.0/components/nifosg/nifloader.hpp --- openmw-0.37.0/components/nifosg/nifloader.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifosg/nifloader.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -37,11 +37,20 @@ }; - class KeyframeHolder : public osg::Referenced + class KeyframeHolder : public osg::Object { public: + KeyframeHolder() {} + KeyframeHolder(const KeyframeHolder& copy, const osg::CopyOp& copyop) + : mTextKeys(copy.mTextKeys) + , mKeyframeControllers(copy.mKeyframeControllers) + { + } + TextKeyMap mTextKeys; + META_Object(OpenMW, KeyframeHolder) + typedef std::map > KeyframeControllerMap; KeyframeControllerMap mKeyframeControllers; }; diff -Nru openmw-0.37.0/components/nifosg/particle.cpp openmw-0.38.0/components/nifosg/particle.cpp --- openmw-0.37.0/components/nifosg/particle.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifosg/particle.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,7 +23,7 @@ { // For some reason the osgParticle constructor doesn't copy the particles for (int i=0;isetMatrix(mat); } traverse(node,nv); @@ -127,7 +127,7 @@ } ParticleColorAffector::ParticleColorAffector(const Nif::NiColorData *clrdata) - : mData(*clrdata) + : mData(clrdata->mKeyMap, osg::Vec4f(1,1,1,1)) { } @@ -145,7 +145,7 @@ void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */) { float time = static_cast(particle->getAge()/particle->getLifeTime()); - osg::Vec4f color = interpKey(mData.mKeyMap->mKeys, time, osg::Vec4f(1,1,1,1)); + osg::Vec4f color = mData.interpKey(time); particle->setColorRange(osgParticle::rangev4(color, color)); } @@ -276,7 +276,7 @@ int randomRecIndex = mTargets[(std::rand() / (static_cast(RAND_MAX)+1.0)) * mTargets.size()]; // we could use a map here for faster lookup - FindRecIndexVisitor visitor(randomRecIndex); + FindGroupByRecIndex visitor(randomRecIndex); getParent(0)->accept(visitor); if (!visitor.mFound) @@ -306,21 +306,25 @@ } } -FindRecIndexVisitor::FindRecIndexVisitor(int recIndex) +FindGroupByRecIndex::FindGroupByRecIndex(int recIndex) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mFound(NULL) , mRecIndex(recIndex) { } -void FindRecIndexVisitor::apply(osg::Node &searchNode) +void FindGroupByRecIndex::apply(osg::Node &searchNode) { if (searchNode.getUserDataContainer() && searchNode.getUserDataContainer()->getNumUserObjects()) { NodeUserData* holder = dynamic_cast(searchNode.getUserDataContainer()->getUserObject(0)); if (holder && holder->mIndex == mRecIndex) { - mFound = static_cast(&searchNode); + osg::Group* group = searchNode.asGroup(); + if (!group) + group = searchNode.getParent(0); + + mFound = group; mFoundPath = getNodePath(); return; } diff -Nru openmw-0.37.0/components/nifosg/particle.hpp openmw-0.38.0/components/nifosg/particle.hpp --- openmw-0.37.0/components/nifosg/particle.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/nifosg/particle.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -30,7 +30,7 @@ ParticleSystem(); ParticleSystem(const ParticleSystem& copy, const osg::CopyOp& copyop); - META_Object(NifOsg, NifOsg::ParticleSystem) + META_Object(NifOsg, ParticleSystem) virtual osgParticle::Particle* createParticle(const osgParticle::Particle *ptemplate); @@ -130,7 +130,8 @@ float mCachedDefaultSize; }; - class ParticleColorAffector : public osgParticle::Operator, public ValueInterpolator + typedef ValueInterpolator Vec4Interpolator; + class ParticleColorAffector : public osgParticle::Operator { public: ParticleColorAffector(const Nif::NiColorData* clrdata); @@ -142,7 +143,7 @@ virtual void operate(osgParticle::Particle* particle, double dt); private: - Nif::NiColorData mData; + Vec4Interpolator mData; }; class GravityAffector : public osgParticle::Operator @@ -171,11 +172,12 @@ osg::Vec3f mCachedWorldDirection; }; - // NodeVisitor to find a child node with the given record index, stored in the node's user data container. - class FindRecIndexVisitor : public osg::NodeVisitor + // NodeVisitor to find a Group node with the given record index, stored in the node's user data container. + // Alternatively, returns the node's parent Group if that node is not a Group (i.e. a leaf node). + class FindGroupByRecIndex : public osg::NodeVisitor { public: - FindRecIndexVisitor(int recIndex); + FindGroupByRecIndex(int recIndex); virtual void apply(osg::Node &searchNode); @@ -193,7 +195,7 @@ Emitter(); Emitter(const Emitter& copy, const osg::CopyOp& copyop); - META_Object(NifOsg, NifOsg::Emitter) + META_Object(NifOsg, Emitter) virtual void emitParticles(double dt); diff -Nru openmw-0.37.0/components/resource/bulletshape.cpp openmw-0.38.0/components/resource/bulletshape.cpp --- openmw-0.37.0/components/resource/bulletshape.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/bulletshape.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,111 @@ +#include "bulletshape.hpp" + +#include + +#include +#include +#include +#include + +namespace Resource +{ + +BulletShape::BulletShape() + : mCollisionShape(NULL) +{ + +} + +BulletShape::~BulletShape() +{ + deleteShape(mCollisionShape); +} + +void BulletShape::deleteShape(btCollisionShape* shape) +{ + if(shape!=NULL) + { + if(shape->isCompound()) + { + btCompoundShape* ms = static_cast(shape); + int a = ms->getNumChildShapes(); + for(int i=0; i getChildShape(i)); + } + delete shape; + } +} + +btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape) const +{ + if(shape->isCompound()) + { + btCompoundShape *comp = static_cast(shape); + btCompoundShape *newShape = new btCompoundShape; + + int numShapes = comp->getNumChildShapes(); + for(int i = 0;i < numShapes;++i) + { + btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i)); + btTransform trans = comp->getChildTransform(i); + newShape->addChildShape(trans, child); + } + + return newShape; + } + + if(btBvhTriangleMeshShape* trishape = dynamic_cast(shape)) + { +#if BT_BULLET_VERSION >= 283 + btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(trishape, btVector3(1.f, 1.f, 1.f)); +#else + // work around btScaledBvhTriangleMeshShape bug ( https://code.google.com/p/bullet/issues/detail?id=371 ) in older bullet versions + btTriangleMesh* oldMesh = static_cast(trishape->getMeshInterface()); + btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh); + + // Do not build a new bvh (not needed, since it's the same as the original shape's bvh) + bool buildBvh = true; + if (trishape->getOptimizedBvh()) + buildBvh = false; + TriangleMeshShape* newShape = new TriangleMeshShape(newMesh, true, buildBvh); + // Set original shape's bvh via pointer + // The pointer is safe because the BulletShapeInstance keeps a ref_ptr to the original BulletShape + if (!buildBvh) + newShape->setOptimizedBvh(trishape->getOptimizedBvh()); +#endif + return newShape; + } + + if (btBoxShape* boxshape = dynamic_cast(shape)) + { + return new btBoxShape(*boxshape); + } + + throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName()); +} + +btCollisionShape *BulletShape::getCollisionShape() +{ + return mCollisionShape; +} + +osg::ref_ptr BulletShape::makeInstance() +{ + osg::ref_ptr instance (new BulletShapeInstance(this)); + return instance; +} + +BulletShapeInstance::BulletShapeInstance(osg::ref_ptr source) + : BulletShape() + , mSource(source) +{ + mCollisionBoxHalfExtents = source->mCollisionBoxHalfExtents; + mCollisionBoxTranslate = source->mCollisionBoxTranslate; + + mAnimatedShapes = source->mAnimatedShapes; + + if (source->mCollisionShape) + mCollisionShape = duplicateCollisionShape(source->mCollisionShape); +} + +} diff -Nru openmw-0.37.0/components/resource/bulletshape.hpp openmw-0.38.0/components/resource/bulletshape.hpp --- openmw-0.37.0/components/resource/bulletshape.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/bulletshape.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,78 @@ +#ifndef OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H +#define OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H + +#include + +#include +#include +#include + +#include + +class btCollisionShape; + +namespace Resource +{ + + class BulletShapeInstance; + class BulletShape : public osg::Referenced + { + public: + BulletShape(); + virtual ~BulletShape(); + + btCollisionShape* mCollisionShape; + + // Used for actors. Note, ideally actors would use a separate loader - as it is + // we have to keep a redundant copy of the actor model around in mCollisionShape, which isn't used. + // For now, use one file <-> one resource for simplicity. + osg::Vec3f mCollisionBoxHalfExtents; + osg::Vec3f mCollisionBoxTranslate; + + // Stores animated collision shapes. If any collision nodes in the NIF are animated, then mCollisionShape + // will be a btCompoundShape (which consists of one or more child shapes). + // In this map, for each animated collision shape, + // we store the node's record index mapped to the child index of the shape in the btCompoundShape. + std::map mAnimatedShapes; + + osg::ref_ptr makeInstance(); + + btCollisionShape* duplicateCollisionShape(btCollisionShape* shape) const; + + btCollisionShape* getCollisionShape(); + + private: + void deleteShape(btCollisionShape* shape); + }; + + + // An instance of a BulletShape that may have its own unique scaling set on the mCollisionShape. + // Vertex data is shallow-copied where possible. A ref_ptr to the original shape is held to keep vertex pointers intact. + class BulletShapeInstance : public BulletShape + { + public: + BulletShapeInstance(osg::ref_ptr source); + + private: + osg::ref_ptr mSource; + }; + + // Subclass btBhvTriangleMeshShape to auto-delete the meshInterface + struct TriangleMeshShape : public btBvhTriangleMeshShape + { + TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression, bool buildBvh = true) + : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression, buildBvh) + { + } + + virtual ~TriangleMeshShape() + { + delete getTriangleInfoMap(); + delete m_meshInterface; + } + }; + + +} + +#endif diff -Nru openmw-0.37.0/components/resource/bulletshapemanager.cpp openmw-0.38.0/components/resource/bulletshapemanager.cpp --- openmw-0.37.0/components/resource/bulletshapemanager.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/bulletshapemanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,152 @@ +#include "bulletshapemanager.hpp" + +#include +#include +#include + +#include + +#include + +#include + +#include "bulletshape.hpp" +#include "scenemanager.hpp" +#include "niffilemanager.hpp" + + +namespace Resource +{ + +struct GetTriangleFunctor +{ + GetTriangleFunctor() + : mTriMesh(NULL) + { + } + + void setTriMesh(btTriangleMesh* triMesh) + { + mTriMesh = triMesh; + } + + void setMatrix(const osg::Matrixf& matrix) + { + mMatrix = matrix; + } + + inline btVector3 toBullet(const osg::Vec3f& vec) + { + return btVector3(vec.x(), vec.y(), vec.z()); + } + + void inline operator()( const osg::Vec3 v1, const osg::Vec3 v2, const osg::Vec3 v3, bool _temp ) + { + if (mTriMesh) + mTriMesh->addTriangle( toBullet(mMatrix.preMult(v1)), toBullet(mMatrix.preMult(v2)), toBullet(mMatrix.preMult(v3))); + } + + btTriangleMesh* mTriMesh; + osg::Matrixf mMatrix; +}; + +/// Creates a BulletShape out of a Node hierarchy. +class NodeToShapeVisitor : public osg::NodeVisitor +{ +public: + NodeToShapeVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mTriangleMesh(NULL) + { + + } + + virtual void apply(osg::Geode& geode) + { + for (unsigned int i=0; i functor; + functor.setTriMesh(mTriangleMesh); + functor.setMatrix(worldMat); + drawable.accept(functor); + } + + osg::ref_ptr getShape() + { + if (!mTriangleMesh) + return osg::ref_ptr(); + + osg::ref_ptr shape (new BulletShape); + TriangleMeshShape* meshShape = new TriangleMeshShape(mTriangleMesh, true); + shape->mCollisionShape = meshShape; + mTriangleMesh = NULL; + return shape; + } + +private: + btTriangleMesh* mTriangleMesh; +}; + +BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager) + : mVFS(vfs) + , mSceneManager(sceneMgr) + , mNifFileManager(nifFileManager) +{ + +} + +BulletShapeManager::~BulletShapeManager() +{ + +} + +osg::ref_ptr BulletShapeManager::createInstance(const std::string &name) +{ + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + osg::ref_ptr shape; + Index::iterator it = mIndex.find(normalized); + if (it == mIndex.end()) + { + size_t extPos = normalized.find_last_of('.'); + std::string ext; + if (extPos != std::string::npos && extPos+1 < normalized.size()) + ext = normalized.substr(extPos+1); + + if (ext == "nif") + { + NifBullet::BulletNifLoader loader; + shape = loader.load(mNifFileManager->get(normalized)); + } + else + { + // TODO: support .bullet shape files + + osg::ref_ptr constNode (mSceneManager->getTemplate(normalized)); + osg::ref_ptr node (const_cast(constNode.get())); // const-trickery required because there is no const version of NodeVisitor + NodeToShapeVisitor visitor; + node->accept(visitor); + shape = visitor.getShape(); + if (!shape) + return osg::ref_ptr(); + } + + mIndex[normalized] = shape; + } + else + shape = it->second; + + osg::ref_ptr instance = shape->makeInstance(); + return instance; +} + +} diff -Nru openmw-0.37.0/components/resource/bulletshapemanager.hpp openmw-0.38.0/components/resource/bulletshapemanager.hpp --- openmw-0.37.0/components/resource/bulletshapemanager.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/bulletshapemanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,43 @@ +#ifndef OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H +#define OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H + +#include +#include + +#include + +#include "bulletshape.hpp" + +namespace VFS +{ + class Manager; +} + +namespace Resource +{ + class SceneManager; + class NifFileManager; + + class BulletShape; + class BulletShapeInstance; + + class BulletShapeManager + { + public: + BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager); + ~BulletShapeManager(); + + osg::ref_ptr createInstance(const std::string& name); + + private: + const VFS::Manager* mVFS; + SceneManager* mSceneManager; + NifFileManager* mNifFileManager; + + typedef std::map > Index; + Index mIndex; + }; + +} + +#endif diff -Nru openmw-0.37.0/components/resource/keyframemanager.cpp openmw-0.38.0/components/resource/keyframemanager.cpp --- openmw-0.37.0/components/resource/keyframemanager.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/keyframemanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,41 @@ +#include "keyframemanager.hpp" + +#include +#include + +#include "objectcache.hpp" + +namespace Resource +{ + + KeyframeManager::KeyframeManager(const VFS::Manager* vfs) + : mCache(new osgDB::ObjectCache) + , mVFS(vfs) + { + } + + KeyframeManager::~KeyframeManager() + { + } + + osg::ref_ptr KeyframeManager::get(const std::string &name) + { + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); + if (obj) + return osg::ref_ptr(static_cast(obj.get())); + else + { + osg::ref_ptr loaded (new NifOsg::KeyframeHolder); + NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get()); + + mCache->addEntryToObjectCache(normalized, loaded); + return loaded; + } + } + + + +} diff -Nru openmw-0.37.0/components/resource/keyframemanager.hpp openmw-0.38.0/components/resource/keyframemanager.hpp --- openmw-0.37.0/components/resource/keyframemanager.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/keyframemanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,47 @@ +#ifndef OPENMW_COMPONENTS_KEYFRAMEMANAGER_H +#define OPENMW_COMPONENTS_KEYFRAMEMANAGER_H + +#include +#include + +namespace VFS +{ + class Manager; +} + +namespace osgDB +{ + class ObjectCache; +} + +namespace NifOsg +{ + class KeyframeHolder; +} + +namespace Resource +{ + + /// @brief Managing of keyframe resources + class KeyframeManager + { + public: + KeyframeManager(const VFS::Manager* vfs); + ~KeyframeManager(); + + void clearCache(); + + /// Retrieve a read-only keyframe resource by name (case-insensitive). + /// @note This method is safe to call from any thread. + /// @note Throws an exception if the resource is not found. + osg::ref_ptr get(const std::string& name); + + private: + osg::ref_ptr mCache; + + const VFS::Manager* mVFS; + }; + +} + +#endif diff -Nru openmw-0.37.0/components/resource/niffilemanager.cpp openmw-0.38.0/components/resource/niffilemanager.cpp --- openmw-0.37.0/components/resource/niffilemanager.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/niffilemanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,63 @@ +#include "niffilemanager.hpp" + +#include + +#include "objectcache.hpp" + +namespace Resource +{ + + class NifFileHolder : public osg::Object + { + public: + NifFileHolder(const Nif::NIFFilePtr& file) + : mNifFile(file) + { + } + NifFileHolder(const NifFileHolder& copy, const osg::CopyOp& copyop) + : mNifFile(copy.mNifFile) + { + } + + NifFileHolder() + { + } + + META_Object(Resource, NifFileHolder) + + Nif::NIFFilePtr mNifFile; + }; + + NifFileManager::NifFileManager(const VFS::Manager *vfs) + : mVFS(vfs) + { + mCache = new osgDB::ObjectCache; + } + + NifFileManager::~NifFileManager() + { + + } + + void NifFileManager::clearCache() + { + // NIF files aren't needed any more when the converted objects are cached in SceneManager / BulletShapeManager, + // so we'll simply drop all nif files here, unlikely to need them again + mCache->clear(); + } + + Nif::NIFFilePtr NifFileManager::get(const std::string &name) + { + osg::ref_ptr obj = mCache->getRefFromObjectCache(name); + if (obj) + return static_cast(obj.get())->mNifFile; + else + { + Nif::NIFFilePtr file (new Nif::NIFFile(mVFS->getNormalized(name), name)); + obj = new NifFileHolder(file); + mCache->addEntryToObjectCache(name, obj); + return file; + } + } + +} diff -Nru openmw-0.37.0/components/resource/niffilemanager.hpp openmw-0.38.0/components/resource/niffilemanager.hpp --- openmw-0.37.0/components/resource/niffilemanager.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/niffilemanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,45 @@ +#ifndef OPENMW_COMPONENTS_RESOURCE_NIFFILEMANAGER_H +#define OPENMW_COMPONENTS_RESOURCE_NIFFILEMANAGER_H + +#include + +#include + +namespace VFS +{ + class Manager; +} + +namespace osgDB +{ + class ObjectCache; +} + +namespace Resource +{ + + /// @brief Handles caching of NIFFiles. + /// @note The NifFileManager is completely thread safe. + class NifFileManager + { + public: + NifFileManager(const VFS::Manager* vfs); + ~NifFileManager(); + + void clearCache(); + + /// Retrieve a NIF file from the cache, or load it from the VFS if not cached yet. + /// @note For performance reasons the NifFileManager does not handle case folding, needs + /// to be done in advance by other managers accessing the NifFileManager. + Nif::NIFFilePtr get(const std::string& name); + + private: + // Use the osgDB::ObjectCache so objects are retrieved in thread safe way + osg::ref_ptr mCache; + + const VFS::Manager* mVFS; + }; + +} + +#endif diff -Nru openmw-0.37.0/components/resource/objectcache.cpp openmw-0.38.0/components/resource/objectcache.cpp --- openmw-0.37.0/components/resource/objectcache.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/objectcache.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,141 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#include + +#if OSG_VERSION_LESS_THAN(3,3,3) + +#include "objectcache.hpp" + +using namespace osgDB; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// ObjectCache +// +ObjectCache::ObjectCache(): + osg::Referenced(true) +{ +// OSG_NOTICE<<"Constructed ObjectCache"< lock1(_objectCacheMutex); + OpenThreads::ScopedLock lock2(objectCache->_objectCacheMutex); + + // OSG_NOTICE<<"Inserting objects to main ObjectCache "<_objectCache.size()<_objectCache.begin(), objectCache->_objectCache.end()); +} + + +void ObjectCache::addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + _objectCache[filename]=ObjectTimeStampPair(object,timestamp); +} + +osg::Object* ObjectCache::getFromObjectCache(const std::string& fileName) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + ObjectCacheMap::iterator itr = _objectCache.find(fileName); + if (itr!=_objectCache.end()) return itr->second.first.get(); + else return 0; +} + +osg::ref_ptr ObjectCache::getRefFromObjectCache(const std::string& fileName) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + ObjectCacheMap::iterator itr = _objectCache.find(fileName); + if (itr!=_objectCache.end()) + { + // OSG_NOTICE<<"Found "<second.first; + } + else return 0; +} + +void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + + // look for objects with external references and update their time stamp. + for(ObjectCacheMap::iterator itr=_objectCache.begin(); + itr!=_objectCache.end(); + ++itr) + { + // if ref count is greater the 1 the object has an external reference. + if (itr->second.first->referenceCount()>1) + { + // so update it time stamp. + itr->second.second = referenceTime; + } + } +} + +void ObjectCache::removeExpiredObjectsInCache(double expiryTime) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + + // Remove expired entries from object cache + ObjectCacheMap::iterator oitr = _objectCache.begin(); + while(oitr != _objectCache.end()) + { + if (oitr->second.second<=expiryTime) + { + _objectCache.erase(oitr++); + } + else + { + ++oitr; + } + } +} + +void ObjectCache::removeFromObjectCache(const std::string& fileName) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + ObjectCacheMap::iterator itr = _objectCache.find(fileName); + if (itr!=_objectCache.end()) _objectCache.erase(itr); +} + +void ObjectCache::clear() +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + _objectCache.clear(); +} + +void ObjectCache::releaseGLObjects(osg::State* state) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + + for(ObjectCacheMap::iterator itr = _objectCache.begin(); + itr != _objectCache.end(); + ++itr) + { + osg::Object* object = itr->second.first.get(); + object->releaseGLObjects(state); + } +} + +#endif diff -Nru openmw-0.37.0/components/resource/objectcache.hpp openmw-0.38.0/components/resource/objectcache.hpp --- openmw-0.37.0/components/resource/objectcache.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/resource/objectcache.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,92 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// Wrapper for osgDB/ObjectCache. Works around ObjectCache not being available in old OSG 3.2. +// Use "#include objectcache.hpp" in place of "#include + +#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) +#include +#else + +#include + +#include +#include + +#include + +namespace osgDB { + +class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced +{ + public: + + ObjectCache(); + + /** For each object in the cache which has an reference count greater than 1 + * (and therefore referenced by elsewhere in the application) set the time stamp + * for that object in the cache to specified time. + * This would typically be called once per frame by applications which are doing database paging, + * and need to prune objects that are no longer required. + * The time used should be taken from the FrameStamp::getReferenceTime().*/ + void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime); + + /** Removed object in the cache which have a time stamp at or before the specified expiry time. + * This would typically be called once per frame by applications which are doing database paging, + * and need to prune objects that are no longer required, and called after the a called + * after the call to updateTimeStampOfObjectsInCacheWithExternalReferences(expirtyTime).*/ + void removeExpiredObjectsInCache(double expiryTime); + + /** Remove all objects in the cache regardless of having external references or expiry times.*/ + void clear(); + + /** Add contents of specified ObjectCache to this object cache.*/ + void addObjectCache(ObjectCache* object); + + /** Add a filename,object,timestamp triple to the Registry::ObjectCache.*/ + void addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp = 0.0); + + /** Remove Object from cache.*/ + void removeFromObjectCache(const std::string& fileName); + + /** Get an Object from the object cache*/ + osg::Object* getFromObjectCache(const std::string& fileName); + + /** Get an ref_ptr from the object cache*/ + osg::ref_ptr getRefFromObjectCache(const std::string& fileName); + + /** call rleaseGLObjects on all objects attached to the object cache.*/ + void releaseGLObjects(osg::State* state); + + protected: + + virtual ~ObjectCache(); + + typedef std::pair, double > ObjectTimeStampPair; + typedef std::map ObjectCacheMap; + + ObjectCacheMap _objectCache; + OpenThreads::Mutex _objectCacheMutex; + +}; + +} + +#endif + +#endif diff -Nru openmw-0.37.0/components/resource/resourcesystem.cpp openmw-0.38.0/components/resource/resourcesystem.cpp --- openmw-0.37.0/components/resource/resourcesystem.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/resource/resourcesystem.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -2,6 +2,8 @@ #include "scenemanager.hpp" #include "texturemanager.hpp" +#include "niffilemanager.hpp" +#include "keyframemanager.hpp" namespace Resource { @@ -9,8 +11,10 @@ ResourceSystem::ResourceSystem(const VFS::Manager *vfs) : mVFS(vfs) { + mNifFileManager.reset(new NifFileManager(vfs)); + mKeyframeManager.reset(new KeyframeManager(vfs)); mTextureManager.reset(new TextureManager(vfs)); - mSceneManager.reset(new SceneManager(vfs, mTextureManager.get())); + mSceneManager.reset(new SceneManager(vfs, mTextureManager.get(), mNifFileManager.get())); } ResourceSystem::~ResourceSystem() @@ -28,6 +32,21 @@ return mTextureManager.get(); } + NifFileManager* ResourceSystem::getNifFileManager() + { + return mNifFileManager.get(); + } + + KeyframeManager* ResourceSystem::getKeyframeManager() + { + return mKeyframeManager.get(); + } + + void ResourceSystem::clearCache() + { + mNifFileManager->clearCache(); + } + const VFS::Manager* ResourceSystem::getVFS() const { return mVFS; diff -Nru openmw-0.37.0/components/resource/resourcesystem.hpp openmw-0.38.0/components/resource/resourcesystem.hpp --- openmw-0.37.0/components/resource/resourcesystem.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/resource/resourcesystem.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -13,8 +13,10 @@ class SceneManager; class TextureManager; + class NifFileManager; + class KeyframeManager; - /// @brief Wrapper class that constructs and provides access to the various resource subsystems. + /// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems. /// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but /// are built around the use of a single virtual file system. class ResourceSystem @@ -25,12 +27,19 @@ SceneManager* getSceneManager(); TextureManager* getTextureManager(); + NifFileManager* getNifFileManager(); + KeyframeManager* getKeyframeManager(); + + /// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced. + void clearCache(); const VFS::Manager* getVFS() const; private: std::auto_ptr mSceneManager; std::auto_ptr mTextureManager; + std::auto_ptr mNifFileManager; + std::auto_ptr mKeyframeManager; const VFS::Manager* mVFS; diff -Nru openmw-0.37.0/components/resource/scenemanager.cpp openmw-0.38.0/components/resource/scenemanager.cpp --- openmw-0.37.0/components/resource/scenemanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/resource/scenemanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,6 +20,9 @@ #include #include +#include "texturemanager.hpp" +#include "niffilemanager.hpp" + namespace { @@ -80,7 +83,7 @@ osg::MatrixList mats = node->getWorldMatrices(); if (mats.empty()) return; - osg::Matrix worldMat = mats[0]; + osg::Matrixf worldMat = mats[0]; worldMat.orthoNormalize(worldMat); // scale is already applied on the particle node for (int i=0; inumParticles(); ++i) { @@ -102,9 +105,10 @@ namespace Resource { - SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager) + SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager) : mVFS(vfs) , mTextureManager(textureManager) + , mNifFileManager(nifFileManager) , mParticleSystemMask(~0u) { } @@ -112,6 +116,72 @@ SceneManager::~SceneManager() { // this has to be defined in the .cpp file as we can't delete incomplete types + + } + + /// @brief Callback to read image files from the VFS. + class ImageReadCallback : public osgDB::ReadFileCallback + { + public: + ImageReadCallback(Resource::TextureManager* textureMgr) + : mTextureManager(textureMgr) + { + } + + virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& filename, const osgDB::Options* options) + { + try + { + return osgDB::ReaderWriter::ReadResult(mTextureManager->getImage(filename), osgDB::ReaderWriter::ReadResult::FILE_LOADED); + } + catch (std::exception& e) + { + return osgDB::ReaderWriter::ReadResult(e.what()); + } + } + + private: + Resource::TextureManager* mTextureManager; + }; + + std::string getFileExtension(const std::string& file) + { + size_t extPos = file.find_last_of('.'); + if (extPos != std::string::npos && extPos+1 < file.size()) + return file.substr(extPos+1); + return std::string(); + } + + osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::TextureManager* textureMgr, Resource::NifFileManager* nifFileManager) + { + std::string ext = getFileExtension(normalizedFilename); + if (ext == "nif") + return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), textureMgr); + else + { + osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); + if (!reader) + { + std::stringstream errormsg; + errormsg << "Error loading " << normalizedFilename << ": no readerwriter for '" << ext << "' found" << std::endl; + throw std::runtime_error(errormsg.str()); + } + + osg::ref_ptr options (new osgDB::Options); + // Set a ReadFileCallback so that image files referenced in the model are read from our virtual file system instead of the osgDB. + // Note, for some formats (.obj/.mtl) that reference other (non-image) files a findFileCallback would be necessary. + // but findFileCallback does not support virtual files, so we can't implement it. + options->setReadFileCallback(new ImageReadCallback(textureMgr)); + + osgDB::ReaderWriter::ReadResult result = reader->readNode(*file, options); + if (!result.success()) + { + std::stringstream errormsg; + errormsg << "Error loading " << normalizedFilename << ": " << result.message() << " code " << result.status() << std::endl; + throw std::runtime_error(errormsg.str()); + } + return result.getNode(); + } } osg::ref_ptr SceneManager::getTemplate(const std::string &name) @@ -122,23 +192,22 @@ Index::iterator it = mIndex.find(normalized); if (it == mIndex.end()) { - // TODO: add support for non-NIF formats osg::ref_ptr loaded; try { Files::IStreamPtr file = mVFS->get(normalized); - loaded = NifOsg::Loader::load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized)), mTextureManager); + loaded = load(file, normalized, mTextureManager, mNifFileManager); } catch (std::exception& e) { std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error.nif instead" << std::endl; Files::IStreamPtr file = mVFS->get("meshes/marker_error.nif"); - loaded = NifOsg::Loader::load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized)), mTextureManager); + normalized = "meshes/marker_error.nif"; + loaded = load(file, normalized, mTextureManager, mNifFileManager); } osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); - // TODO: run SharedStateManager::prune on unload if (mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); @@ -164,26 +233,6 @@ return cloned; } - osg::ref_ptr SceneManager::getKeyframes(const std::string &name) - { - std::string normalized = name; - mVFS->normalizeFilename(normalized); - - KeyframeIndex::iterator it = mKeyframeIndex.find(normalized); - if (it == mKeyframeIndex.end()) - { - Files::IStreamPtr file = mVFS->get(normalized); - - osg::ref_ptr loaded (new NifOsg::KeyframeHolder); - NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized)), *loaded.get()); - - mKeyframeIndex[normalized] = loaded; - return loaded; - } - else - return it->second; - } - void SceneManager::attachTo(osg::Node *instance, osg::Group *parentNode) const { parentNode->addChild(instance); diff -Nru openmw-0.37.0/components/resource/scenemanager.hpp openmw-0.38.0/components/resource/scenemanager.hpp --- openmw-0.37.0/components/resource/scenemanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/resource/scenemanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,6 +10,7 @@ namespace Resource { class TextureManager; + class NifFileManager; } namespace VFS @@ -17,11 +18,6 @@ class Manager; } -namespace NifOsg -{ - class KeyframeHolder; -} - namespace osgUtil { class IncrementalCompileOperation; @@ -34,7 +30,7 @@ class SceneManager { public: - SceneManager(const VFS::Manager* vfs, Resource::TextureManager* textureManager); + SceneManager(const VFS::Manager* vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager); ~SceneManager(); /// Get a read-only copy of this scene "template" @@ -56,9 +52,6 @@ /// @note Assumes the given instance was not attached to any parents before. void attachTo(osg::Node* instance, osg::Group* parentNode) const; - /// Get a read-only copy of the given keyframe file. - osg::ref_ptr getKeyframes(const std::string& name); - /// Manually release created OpenGL objects for the given graphics context. This may be required /// in cases where multiple contexts are used over the lifetime of the application. void releaseGLObjects(osg::State* state); @@ -66,7 +59,7 @@ /// Set up an IncrementalCompileOperation for background compiling of loaded scenes. void setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico); - /// @note If you used SceneManager::attachTo, this was called automatically. + /// @note SceneManager::attachTo calls this method automatically, only needs to be called by users if manually attaching void notifyAttached(osg::Node* node) const; const VFS::Manager* getVFS() const; @@ -79,6 +72,7 @@ private: const VFS::Manager* mVFS; Resource::TextureManager* mTextureManager; + Resource::NifFileManager* mNifFileManager; osg::ref_ptr mIncrementalCompileOperation; @@ -88,9 +82,6 @@ typedef std::map > Index; Index mIndex; - typedef std::map > KeyframeIndex; - KeyframeIndex mKeyframeIndex; - SceneManager(const SceneManager&); void operator = (const SceneManager&); }; diff -Nru openmw-0.37.0/components/resource/texturemanager.cpp openmw-0.38.0/components/resource/texturemanager.cpp --- openmw-0.37.0/components/resource/texturemanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/resource/texturemanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -65,6 +66,45 @@ mUnRefImageDataAfterApply = unref; } + void TextureManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter, + const std::string &mipmap, int maxAnisotropy, + osgViewer::Viewer *viewer) + { + osg::Texture::FilterMode min = osg::Texture::LINEAR; + osg::Texture::FilterMode mag = osg::Texture::LINEAR; + + if(magfilter == "nearest") + mag = osg::Texture::NEAREST; + else if(magfilter != "linear") + std::cerr<< "Invalid texture mag filter: "<stopThreading(); + setFilterSettings(min, mag, maxAnisotropy); + if(viewer) viewer->startThreading(); + } + void TextureManager::setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) { mMinFilter = minFilter; @@ -143,6 +183,57 @@ return true; } + osg::ref_ptr TextureManager::getImage(const std::string &filename) + { + std::string normalized = filename; + mVFS->normalizeFilename(normalized); + std::map >::iterator found = mImages.find(normalized); + if (found != mImages.end()) + return found->second; + else + { + Files::IStreamPtr stream; + try + { + stream = mVFS->get(normalized.c_str()); + } + catch (std::exception& e) + { + std::cerr << "Failed to open image: " << e.what() << std::endl; + return NULL; + } + + osg::ref_ptr opts (new osgDB::Options); + opts->setOptionString("dds_dxt1_detect_rgba"); // tx_creature_werewolf.dds isn't loading in the correct format without this option + size_t extPos = normalized.find_last_of('.'); + std::string ext; + if (extPos != std::string::npos && extPos+1 < normalized.size()) + ext = normalized.substr(extPos+1); + osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); + if (!reader) + { + std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl; + return NULL; + } + + osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, opts); + if (!result.success()) + { + std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; + return NULL; + } + + osg::Image* image = result.getImage(); + if (!checkSupported(image, filename)) + { + return NULL; + } + + mImages.insert(std::make_pair(normalized, image)); + return image; + } + } + osg::ref_ptr TextureManager::getTexture2D(const std::string &filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT) { std::string normalized = filename; diff -Nru openmw-0.37.0/components/resource/texturemanager.hpp openmw-0.38.0/components/resource/texturemanager.hpp --- openmw-0.37.0/components/resource/texturemanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/resource/texturemanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -8,6 +8,11 @@ #include #include +namespace osgViewer +{ + class Viewer; +} + namespace VFS { class Manager; @@ -23,18 +28,20 @@ TextureManager(const VFS::Manager* vfs); ~TextureManager(); - /// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first! - void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy); + void setFilterSettings(const std::string &magfilter, const std::string &minfilter, + const std::string &mipmap, int maxAnisotropy, + osgViewer::Viewer *view); /// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts, /// otherwise should be disabled to reduce memory usage. void setUnRefImageDataAfterApply(bool unref); /// Create or retrieve a Texture2D using the specified image filename, and wrap parameters. + /// Returns the dummy texture if the given texture is not found. osg::ref_ptr getTexture2D(const std::string& filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT); /// Create or retrieve an Image - //osg::ref_ptr getImage(const std::string& filename); + osg::ref_ptr getImage(const std::string& filename); const VFS::Manager* getVFS() { return mVFS; } @@ -49,7 +56,7 @@ typedef std::pair, std::string> MapKey; - std::map > mImages; + std::map > mImages; std::map > mTextures; @@ -57,6 +64,9 @@ bool mUnRefImageDataAfterApply; + /// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first! + void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy); + TextureManager(const TextureManager&); void operator = (const TextureManager&); }; diff -Nru openmw-0.37.0/components/sceneutil/clone.cpp openmw-0.38.0/components/sceneutil/clone.cpp --- openmw-0.37.0/components/sceneutil/clone.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/clone.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,6 +1,7 @@ #include "clone.hpp" #include +#include #include #include @@ -53,6 +54,7 @@ osg::CopyOp copyop = *this; copyop.setCopyFlags(copyop.getCopyFlags()|osg::CopyOp::DEEP_COPY_ARRAYS); +#if OSG_VERSION_LESS_THAN(3,5,0) /* Deep copy of primitives required to work around the following (bad?) code in osg::Geometry copy constructor: @@ -71,12 +73,12 @@ In case of DEEP_COPY_PRIMITIVES=Off, DEEP_COPY_ARRAYS=On, the above code makes a modification to the original const Geometry& we copied from, causing problems if we relied on the original Geometry to remain static such as when it was added to an osgUtil::IncrementalCompileOperation. - Possible fix submitted to osg-submissions ( http://forum.openscenegraph.org/viewtopic.php?t=15217 ). + Fixed in OSG 3.5 ( http://forum.openscenegraph.org/viewtopic.php?t=15217 ). */ copyop.setCopyFlags(copyop.getCopyFlags()|osg::CopyOp::DEEP_COPY_PRIMITIVES); - +#endif osg::Drawable* cloned = osg::clone(drawable, copyop); if (cloned->getUpdateCallback()) diff -Nru openmw-0.37.0/components/sceneutil/lightcontroller.cpp openmw-0.38.0/components/sceneutil/lightcontroller.cpp --- openmw-0.37.0/components/sceneutil/lightcontroller.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/lightcontroller.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -61,8 +61,10 @@ void LightController::operator ()(osg::Node* node, osg::NodeVisitor* nv) { double time = nv->getFrameStamp()->getSimulationTime(); - if (time == mLastTime) - return; + + // disabled early out, light state needs to be set every frame regardless of change, due to the double buffering + //if (time == mLastTime) + // return; float dt = static_cast(time - mLastTime); mLastTime = time; @@ -76,7 +78,7 @@ if(mType == LT_Pulse || mType == LT_PulseSlow) { cycle_time = 2.0f * pi; - time_distortion = 20.0f; + time_distortion = mType == LT_Pulse ? 20.0f : 4.f; } else { @@ -114,14 +116,16 @@ else if(mType == LT_FlickerSlow) brightness = 0.75f + flickerAmplitude(mDeltaCount*slow)*0.25f; else if(mType == LT_Pulse) - brightness = 1.0f + pulseAmplitude(mDeltaCount*fast)*0.25f; + brightness = 0.7f + pulseAmplitude(mDeltaCount*fast)*0.3f; else if(mType == LT_PulseSlow) - brightness = 1.0f + pulseAmplitude(mDeltaCount*slow)*0.25f; + brightness = 0.7f + pulseAmplitude(mDeltaCount*slow)*0.3f; + + static_cast(node)->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * brightness); - static_cast(node)->getLight()->setDiffuse(mDiffuseColor * brightness); + traverse(node, nv); } - void LightController::setDiffuse(osg::Vec4f color) + void LightController::setDiffuse(const osg::Vec4f& color) { mDiffuseColor = color; } diff -Nru openmw-0.37.0/components/sceneutil/lightcontroller.hpp openmw-0.38.0/components/sceneutil/lightcontroller.hpp --- openmw-0.37.0/components/sceneutil/lightcontroller.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/lightcontroller.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -24,7 +24,7 @@ void setType(LightType type); - void setDiffuse(osg::Vec4f color); + void setDiffuse(const osg::Vec4f& color); virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); diff -Nru openmw-0.37.0/components/sceneutil/lightmanager.cpp openmw-0.38.0/components/sceneutil/lightmanager.cpp --- openmw-0.37.0/components/sceneutil/lightmanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/lightmanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -98,7 +98,7 @@ throw std::runtime_error("can't find parent LightManager"); } - mLightManager->addLight(static_cast(node), osg::computeLocalToWorld(nv->getNodePath())); + mLightManager->addLight(static_cast(node), osg::computeLocalToWorld(nv->getNodePath()), nv->getTraversalNumber()); traverse(node, nv); } @@ -160,37 +160,42 @@ mLightsInViewSpace.clear(); // do an occasional cleanup for orphaned lights - if (mStateSetCache.size() > 5000) - mStateSetCache.clear(); + for (int i=0; i<2; ++i) + { + if (mStateSetCache[i].size() > 5000) + mStateSetCache[i].clear(); + } } - void LightManager::addLight(LightSource* lightSource, osg::Matrix worldMat) + void LightManager::addLight(LightSource* lightSource, const osg::Matrixf& worldMat, unsigned int frameNum) { LightSourceTransform l; l.mLightSource = lightSource; l.mWorldMatrix = worldMat; - lightSource->getLight()->setPosition(osg::Vec4f(worldMat.getTrans().x(), + lightSource->getLight(frameNum)->setPosition(osg::Vec4f(worldMat.getTrans().x(), worldMat.getTrans().y(), worldMat.getTrans().z(), 1.f)); mLights.push_back(l); } - osg::ref_ptr LightManager::getLightListStateSet(const LightList &lightList) + osg::ref_ptr LightManager::getLightListStateSet(const LightList &lightList, unsigned int frameNum) { // possible optimization: return a StateSet containing all requested lights plus some extra lights (if a suitable one exists) size_t hash = 0; for (unsigned int i=0; imLightSource->getId()); - LightStateSetMap::iterator found = mStateSetCache.find(hash); - if (found != mStateSetCache.end()) + LightStateSetMap& stateSetCache = mStateSetCache[frameNum%2]; + + LightStateSetMap::iterator found = stateSetCache.find(hash); + if (found != stateSetCache.end()) return found->second; else { std::vector > lights; for (unsigned int i=0; imLightSource->getLight()); + lights.push_back(lightList[i]->mLightSource->getLight(frameNum)); osg::ref_ptr attr = new LightStateAttribute(mStartLight, lights); @@ -200,7 +205,7 @@ stateset->setAttribute(attr, osg::StateAttribute::ON); stateset->setAssociatedModes(attr, osg::StateAttribute::ON); - mStateSetCache.insert(std::make_pair(hash, stateset)); + stateSetCache.insert(std::make_pair(hash, stateset)); return stateset; } } @@ -221,7 +226,7 @@ for (std::vector::iterator lightIt = mLights.begin(); lightIt != mLights.end(); ++lightIt) { - osg::Matrix worldViewMat = lightIt->mWorldMatrix * (*viewMatrix); + osg::Matrixf worldViewMat = lightIt->mWorldMatrix * (*viewMatrix); osg::BoundingSphere viewBound = osg::BoundingSphere(osg::Vec3f(0,0,0), lightIt->mLightSource->getRadius()); transformBoundingSphere(worldViewMat, viewBound); @@ -255,10 +260,12 @@ LightSource::LightSource(const LightSource ©, const osg::CopyOp ©op) : osg::Node(copy, copyop) - , mLight(copy.mLight) , mRadius(copy.mRadius) { mId = sLightId++; + + for (int i=0; i<2; ++i) + mLight[i] = osg::clone(copy.mLight[i].get(), copyop); } @@ -294,9 +301,9 @@ // update light list if necessary // makes sure we don't update it more than once per frame when rendering with multiple cameras - if (mLastFrameNumber != nv->getFrameStamp()->getFrameNumber()) + if (mLastFrameNumber != nv->getTraversalNumber()) { - mLastFrameNumber = nv->getFrameStamp()->getFrameNumber(); + mLastFrameNumber = nv->getTraversalNumber(); // Don't use Camera::getViewMatrix, that one might be relative to another camera! const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix(); @@ -348,10 +355,10 @@ while (lightList.size() > maxLights) lightList.pop_back(); } - stateset = mLightManager->getLightListStateSet(lightList); + stateset = mLightManager->getLightListStateSet(lightList, nv->getTraversalNumber()); } else - stateset = mLightManager->getLightListStateSet(mLightList); + stateset = mLightManager->getLightListStateSet(mLightList, nv->getTraversalNumber()); cv->pushStateSet(stateset); diff -Nru openmw-0.37.0/components/sceneutil/lightmanager.hpp openmw-0.38.0/components/sceneutil/lightmanager.hpp --- openmw-0.37.0/components/sceneutil/lightmanager.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/lightmanager.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -10,11 +10,19 @@ { /// LightSource managed by a LightManager. + /// @par Typically used for point lights. Spot lights are not supported yet. Directional lights affect the whole scene + /// so do not need to be managed by a LightManager - so for directional lights use a plain osg::LightSource instead. + /// @note LightSources must be decorated by a LightManager node in order to have an effect. Typical use would + /// be one LightManager as the root of the scene graph. + /// @note One needs to attach LightListCallback's to the scene to have objects receive lighting from LightSources. + /// See the documentation of LightListCallback for more information. + /// @note The position of the contained osg::Light is automatically updated based on the LightSource's world position. class LightSource : public osg::Node { - osg::ref_ptr mLight; + // double buffered osg::Light's, since one of them may be in use by the draw thread at any given time + osg::ref_ptr mLight[2]; - // The activation radius + // LightSource will affect objects within this radius float mRadius; int mId; @@ -32,29 +40,38 @@ return mRadius; } + /// The LightSource will affect objects within this radius. void setRadius(float radius) { mRadius = radius; } - osg::Light* getLight() + /// Get the osg::Light safe for modification in the given frame. + /// @par May be used externally to animate the light's color/attenuation properties, + /// and is used internally to synchronize the light's position with the position of the LightSource. + osg::Light* getLight(unsigned int frame) { - return mLight; + return mLight[frame % 2]; } + /// @warning It is recommended not to replace an existing osg::Light, because there might still be + /// references to it in the light StateSet cache that are associated with this LightSource's ID. + /// These references will stay valid due to ref_ptr but will point to the old object. + /// @warning Do not modify the \a light after you've called this function. void setLight(osg::Light* light) { - mLight = light; + mLight[0] = light; + mLight[1] = osg::clone(light); } - int getId() + /// Get the unique ID for this light source. + int getId() const { return mId; } }; - /// All light sources must be a child of the LightManager node. The LightManager can be anywhere in the scene graph, - /// but would be typically somewhere near the top. + /// @brief Decorator node implementing the rendering of any number of LightSources that can be anywhere in the subgraph. class LightManager : public osg::Group { public: @@ -73,16 +90,21 @@ unsigned int getLightingMask() const; - // Called automatically by the UpdateCallback + /// Set the first light index that should be used by this manager, typically the number of directional lights in the scene. + void setStartLight(int start); + + int getStartLight() const; + + /// Internal use only, called automatically by the LightManager's UpdateCallback void update(); - // Called automatically by the LightSource's UpdateCallback - void addLight(LightSource* lightSource, osg::Matrix worldMat); + /// Internal use only, called automatically by the LightSource's UpdateCallback + void addLight(LightSource* lightSource, const osg::Matrixf& worldMat, unsigned int frameNum); struct LightSourceTransform { LightSource* mLightSource; - osg::Matrix mWorldMatrix; + osg::Matrixf mWorldMatrix; }; const std::vector& getLights() const; @@ -97,12 +119,7 @@ typedef std::vector LightList; - osg::ref_ptr getLightListStateSet(const LightList& lightList); - - /// Set the first light index that should be used by this manager, typically the number of directional lights in the scene. - void setStartLight(int start); - - int getStartLight() const; + osg::ref_ptr getLightListStateSet(const LightList& lightList, unsigned int frameNum); private: // Lights collected from the scene graph. Only valid during the cull traversal. @@ -113,13 +130,20 @@ // < Light list hash , StateSet > typedef std::map > LightStateSetMap; - LightStateSetMap mStateSetCache; + LightStateSetMap mStateSetCache[2]; int mStartLight; unsigned int mLightingMask; }; + /// To receive lighting, objects must be decorated by a LightListCallback. Light list callbacks must be added via + /// node->addCullCallback(new LightListCallback). Once a light list callback is added to a node, that node and all + /// its child nodes can receive lighting. + /// @par The placement of these LightListCallbacks affects the granularity of light lists. Having too fine grained + /// light lists can result in degraded performance. Too coarse grained light lists can result in lights no longer + /// rendering when the size of a light list exceeds the OpenGL limit on the number of concurrent lights (8). A good + /// starting point is to attach a LightListCallback to each game object's base node. /// @note Not thread safe for CullThreadPerCamera threading mode. class LightListCallback : public osg::NodeCallback { @@ -134,7 +158,7 @@ , mLastFrameNumber(0) {} - META_Object(NifOsg, LightListCallback) + META_Object(SceneUtil, LightListCallback) void operator()(osg::Node* node, osg::NodeVisitor* nv); diff -Nru openmw-0.37.0/components/sceneutil/positionattitudetransform.cpp openmw-0.38.0/components/sceneutil/positionattitudetransform.cpp --- openmw-0.37.0/components/sceneutil/positionattitudetransform.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/positionattitudetransform.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,51 @@ +#include "positionattitudetransform.hpp" + +#include + +namespace SceneUtil +{ + +PositionAttitudeTransform::PositionAttitudeTransform(): + _scale(1.0,1.0,1.0) +{ +} + +bool PositionAttitudeTransform::computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor*) const +{ + if (_referenceFrame==RELATIVE_RF) + { + matrix.preMultTranslate(_position); + matrix.preMultRotate(_attitude); + matrix.preMultScale(_scale); + } + else // absolute + { + matrix.makeRotate(_attitude); + matrix.postMultTranslate(_position); + matrix.preMultScale(_scale); + } + return true; +} + + +bool PositionAttitudeTransform::computeWorldToLocalMatrix(osg::Matrix& matrix, osg::NodeVisitor*) const +{ + if (_scale.x() == 0.0 || _scale.y() == 0.0 || _scale.z() == 0.0) + return false; + + if (_referenceFrame==RELATIVE_RF) + { + matrix.postMultTranslate(-_position); + matrix.postMultRotate(_attitude.inverse()); + matrix.postMultScale(osg::Vec3f(1.0/_scale.x(), 1.0/_scale.y(), 1.0/_scale.z())); + } + else // absolute + { + matrix.makeRotate(_attitude.inverse()); + matrix.preMultTranslate(-_position); + matrix.postMultScale(osg::Vec3f(1.0/_scale.x(), 1.0/_scale.y(), 1.0/_scale.z())); + } + return true; +} + +} diff -Nru openmw-0.37.0/components/sceneutil/positionattitudetransform.hpp openmw-0.38.0/components/sceneutil/positionattitudetransform.hpp --- openmw-0.37.0/components/sceneutil/positionattitudetransform.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/positionattitudetransform.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -0,0 +1,53 @@ +#ifndef OPENMW_COMPONENTS_POSITIONATTITUDE_TRANSFORM_H +#define OPENMW_COMPONENTS_POSITIONATTITUDE_TRANSFORM_H + +#include + +namespace SceneUtil +{ + +/// @brief A customized version of osg::PositionAttitudeTransform optimized for speed. +/// Uses single precision values. Also removed _pivotPoint which we don't need. +class PositionAttitudeTransform : public osg::Transform +{ + public : + PositionAttitudeTransform(); + + PositionAttitudeTransform(const PositionAttitudeTransform& pat,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY): + Transform(pat,copyop), + _position(pat._position), + _attitude(pat._attitude), + _scale(pat._scale){} + + + META_Node(SceneUtil, PositionAttitudeTransform) + + inline void setPosition(const osg::Vec3f& pos) { _position = pos; dirtyBound(); } + inline const osg::Vec3f& getPosition() const { return _position; } + + + inline void setAttitude(const osg::Quat& quat) { _attitude = quat; dirtyBound(); } + inline const osg::Quat& getAttitude() const { return _attitude; } + + + inline void setScale(const osg::Vec3f& scale) { _scale = scale; dirtyBound(); } + inline const osg::Vec3f& getScale() const { return _scale; } + + + + virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const; + virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const; + + + protected : + + virtual ~PositionAttitudeTransform() {} + + osg::Vec3f _position; + osg::Quat _attitude; + osg::Vec3f _scale; +}; + +} + +#endif diff -Nru openmw-0.37.0/components/sceneutil/riggeometry.cpp openmw-0.38.0/components/sceneutil/riggeometry.cpp --- openmw-0.37.0/components/sceneutil/riggeometry.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/riggeometry.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -82,7 +82,7 @@ , mSkeleton(NULL) , mInfluenceMap(copy.mInfluenceMap) , mLastFrameNumber(0) - , mBoundsFirstFrame(copy.mBoundsFirstFrame) + , mBoundsFirstFrame(true) { setSourceGeometry(copy.mSourceGeometry); } @@ -177,7 +177,7 @@ } } - for (Vertex2BoneMap::iterator it = vertex2BoneMap.begin(); it != vertex2BoneMap.end(); it++) + for (Vertex2BoneMap::iterator it = vertex2BoneMap.begin(); it != vertex2BoneMap.end(); ++it) { mBone2VertexMap[it->second].push_back(it->first); } @@ -185,7 +185,7 @@ return true; } -void accummulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, float weight, osg::Matrixf& result) +void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, float weight, osg::Matrixf& result) { osg::Matrixf m = invBindMatrix * matrix; float* ptr = m.ptr(); @@ -211,6 +211,8 @@ { if (!mSkeleton) { + std::cerr << "RigGeometry rendering with no skeleton, should have been initialized by UpdateVisitor" << std::endl; + // try to recover anyway, though rendering is likely to be incorrect. if (!initFromParentSkeleton(nv)) return; } @@ -218,14 +220,12 @@ if (!mSkeleton->getActive() && mLastFrameNumber != 0) return; - if (mLastFrameNumber == nv->getFrameStamp()->getFrameNumber()) + if (mLastFrameNumber == nv->getTraversalNumber()) return; - mLastFrameNumber = nv->getFrameStamp()->getFrameNumber(); + mLastFrameNumber = nv->getTraversalNumber(); mSkeleton->updateBoneMatrices(nv); - osg::Matrixf geomToSkel = getGeomToSkelMatrix(nv); - // skinning osg::Vec3Array* positionSrc = static_cast(mSourceGeometry->getVertexArray()); osg::Vec3Array* normalSrc = static_cast(mSourceGeometry->getNormalArray()); @@ -246,9 +246,9 @@ const osg::Matrix& invBindMatrix = weightIt->first.second; float weight = weightIt->second; const osg::Matrixf& boneMatrix = bone->mMatrixInSkeletonSpace; - accummulateMatrix(invBindMatrix, boneMatrix, weight, resultMat); + accumulateMatrix(invBindMatrix, boneMatrix, weight, resultMat); } - resultMat = resultMat * geomToSkel; + resultMat = resultMat * mGeomToSkelMatrix; for (std::vector::const_iterator vertexIt = it->second.begin(); vertexIt != it->second.end(); ++vertexIt) { @@ -276,13 +276,14 @@ mSkeleton->updateBoneMatrices(nv); - osg::Matrixf geomToSkel = getGeomToSkelMatrix(nv); + updateGeomToSkelMatrix(nv); + osg::BoundingBox box; for (BoneSphereMap::const_iterator it = mBoneSphereMap.begin(); it != mBoneSphereMap.end(); ++it) { Bone* bone = it->first; osg::BoundingSpheref bs = it->second; - transformBoundingSphere(bone->mMatrixInSkeletonSpace * geomToSkel, bs); + transformBoundingSphere(bone->mMatrixInSkeletonSpace * mGeomToSkelMatrix, bs); box.expandBy(bs); } @@ -297,9 +298,9 @@ getParent(i)->dirtyBound(); } -osg::Matrixf RigGeometry::getGeomToSkelMatrix(osg::NodeVisitor *nv) +void RigGeometry::updateGeomToSkelMatrix(osg::NodeVisitor *nv) { - osg::NodePath path; + mSkelToGeomPath.clear(); bool foundSkel = false; for (osg::NodePath::const_iterator it = nv->getNodePath().begin(); it != nv->getNodePath().end(); ++it) { @@ -309,10 +310,9 @@ foundSkel = true; } else - path.push_back(*it); + mSkelToGeomPath.push_back(*it); } - return osg::computeWorldToLocal(path); - + mGeomToSkelMatrix = osg::computeWorldToLocal(mSkelToGeomPath); } void RigGeometry::setInfluenceMap(osg::ref_ptr influenceMap) diff -Nru openmw-0.37.0/components/sceneutil/riggeometry.hpp openmw-0.38.0/components/sceneutil/riggeometry.hpp --- openmw-0.37.0/components/sceneutil/riggeometry.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/riggeometry.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -19,7 +19,7 @@ RigGeometry(); RigGeometry(const RigGeometry& copy, const osg::CopyOp& copyop); - META_Object(NifOsg, RigGeometry) + META_Object(SceneUtil, RigGeometry) struct BoneInfluence { @@ -48,6 +48,9 @@ osg::ref_ptr mSourceGeometry; Skeleton* mSkeleton; + osg::NodePath mSkelToGeomPath; + osg::Matrixf mGeomToSkelMatrix; + osg::ref_ptr mInfluenceMap; typedef std::pair BoneBindMatrixPair; @@ -69,7 +72,7 @@ bool initFromParentSkeleton(osg::NodeVisitor* nv); - osg::Matrixf getGeomToSkelMatrix(osg::NodeVisitor* nv); + void updateGeomToSkelMatrix(osg::NodeVisitor* nv); }; } diff -Nru openmw-0.37.0/components/sceneutil/skeleton.cpp openmw-0.38.0/components/sceneutil/skeleton.cpp --- openmw-0.37.0/components/sceneutil/skeleton.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/skeleton.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,6 +3,8 @@ #include #include +#include + #include namespace SceneUtil @@ -23,7 +25,7 @@ if (!bone) return; - mCache[bone->getName()] = std::make_pair(getNodePath(), bone); + mCache[Misc::StringUtils::lowerCase(bone->getName())] = std::make_pair(getNodePath(), bone); traverse(node); } @@ -59,7 +61,7 @@ mBoneCacheInit = true; } - BoneCache::iterator found = mBoneCache.find(name); + BoneCache::iterator found = mBoneCache.find(Misc::StringUtils::lowerCase(name)); if (found == mBoneCache.end()) return NULL; @@ -104,10 +106,10 @@ void Skeleton::updateBoneMatrices(osg::NodeVisitor* nv) { - if (nv->getFrameStamp()->getFrameNumber() != mLastFrameNumber) + if (nv->getTraversalNumber() != mLastFrameNumber) mNeedToUpdateBoneMatrices = true; - mLastFrameNumber = nv->getFrameStamp()->getFrameNumber(); + mLastFrameNumber = nv->getTraversalNumber(); if (mNeedToUpdateBoneMatrices) { @@ -135,7 +137,10 @@ void Skeleton::traverse(osg::NodeVisitor& nv) { - if (!mActive && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && mLastFrameNumber != 0) + if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR + // need to process at least 2 frames before shutting off update, since we need to have both frame-alternating RigGeometries initialized + // this would be more naturally handled if the double-buffering was implemented in RigGeometry itself rather than in a FrameSwitch decorator node + && mLastFrameNumber != 0 && mLastFrameNumber+2 <= nv.getTraversalNumber()) return; osg::Group::traverse(nv); } @@ -157,6 +162,7 @@ if (!mNode) { std::cerr << "Bone without node " << std::endl; + return; } if (parentMatrixInSkeletonSpace) mMatrixInSkeletonSpace = mNode->getMatrix() * (*parentMatrixInSkeletonSpace); diff -Nru openmw-0.37.0/components/sceneutil/skeleton.hpp openmw-0.38.0/components/sceneutil/skeleton.hpp --- openmw-0.37.0/components/sceneutil/skeleton.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/skeleton.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -39,7 +39,7 @@ Skeleton(); Skeleton(const Skeleton& copy, const osg::CopyOp& copyop); - META_Node(NifOsg, Skeleton) + META_Node(SceneUtil, Skeleton) /// Retrieve a bone by name. Bone* getBone(const std::string& name); diff -Nru openmw-0.37.0/components/sceneutil/statesetupdater.cpp openmw-0.38.0/components/sceneutil/statesetupdater.cpp --- openmw-0.37.0/components/sceneutil/statesetupdater.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/statesetupdater.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -20,11 +20,10 @@ } } - // Swap to make the StateSet in [0] writable, [1] is now the StateSet that was queued by the last frame - std::swap(mStateSets[0], mStateSets[1]); - node->setStateSet(mStateSets[0]); + osg::StateSet* stateset = mStateSets[nv->getTraversalNumber()%2]; + node->setStateSet(stateset); - apply(mStateSets[0], nv); + apply(stateset, nv); traverse(node, nv); } diff -Nru openmw-0.37.0/components/sceneutil/statesetupdater.hpp openmw-0.38.0/components/sceneutil/statesetupdater.hpp --- openmw-0.37.0/components/sceneutil/statesetupdater.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/statesetupdater.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -12,8 +12,7 @@ /// DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw /// traversals run in parallel can yield up to 200% framerates. /// @par Race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns, - /// the first StateSet is the one we can write to, the second is the one currently in use by the draw traversal of the last frame. - /// After a frame is completed the places are swapped. + /// one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame. /// @par Must be set as UpdateCallback on a Node. /// @note Do not add the same StateSetUpdater to multiple nodes. /// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater. diff -Nru openmw-0.37.0/components/sceneutil/util.cpp openmw-0.38.0/components/sceneutil/util.cpp --- openmw-0.37.0/components/sceneutil/util.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/util.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -3,7 +3,7 @@ namespace SceneUtil { -void transformBoundingSphere (const osg::Matrix& matrix, osg::BoundingSphere& bsphere) +void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere) { osg::BoundingSphere::vec_type xdash = bsphere._center; xdash.x() += bsphere._radius; diff -Nru openmw-0.37.0/components/sceneutil/util.hpp openmw-0.38.0/components/sceneutil/util.hpp --- openmw-0.37.0/components/sceneutil/util.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/util.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -11,7 +11,7 @@ // Transform a bounding sphere by a matrix // based off private code in osg::Transform // TODO: patch osg to make public - void transformBoundingSphere (const osg::Matrix& matrix, osg::BoundingSphere& bsphere); + void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere); osg::Vec4f colourFromRGB (unsigned int clr); diff -Nru openmw-0.37.0/components/sceneutil/workqueue.cpp openmw-0.38.0/components/sceneutil/workqueue.cpp --- openmw-0.37.0/components/sceneutil/workqueue.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sceneutil/workqueue.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -59,7 +59,7 @@ { { OpenThreads::ScopedLock lock(mMutex); - while (mQueue.size()) + while (!mQueue.empty()) { WorkItem* item = mQueue.front(); delete item; @@ -88,7 +88,7 @@ WorkItem *WorkQueue::removeWorkItem() { OpenThreads::ScopedLock lock(mMutex); - while (!mQueue.size() && !mIsReleased) + while (mQueue.empty() && !mIsReleased) { mCondition.wait(&mMutex); } diff -Nru openmw-0.37.0/components/sdlutil/sdlcursormanager.cpp openmw-0.38.0/components/sdlutil/sdlcursormanager.cpp --- openmw-0.37.0/components/sdlutil/sdlcursormanager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/sdlutil/sdlcursormanager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -52,7 +53,7 @@ if (!_gc) { - osg::notify(osg::NOTICE)<<"Failed to create pbuffer, failing back to normal graphics window."<pbuffer = false; _gc = osg::GraphicsContext::createGraphicsContext(traits.get()); @@ -217,10 +218,18 @@ void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) { + osg::ref_ptr decompressed; + if (mCursorMap.find(name) != mCursorMap.end()) return; - osg::ref_ptr decompressed = decompress(image, static_cast(rotDegrees)); + try { + decompressed = decompress(image, static_cast(rotDegrees)); + } catch (std::exception& e) { + std::cerr << e.what() << std::endl; + std::cerr <<"Using default cursor."< #include +#include #include @@ -58,6 +59,7 @@ CategorySettingValueMap Manager::mUserSettings = CategorySettingValueMap(); CategorySettingVector Manager::mChangedSettings = CategorySettingVector(); +typedef std::map< CategorySetting, bool > CategorySettingStatusMap; class SettingsFileParser { @@ -69,6 +71,7 @@ mFile = file; boost::filesystem::ifstream stream; stream.open(boost::filesystem::path(file)); + std::cout << "Loading settings file: " << file << std::endl; std::string currentCategory; mLine = 0; while (!stream.eof() && !stream.fail()) @@ -117,6 +120,215 @@ } } + void saveSettingsFile (const std::string& file, CategorySettingValueMap& settings) + { + // No options have been written to the file yet. + CategorySettingStatusMap written; + for (CategorySettingValueMap::iterator it = settings.begin(); it != settings.end(); ++it) { + written[it->first] = false; + } + + // Have we substantively changed the settings file? + bool changed = false; + + // Were there any lines at all in the file? + bool existing = false; + + // The category/section we're currently in. + std::string currentCategory; + + // Open the existing settings.cfg file to copy comments. This might not be the same file + // as the output file if we're copying the setting from the default settings.cfg for the + // first time. A minor change in API to pass the source file might be in order here. + boost::filesystem::ifstream istream; + boost::filesystem::path ipath(file); + istream.open(ipath); + + // Create a new string stream to write the current settings to. It's likely that the + // input file and the output file are the same, so this stream serves as a temporary file + // of sorts. The setting files aren't very large so there's no performance issue. + std::stringstream ostream; + + // For every line in the input file... + while (!istream.eof() && !istream.fail()) { + std::string line; + std::getline(istream, line); + + // The current character position in the line. + size_t i = 0; + + // Don't add additional newlines at the end of the file. + if (istream.eof()) continue; + + // Copy entirely blank lines. + if (!skipWhiteSpace(i, line)) { + ostream << line << std::endl; + continue; + } + + // There were at least some comments in the input file. + existing = true; + + // Copy comments. + if (line[i] == '#') { + ostream << line << std::endl; + continue; + } + + // Category heading. + if (line[i] == '[') { + size_t end = line.find(']', i); + // This should never happen unless the player edited the file while playing. + if (end == std::string::npos) { + ostream << "# unterminated category: " << line << std::endl; + changed = true; + continue; + } + + // Ensure that all options in the current category have been written. + for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { + if (mit->second == false && mit->first.first == currentCategory) { + std::cout << "Added new setting: [" << currentCategory << "] " + << mit->first.second << " = " << settings[mit->first] << std::endl; + ostream << mit->first.second << " = " << settings[mit->first] << std::endl; + mit->second = true; + changed = true; + } + } + + // Update the current category. + currentCategory = line.substr(i+1, end - (i+1)); + boost::algorithm::trim(currentCategory); + + // Write the (new) current category to the file. + ostream << "[" << currentCategory << "]" << std::endl; + //std::cout << "Wrote category: " << currentCategory << std::endl; + + // A setting can apparently follow the category on an input line. That's rather + // inconvenient, since it makes it more likely to have duplicative sections, + // which our algorithm doesn't like. Do the best we can. + i = end+1; + } + + // Truncate trailing whitespace, since we're changing the file anayway. + if (!skipWhiteSpace(i, line)) + continue; + + // If we've found settings before the first category, something's wrong. This + // should never happen unless the player edited the file while playing, since + // the loadSettingsFile() logic rejects it. + if (currentCategory.empty()) { + ostream << "# empty category name: " << line << std::endl; + changed = true; + continue; + } + + // Which setting was at this location in the input file? + size_t settingEnd = line.find('=', i); + // This should never happen unless the player edited the file while playing. + if (settingEnd == std::string::npos) { + ostream << "# unterminated setting name: " << line << std::endl; + changed = true; + continue; + } + std::string setting = line.substr(i, (settingEnd-i)); + boost::algorithm::trim(setting); + + // Get the existing value so we can see if we've changed it. + size_t valueBegin = settingEnd+1; + std::string value = line.substr(valueBegin); + boost::algorithm::trim(value); + + // Construct the setting map key to determine whether the setting has already been + // written to the file. + CategorySetting key = std::make_pair(currentCategory, setting); + CategorySettingStatusMap::iterator finder = written.find(key); + + // Settings not in the written map are definitely invalid. Currently, this can only + // happen if the player edited the file while playing, because loadSettingsFile() + // will accept anything and pass it along in the map, but in the future, we might + // want to handle invalid settings more gracefully here. + if (finder == written.end()) { + ostream << "# invalid setting: " << line << std::endl; + changed = true; + continue; + } + + // Write the current value of the setting to the file. The key must exist in the + // settings map because of how written was initialized and finder != end(). + ostream << setting << " = " << settings[key] << std::endl; + // Mark that setting as written. + finder->second = true; + // Did we really change it? + if (value != settings[key]) { + std::cout << "Changed setting: [" << currentCategory << "] " + << setting << " = " << settings[key] << std::endl; + changed = true; + } + // No need to write the current line, because we just emitted a replacement. + + // Curiously, it appears that comments at the ends of lines with settings are not + // allowed, and the comment becomes part of the value. Was that intended? + } + + // We're done with the input stream file. + istream.close(); + + // Ensure that all options in the current category have been written. We must complete + // the current category at the end of the file before moving on to any new categories. + for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { + if (mit->second == false && mit->first.first == currentCategory) { + std::cout << "Added new setting: [" << mit->first.first << "] " + << mit->first.second << " = " << settings[mit->first] << std::endl; + ostream << mit->first.second << " = " << settings[mit->first] << std::endl; + mit->second = true; + changed = true; + } + } + + // If there was absolutely nothing in the file (or more likely the file didn't + // exist), start the newly created file with a helpful comment. + if (!existing) { + ostream << "# This is the OpenMW user 'settings.cfg' file. This file only contains" << std::endl; + ostream << "# explicitly changed settings. If you would like to revert a setting" << std::endl; + ostream << "# to its default, simply remove it from this file. For available" << std::endl; + ostream << "# settings, see the file 'settings-default.cfg' or the documentation at:" << std::endl; + ostream << "#" << std::endl; + ostream << "# https://wiki.openmw.org/index.php?title=Settings" << std::endl; + } + + // We still have one more thing to do before we're completely done writing the file. + // It's possible that there are entirely new categories, or that the input file had + // disappeared completely, so we need ensure that all settings are written to the file + // regardless of those issues. + for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { + // If the setting hasn't been written yet. + if (mit->second == false) { + // If the catgory has changed, write a new category header. + if (mit->first.first != currentCategory) { + currentCategory = mit->first.first; + std::cout << "Created new setting section: " << mit->first.first << std::endl; + ostream << std::endl; + ostream << "[" << currentCategory << "]" << std::endl; + } + std::cout << "Added new setting: [" << mit->first.first << "] " + << mit->first.second << " = " << settings[mit->first] << std::endl; + // Then write the setting. No need to mark it as written because we're done. + ostream << mit->first.second << " = " << settings[mit->first] << std::endl; + changed = true; + } + } + + // Now install the newly written file in the requested place. + if (changed) { + std::cout << "Updating settings file: " << ipath << std::endl; + boost::filesystem::ofstream ofstream; + ofstream.open(ipath); + ofstream << ostream.rdbuf(); + ofstream.close(); + } + } + private: /// Increment i until it longer points to a whitespace character /// in the string or has reached the end of the string. @@ -141,6 +353,13 @@ int mLine; }; +void Manager::clear() +{ + mDefaultSettings.clear(); + mUserSettings.clear(); + mChangedSettings.clear(); +} + void Manager::loadDefault(const std::string &file) { SettingsFileParser parser; @@ -155,18 +374,8 @@ void Manager::saveUser(const std::string &file) { - boost::filesystem::ofstream stream; - stream.open(boost::filesystem::path(file)); - std::string currentCategory; - for (CategorySettingValueMap::iterator it = mUserSettings.begin(); it != mUserSettings.end(); ++it) - { - if (it->first.first != currentCategory) - { - currentCategory = it->first.first; - stream << "\n[" << currentCategory << "]\n"; - } - stream << it->first.second << " = " << it->second << "\n"; - } + SettingsFileParser parser; + parser.saveSettingsFile(file, mUserSettings); } std::string Manager::getString(const std::string &setting, const std::string &category) diff -Nru openmw-0.37.0/components/settings/settings.hpp openmw-0.38.0/components/settings/settings.hpp --- openmw-0.37.0/components/settings/settings.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/settings/settings.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -23,6 +23,9 @@ static CategorySettingVector mChangedSettings; ///< tracks all the settings that were changed since the last apply() call + void clear(); + ///< clears all settings and default settings + void loadDefault (const std::string& file); ///< load file as the default settings (can be overridden by user settings) diff -Nru openmw-0.37.0/components/terrain/terraingrid.cpp openmw-0.38.0/components/terrain/terraingrid.cpp --- openmw-0.37.0/components/terrain/terraingrid.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/terrain/terraingrid.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -6,13 +6,14 @@ #include #include +#include #include -#include #include #include #include +#include #include @@ -90,7 +91,7 @@ return NULL; // no terrain defined osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); - osg::ref_ptr transform (new osg::PositionAttitudeTransform); + osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); if (parent) @@ -126,10 +127,6 @@ osg::BoundingBox bounds(min, max); geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds)); - osg::ref_ptr geode (new osg::Geode); - geode->addDrawable(geometry); - - std::vector layerList; std::vector > blendmaps; mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList); @@ -169,11 +166,20 @@ effect->addCullCallback(new SceneUtil::LightListCallback); transform->addChild(effect); - effect->addChild(geode); + +#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) + osg::Node* toAttach = geometry.get(); +#else + osg::ref_ptr geode (new osg::Geode); + geode->addDrawable(geometry); + osg::Node* toAttach = geode.get(); +#endif + + effect->addChild(toAttach); if (mIncrementalCompileOperation) { - mIncrementalCompileOperation->add(geode); + mIncrementalCompileOperation->add(toAttach); mIncrementalCompileOperation->add(textureCompileDummy); } diff -Nru openmw-0.37.0/components/to_utf8/to_utf8.cpp openmw-0.38.0/components/to_utf8/to_utf8.cpp --- openmw-0.37.0/components/to_utf8/to_utf8.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/to_utf8/to_utf8.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -85,10 +85,10 @@ assert(input[size] == 0); // Note: The rest of this function is designed for single-character - // input encodings only. It also assumes that the input the input - // encoding shares its first 128 values (0-127) with ASCII. There are - // no plans to add more encodings to this module (we are using utf8 - // for new content files), so that shouldn't be an issue. + // input encodings only. It also assumes that the input encoding + // shares its first 128 values (0-127) with ASCII. There are no plans + // to add more encodings to this module (we are using utf8 for new + // content files), so that shouldn't be an issue. // Compute output length, and check for pure ascii input at the same // time. diff -Nru openmw-0.37.0/components/vfs/manager.cpp openmw-0.38.0/components/vfs/manager.cpp --- openmw-0.37.0/components/vfs/manager.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/vfs/manager.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -1,7 +1,9 @@ #include "manager.hpp" +#include #include -#include + +#include #include "archive.hpp" @@ -15,7 +17,7 @@ char nonstrict_normalize_char(char ch) { - return ch == '\\' ? '/' : std::tolower(ch,std::locale::classic()); + return ch == '\\' ? '/' : Misc::StringUtils::toLower(ch); } void normalize_path(std::string& path, bool strict) diff -Nru openmw-0.37.0/components/widgets/imagebutton.cpp openmw-0.38.0/components/widgets/imagebutton.cpp --- openmw-0.37.0/components/widgets/imagebutton.cpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/widgets/imagebutton.cpp 2016-01-12 16:11:28.000000000 +0000 @@ -42,18 +42,31 @@ ImageBox::onMouseButtonPressed(_left, _top, _id); } - MyGUI::IntSize ImageButton::getRequestedSize(bool logError) + MyGUI::IntSize ImageButton::getRequestedSize() { MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(mImageNormal); if (!texture) { - if (logError) - std::cerr << "ImageButton: can't find " << mImageNormal << std::endl; + std::cerr << "ImageButton: can't find " << mImageNormal << std::endl; return MyGUI::IntSize(0,0); } return MyGUI::IntSize (texture->getWidth(), texture->getHeight()); } + void ImageButton::setImage(const std::string &image) + { + size_t extpos = image.find_last_of("."); + std::string imageNoExt = image.substr(0, extpos); + + std::string ext = image.substr(extpos); + + mImageNormal = imageNoExt + "_idle" + ext; + mImageHighlighted = imageNoExt + "_over" + ext; + mImagePushed = imageNoExt + "_pressed" + ext; + + setImageTexture(mImageNormal); + } + void ImageButton::onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) { if (_id == MyGUI::MouseButton::Left) diff -Nru openmw-0.37.0/components/widgets/imagebutton.hpp openmw-0.38.0/components/widgets/imagebutton.hpp --- openmw-0.37.0/components/widgets/imagebutton.hpp 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/components/widgets/imagebutton.hpp 2016-01-12 16:11:28.000000000 +0000 @@ -14,7 +14,10 @@ MYGUI_RTTI_DERIVED(ImageButton) public: - MyGUI::IntSize getRequestedSize(bool logError = true); + MyGUI::IntSize getRequestedSize(); + + /// Set mImageNormal, mImageHighlighted and mImagePushed based on file convention (image_idle.ext, image_over.ext and image_pressed.ext) + void setImage(const std::string& image); protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); diff -Nru openmw-0.37.0/debian/changelog openmw-0.38.0/debian/changelog --- openmw-0.37.0/debian/changelog 2015-11-27 15:36:40.000000000 +0000 +++ openmw-0.38.0/debian/changelog 2016-01-20 13:39:41.000000000 +0000 @@ -1,8 +1,9 @@ -openmw (0.37.0-1~wily) wily; urgency=low +openmw (0.38.0-1~wily) wily; urgency=low - * New release: OpenMW-0.37.0 + * Upstream release: OpenMW-0.38.0 + * Updated libpng12-dev to libpng-dev to ease transition (Closes: #810201) - -- Bret Curtis Fri, 27 Nov 2015 08:14:09 +0200 + -- Bret Curtis Mon, 01 Jun 2015 08:14:09 +0200 openmw (0.37.0-1) unstable; urgency=medium @@ -15,9 +16,9 @@ [Bret Curtis] * Switch from libav to ffmpeg - * Upstream release: OpenMW-0.37.0 + * Upstream release: OpenMW-0.37.0 (Closes: #803848, #799722) - -- Bret Curtis Thu, 26 Nov 2015 08:00:37 -0400 + -- Bret Curtis Thu, 26 Nov 2015 08:00:37 -0400 openmw (0.36.1-1) unstable; urgency=low diff -Nru openmw-0.37.0/debian/control openmw-0.38.0/debian/control --- openmw-0.37.0/debian/control 2015-11-27 15:34:55.000000000 +0000 +++ openmw-0.38.0/debian/control 2016-01-19 10:51:41.000000000 +0000 @@ -8,7 +8,7 @@ libgl1-mesa-dev | libgl-dev, libglu1-mesa-dev | libglu-dev, libice-dev, libopenal-dev, libsm-dev, uuid-dev, libqt4-dev, libtinyxml-dev, libx11-dev, libxaw7-dev, libxrandr-dev, libxt-dev, libzzip-dev, libz-dev, - libpng12-dev, libavcodec-dev, libavformat-dev, libavdevice-dev, libavutil-dev, + libpng-dev, libavcodec-dev, libavformat-dev, libavdevice-dev, libavutil-dev, libswscale-dev, libpostproc-dev, libswresample-dev, libsdl2-dev, libmygui-dev (>= 3.2.1), libunshield-dev, libopenscenegraph-dev, libqt4-opengl-dev @@ -20,8 +20,7 @@ Package: openmw Architecture: any Recommends: openmw-launcher -Depends: ${shlibs:Depends}, ${misc:Depends}, openmw-data (= ${source:Version}), - libtxc-dxtn-s2tc0 +Depends: ${shlibs:Depends}, ${misc:Depends}, openmw-data (= ${source:Version}) Description: Reimplementation of The Elder Scrolls III: Morrowind OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind. diff -Nru openmw-0.37.0/files/mygui/openmw_journal.layout openmw-0.38.0/files/mygui/openmw_journal.layout --- openmw-0.37.0/files/mygui/openmw_journal.layout 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/files/mygui/openmw_journal.layout 2016-01-12 16:11:28.000000000 +0000 @@ -26,9 +26,7 @@ - - - + @@ -66,15 +64,11 @@ - - - + - - - + @@ -90,9 +84,7 @@ - - - + diff -Nru openmw-0.37.0/files/mygui/openmw_layers.xml openmw-0.38.0/files/mygui/openmw_layers.xml --- openmw-0.37.0/files/mygui/openmw_layers.xml 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/files/mygui/openmw_layers.xml 2016-01-12 16:11:28.000000000 +0000 @@ -4,7 +4,7 @@ - + diff -Nru openmw-0.37.0/files/mygui/openmw_settings_window.layout openmw-0.38.0/files/mygui/openmw_settings_window.layout --- openmw-0.37.0/files/mygui/openmw_settings_window.layout 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/files/mygui/openmw_settings_window.layout 2016-01-12 16:11:28.000000000 +0000 @@ -280,7 +280,7 @@ - + diff -Nru openmw-0.37.0/files/settings-default.cfg openmw-0.38.0/files/settings-default.cfg --- openmw-0.37.0/files/settings-default.cfg 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/files/settings-default.cfg 2016-01-12 16:11:28.000000000 +0000 @@ -1,244 +1,368 @@ -# WARNING: Editing this file might have no effect, as these -# settings are overwritten by your user settings file. +# WARNING: If this file is named settings-default.cfg, then editing +# this file might have no effect, as these settings may be overwritten +# by your user settings.cfg file (see documentation for its location). +# +# This file provides minimal documentation for each setting, and +# ranges of recommended values. For detailed explanations of the +# significance of each setting, interaction with other settings, hard +# limits on value ranges and more information in general, please read +# the detailed documentation at the OpenMW Wiki page: +# +# https://wiki.openmw.org/index.php?title=Settings +# -[Video] -resolution x = 800 -resolution y = 600 +[Camera] -fullscreen = false -window border = true -screen = 0 +# Near clipping plane (>0.0, e.g. 0.01 to 18.0). +near clip = 5.0 -# Minimize the window if it loses key focus? -minimize on focus loss = true +# Cull objects smaller than one pixel. +small feature culling = true -# Valid values: 0 for no antialiasing, or any power of two -antialiasing = 0 +# Maximum visible distance (e.g. 2000.0 to 6666.0). Caution: this setting +# can dramatically affect performance, see documentation for details. +viewing distance = 6666.0 + +# Camera field of view in degrees (e.g. 30.0 to 110.0). +# Does not affect the player's hands in the first person camera. +field of view = 55.0 + +# Field of view for first person meshes (i.e. the player's hands) +# Best to leave this at the default since vanilla assets are not complete enough to adapt to high FoV's. Too low FoV would clip the hands off screen. +first person field of view = 55.0 -vsync = false +[Cells] -gamma = 1.00 -contrast = 1.00 +# Adjacent exterior cells loaded (>0). Caution: this setting can +# dramatically affect performance, see documentation for details. +exterior cell load distance = 1 -# Maximum framerate in frames per second, 0 = unlimited -framerate limit = 0 +[Map] + +# Size of each exterior cell in pixels in the world map. (e.g. 12 to 24). +# Warning: affects explored areas in save files, see documentation. +global map cell size = 18 + +# Zoom level in pixels for HUD map widget. 64 is one cell, 128 is 1/4 +# cell, 256 is 1/8 cell. See documentation for details. (e.g. 64 to 256). +local map hud widget size = 256 + +# Resolution of local map in GUI window in pixels. See documentation +# for details which may affect cell load performance. (e.g. 128 to 1024). +local map resolution = 256 + +# Size of local map in GUI window in pixels. (e.g. 256 to 1024). +local map widget size = 512 [GUI] + +# Scales GUI window and widget size. (<1.0 is smaller, >1.0 is larger). scaling factor = 1.0 -# 1 is fully opaque +# Transparency of GUI windows (0.0 to 1.0, transparent to opaque). menu transparency = 0.84 -# 0 - instantly, 1 - max. delay -tooltip delay = 0 +# Time until tool tip appears when hovering over an object (0.0 is +# instantly, 1.0 is the maximum delay of about 1.5 seconds). +tooltip delay = 0.0 + +# Stretch menus, load screens, etc. to the window aspect ratio. +stretch menu background = false +# Subtitles for NPC spoken dialog and some sound effects. subtitles = false +# Red flash visually showing player damage. hit fader = true + +# Werewolf overlay border around screen or window. werewolf overlay = true -stretch menu background = false +# Color for tool tips and crosshair when owned by an NPC (R G B A). +color background owned = 0.15 0.0 0.0 1.0 +color crosshair owned = 1.0 0.15 0.15 1.0 -# colour definitions (red green blue alpha) -color background owned = 0.15 0 0 1 -color crosshair owned = 1 0.15 0.15 1 +[HUD] -[General] -# Camera field of view -field of view = 55 +# Displays the crosshair or reticle when not in GUI mode. +crosshair = true + +[Game] + +# Color crosshair and tool tip when object is owned by an NPC. (O is +# no color, 1 is tool tip only, 2 is crosshair only, and 3 is both). +show owned = 0 -# Texture filtering mode. valid values: -# bilinear -# trilinear -texture filtering = +# Always use the best mode of attack: e.g. chop, slash or thrust. +best attack = false + +# Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100). +difficulty = 0 + +# Show duration of magic effect and lights in the spells window. +show effect duration = false + +[General] +# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). anisotropy = 4 +# File format for screenshots. (jpg, png, tga, and possibly more). screenshot format = png -[Shadows] -# Shadows are only supported when object shaders are on! -enabled = false +# Texture magnification filter type. (nearest or linear). +texture mag filter = linear -# Split the shadow maps, allows for a larger shadow distance -split = false +# Texture minification filter type. (nearest or linear). +texture min filter = linear -# Increasing shadow distance will lower the shadow quality. -# Uses "shadow distance" or "split shadow distance" depending on "split" setting. -shadow distance = 1300 -# This one shouldn't be too low, otherwise you'll see artifacts. Use at least 2x max viewing distance. -split shadow distance = 14000 +# Texture mipmap type. (none, nearest, or linear). +texture mipmap = nearest -# Size of the shadow textures, higher means higher quality -texture size = 1024 +[Input] -# Turn on/off various shadow casters -actor shadows = true -misc shadows = true -statics shadows = true -terrain shadows = true +# Capture control of the cursor prevent movement outside the window. +grab cursor = true -# Fraction of the total shadow distance after which the shadow starts to fade out -fade start = 0.8 +# Key controlling sneak toggles setting instead of being held down. +toggle sneak = false -debug = false +# Player is running by default. +always run = false -[HUD] -crosshair = true +# Zoom in and out from player in third person view with mouse wheel. +allow third person zoom = false -[Objects] -shaders = true +# Camera sensitivity when not in GUI mode. (>0.0, e.g. 0.1 to 5.0). +camera sensitivity = 1.0 -[Map] -# Adjusts the scale of the global map -global map cell size = 18 +# Vertical camera sensitivity multiplier when not in GUI mode. +# (>0.0, Because it's a multiplier values should be near 1.0) +camera y multiplier = 1.0 -local map resolution = 256 +# Invert the vertical axis while not in GUI mode. +invert y axis = false -local map widget size = 512 -local map hud widget size = 256 +[Saves] -[Cells] -exterior cell load distance = 1 +# Name of last character played, and default for loading save files. +character = -[Camera] -near clip = 5 +# Automatically save the game whenever the player rests. +autosave = true -# The maximum distance with no pop-in will be: (see RenderingManager::configureFog) -# viewing distance * view frustum factor <= cell size (8192) - loading threshold (1024) -# view frustum factor takes into account that the view frustum end is a plane, so at the edges of the screen you can see further than you should be able to. -# exact factor would depend on FOV -viewing distance = 6666 +# Display the time played on each save file in the load menu. +timeplayed = false -# Culling of objects smaller than a pixel -small feature culling = true +[Sound] -[Terrain] -distant land = false +# Name of audio device file. Blank means use the default device. +device = -shader = true +# Volumes are 0.0 for silent and 1.0 for the maximum volume. + +# Master volume. Controls all other volumes. +master volume = 1.0 + +# Footsteps volume. +footsteps volume = 0.2 + +# Music tracks volume. +music volume = 0.5 + +# Sound effects volume. +sfx volume = 1.0 + +# Voice dialog volume. +voice volume = 0.8 + +# Minimum size to use for the sound buffer cache, in MB. When the cache is +# filled, old buffers will be unloaded until it's using no more than this much +# memory. Must be less than or equal to 'buffer cache max'. +buffer cache min = 14 + +# Maximum size to use for the sound buffer cache, in MB. The cache can use up +# to this much memory until old buffers get purged. +buffer cache max = 16 + +# Specifies whether to enable HRTF processing. Valid values are: -1 = auto, +# 0 = off, 1 = on. +hrtf enable = -1 + +# Specifies which HRTF to use when HRTF is used. Blank means use the default. +hrtf = + +[Video] + +# Resolution of the OpenMW window or screen. +resolution x = 800 +resolution y = 600 + +# OpenMW takes complete control of the screen. +fullscreen = false + +# Determines which screen OpenMW is on. (>=0). +screen = 0 + +# Minimize OpenMW if it loses cursor or keyboard focus. +minimize on focus loss = true + +# An operating system border is drawn around the OpenMW window. +window border = true + +# Anti-aliasing reduces jagged polygon edges. (0, 2, 4, 8, 16). +antialiasing = 0 + +# Enable vertical syncing to reduce tearing defects. +vsync = false + +# Maximum frames per second. 0.0 is unlimited, or >0.0 to limit. +framerate limit = 0.0 + +# Game video contrast. (>0.0). No effect in Linux. +contrast = 1.0 + +# Video gamma setting. (>0.0). No effect in Linux. +gamma = 1.0 [Water] + +# Enable water shader with reflections and optionally refraction. shader = false +# Reflection and refraction texture size in pixels. (512, 1024, 2048). +rtt size = 512 + +# Enable refraction which affects visibility through water plane. refraction = false -rtt size = 512 +[Objects] -[Sound] -# Device name. Blank means default -device = +# Enable shaders for objects other than water. Unused. +shaders = true -# Volumes. master volume affects all other volumes. -master volume = 1.0 -sfx volume = 1.0 -music volume = 0.5 -footsteps volume = 0.2 -voice volume = 0.8 +[Terrain] +# Use shaders for terrain? Unused. +shader = true -[Input] +# Distant land is rendered? Unused. +distant land = false -grab cursor = true +[Shadows] -invert y axis = false +# Enable shadows. Other shadow settings disabled if false. Unused. +enabled = false -camera sensitivity = 1.0 +# Size of the shadow textures in pixels. Unused. (e.g. 256 to 2048). +texture size = 1024 -ui sensitivity = 1.0 +# Actors cast shadows. Unused. +actor shadows = true -camera y multiplier = 1.0 +# Static objects cast shadows. Unused. +statics shadows = true -always run = false +# Terrain cast shadows. Unused. +terrain shadows = true -allow third person zoom = false +# Miscellaneous objects cast shadows. Unused. +misc shadows = true -toggle sneak = false +# Debugging of shadows. Unused. +debug = false -[Game] -# Always use the most powerful attack when striking with a weapon (chop, slash or thrust) -best attack = false +# Fraction of distance after which shadow starts to fade out. Unused. +fade start = 0.8 -difficulty = 0 +# Split shadow maps, allowing for a larger shadow distance. Unused. +split = false -# Change crosshair/toolTip color when pointing on owned object -#0: nothing changed -#1: tint toolTip -#2: tint crosshair -#3: both -show owned = 0 -# Show the remaining duration of magic effects and lights -show effect duration = false +# Distance for shadows if not split. Smaller is poorer. Unused. +shadow distance = 1300 -[Saves] -character = -# Save when resting -autosave = true -# display time played -timeplayed = false +# Distance for shadows if split. Unused. +split shadow distance = 14000 [Windows] -inventory x = 0 + +# Location and sizes of windows as a fraction of the OpenMW window or +# screen size. (0.0 to 1.0). X & Y, Width & Height. + +# Stats window displaying level, race, class, skills and stats. +stats x = 0.0 +stats y = 0.0 +stats w = 0.375 +stats h = 0.4275 + +# Spells window displaying powers, spells, and magical items. +spells x = 0.625 +spells y = 0.5725 +spells w = 0.375 +spells h = 0.4275 + +# Local and world map window. +map x = 0.625 +map y = 0.0 +map w = 0.375 +map h = 0.5725 + +# Dialog window for talking with NPCs. +dialogue x = 0.095 +dialogue y = 0.095 +dialogue w = 0.810 +dialogue h = 0.810 + +# Alchemy window for crafting potions. +alchemy x = 0.25 +alchemy y = 0.25 +alchemy w = 0.5 +alchemy h = 0.5 + +# Console command window for debugging commands. +console x = 0.0 +console y = 0.0 +console w = 1.0 +console h = 0.5 + +# Player inventory window when explicitly opened. +inventory x = 0.0 inventory y = 0.4275 inventory w = 0.6225 inventory h = 0.5725 -inventory container x = 0 +# Player inventory window when searching a container. +inventory container x = 0.0 inventory container y = 0.4275 inventory container w = 0.6225 inventory container h = 0.5725 -inventory barter x = 0 +# Player inventory window when bartering with a shopkeeper. +inventory barter x = 0.0 inventory barter y = 0.4275 inventory barter w = 0.6225 inventory barter h = 0.5725 -inventory companion x = 0 +# Player inventory window when trading with a companion. +inventory companion x = 0.0 inventory companion y = 0.4275 inventory companion w = 0.6225 inventory companion h = 0.5725 +# Container inventory when searching a container. container x = 0.25 -container y = 0 +container y = 0.0 container w = 0.75 container h = 0.375 -companion x = 0.25 -companion y = 0 -companion w = 0.75 -companion h = 0.375 - -map x = 0.625 -map y = 0 -map w = 0.375 -map h = 0.5725 - +# NPC inventory window when bartering with a shopkeeper. barter x = 0.25 -barter y = 0 +barter y = 0.0 barter w = 0.75 barter h = 0.375 -alchemy x = 0.25 -alchemy y = 0.25 -alchemy w = 0.5 -alchemy h = 0.5 - -stats x = 0 -stats y = 0 -stats w = 0.375 -stats h = 0.4275 - -spells x = 0.625 -spells y = 0.5725 -spells w = 0.375 -spells h = 0.4275 - -console x = 0 -console y = 0 -console w = 1 -console h = 0.5 - -dialogue h = 0.810 -dialogue w = 0.810 -dialogue x = 0.095 -dialogue y = 0.095 +# NPC inventory window when trading with a companion. +companion x = 0.25 +companion y = 0.0 +companion w = 0.75 +companion h = 0.375 diff -Nru openmw-0.37.0/README.md openmw-0.38.0/README.md --- openmw-0.37.0/README.md 2015-11-21 08:31:43.000000000 +0000 +++ openmw-0.38.0/README.md 2016-01-12 16:11:28.000000000 +0000 @@ -7,7 +7,7 @@ OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set. -* Version: 0.37.0 +* Version: 0.38.0 * License: GPL (see docs/license/GPL3.txt for more information) * Website: http://www.openmw.org * IRC: #openmw on irc.freenode.net