diff --git a/README.md b/README.md index 7c24a4c81..e17784752 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 9bbf20266..98e18521e 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 2f07a5f08..df2631c13 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 409b8bcff..76fc9aa94 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 3b82988b8..4d21d2b7a 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 90b75fee1..c80ccb951 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 000000000..ca81ebbbf --- /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 000000000..3390bd607 --- /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 136183668..9a062484b 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 211195d3f..a466770d0 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 0b3a4f1a7..1012541b4 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 7bb814320..fca7d72a0 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 374fd05fa..652383cda 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 ca2439dda..ce54c3a73 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 f58ef0809..ccda90832 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 8f62cc1e8..d01a9ec7f 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 56931a419..9aece7368 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 7c67cdc5c..adb7d3892 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()) - return 0; - - const std::pair faction = - *mActor.getClass().getNpcStats (mActor).getFactionRanks().begin(); + std::string faction = mActor.getClass().getPrimaryFaction(mActor); - int rank = getFactionRank (player, faction.first); + if (faction.empty()) + return 0; - 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 b92d7cace..9f07f7b6f 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 3b57912da..99dab0cf8 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 a699286a1..a9e39b379 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 1e87c0ff1..9c07c5780 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 3d1a61eaf..8c963b762 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 5629c25f1..4eb9a271c 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 49e641aed..53b1e679a 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 41c3604b4..e30f6e7a8 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 1a5bd79a3..9aeba6752 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 d10a295c1..270066c97 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 c7855b367..f1c4c4088 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 3e5a01e3a..1c17a11f6 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 30daf8671..005d206f4 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 19f9c749c..a407aedc7 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 bafa89d5b..fab7c63a6 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 2ade0f4c5..046070841 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 e350573c4..ee44328eb 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 e976b984f..cf4679287 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 f7e285ff5..f2b125add 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 fa7e52af2..8d438c703 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 ad4d3aabf..1d243ef45 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 5d9e29118..96410c35d 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 0a2c3cfff..d52dcb43c 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 6d9388408..c3a4d2312 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 9e543bb79..d183e23dd 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 55ebfeab5..6bf81e861 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 a4460c59d..f0f6158ed 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); - - //if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) - if (1) - { - std::vector data (mWidth * mHeight * 3); + std::vector data (mWidth * mHeight * 3); - for (int x = mMinX; x <= mMaxX; ++x) + 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); + ESM::Land* land = esmStore.get().search (x,y); - if (land) - { - int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; - if (!land->isDataLoaded(mask)) - land->loadData(mask); - } + if (land) + { + int mask = ESM::Land::DATA_WNAM; + if (!land->isDataLoaded(mask)) + land->loadData(mask); + } - for (int cellY=0; cellYmLandData->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; - } - } + unsigned char r,g,b; - } + float y = 0; + if (land && land->mDataTypes & ESM::Land::DATA_WNAM) + y = (land->mLandData->mWnam[vertexY * 9 + vertexX] << 4) / 2048.f; + else + y = (SCHAR_MIN << 4) / 2048.f; + if (y < 0) + { + 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())); + 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"); + 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 9b05ce0a2..0926498c0 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 8e8b18cd1..149080d93 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 cbd9e2444..e74b6af12 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 30a2a61ac..e6f4a04ad 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 1d82b8418..ea4b8d06e 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 6c4bb3be5..92fd51d87 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 5cc4d69b6..435f82de7 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 09ab0183c..f0cf12e5e 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 edb3f3788..afc5ed635 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 3c44abe66..751375c55 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 1b8880d37..13a786d00 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 12e02f938..36c198f01 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 d74aab694..5be66ccea 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 7de4f5565..500f86b1e 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 286721df4..b6ad5c104 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 7d58013e7..cdcc00b4d 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 adca81adc..929ff62a2 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 8bcfcb421..8cdd7037b 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 9834015ac..18fb354bc 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 a0500127c..814dde6a2 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 80706793a..54b18aeaf 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 544f8bbed..14951608d 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 e57c6e45d..30cec58b4 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 43c859e09..8b7e62795 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 0c0d662a8..59b1af31a 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 91b062596..ae73eee52 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 002066981..e510616af 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 ef4b5211b..6ca070cf3 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 0e90108c3..9dc3be513 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 6b3cd533f..000000000 --- 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 a8ec4cf44..1eb1a3d1a 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 1769336f2..4966cdc90 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 c01c89d57..5c183b6f6 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 a66193f97..d4a0a0df2 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 82839c6c9..3b6361cf8 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 000000000..bcb0a7c12 --- /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 000000000..bdd4c0a2e --- /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 b36543880..9dac0bacd 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 0c29be939..a6c7d3e5c 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 339e99de1..af16ab68d 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 5a7cd772d..a0a4bf441 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 e7457e7d2..4e6349768 100644 --- a/files/mygui/openmw_hud_box.skin.xml +++ b/files/mygui/openmw_hud_box.skin.xml @@ -6,13 +6,13 @@ - + - + - + - + @@ -22,13 +22,13 @@ - + - + - + - + diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index d01bd8ef2..ebbc77bef 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 @@ - - - - - - +