diff --git a/README.md b/README.md index 7c24a4c815..e177847521 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ OpenMW ====== -[![Build Status](https://travis-ci.org/OpenMW/openmw.svg?branch=coverity_scan)](https://travis-ci.org/OpenMW/openmw) - -[![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) +[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 9bbf202665..98e18521e0 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -59,6 +59,7 @@ struct Arguments std::string outname; std::vector types; + std::string name; ESMData data; ESM::ESMReader reader; @@ -78,6 +79,8 @@ bool parseOptions (int argc, char** argv, Arguments &info) ("type,t", bpo::value< std::vector >(), "Show only records of this type (four character record code). May " "be specified multiple times. Only affects dump mode.") + ("name,n", bpo::value(), + "Show only the record with this name. Only affects dump mode.") ("plain,p", "Print contents of dialogs, books and scripts. " "(skipped by default)" "Only affects dump mode.") @@ -148,7 +151,9 @@ bool parseOptions (int argc, char** argv, Arguments &info) } if (variables.count("type") > 0) - info.types = variables["type"].as< std::vector >(); + info.types = variables["type"].as< std::vector >(); + if (variables.count("name") > 0) + info.name = variables["name"].as(); info.mode = variables["mode"].as(); if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp")) @@ -265,6 +270,8 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Deleted: " << deleted << std::endl; + if (!ref.mKey.empty()) + std::cout << " Key: '" << ref.mKey << "'" << std::endl; } } @@ -358,6 +365,9 @@ int load(Arguments& info) if (id.empty()) id = esm.getHNOString("INAM"); + if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id)) + interested = false; + if(!quiet && interested) std::cout << "\nRecord: " << n.toString() << " '" << id << "'\n"; @@ -385,7 +395,7 @@ int load(Arguments& info) record->load(esm); if (!quiet && interested) record->print(); - if (record->getType().val == ESM::REC_CELL && loadCells) { + if (record->getType().val == ESM::REC_CELL && loadCells && interested) { loadCell(record->cast()->get(), esm, info); } diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 2f07a5f088..df2631c13f 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -2,6 +2,8 @@ #include "labels.hpp" #include +#include + #include void printAIPackage(ESM::AIPackage p) @@ -752,7 +754,7 @@ void Record::print() if (mData.mCell != "") std::cout << " Cell: " << mData.mCell << std::endl; if (mData.mData.mDisposition > 0) - std::cout << " Disposition: " << mData.mData.mDisposition << std::endl; + std::cout << " Disposition/Journal index: " << mData.mData.mDisposition << std::endl; if (mData.mData.mGender != ESM::DialInfo::NA) std::cout << " Gender: " << mData.mData.mGender << std::endl; if (mData.mSound != "") @@ -812,7 +814,6 @@ void Record::print() { std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl; std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl; - std::cout << " HasData: " << mData.mHasData << std::endl; std::cout << " DataTypes: " << mData.mDataTypes << std::endl; // Seems like this should done with reference counting in the diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 409b8bcfff..76fc9aa94a 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -24,6 +24,7 @@ set(ESSIMPORTER_FILES convertcrec.cpp convertcntc.cpp convertscri.cpp + convertscpt.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 3b82988b8f..4d21d2b7ac 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -69,7 +70,65 @@ namespace ESSImport esm.getSubHeader(); data.resize(esm.getSubSize()); esm.getExact(&data[0], data.size()); - convertImage(&data[0], data.size(), maph.size, maph.size, Ogre::PF_BYTE_RGB, "map.tga"); + + Ogre::DataStreamPtr stream (new Ogre::MemoryDataStream(&data[0], data.size())); + mGlobalMapImage.loadRawData(stream, maph.size, maph.size, 1, Ogre::PF_BYTE_RGB); + // to match openmw size + mGlobalMapImage.resize(maph.size*2, maph.size*2, Ogre::Image::FILTER_BILINEAR); + } + + void ConvertFMAP::write(ESM::ESMWriter &esm) + { + int numcells = mGlobalMapImage.getWidth() / 18; // NB truncating, doesn't divide perfectly + // with the 512x512 map the game has by default + int cellSize = mGlobalMapImage.getWidth()/numcells; + + // Note the upper left corner of the (0,0) cell should be at (width/2, height/2) + + mContext->mGlobalMapState.mBounds.mMinX = -numcells/2; + mContext->mGlobalMapState.mBounds.mMaxX = (numcells-1)/2; + mContext->mGlobalMapState.mBounds.mMinY = -(numcells-1)/2; + mContext->mGlobalMapState.mBounds.mMaxY = numcells/2; + + Ogre::Image image2; + std::vector data; + int width = cellSize*numcells; + int height = cellSize*numcells; + data.resize(width*height*4, 0); + image2.loadDynamicImage(&data[0], width, height, Ogre::PF_BYTE_RGBA); + + for (std::set >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it) + { + if (it->first > mContext->mGlobalMapState.mBounds.mMaxX + || it->first < mContext->mGlobalMapState.mBounds.mMinX + || it->second > mContext->mGlobalMapState.mBounds.mMaxY + || it->second < mContext->mGlobalMapState.mBounds.mMinY) + { + // out of bounds, I think this could happen, since the original engine had a fixed-size map + continue; + } + + int imageLeftSrc = mGlobalMapImage.getWidth()/2; + int imageTopSrc = mGlobalMapImage.getHeight()/2; + imageLeftSrc += it->first * cellSize; + imageTopSrc -= it->second * cellSize; + int imageLeftDst = width/2; + int imageTopDst = height/2; + imageLeftDst += it->first * cellSize; + imageTopDst -= it->second * cellSize; + for (int x=0; xmGlobalMapState.mImageData.resize(encoded->size()); + encoded->read(&mContext->mGlobalMapState.mImageData[0], encoded->size()); + + esm.startRecord(ESM::REC_GMAP); + mContext->mGlobalMapState.save(esm); + esm.endRecord(ESM::REC_GMAP); } void ConvertCell::read(ESM::ESMReader &esm) @@ -103,6 +162,10 @@ namespace ESSImport // (probably offset of that specific fog texture?) while (esm.isNextSub("NAM8")) { + if (cell.isExterior()) // TODO: NAM8 occasionally exists for cells that haven't been explored. + // are there any flags marking explored cells? + mContext->mExploredCells.insert(std::make_pair(cell.mData.mX, cell.mData.mY)); + esm.getSubHeader(); if (esm.getSubSize() == 36) @@ -313,10 +376,6 @@ namespace ESSImport it->save(esm); esm.endRecord(ESM::REC_MARK); } - - esm.startRecord(ESM::REC_GMAP); - mContext->mGlobalMapState.save(esm); - esm.endRecord(ESM::REC_GMAP); } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 90b75fee1f..c80ccb951a 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_ESSIMPORT_CONVERTER_H #define OPENMW_ESSIMPORT_CONVERTER_H +#include + #include #include @@ -13,6 +15,9 @@ #include #include #include +#include +#include +#include #include "importcrec.hpp" #include "importcntc.hpp" @@ -29,6 +34,7 @@ #include "convertacdt.hpp" #include "convertnpcc.hpp" +#include "convertscpt.hpp" namespace ESSImport { @@ -206,7 +212,7 @@ public: else { int index = npcc.mNPDT.mIndex; - mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)).second; + mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)); } } }; @@ -266,7 +272,7 @@ public: faction.mExpelled = (it->mFlags & 0x2) != 0; faction.mRank = it->mRank; faction.mReputation = it->mReputation; - mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction; + mContext->mPlayer.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction; } for (int i=0; i<8; ++i) mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; @@ -308,6 +314,10 @@ class ConvertFMAP : public Converter { public: virtual void read(ESM::ESMReader &esm); + virtual void write(ESM::ESMWriter &esm); + +private: + Ogre::Image mGlobalMapImage; }; class ConvertCell : public Converter @@ -428,7 +438,24 @@ public: std::string id = esm.getHNString("NAME"); DIAL dial; dial.load(esm); + if (dial.mIndex > 0) + mDials[id] = dial; } + virtual void write(ESM::ESMWriter &esm) + { + for (std::map::const_iterator it = mDials.begin(); it != mDials.end(); ++it) + { + esm.startRecord(ESM::REC_QUES); + ESM::QuestState state; + state.mFinished = 0; + state.mState = it->second.mIndex; + state.mTopic = Misc::StringUtils::lowerCase(it->first); + state.save(esm); + esm.endRecord(ESM::REC_QUES); + } + } +private: + std::map mDials; }; class ConvertQUES : public Converter @@ -455,11 +482,69 @@ public: class ConvertGAME : public Converter { public: + ConvertGAME() : mHasGame(false) {} + + std::string toString(int weatherId) + { + switch (weatherId) + { + case 0: + return "clear"; + case 1: + return "cloudy"; + case 2: + return "foggy"; + case 3: + return "overcast"; + case 4: + return "rain"; + case 5: + return "thunderstorm"; + case 6: + return "ashstorm"; + case 7: + return "blight"; + case 8: + return "snow"; + case 9: + return "blizzard"; + case -1: + return ""; + default: + { + std::stringstream error; + error << "unknown weather id: " << weatherId; + throw std::runtime_error(error.str()); + } + } + } + virtual void read(ESM::ESMReader &esm) { - GAME game; - game.load(esm); + mGame.load(esm); + mHasGame = true; } + + virtual void write(ESM::ESMWriter &esm) + { + if (!mHasGame) + return; + esm.startRecord(ESM::REC_WTHR); + ESM::WeatherState weather; + weather.mCurrentWeather = toString(mGame.mGMDT.mCurrentWeather); + weather.mNextWeather = toString(mGame.mGMDT.mNextWeather); + weather.mRemainingTransitionTime = mGame.mGMDT.mWeatherTransition/100.f*(0.015*24*3600); + weather.mHour = mContext->mHour; + weather.mWindSpeed = 0.f; + weather.mTimePassed = 0.0; + weather.mFirstUpdate = false; + weather.save(esm); + esm.endRecord(ESM::REC_WTHR); + } + +private: + bool mHasGame; + GAME mGame; }; /// Running global script @@ -470,7 +555,21 @@ public: { SCPT script; script.load(esm); + ESM::GlobalScript out; + convertSCPT(script, out); + mScripts.push_back(out); } + virtual void write(ESM::ESMWriter &esm) + { + for (std::vector::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it) + { + esm.startRecord(ESM::REC_GSCR); + it->save(esm); + esm.endRecord(ESM::REC_GSCR); + } + } +private: + std::vector mScripts; }; } diff --git a/apps/essimporter/convertscpt.cpp b/apps/essimporter/convertscpt.cpp new file mode 100644 index 0000000000..ca81ebbbf2 --- /dev/null +++ b/apps/essimporter/convertscpt.cpp @@ -0,0 +1,17 @@ +#include "convertscpt.hpp" + +#include + +#include "convertscri.hpp" + +namespace ESSImport +{ + + void convertSCPT(const SCPT &scpt, ESM::GlobalScript &out) + { + out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString()); + out.mRunning = scpt.mRunning; + convertSCRI(scpt.mSCRI, out.mLocals); + } + +} diff --git a/apps/essimporter/convertscpt.hpp b/apps/essimporter/convertscpt.hpp new file mode 100644 index 0000000000..3390bd6070 --- /dev/null +++ b/apps/essimporter/convertscpt.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTSCPT_H +#define OPENMW_ESSIMPORT_CONVERTSCPT_H + +#include + +#include "importscpt.hpp" + +namespace ESSImport +{ + +void convertSCPT(const SCPT& scpt, ESM::GlobalScript& out); + +} + +#endif diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index 136183668b..9a062484b3 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -60,10 +60,6 @@ namespace ESSImport if (esm.isNextSub("PWPS")) esm.skipHSub(); - // unsure at which point between LSTN and CHRD - if (esm.isNextSub("APUD")) - esm.skipHSub(); // 40 bytes, starts with string "ancestor guardian". maybe spellcasting in progress? - if (esm.isNextSub("WNAM")) { std::string id = esm.getHString(); @@ -77,6 +73,20 @@ namespace ESSImport esm.skipHSub(); // 4 byte, 0 } + while (esm.isNextSub("APUD")) + { + // used power + esm.getSubHeader(); + std::string id = esm.getString(32); + (void)id; + // timestamp can't be used: this is the total hours passed, calculated by + // timestamp = 24 * (365 * year + cumulativeDays[month] + day) + // unfortunately cumulativeDays[month] is not clearly defined, + // in the (non-MCP) vanilla version the first month was missing, but MCP added it. + double timestamp; + esm.getT(timestamp); + } + // FIXME: not all actors have this, add flag if (esm.isNextSub("CHRD")) // npc only esm.getHExact(mSkills, 27*2*sizeof(int)); diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 211195d3f5..a466770d0d 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -29,6 +29,9 @@ namespace ESSImport ESM::DialogueState mDialogueState; + // cells which should show an explored overlay on the global map + std::set > mExploredCells; + ESM::GlobalMap mGlobalMapState; int mDay, mMonth, mYear; diff --git a/apps/essimporter/importgame.cpp b/apps/essimporter/importgame.cpp index 0b3a4f1a7f..1012541b49 100644 --- a/apps/essimporter/importgame.cpp +++ b/apps/essimporter/importgame.cpp @@ -7,7 +7,23 @@ namespace ESSImport void GAME::load(ESM::ESMReader &esm) { - esm.getHNT(mGMDT, "GMDT"); + esm.getSubNameIs("GMDT"); + esm.getSubHeader(); + if (esm.getSubSize() == 92) + { + esm.getExact(&mGMDT, 92); + mGMDT.mSecundaPhase = 0; + } + else if (esm.getSubSize() == 96) + { + esm.getT(mGMDT); + } + else + esm.fail("unexpected subrecord size for GAME.GMDT"); + + mGMDT.mWeatherTransition &= (0x000000ff); + mGMDT.mSecundaPhase &= (0x000000ff); + mGMDT.mMasserPhase &= (0x000000ff); } } diff --git a/apps/essimporter/importgame.hpp b/apps/essimporter/importgame.hpp index 7bb8143207..fca7d72a0a 100644 --- a/apps/essimporter/importgame.hpp +++ b/apps/essimporter/importgame.hpp @@ -20,7 +20,7 @@ namespace ESSImport int mCurrentWeather, mNextWeather; int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition - int masserPhase, secundaPhase; // top 3 bytes may be garbage + int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage }; GMDT mGMDT; diff --git a/apps/essimporter/importscpt.cpp b/apps/essimporter/importscpt.cpp index 374fd05fad..652383cdaa 100644 --- a/apps/essimporter/importscpt.cpp +++ b/apps/essimporter/importscpt.cpp @@ -13,8 +13,14 @@ namespace ESSImport mSCRI.load(esm); - mRNAM = -1; - esm.getHNOT(mRNAM, "RNAM"); + mRefNum = -1; + if (esm.isNextSub("RNAM")) + { + mRunning = true; + esm.getHT(mRefNum); + } + else + mRunning = false; } } diff --git a/apps/essimporter/importscpt.hpp b/apps/essimporter/importscpt.hpp index ca2439dda2..ce54c3a734 100644 --- a/apps/essimporter/importscpt.hpp +++ b/apps/essimporter/importscpt.hpp @@ -14,7 +14,6 @@ namespace ESSImport { // A running global script - // TODO: test how targeted scripts are saved struct SCPT { ESM::Script::SCHD mSCHD; @@ -22,7 +21,8 @@ namespace ESSImport // values of local variables SCRI mSCRI; - int mRNAM; // unknown, seems to be -1 for some scripts, some huge integer for others + bool mRunning; + int mRefNum; // Targeted reference, -1: no reference void load(ESM::ESMReader& esm); }; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f58ef08095..ccda90832b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -453,6 +453,7 @@ namespace MWBase /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; + virtual void reattachPlayerCamera() = 0; /// \todo this does not belong here virtual void frameStarted (float dt, bool paused) = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8f62cc1e81..d01a9ec7ff 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -296,25 +296,6 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - // NPC stats - if (!ref->mBase->mFaction.empty()) - { - std::string faction = ref->mBase->mFaction; - if (const ESM::Faction* fact = MWBase::Environment::get().getWorld()->getStore().get().search(faction)) - { - if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) - { - data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt52.mRank); - } - else - { - data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt12.mRank); - } - } - else - std::cerr << "Warning: ignoring nonexistent faction '" << faction << "' on NPC '" << ref->mBase->mId << "'" << std::endl; - } - // creature stats int gold=0; if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) @@ -371,13 +352,13 @@ namespace MWClass std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; } - if (data->mNpcStats.getFactionRanks().size()) + if (!ref->mBase->mFaction.empty()) { static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get() .find("iAutoRepFacMod")->getInt(); static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get() .find("iAutoRepLevMod")->getInt(); - int rank = data->mNpcStats.getFactionRanks().begin()->second; + int rank = ref->mBase->getFactionRank(); data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1)); } @@ -1371,4 +1352,16 @@ namespace MWClass { return true; } + + std::string Npc::getPrimaryFaction (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + return ref->mBase->mFaction; + } + + int Npc::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + return ref->mBase->getFactionRank(); + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 56931a4199..9aece7368c 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -189,6 +189,9 @@ namespace MWClass virtual void restock (const MWWorld::Ptr& ptr) const; virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const; + + virtual std::string getPrimaryFaction(const MWWorld::Ptr &ptr) const; + virtual int getPrimaryFactionRank(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 7c67cdc5c7..adb7d38923 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -67,14 +67,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const if (isCreature) return false; - MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor); - std::map::const_iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction)); - - if (iter==stats.getFactionRanks().end()) + if (!Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), info.mFaction)) return false; // check rank - if (iter->second < info.mData.mRank) + if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank) return false; } else if (info.mData.mRank != -1) @@ -83,13 +80,8 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const return false; // Rank requirement, but no faction given. Use the actor's faction, if there is one. - MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor); - - if (!stats.getFactionRanks().size()) - return false; - // check rank - if (stats.getFactionRanks().begin()->second < info.mData.mRank) + if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank) return false; } @@ -336,12 +328,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con case SelectWrapper::Function_RankRequirement: { - if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) + std::string faction = mActor.getClass().getPrimaryFaction(mActor); + if (faction.empty()) return 0; - std::string faction = - mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first; - int rank = getFactionRank (player, faction); if (rank>=9) @@ -376,15 +366,14 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con case SelectWrapper::Function_FactionRankDiff: { - if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) + std::string faction = mActor.getClass().getPrimaryFaction(mActor); + + if (faction.empty()) return 0; - const std::pair faction = - *mActor.getClass().getNpcStats (mActor).getFactionRanks().begin(); - - int rank = getFactionRank (player, faction.first); - - return rank-faction.second; + int rank = getFactionRank (player, faction); + int npcRank = mActor.getClass().getPrimaryFactionRank(mActor); + return rank-npcRank; } case SelectWrapper::Function_WerewolfKills: @@ -396,11 +385,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con { bool low = select.getFunction()==SelectWrapper::Function_RankLow; - if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) - return 0; + std::string factionId = mActor.getClass().getPrimaryFaction(mActor); - std::string factionId = - mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first; + if (factionId.empty()) + return 0; int value = 0; @@ -454,7 +442,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_NotFaction: - return !Misc::StringUtils::ciEqual(mActor.get()->mBase->mFaction, select.getName()); + return !Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), select.getName()); case SelectWrapper::Function_NotClass: @@ -494,8 +482,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_SameFaction: - return mActor.getClass().getNpcStats (mActor).isSameFaction ( - player.getClass().getNpcStats (player)); + return player.getClass().getNpcStats (player).isInFaction(mActor.getClass().getPrimaryFaction(mActor)); case SelectWrapper::Function_PcCommonDisease: @@ -512,11 +499,10 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_PcExpelled: { - if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) - return false; + std::string faction = mActor.getClass().getPrimaryFaction(mActor); - std::string faction = - mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first; + if (faction.empty()) + return false; return player.getClass().getNpcStats(player).getExpelled(faction); } @@ -561,7 +547,7 @@ int MWDialogue::Filter::getFactionRank (const MWWorld::Ptr& actor, const std::st { MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor); - std::map::const_iterator iter = stats.getFactionRanks().find (factionId); + std::map::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase(factionId)); if (iter==stats.getFactionRanks().end()) return -1; diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index b92d7cacea..9f07f7b6fc 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -89,7 +89,7 @@ namespace MWDialogue for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) - if (iter->mData.mDisposition==index) /// \todo cleanup info structure + if (iter->mData.mJournalIndex==index) { return iter->mId; } diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 3b57912dae..99dab0cf8b 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -259,7 +259,12 @@ namespace MWDialogue record.load (reader); if (isThere (record.mTopic)) - mQuests.insert (std::make_pair (record.mTopic, record)); + { + std::pair result = mQuests.insert (std::make_pair (record.mTopic, record)); + // reapply quest index, this is to handle users upgrading from only + // Morrowind.esm (no quest states) to Morrowind.esm + Tribunal.esm + result.first->second.setIndex(record.mState); + } } } } diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index a699286a15..a9e39b3798 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -75,7 +75,7 @@ namespace MWDialogue iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == entry.mInfoId) { - index = iter->mData.mDisposition; /// \todo cleanup info structure + index = iter->mData.mJournalIndex; break; } diff --git a/apps/openmw/mwgui/backgroundimage.cpp b/apps/openmw/mwgui/backgroundimage.cpp index 1e87c0ff19..9c07c5780b 100644 --- a/apps/openmw/mwgui/backgroundimage.cpp +++ b/apps/openmw/mwgui/backgroundimage.cpp @@ -5,14 +5,14 @@ namespace MWGui { -void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool correct) +void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool stretch) { if (mChild) { MyGUI::Gui::getInstance().destroyWidget(mChild); mChild = NULL; } - if (correct) + if (!stretch) { setImageTexture("black.png"); diff --git a/apps/openmw/mwgui/backgroundimage.hpp b/apps/openmw/mwgui/backgroundimage.hpp index 3d1a61eaf7..8c963b7620 100644 --- a/apps/openmw/mwgui/backgroundimage.hpp +++ b/apps/openmw/mwgui/backgroundimage.hpp @@ -18,9 +18,9 @@ namespace MWGui /** * @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions - * @param correct Add black bars? + * @param stretch Stretch to fill the whole screen, or add black bars? */ - void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool correct=true); + void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool stretch=true); virtual void setSize (const MyGUI::IntSize &_value); virtual void setCoord (const MyGUI::IntCoord &_value); diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 5629c25f1b..4eb9a271c1 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -290,7 +290,8 @@ namespace MWGui // Add the command to the history, and set the current pointer to // the end of the list - mCommandHistory.push_back(cm); + if (mCommandHistory.empty() || mCommandHistory.back() != cm) + mCommandHistory.push_back(cm); mCurrent = mCommandHistory.end(); mEditString.clear(); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 49e641aedc..53b1e679a0 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -154,8 +154,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); - // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last - // or we end up using a possibly invalid model. setTitle(container.getClass().getName(container)); } diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 41c3604b42..e30f6e7a81 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -155,11 +155,6 @@ void ItemView::setSize(const MyGUI::IntSize &_value) layoutWidgets(); } -void ItemView::setSize(int _width, int _height) -{ - setSize(MyGUI::IntSize(_width, _height)); -} - void ItemView::setCoord(const MyGUI::IntCoord &_value) { bool changed = (_value.width != getWidth() || _value.height != getHeight()); @@ -168,11 +163,6 @@ void ItemView::setCoord(const MyGUI::IntCoord &_value) layoutWidgets(); } -void ItemView::setCoord(int _left, int _top, int _width, int _height) -{ - setCoord(MyGUI::IntCoord(_left, _top, _width, _height)); -} - void ItemView::registerComponents() { MyGUI::FactoryManager::getInstance().registerFactory("Widget"); diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index 1a5bd79a3d..9aeba67520 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -37,8 +37,6 @@ namespace MWGui virtual void setSize(const MyGUI::IntSize& _value); virtual void setCoord(const MyGUI::IntCoord& _value); - void setSize(int _width, int _height); - void setCoord(int _left, int _top, int _width, int _height); void onSelectedItem (MyGUI::Widget* sender); void onSelectedBackground (MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index d10a295c16..270066c97a 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -149,7 +149,9 @@ namespace MWGui Ogre::TextureManager::getSingleton ().load (randomSplash, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); // TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3 - mBackgroundImage->setBackgroundImage(randomSplash, true, true); + // we can't do this by default, because the Morrowind splash screens are 1024x1024, but should be displayed as 4:3 + bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); + mBackgroundImage->setBackgroundImage(randomSplash, true, stretch); } else std::cerr << "No loading screens found!" << std::endl; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index c7855b367e..f1c4c40885 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -160,6 +160,8 @@ namespace MWGui if (!show) return; + bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); + if (mHasAnimatedMenu) { if (!mVideo) @@ -180,16 +182,7 @@ namespace MWGui int screenHeight = viewSize.height; mVideoBackground->setSize(screenWidth, screenHeight); - if (mVideo->getVideoHeight() > 0) - { - double imageaspect = static_cast(mVideo->getVideoWidth())/mVideo->getVideoHeight(); - - int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2); - int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2); - - mVideo->setCoord(leftPadding, topPadding, - screenWidth - leftPadding*2, screenHeight - topPadding*2); - } + mVideo->autoResize(stretch); mVideo->setVisible(true); } @@ -199,7 +192,7 @@ namespace MWGui { mBackground = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Stretch, "Menu"); - mBackground->setBackgroundImage("textures\\menu_morrowind.dds"); + mBackground->setBackgroundImage("textures\\menu_morrowind.dds", true, stretch); } mBackground->setVisible(true); } diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 3e5a01e3a7..1c17a11f66 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -201,11 +201,6 @@ namespace MWGui layoutWidgets(); } - void SpellView::setSize(int _width, int _height) - { - setSize(MyGUI::IntSize(_width, _height)); - } - void SpellView::setCoord(const MyGUI::IntCoord &_value) { bool changed = (_value.width != getWidth() || _value.height != getHeight()); @@ -214,11 +209,6 @@ namespace MWGui layoutWidgets(); } - void SpellView::setCoord(int _left, int _top, int _width, int _height) - { - setCoord(MyGUI::IntCoord(_left, _top, _width, _height)); - } - void SpellView::adjustSpellWidget(const Spell &spell, SpellModel::ModelIndex index, MyGUI::Widget *widget) { if (spell.mType == Spell::Type_EnchantedItem) diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index 30daf86711..005d206f4d 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -45,8 +45,6 @@ namespace MWGui virtual void setSize(const MyGUI::IntSize& _value); virtual void setCoord(const MyGUI::IntCoord& _value); - void setSize(int _width, int _height); - void setCoord(int _left, int _top, int _width, int _height); private: MyGUI::ScrollView* mScrollView; diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 19f9c749c8..a407aedc72 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -108,7 +108,6 @@ namespace MWGui void StatsWindow::setPlayerName(const std::string& playerName) { mMainWidget->castType()->setCaption(playerName); - adjustWindowCaption(); } void StatsWindow::setValue (const std::string& id, const MWMechanics::AttributeValue& value) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index bafa89d5b1..fab7c63a6c 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -133,8 +133,6 @@ namespace MWGui updateLabels(); - // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last - // or we end up using a possibly invalid model. setTitle(actor.getClass().getName(actor)); onFilterChanged(mFilterAll); diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 2ade0f4c52..0460708411 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwsound/movieaudiofactory.hpp" namespace MWGui @@ -46,4 +48,24 @@ bool VideoWidget::hasAudioStream() return mPlayer->hasAudioStream(); } +void VideoWidget::autoResize(bool stretch) +{ + MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); + if (getParent()) + screenSize = getParent()->getSize(); + + if (getVideoHeight() > 0 && !stretch) + { + double imageaspect = static_cast(getVideoWidth())/getVideoHeight(); + + int leftPadding = std::max(0.0, (screenSize.width - screenSize.height * imageaspect) / 2); + int topPadding = std::max(0.0, (screenSize.height - screenSize.width / imageaspect) / 2); + + setCoord(leftPadding, topPadding, + screenSize.width - leftPadding*2, screenSize.height - topPadding*2); + } + else + setCoord(0,0,screenSize.width,screenSize.height); +} + } diff --git a/apps/openmw/mwgui/videowidget.hpp b/apps/openmw/mwgui/videowidget.hpp index e350573c4f..ee44328eb6 100644 --- a/apps/openmw/mwgui/videowidget.hpp +++ b/apps/openmw/mwgui/videowidget.hpp @@ -35,6 +35,12 @@ namespace MWGui /// Stop video and free resources (done automatically on destruction) void stop(); + /// Adjust the coordinates of this video widget relative to its parent, + /// based on the dimensions of the playing video. + /// @param stretch Stretch the video to fill the whole screen? If false, + /// black bars may be added to fix the aspect ratio. + void autoResize (bool stretch); + private: std::auto_ptr mPlayer; }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e976b984ff..cf46792872 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1712,18 +1712,9 @@ namespace MWGui void WindowManager::sizeVideo(int screenWidth, int screenHeight) { // Use black bars to correct aspect ratio + bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); mVideoBackground->setSize(screenWidth, screenHeight); - - if (mVideoWidget->getVideoHeight() > 0) - { - double imageaspect = static_cast(mVideoWidget->getVideoWidth())/mVideoWidget->getVideoHeight(); - - int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2); - int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2); - - mVideoWidget->setCoord(leftPadding, topPadding, - screenWidth - leftPadding*2, screenHeight - topPadding*2); - } + mVideoWidget->autoResize(stretch); } WindowModal* WindowManager::getCurrentModal() const diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index f7e285ff5b..f2b125add6 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -58,7 +58,7 @@ namespace // magnitude of pits/obstacles is defined by PATHFIND_Z_REACH bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY) { - if((to - from).length() >= PATHFIND_CAUTION_DIST || abs(from.z - to.z) <= PATHFIND_Z_REACH) + if((to - from).length() >= PATHFIND_CAUTION_DIST || std::abs(from.z - to.z) <= PATHFIND_Z_REACH) { Ogre::Vector3 dir = to - from; dir.z = 0; @@ -69,7 +69,7 @@ namespace // cast up-down ray and find height in world space of hit float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1); - if(abs(from.z - h) <= PATHFIND_Z_REACH) + if(std::abs(from.z - h) <= PATHFIND_Z_REACH) return true; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fa7e52af22..8d438c7031 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1372,7 +1372,7 @@ void CharacterController::update(float duration) //Force Jump Logic - bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5); + bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || std::abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5); if(!inwater && !flying) { //Force Jump diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index ad4d3aabff..1d243ef453 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -29,7 +29,7 @@ Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 norma ); } -void applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const Ogre::Vector3& hitPosition) +bool applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const Ogre::Vector3& hitPosition) { std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : ""; if (!enchantmentName.empty()) @@ -41,8 +41,10 @@ void applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWMechanics::CastSpell cast(attacker, victim); cast.mHitPosition = hitPosition; cast.cast(object); + return true; } } + return false; } } @@ -216,15 +218,16 @@ namespace MWMechanics damage *= gmst.find("fCombatKODamageMult")->getFloat(); // Apply "On hit" effect of the weapon - applyEnchantment(attacker, victim, weapon, hitPosition); + bool appliedEnchantment = applyEnchantment(attacker, victim, weapon, hitPosition); if (weapon != projectile) - applyEnchantment(attacker, victim, projectile, hitPosition); + appliedEnchantment = applyEnchantment(attacker, victim, projectile, hitPosition); if (damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); - // Arrows shot at enemies have a chance to turn up in their inventory - if (victim != MWBase::Environment::get().getWorld()->getPlayerPtr()) + // Non-enchanted arrows shot at enemies have a chance to turn up in their inventory + if (victim != MWBase::Environment::get().getWorld()->getPlayerPtr() + && !appliedEnchantment) { float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat(); if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f) diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index 5d9e291181..96410c35d7 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -70,9 +70,9 @@ namespace MWMechanics else { if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name()) - return getLevelledItem(ref.getPtr().get()->mBase, failChance); + return getLevelledItem(ref.getPtr().get()->mBase, false, failChance); else - return getLevelledItem(ref.getPtr().get()->mBase, failChance); + return getLevelledItem(ref.getPtr().get()->mBase, true, failChance); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 0a2c3cfff6..d52dcb43c2 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -572,8 +572,7 @@ namespace MWMechanics float reaction = 0; int rank = 0; - std::string npcFaction = ""; - if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first; + std::string npcFaction = ptr.getClass().getPrimaryFaction(ptr); Misc::StringUtils::toLower(npcFaction); @@ -1156,10 +1155,10 @@ namespace MWMechanics // If committing a crime against a faction member, expell from the faction if (!victim.isEmpty() && victim.getClass().isNpc()) { - std::string factionID; - if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty()) - factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first; - if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim))) + std::string factionID = victim.getClass().getPrimaryFaction(victim); + + const std::map& playerRanks = player.getClass().getNpcStats(player).getFactionRanks(); + if (playerRanks.find(Misc::StringUtils::lowerCase(factionID)) != playerRanks.end()) { player.getClass().getNpcStats(player).expell(factionID); } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 6d9388408f..c3a4d23127 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -91,12 +91,6 @@ void MWMechanics::NpcStats::lowerRank(const std::string &faction) } } -void MWMechanics::NpcStats::setFactionRank(const std::string &faction, int rank) -{ - const std::string lower = Misc::StringUtils::lowerCase(faction); - mFactionRank[lower] = rank; -} - void MWMechanics::NpcStats::joinFaction(const std::string& faction) { const std::string lower = Misc::StringUtils::lowerCase(faction); @@ -127,14 +121,9 @@ void MWMechanics::NpcStats::clearExpelled(const std::string& factionID) mExpelled.erase(Misc::StringUtils::lowerCase(factionID)); } -bool MWMechanics::NpcStats::isSameFaction (const NpcStats& npcStats) const +bool MWMechanics::NpcStats::isInFaction (const std::string& faction) const { - for (std::map::const_iterator iter (mFactionRank.begin()); iter!=mFactionRank.end(); - ++iter) - if (npcStats.mFactionRank.find (iter->first)!=npcStats.mFactionRank.end()) - return true; - - return false; + return (mFactionRank.find(Misc::StringUtils::lowerCase(faction)) != mFactionRank.end()); } float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType, diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 9e543bb798..d183e23dd1 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -33,9 +33,7 @@ namespace MWMechanics // ----- used by the player only, maybe should be moved at some point ------- int mBounty; int mWerewolfKills; - /// NPCs other than the player can only have one faction. But for the sake of consistency - /// we use the same data structure for the PC and the NPCs. - /// \note the faction key must be in lowercase + /// Used for the player only; NPCs have maximum one faction defined in their NPC record std::map mFactionRank; std::set mExpelled; std::map mFactionReputation; @@ -74,17 +72,13 @@ namespace MWMechanics void lowerRank(const std::string& faction); /// Join this faction, setting the initial rank to 0. void joinFaction(const std::string& faction); - /// Warning: this function performs no check whether the rank exists, - /// and should be used in initial actor setup only. - void setFactionRank(const std::string& faction, int rank); const std::set& getExpelled() const { return mExpelled; } bool getExpelled(const std::string& factionID) const; void expell(const std::string& factionID); void clearExpelled(const std::string& factionID); - bool isSameFaction (const NpcStats& npcStats) const; - ///< Do *this and \a npcStats share a faction? + bool isInFaction (const std::string& faction) const; float getSkillGain (int skillIndex, const ESM::Class& class_, int usageType = -1, int level = -1, float extraFactor=1.f) const; diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 55ebfeab54..6bf81e8611 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -115,8 +115,8 @@ namespace MWMechanics if(mDistSameSpot == -1) mDistSameSpot = DIST_SAME_SPOT * (cls.getSpeed(actor) / 150); - bool samePosition = (abs(pos.pos[0] - mPrevX) < mDistSameSpot) && - (abs(pos.pos[1] - mPrevY) < mDistSameSpot); + bool samePosition = (std::abs(pos.pos[0] - mPrevX) < mDistSameSpot) && + (std::abs(pos.pos[1] - mPrevY) < mDistSameSpot); // update position mPrevX = pos.pos[0]; mPrevY = pos.pos[1]; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index a4460c59d2..f0f6158ed4 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -66,108 +66,83 @@ namespace MWRender loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgress(0); - const Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19); - const Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13); - const Ogre::ColourValue groundColour(0.254, 0.19, 0.13); - const Ogre::ColourValue mountainColour(0.05, 0.05, 0.05); - const Ogre::ColourValue hillColour(0.16, 0.12, 0.08); + std::vector data (mWidth * mHeight * 3); - //if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) - if (1) + for (int x = mMinX; x <= mMaxX; ++x) { - std::vector data (mWidth * mHeight * 3); - - for (int x = mMinX; x <= mMaxX; ++x) + for (int y = mMinY; y <= mMaxY; ++y) { - for (int y = mMinY; y <= mMaxY; ++y) + ESM::Land* land = esmStore.get().search (x,y); + + if (land) { - ESM::Land* land = esmStore.get().search (x,y); + int mask = ESM::Land::DATA_WNAM; + if (!land->isDataLoaded(mask)) + land->loadData(mask); + } - if (land) + for (int cellY=0; cellYisDataLoaded(mask)) - land->loadData(mask); - } + int vertexX = float(cellX)/float(mCellSize) * 9; + int vertexY = float(cellY)/float(mCellSize) * 9; - for (int cellY=0; cellYmDataTypes & ESM::Land::DATA_WNAM) + y = (land->mLandData->mWnam[vertexY * 9 + vertexX] << 4) / 2048.f; + else + y = (SCHAR_MIN << 4) / 2048.f; + if (y < 0) { - int vertexX = float(cellX)/float(mCellSize) * ESM::Land::LAND_SIZE; - int vertexY = float(cellY)/float(mCellSize) * ESM::Land::LAND_SIZE; - - - int texelX = (x-mMinX) * mCellSize + cellX; - int texelY = (mHeight-1) - ((y-mMinY) * mCellSize + cellY); - - unsigned char r,g,b; - - if (land) - { - const float landHeight = land->mLandData->mHeights[vertexY * ESM::Land::LAND_SIZE + vertexX]; - - if (landHeight >= 0) - { - const float hillHeight = 2500.f; - if (landHeight >= hillHeight) - { - const float mountainHeight = 15000.f; - float factor = std::min(1.f, float(landHeight-hillHeight)/mountainHeight); - r = (hillColour.r * (1-factor) + mountainColour.r * factor) * 255; - g = (hillColour.g * (1-factor) + mountainColour.g * factor) * 255; - b = (hillColour.b * (1-factor) + mountainColour.b * factor) * 255; - } - else - { - float factor = std::min(1.f, float(landHeight)/hillHeight); - r = (groundColour.r * (1-factor) + hillColour.r * factor) * 255; - g = (groundColour.g * (1-factor) + hillColour.g * factor) * 255; - b = (groundColour.b * (1-factor) + hillColour.b * factor) * 255; - } - } - else - { - if (landHeight >= -100) - { - float factor = std::min(1.f, -1*landHeight/100.f); - r = (((waterShallowColour+groundColour)/2).r * (1-factor) + waterShallowColour.r * factor) * 255; - g = (((waterShallowColour+groundColour)/2).g * (1-factor) + waterShallowColour.g * factor) * 255; - b = (((waterShallowColour+groundColour)/2).b * (1-factor) + waterShallowColour.b * factor) * 255; - } - else - { - float factor = std::min(1.f, -1*(landHeight-100)/1000.f); - r = (waterShallowColour.r * (1-factor) + waterDeepColour.r * factor) * 255; - g = (waterShallowColour.g * (1-factor) + waterDeepColour.g * factor) * 255; - b = (waterShallowColour.b * (1-factor) + waterDeepColour.b * factor) * 255; - } - } - - } + r = (14 * y + 38); + g = 20 * y + 56; + b = 18 * y + 51; + } + else if (y < 0.3f) + { + if (y < 0.1f) + y *= 8.f; else { - r = waterDeepColour.r * 255; - g = waterDeepColour.g * 255; - b = waterDeepColour.b * 255; + y -= 0.1; + y += 0.8; } - - data[texelY * mWidth * 3 + texelX * 3] = r; - data[texelY * mWidth * 3 + texelX * 3+1] = g; - data[texelY * mWidth * 3 + texelX * 3+2] = b; + r = 66 - 32 * y; + g = 48 - 23 * y; + b = 33 - 16 * y; } + else + { + y -= 0.3f; + y *= 1.428f; + r = 34 - 29 * y; + g = 25 - 20 * y; + b = 17 - 12 * y; + } + + data[texelY * mWidth * 3 + texelX * 3] = r; + data[texelY * mWidth * 3 + texelX * 3+1] = g; + data[texelY * mWidth * 3 + texelX * 3+2] = b; } } + loadingListener->increaseProgress(); + if (land) + land->unloadData(); } - - Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); - - tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_STATIC); - tex->loadRawData(stream, mWidth, mHeight, Ogre::PF_B8G8R8); } - else - tex = Ogre::TextureManager::getSingleton ().getByName ("GlobalMap.png"); + + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); + + tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_STATIC); + tex->loadRawData(stream, mWidth, mHeight, Ogre::PF_B8G8R8); tex->load(); @@ -267,9 +242,9 @@ namespace MWRender { const ESM::GlobalMap::Bounds& bounds = map.mBounds; - if (bounds.mMaxX-bounds.mMinX <= 0) + if (bounds.mMaxX-bounds.mMinX < 0) return; - if (bounds.mMaxY-bounds.mMinY <= 0) + if (bounds.mMaxY-bounds.mMinY < 0) return; if (bounds.mMinX > bounds.mMaxX diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 9b05ce0a23..0926498c0a 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -1020,7 +1020,10 @@ void NpcAnimation::setVampire(bool vampire) return; if ((mNpcType == Type_Vampire) != vampire) { - rebuild(); + if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + MWBase::Environment::get().getWorld()->reattachPlayerCamera(); + else + rebuild(); } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8e8b18cd12..149080d930 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1036,10 +1036,10 @@ void RenderingManager::enableTerrain(bool enable) if (!mTerrain) { if (Settings::Manager::getBool("distant land", "Terrain")) - mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(true), RV_Terrain, Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64); else - mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(false), RV_Terrain, Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY); mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), Settings::Manager::getBool("split", "Shadows")); diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index cbd9e24443..e74b6af124 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -9,6 +9,22 @@ namespace MWRender { + TerrainStorage::TerrainStorage(bool preload) + { + if (preload) + { + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + + MWWorld::Store::iterator it = esmStore.get().begin(); + for (; it != esmStore.get().end(); ++it) + { + ESM::Land* land = const_cast(&*it); // TODO: fix store interface + land->loadData(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX); + } + } + } + void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY) { minX = 0, minY = 0, maxX = 0, maxY = 0; @@ -39,6 +55,12 @@ namespace MWRender const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); ESM::Land* land = esmStore.get().search(cellX, cellY); + if (!land) + return NULL; + + const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; + if (!land->isDataLoaded(flags)) + land->loadData(flags); return land; } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 30a2a61ac3..e6f4a04ad3 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -14,6 +14,10 @@ namespace MWRender virtual const ESM::LandTexture* getLandTexture(int index, short plugin); public: + ///@param preload Preload all Land records at startup? If using the multithreaded terrain component, this + /// should be set to "true" in order to avoid race conditions. + TerrainStorage(bool preload); + /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); }; diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 1d82b84181..ea4b8d06e8 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -196,7 +196,7 @@ namespace MWScript MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - runtime.push (ptr.getClass().getNpcStats (ptr).isSameFaction (player.getClass().getNpcStats (player))); + player.getClass().getNpcStats (player).isInFaction(ptr.getClass().getPrimaryFaction(ptr)); } }; diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 6c4bb3be52..92fd51d87d 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -2,6 +2,7 @@ #include "globalscripts.hpp" #include +#include #include #include diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 5cc4d69b65..435f82de7a 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -313,17 +313,19 @@ namespace MWScript std::string InterpreterContext::getNPCRank() const { - if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty()) + const MWWorld::Ptr& ptr = getReferenceImp(); + std::string faction = ptr.getClass().getPrimaryFaction(ptr); + if (faction.empty()) throw std::runtime_error("getNPCRank(): NPC is not in a faction"); - const std::map& ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks(); - std::map::const_iterator it = ranks.begin(); + int rank = ptr.getClass().getPrimaryFactionRank(ptr); + if (rank < 0 || rank > 9) + throw std::runtime_error("getNPCRank(): invalid rank"); MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::ESMStore &store = world->getStore(); - const ESM::Faction *faction = store.get().find(it->first); - - return faction->mRanks[it->second]; + const ESM::Faction *fact = store.get().find(faction); + return fact->mRanks[rank]; } std::string InterpreterContext::getPCName() const @@ -352,13 +354,12 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); - if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty()) + std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp()); + if (factionId.empty()) throw std::runtime_error("getPCRank(): NPC is not in a faction"); - std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first; - const std::map& ranks = player.getClass().getNpcStats (player).getFactionRanks(); - std::map::const_iterator it = ranks.find(factionId); + std::map::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId)); int rank = -1; if (it != ranks.end()) rank = it->second; @@ -382,13 +383,12 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); - if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty()) + std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp()); + if (factionId.empty()) throw std::runtime_error("getPCNextRank(): NPC is not in a faction"); - std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first; - const std::map& ranks = player.getClass().getNpcStats (player).getFactionRanks(); - std::map::const_iterator it = ranks.find(factionId); + std::map::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId)); int rank = -1; if (it != ranks.end()) rank = it->second; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 09ab0183c7..f0cf12e5ea 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -32,13 +32,12 @@ namespace { std::string getDialogueActorFaction(MWWorld::Ptr actor) { - const MWMechanics::NpcStats &stats = actor.getClass().getNpcStats (actor); - - if (stats.getFactionRanks().empty()) + std::string factionId = actor.getClass().getPrimaryFaction(actor); + if (factionId.empty()) throw std::runtime_error ( "failed to determine dialogue actors faction (because actor is factionless)"); - return stats.getFactionRanks().begin()->first; + return factionId; } } @@ -665,14 +664,7 @@ namespace MWScript } else { - if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty()) - { - factionID = ""; - } - else - { - factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first; - } + factionID = ptr.getClass().getPrimaryFaction(ptr); } ::Misc::StringUtils::toLower(factionID); // Make sure this faction exists @@ -779,8 +771,7 @@ namespace MWScript } else { - if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty()) - factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first; + factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) @@ -815,8 +806,7 @@ namespace MWScript } else { - if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty()) - factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first; + factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) @@ -850,8 +840,7 @@ namespace MWScript } else { - if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty()) - factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first; + factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) @@ -941,14 +930,7 @@ namespace MWScript } else { - if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty()) - { - factionID = ""; - } - else - { - factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first; - } + factionID = ptr.getClass().getPrimaryFaction(ptr); } ::Misc::StringUtils::toLower(factionID); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -980,14 +962,7 @@ namespace MWScript } else { - if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty()) - { - factionID = ""; - } - else - { - factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first; - } + factionID = ptr.getClass().getPrimaryFaction(ptr); } MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") @@ -1014,14 +989,7 @@ namespace MWScript } else { - if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty()) - { - factionID = ""; - } - else - { - factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first; - } + factionID = ptr.getClass().getPrimaryFaction(ptr); } MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") @@ -1038,13 +1006,10 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - std::string factionID = ""; - if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty()) + std::string factionID = ptr.getClass().getPrimaryFaction(ptr); + if(factionID.empty()) return; - else - { - factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first; - } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // no-op when executed on the player @@ -1064,13 +1029,10 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - std::string factionID = ""; - if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty()) + std::string factionID = ptr.getClass().getPrimaryFaction(ptr); + if(factionID.empty()) return; - else - { - factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first; - } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // no-op when executed on the player diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index edb3f37883..afc5ed6356 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -445,4 +445,13 @@ namespace MWWorld { throw std::runtime_error("class does not support fight rating"); } + + std::string Class::getPrimaryFaction (const MWWorld::Ptr& ptr) const + { + return std::string(); + } + int Class::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const + { + return -1; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 3c44abe668..751375c556 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -342,6 +342,9 @@ namespace MWWorld virtual std::string getSound(const MWWorld::Ptr& ptr) const; virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const; + + virtual std::string getPrimaryFaction (const MWWorld::Ptr& ptr) const; + virtual int getPrimaryFactionRank (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwworld/esmloader.cpp b/apps/openmw/mwworld/esmloader.cpp index 1b8880d375..13a786d008 100644 --- a/apps/openmw/mwworld/esmloader.cpp +++ b/apps/openmw/mwworld/esmloader.cpp @@ -1,7 +1,7 @@ #include "esmloader.hpp" #include "esmstore.hpp" -#include "components/to_utf8/to_utf8.hpp" +#include namespace MWWorld { diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 12e02f9383..36c198f010 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -7,6 +7,8 @@ #include +#include + namespace MWWorld { diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index d74aab6943..5be66ccea3 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -1,5 +1,7 @@ #include "localscripts.hpp" +#include + #include "esmstore.hpp" #include "cellstore.hpp" diff --git a/apps/openmw/mwworld/recordcmp.hpp b/apps/openmw/mwworld/recordcmp.hpp index 7de4f5565a..500f86b1eb 100644 --- a/apps/openmw/mwworld/recordcmp.hpp +++ b/apps/openmw/mwworld/recordcmp.hpp @@ -5,6 +5,8 @@ #include +#include + namespace MWWorld { struct RecordCmp diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 286721df42..b6ad5c1045 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -231,6 +231,11 @@ namespace MWWorld cell->getCell()->getGridY() ); if (land) { + // Actually only VHGT is needed here, but we'll need the rest for rendering anyway. + // Load everything now to reduce IO overhead. + const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; + if (!land->isDataLoaded(flags)) + land->loadData(flags); mPhysics->addHeightField ( land->mLandData->mHeights, cell->getCell()->getGridX(), diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 7d58013e7e..cdcc00b4d3 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1,6 +1,8 @@ #include "store.hpp" #include "esmstore.hpp" +#include + namespace MWWorld { void Store::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell) @@ -117,4 +119,9 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) } } +void Store::load(ESM::ESMReader &esm, const std::string &id) +{ + load(esm, id, esm.getIndex()); +} + } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index adca81adcb..929ff62a23 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -436,9 +437,7 @@ namespace MWWorld ltexl[lt.mIndex] = lt; } - void load(ESM::ESMReader &esm, const std::string &id) { - load(esm, id, esm.getIndex()); - } + void load(ESM::ESMReader &esm, const std::string &id); iterator begin(size_t plugin) const { assert(plugin < mStatic.size()); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8bcfcb4211..8cdd7037b0 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2457,6 +2457,11 @@ namespace MWWorld return mLevitationEnabled; } + void World::reattachPlayerCamera() + { + mRendering->rebuildPtr(getPlayerPtr()); + } + void World::setWerewolf(const MWWorld::Ptr& actor, bool werewolf) { MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 9834015ac6..18fb354bc9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -537,6 +537,7 @@ namespace MWWorld /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr); + virtual void reattachPlayerCamera(); /// \todo this does not belong here virtual void frameStarted (float dt, bool paused); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a0500127c9..814dde6a29 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -58,7 +58,7 @@ add_component_dir (to_utf8 add_component_dir (esm attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst - loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc + loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate @@ -113,7 +113,7 @@ add_component_dir (ogreinit ) add_component_dir (widgets - box imagebutton tags list numericeditbox sharedstatebutton widgets + box imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets ) add_component_dir (fontloader diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index 80706793a5..54b18aeaf3 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -53,18 +53,6 @@ typedef NAME_T<32> NAME32; typedef NAME_T<64> NAME64; typedef NAME_T<256> NAME256; -#pragma pack(push) -#pragma pack(1) -// Data that is only present in save game files -struct SaveData -{ - float pos[6]; // Player position and rotation - NAME64 cell; // Cell name - float unk2; // Unknown value - possibly game time? - NAME32 player; // Player name -}; -#pragma pack(pop) - /* This struct defines a file 'context' which can be saved and later restored by an ESMReader instance. It will save the position within a file, and when restored will let you read from that position as diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 544f8bbedf..14951608d2 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -4,6 +4,8 @@ #include #include +#include + namespace ESM { ESMWriter::ESMWriter() diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index e57c6e45d8..30cec58b46 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -4,11 +4,14 @@ #include #include -#include - #include "esmcommon.hpp" #include "loadtes3.hpp" +namespace ToUTF8 +{ + class Utf8Encoder; +} + namespace ESM { class ESMWriter diff --git a/components/esm/globalscript.hpp b/components/esm/globalscript.hpp index 43c859e095..8b7e627953 100644 --- a/components/esm/globalscript.hpp +++ b/components/esm/globalscript.hpp @@ -12,7 +12,7 @@ namespace ESM struct GlobalScript { - std::string mId; + std::string mId; /// \note must be lowercase Locals mLocals; int mRunning; std::string mTargetId; // for targeted scripts diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 0c0d662a8b..59b1af31a2 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -32,7 +32,11 @@ struct DialInfo struct DATAstruct { int mUnknown1; - int mDisposition; + union + { + int mDisposition; // Used for dialogue responses + int mJournalIndex; // Used for journal entries + }; signed char mRank; // Rank of NPC signed char mGender; // See Gender enum signed char mPCrank; // Player rank diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 91b062596b..ae73eee521 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -72,7 +72,6 @@ Land::Land() , mDataLoaded(false) , mLandData(NULL) , mPlugin(0) - , mHasData(false) { } @@ -97,8 +96,6 @@ void Land::load(ESMReader &esm) // Store the file position mContext = esm.getContext(); - mHasData = false; - // Skip these here. Load the actual data when the cell is loaded. if (esm.isNextSub("VNML")) { @@ -126,10 +123,6 @@ void Land::load(ESMReader &esm) mDataTypes |= DATA_VTEX; } - // We need all three of VNML, VHGT and VTEX in order to use the - // landscape. (Though Morrowind seems to accept terrain without VTEX/VCLR entries) - mHasData = mDataTypes & (DATA_VNML|DATA_VHGT|DATA_WNAM); - mDataLoaded = 0; mLandData = NULL; } @@ -144,13 +137,12 @@ void Land::save(ESMWriter &esm) const esm.writeHNT("DATA", mFlags); } -/// \todo remove memory allocation when only defaults needed void Land::loadData(int flags) { // Try to load only available data - int actual = flags & mDataTypes; + flags = flags & mDataTypes; // Return if all required data is loaded - if (flags == 0 || (actual != 0 && (mDataLoaded & actual) == actual)) { + if ((mDataLoaded & flags) == flags) { return; } // Create storage if nothing is loaded @@ -160,15 +152,13 @@ void Land::loadData(int flags) } mEsm->restoreContext(mContext); - memset(mLandData->mNormals, 0, sizeof(mLandData->mNormals)); - if (mEsm->isNextSub("VNML")) { - condLoad(actual, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); + condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); } if (mEsm->isNextSub("VHGT")) { static VHGT vhgt; - if (condLoad(actual, DATA_VHGT, &vhgt, sizeof(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]; @@ -184,30 +174,18 @@ void Land::loadData(int flags) mLandData->mUnk1 = vhgt.mUnk1; mLandData->mUnk2 = vhgt.mUnk2; } - } else if ((flags & DATA_VHGT) && (mDataLoaded & DATA_VHGT) == 0) { - for (int i = 0; i < LAND_NUM_VERTS; ++i) { - mLandData->mHeights[i] = -256.0f * HEIGHT_SCALE; - } - mDataLoaded |= DATA_VHGT; } if (mEsm->isNextSub("WNAM")) { - condLoad(actual, DATA_WNAM, mLandData->mWnam, 81); - } - if (mEsm->isNextSub("VCLR")) { - mLandData->mUsingColours = true; - condLoad(actual, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); - } else { - mLandData->mUsingColours = false; + 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(actual, DATA_VTEX, vtex, sizeof(vtex))) { + if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) { LandData::transposeTextureData(vtex, mLandData->mTextures); } - } else if ((flags & DATA_VTEX) && (mDataLoaded & DATA_VTEX) == 0) { - memset(mLandData->mTextures, 0, sizeof(mLandData->mTextures)); - mDataLoaded |= DATA_VTEX; } } @@ -232,4 +210,9 @@ bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) return false; } +bool Land::isDataLoaded(int flags) const +{ + return (mDataLoaded & flags) == (flags & mDataTypes); +} + } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 0020669815..e510616aff 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -32,7 +32,6 @@ struct Land ESMReader* mEsm; ESM_Context mContext; - bool mHasData; int mDataTypes; int mDataLoaded; @@ -81,16 +80,12 @@ struct Land VNML mNormals[LAND_NUM_VERTS * 3]; uint16_t mTextures[LAND_NUM_TEXTURES]; - bool mUsingColours; char mColours[3 * LAND_NUM_VERTS]; int mDataTypes; - // WNAM appears to contain the global map image for this cell. Probably a palette-based format, - // since there's only 1 byte for each pixel. - // Currently unused (global map is drawn on the fly in OpenMW, takes ~1/2 second at startup for Morrowind.esm). - // The problem with using the original data is that we would need to exactly replicate the TES CS's algorithm - // for drawing the global map in OpenCS, in order to get seamless edges when creating landmass mods. - uint8_t mWnam[81]; + // low-LOD heightmap (used for rendering the global map) + signed char mWnam[81]; + short mUnk1; uint8_t mUnk2; @@ -116,10 +111,8 @@ struct Land void unloadData(); /// Check if given data type is loaded - /// \todo reimplement this - bool isDataLoaded(int flags) { - return (mDataLoaded & flags) == flags; - } + /// @note We only check data types that *can* be loaded (present in mDataTypes) + bool isDataLoaded(int flags) const; private: Land(const Land& land); diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index ef4b5211bb..6ca070cf37 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -144,4 +144,14 @@ void NPC::save(ESMWriter &esm) const mHair.clear(); mHead.clear(); } + + int NPC::getFactionRank() const + { + if (mFaction.empty()) + return -1; + else if (mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) + return mNpdt12.mRank; + else // NPC_DEFAULT + return mNpdt52.mRank; + } } diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 0e90108c32..9dc3be513a 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -110,6 +110,8 @@ struct NPC NPDTstruct52 mNpdt52; NPDTstruct12 mNpdt12; //for autocalculated characters + int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank + int mFlags; bool mPersistent; diff --git a/components/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp deleted file mode 100644 index 6b3cd533f4..0000000000 --- a/components/esm/loadnpcc.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef OPENMW_ESM_NPCC_H -#define OPENMW_ESM_NPCC_H - -#include - -// TODO: create implementation files to remove this -#include "esmreader.hpp" - -namespace ESM { - -class ESMReader; -class ESMWriter; - -/* - * NPC change information (found in savegame files only). We can't - * read these yet. - * - * Some general observations about savegames: - * - * SCPT records do not define new scripts, but assign values to the - * variables of existing ones. - * - * VFXM, SPLM - no clue - * - * FMAP - MAPH and MAPD, global map image. - * - * JOUR - the entire journal in html - * - * QUES - seems to contain all the quests in the game, not just the - * ones you have done or begun. - * - * REGN - lists all regions in the game, even unvisited ones. - * notable differences to Regions in ESM files: mMapColor may be missing, and includes an unknown WNAM subrecord. - * - * - * The DIAL/INFO blocks contain changes to characters' dialog status. - * - * Dammit there's a lot of stuff in there! Should really have - * suspected as much. The strategy further is to completely ignore - * save files for the time being. - * - * Several records have a "change" variant, like NPCC, CNTC - * (contents), and CREC (creature.) These seem to alter specific - * instances of creatures, npcs, etc. I have not identified most of - * their subrecords yet. - * - * Several NPCC records have names that begin with "chargen ", I don't - * know if it means something special yet. - * - * The CNTC blocks seem to be instances of leveled lists. When a - * container is supposed to contain this leveled list of this type, - * but is referenced elsewhere in the file by an INDX, the CNTC with - * the corresponding leveled list identifier and INDX will determine - * the container contents instead. - * - * Some classes of objects seem to be altered, and these include an - * INDX, which is probably an index used by specific references other - * places within the save file. I guess this means 'use this class for - * these objects, not the general class.' All the indices I have - * encountered so far are zero, but they have been for different - * classes (different containers, really) so possibly we start from - * zero for each class. This looks like a mess, but is probably still - * easier than to duplicate everything. I think WRITING this format - * will be harder than reading it. - */ - -struct LoadNPCC -{ - static unsigned int sRecordId; - - std::string mId; - - void load(ESMReader &esm) - { - esm.skipRecord(); - } - void save(ESMWriter &esm) const - { - } -}; -} -#endif diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index a8ec4cf444..1eb1a3d1af 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -34,7 +34,7 @@ namespace ESM StatState mWerewolfAttributes[8]; bool mIsWerewolf; - std::map mFactions; + std::map mFactions; // lower case IDs int mDisposition; Skill mSkills[27]; int mBounty; @@ -43,7 +43,7 @@ namespace ESM int mProfit; int mLevelProgress; int mSkillIncrease[8]; - std::vector mUsedIds; + std::vector mUsedIds; // lower case IDs float mTimeToStartDrowning; int mCrimeId; diff --git a/components/esm/queststate.hpp b/components/esm/queststate.hpp index 1769336f2f..4966cdc908 100644 --- a/components/esm/queststate.hpp +++ b/components/esm/queststate.hpp @@ -12,7 +12,7 @@ namespace ESM struct QuestState { - std::string mTopic; + std::string mTopic; // lower case id int mState; unsigned char mFinished; @@ -21,4 +21,4 @@ namespace ESM }; } -#endif \ No newline at end of file +#endif diff --git a/components/esm/records.hpp b/components/esm/records.hpp index c01c89d57d..5c183b6f6d 100644 --- a/components/esm/records.hpp +++ b/components/esm/records.hpp @@ -32,7 +32,6 @@ #include "loadmgef.hpp" #include "loadmisc.hpp" #include "loadnpc.hpp" -#include "loadnpcc.hpp" #include "loadpgrd.hpp" #include "loadrace.hpp" #include "loadregn.hpp" diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index a66193f976..d4a0a0df2c 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -31,7 +31,7 @@ namespace ESMTerrain int cellY = origin.y; const ESM::Land* land = getLand(cellX, cellY); - if (!land) + if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) return false; min = std::numeric_limits::max(); @@ -73,7 +73,7 @@ namespace ESMTerrain row += ESM::Land::LAND_SIZE-1; } ESM::Land* land = getLand(cellX, cellY); - if (land && land->mHasData) + if (land && land->mDataTypes&ESM::Land::DATA_VNML) { normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; @@ -108,7 +108,7 @@ namespace ESMTerrain row = 0; } ESM::Land* land = getLand(cellX, cellY); - if (land && land->mLandData->mUsingColours) + if (land && land->mDataTypes&ESM::Land::DATA_VCLR) { color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; @@ -157,9 +157,8 @@ namespace ESMTerrain for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) { ESM::Land* land = getLand(cellX, cellY); - if (land && !land->mHasData) + if (land && !(land->mDataTypes&ESM::Land::DATA_VHGT)) land = NULL; - bool hasColors = land && land->mLandData->mUsingColours; int rowStart = 0; int colStart = 0; @@ -183,7 +182,7 @@ namespace ESMTerrain else positions[vertX*numVerts*3 + vertY*3 + 2] = -2048; - if (land) + if (land && land->mDataTypes&ESM::Land::DATA_VNML) { normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; @@ -207,7 +206,7 @@ namespace ESMTerrain normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y; normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z; - if (hasColors) + if (land && land->mDataTypes&ESM::Land::DATA_VCLR) { color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; @@ -263,7 +262,7 @@ namespace ESMTerrain assert(ymDataTypes&ESM::Land::DATA_VTEX)) { int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0) diff --git a/components/widgets/widgets.cpp b/components/widgets/widgets.cpp index 82839c6c96..3b6361cf88 100644 --- a/components/widgets/widgets.cpp +++ b/components/widgets/widgets.cpp @@ -7,6 +7,7 @@ #include "box.hpp" #include "imagebutton.hpp" #include "sharedstatebutton.hpp" +#include "windowcaption.hpp" namespace Gui { @@ -22,6 +23,7 @@ namespace Gui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } } diff --git a/components/widgets/windowcaption.cpp b/components/widgets/windowcaption.cpp new file mode 100644 index 0000000000..bcb0a7c125 --- /dev/null +++ b/components/widgets/windowcaption.cpp @@ -0,0 +1,58 @@ +#include "windowcaption.hpp" + +#include + +namespace Gui +{ + + WindowCaption::WindowCaption() + : mLeft(NULL) + , mRight(NULL) + { + } + + void WindowCaption::initialiseOverride() + { + Base::initialiseOverride(); + + assignWidget(mLeft, "Left"); + assignWidget(mRight, "Right"); + + assignWidget(mClient, "Client"); + if (!mClient) + throw std::runtime_error("WindowCaption needs an EditBox Client widget in its skin"); + } + + void WindowCaption::setCaption(const MyGUI::UString &_value) + { + EditBox::setCaption(_value); + align(); + } + + void WindowCaption::setSize(const MyGUI::IntSize& _value) + { + Base::setSize(_value); + align(); + } + + void WindowCaption::setCoord(const MyGUI::IntCoord& _value) + { + Base::setCoord(_value); + align(); + } + + void WindowCaption::align() + { + MyGUI::IntSize textSize = getTextSize(); + MyGUI::Widget* caption = mClient; + caption->setSize(textSize.width + 24, caption->getHeight()); + + int barwidth = (getWidth()-caption->getWidth())/2; + caption->setPosition(barwidth, caption->getTop()); + if (mLeft) + mLeft->setCoord(0, mLeft->getTop(), barwidth, mLeft->getHeight()); + if (mRight) + mRight->setCoord(barwidth + caption->getWidth(), mRight->getTop(), barwidth, mRight->getHeight()); + } + +} diff --git a/components/widgets/windowcaption.hpp b/components/widgets/windowcaption.hpp new file mode 100644 index 0000000000..bdd4c0a2e2 --- /dev/null +++ b/components/widgets/windowcaption.hpp @@ -0,0 +1,32 @@ +#ifndef OPENMW_WIDGETS_WINDOWCAPTION_H +#define OPENMW_WIDGETS_WINDOWCAPTION_H + +#include + +namespace Gui +{ + + /// Window caption that automatically adjusts "Left" and "Right" widgets in its skin + /// based on the text size of the caption in the middle + class WindowCaption : public MyGUI::EditBox + { + MYGUI_RTTI_DERIVED(WindowCaption) + public: + WindowCaption(); + + virtual void setCaption(const MyGUI::UString &_value); + virtual void initialiseOverride(); + + virtual void setSize(const MyGUI::IntSize& _value); + virtual void setCoord(const MyGUI::IntCoord& _value); + + private: + MyGUI::Widget* mLeft; + MyGUI::Widget* mRight; + + void align(); + }; + +} + +#endif diff --git a/extern/ogre-ffmpeg-videoplayer/videostate.cpp b/extern/ogre-ffmpeg-videoplayer/videostate.cpp index b365438806..9dac0bacd5 100644 --- a/extern/ogre-ffmpeg-videoplayer/videostate.cpp +++ b/extern/ogre-ffmpeg-videoplayer/videostate.cpp @@ -346,11 +346,11 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts) * buffer. We use this to store the global_pts in * a frame at the time it is allocated. */ -static uint64_t global_video_pkt_pts = static_cast(AV_NOPTS_VALUE); +static int64_t global_video_pkt_pts = AV_NOPTS_VALUE; static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) { int ret = avcodec_default_get_buffer(c, pic); - uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); + int64_t *pts = (int64_t*)av_malloc(sizeof(int64_t)); *pts = global_video_pkt_pts; pic->opaque = pts; return ret; @@ -397,10 +397,10 @@ void VideoState::video_thread_loop(VideoState *self) throw std::runtime_error("Error decoding video frame"); double pts = 0; - if((uint64_t)packet->dts != AV_NOPTS_VALUE) + if(packet->dts != AV_NOPTS_VALUE) pts = packet->dts; - else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) - pts = *(uint64_t*)pFrame->opaque; + else if(pFrame->opaque && *(int64_t*)pFrame->opaque != AV_NOPTS_VALUE) + pts = *(int64_t*)pFrame->opaque; pts *= av_q2d((*self->video_st)->time_base); av_free_packet(packet); diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index 0c29be9395..a6c7d3e5ca 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -30,7 +30,8 @@ namespace SFO mWantMouseVisible(false), mAllowGrab(grab), mWarpX(0), - mWarpY(0) + mWarpY(0), + mFirstMouseMove(true) { _setupOISKeys(); } @@ -316,6 +317,13 @@ namespace SFO pack_evt.y = mMouseY = evt.motion.y; pack_evt.xrel = evt.motion.xrel; pack_evt.yrel = evt.motion.yrel; + if (mFirstMouseMove) + { + // first event should be treated as non-relative, since there's no point of reference + // SDL then (incorrectly) uses (0,0) as point of reference, on Linux at least... + pack_evt.xrel = pack_evt.yrel = 0; + mFirstMouseMove = false; + } } else if(evt.type == SDL_MOUSEWHEEL) { diff --git a/extern/sdl4ogre/sdlinputwrapper.hpp b/extern/sdl4ogre/sdlinputwrapper.hpp index 339e99de14..af16ab68d8 100644 --- a/extern/sdl4ogre/sdlinputwrapper.hpp +++ b/extern/sdl4ogre/sdlinputwrapper.hpp @@ -71,6 +71,8 @@ namespace SFO bool mGrabPointer; bool mMouseRelative; + bool mFirstMouseMove; + Sint32 mMouseZ; Sint32 mMouseX; Sint32 mMouseY; diff --git a/files/mygui/openmw_dialogue_window.layout b/files/mygui/openmw_dialogue_window.layout index 5a7cd772da..a0a4bf4416 100644 --- a/files/mygui/openmw_dialogue_window.layout +++ b/files/mygui/openmw_dialogue_window.layout @@ -6,8 +6,8 @@ - - + + diff --git a/files/mygui/openmw_hud_box.skin.xml b/files/mygui/openmw_hud_box.skin.xml index e7457e7d29..4e63497680 100644 --- a/files/mygui/openmw_hud_box.skin.xml +++ b/files/mygui/openmw_hud_box.skin.xml @@ -6,14 +6,14 @@ + + + + - - - - @@ -22,14 +22,14 @@ + + + + - - - - diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index d01bd8ef20..ebbc77bef6 100644 --- a/files/mygui/openmw_windows.skin.xml +++ b/files/mygui/openmw_windows.skin.xml @@ -395,17 +395,9 @@ - - - - - - - - - - - + + + @@ -538,12 +531,7 @@ - - - - - - + @@ -695,7 +684,8 @@ - + + @@ -814,12 +804,7 @@ - - - - - - +