diff --git a/CMakeLists.txt b/CMakeLists.txt index 3950274790..dbe4c406bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,6 +300,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop "${OpenMW_BINARY_DIR}/openmw.desktop") + configure_file(${OpenMW_SOURCE_DIR}/files/opencs.desktop + "${OpenMW_BINARY_DIR}/opencs.desktop") endif() # Compiler settings diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f76590ae85..ce84b8dfe1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -149,16 +149,22 @@ OMW::Engine::~Engine() delete mOgre; } -// Load all BSA files in data directory. +// Load BSA files void OMW::Engine::loadBSA() { - const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa"); - - for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter) + for (std::vector::const_iterator archive = mArchives.begin(); archive != mArchives.end(); ++archive) { - std::cout << "Adding " << iter->second.string() << std::endl; - Bsa::addBSA(iter->second.string()); + if (mFileCollections.doesExist(*archive)) + { + const std::string archivePath = mFileCollections.getPath(*archive).string(); + std::cout << "Adding BSA archive " << archivePath << std::endl; + Bsa::addBSA(archivePath); + } + else + { + std::cout << "Archive " << *archive << " not found" << std::endl; + } } const Files::PathContainer& dataDirs = mFileCollections.getPaths(); @@ -199,6 +205,11 @@ void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs) mFileCollections = Files::Collections (dataDirs, !mFSStrict); } +// Add BSA archive +void OMW::Engine::addArchive (const std::string& archive) { + mArchives.push_back(archive); +} + // Set resource dir void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir) { diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index a4acee5232..f80b67a358 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -64,6 +64,7 @@ namespace OMW ToUTF8::FromType mEncoding; ToUTF8::Utf8Encoder* mEncoder; Files::PathContainer mDataDirs; + std::vector mArchives; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; std::string mCellName; @@ -99,7 +100,7 @@ namespace OMW /// add a .zip resource void addZipResource (const boost::filesystem::path& path); - /// Load all BSA files in data directory. + /// Load BSA files void loadBSA(); void executeLocalScripts(); @@ -126,6 +127,9 @@ namespace OMW /// Set data dirs void setDataDirs(const Files::PathContainer& dataDirs); + /// Add BSA archive + void addArchive(const std::string& archive); + /// Set resource dir void setResourceDir(const boost::filesystem::path& parResDir); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 86978c9b11..1fa461c2fb 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -100,6 +100,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("data-local", bpo::value()->default_value(""), "set local data directory (highest priority)") + ("fallback-archive", bpo::value()->default_value(StringsVector(), "fallback-archive") + ->multitoken(), "set fallback BSA archives (later archives have higher priority)") + ("resources", bpo::value()->default_value("resources"), "set resources directory") @@ -201,6 +204,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setDataDirs(dataDirs); + // fallback archives + StringsVector archives = variables["fallback-archive"].as(); + for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); it++) + { + engine.addArchive(*it); + } + engine.setResourceDir(variables["resources"].as()); // master and plugin diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 859e3008cc..cdcbfc4d18 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -223,50 +223,60 @@ void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); } -void DialogueWindow::onSelectTopic(std::string topic) +void DialogueWindow::onSelectTopic(const std::string& topic, int id) { if (!mEnabled) return; - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); + int separatorPos = mTopicsList->getItemCount(); + for (unsigned int i=0; igetItemCount(); ++i) + { + if (mTopicsList->getItemNameAt(i) == "") + separatorPos = i; + } - if (topic == gmst.find("sBarter")->getString()) - { - /// \todo check if the player is allowed to trade with this actor (e.g. faction rank high enough)? - mWindowManager.pushGuiMode(GM_Barter); - mWindowManager.getTradeWindow()->startTrade(mPtr); - } - if (topic == gmst.find("sPersuasion")->getString()) - { - mPersuasionDialog.setVisible(true); - } - else if (topic == gmst.find("sSpells")->getString()) - { - mWindowManager.pushGuiMode(GM_SpellBuying); - mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); - } - else if (topic == gmst.find("sTravel")->getString()) - { - mWindowManager.pushGuiMode(GM_Travel); - mWindowManager.getTravelWindow()->startTravel(mPtr); - } - else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) - { - mWindowManager.pushGuiMode(GM_SpellCreation); - mWindowManager.startSpellMaking (mPtr); - } - else if (topic == gmst.find("sEnchanting")->getString()) - { - mWindowManager.pushGuiMode(GM_Enchanting); - mWindowManager.startEnchanting (mPtr); - } - else if (topic == gmst.find("sServiceTrainingTitle")->getString()) - { - mWindowManager.pushGuiMode(GM_Training); - mWindowManager.startTraining (mPtr); - } - else + if (id > separatorPos) MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); + else + { + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (topic == gmst.find("sBarter")->getString()) + { + /// \todo check if the player is allowed to trade with this actor (e.g. faction rank high enough)? + mWindowManager.pushGuiMode(GM_Barter); + mWindowManager.getTradeWindow()->startTrade(mPtr); + } + if (topic == gmst.find("sPersuasion")->getString()) + { + mPersuasionDialog.setVisible(true); + } + else if (topic == gmst.find("sSpells")->getString()) + { + mWindowManager.pushGuiMode(GM_SpellBuying); + mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); + } + else if (topic == gmst.find("sTravel")->getString()) + { + mWindowManager.pushGuiMode(GM_Travel); + mWindowManager.getTravelWindow()->startTravel(mPtr); + } + else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) + { + mWindowManager.pushGuiMode(GM_SpellCreation); + mWindowManager.startSpellMaking (mPtr); + } + else if (topic == gmst.find("sEnchanting")->getString()) + { + mWindowManager.pushGuiMode(GM_Enchanting); + mWindowManager.startEnchanting (mPtr); + } + else if (topic == gmst.find("sServiceTrainingTitle")->getString()) + { + mWindowManager.pushGuiMode(GM_Training); + mWindowManager.startTraining (mPtr); + } + } } void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index e39cecc3cf..a8e0a6d174 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -85,7 +85,7 @@ namespace MWGui }; protected: - void onSelectTopic(std::string topic); + void onSelectTopic(const std::string& topic, int id); void onByeClicked(MyGUI::Widget* _sender); void onHistoryClicked(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); diff --git a/apps/openmw/mwgui/list.cpp b/apps/openmw/mwgui/list.cpp index d60e9b6877..c7b7207305 100644 --- a/apps/openmw/mwgui/list.cpp +++ b/apps/openmw/mwgui/list.cpp @@ -1,9 +1,9 @@ #include "list.hpp" -#include #include #include #include +#include using namespace MWGui; using namespace MWGui::Widgets; @@ -23,7 +23,7 @@ void MWList::initialiseOverride() if (mClient == 0) mClient = this; - mScrollView = mClient->createWidgetReal( + mScrollView = mClient->createWidgetReal( "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); } @@ -48,6 +48,7 @@ void MWList::redraw(bool scrollbarShown) const int _scrollBarWidth = 24; // fetch this from skin? const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; const int spacing = 3; + size_t scrollbarPosition = mScrollView->getScrollPosition(); while (mScrollView->getChildCount()) { @@ -55,6 +56,7 @@ void MWList::redraw(bool scrollbarShown) } mItemHeight = 0; + int i=0; for (std::vector::const_iterator it=mItems.begin(); it!=mItems.end(); ++it) { @@ -71,6 +73,7 @@ void MWList::redraw(bool scrollbarShown) int height = button->getTextSize().height; button->setSize(MyGUI::IntSize(button->getSize().width, height)); + button->setUserData(i); mItemHeight += height + spacing; } @@ -83,11 +86,17 @@ void MWList::redraw(bool scrollbarShown) mItemHeight += 18 + spacing; } + ++i; } mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); if (!scrollbarShown && mItemHeight > mClient->getSize().height) redraw(true); + + size_t scrollbarRange = mScrollView->getScrollRange(); + if(scrollbarPosition > scrollbarRange) + scrollbarPosition = scrollbarRange; + mScrollView->setScrollPosition(scrollbarPosition); } bool MWList::hasItem(const std::string& name) @@ -129,8 +138,8 @@ void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel) void MWList::onItemSelected(MyGUI::Widget* _sender) { std::string name = static_cast(_sender)->getCaption(); - - eventItemSelected(name); + int id = *_sender->getUserData(); + eventItemSelected(name, id); eventWidgetSelected(_sender); } @@ -138,3 +147,17 @@ MyGUI::Widget* MWList::getItemWidget(const std::string& name) { return mScrollView->findWidget (getName() + "_item_" + name); } + +size_t MWScrollView::getScrollPosition() +{ + return getVScroll()->getScrollPosition(); +} + +void MWScrollView::setScrollPosition(size_t position) +{ + getVScroll()->setScrollPosition(position); +} +size_t MWScrollView::getScrollRange() +{ + return getVScroll()->getScrollRange(); +} diff --git a/apps/openmw/mwgui/list.hpp b/apps/openmw/mwgui/list.hpp index 38797e7798..09e42e865e 100644 --- a/apps/openmw/mwgui/list.hpp +++ b/apps/openmw/mwgui/list.hpp @@ -2,16 +2,24 @@ #define MWGUI_LIST_HPP #include - -namespace MyGUI -{ - class ScrollView; -} +#include namespace MWGui { namespace Widgets { + /** + * \brief a custom ScrollView which has access to scrollbar properties + */ + class MWScrollView : public MyGUI::ScrollView + { + MYGUI_RTTI_DERIVED(MWScrollView) + public: + size_t getScrollPosition(); + void setScrollPosition(size_t); + size_t getScrollRange(); + }; + /** * \brief a very simple list widget that supports word-wrapping entries * \note if the width or height of the list changes, you must call adjustSize() method @@ -22,14 +30,14 @@ namespace MWGui public: MWList(); - typedef MyGUI::delegates::CMultiDelegate1 EventHandle_String; + typedef MyGUI::delegates::CMultiDelegate2 EventHandle_StringInt; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Widget; /** * Event: Item selected with the mouse. * signature: void method(std::string itemName) */ - EventHandle_String eventItemSelected; + EventHandle_StringInt eventItemSelected; /** * Event: Item selected with the mouse. @@ -63,7 +71,7 @@ namespace MWGui void onItemSelected(MyGUI::Widget* _sender); private: - MyGUI::ScrollView* mScrollView; + MWGui::Widgets::MWScrollView* mScrollView; MyGUI::Widget* mClient; std::vector mItems; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index d866ec7557..0e4c3a6082 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -132,6 +132,7 @@ WindowManager::WindowManager( MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); MyGUI::ResourceManager::getInstance().load("core.xml"); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 8ab81bfdf0..7182003720 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -144,7 +144,11 @@ void MWMechanics::Alchemy::updateEffects() MWBase::Environment::get().getWorld()->getStore().get().find (iter->mId); if (magicEffect->mData.mBaseCost<=0) - throw std::runtime_error ("invalid base cost for magic effect " + iter->mId); + { + std::ostringstream os; + os << "invalid base cost for magic effect " << iter->mId; + throw std::runtime_error (os.str()); + } float fPotionT1MagMul = MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->getFloat(); diff --git a/components/files/collections.cpp b/components/files/collections.cpp index 50340dca4d..c6195d88cf 100644 --- a/components/files/collections.cpp +++ b/components/files/collections.cpp @@ -31,6 +31,32 @@ namespace Files return iter->second; } + boost::filesystem::path Collections::getPath(const std::string& file) const + { + for (Files::PathContainer::const_iterator iter = mDirectories.begin(); + iter != mDirectories.end(); ++iter) + { + const boost::filesystem::path path = *iter / file; + if (boost::filesystem::exists(path)) + return path.string(); + } + + throw std::runtime_error ("file " + file + " not found"); + } + + bool Collections::doesExist(const std::string& file) const + { + for (Files::PathContainer::const_iterator iter = mDirectories.begin(); + iter != mDirectories.end(); ++iter) + { + const boost::filesystem::path path = *iter / file; + if (boost::filesystem::exists(path)) + return true; + } + + return false; + } + const Files::PathContainer& Collections::getPaths() const { return mDirectories; diff --git a/components/files/collections.hpp b/components/files/collections.hpp index ed4aafa133..def61cf8ef 100644 --- a/components/files/collections.hpp +++ b/components/files/collections.hpp @@ -19,6 +19,16 @@ namespace Files /// leading dot and must be all lower-case. const MultiDirCollection& getCollection(const std::string& extension) const; + boost::filesystem::path getPath(const std::string& file) const; + ///< Return full path (including filename) of \a file. + /// + /// If the file does not exist in any of the collection's + /// directories, an exception is thrown. \a file must include the + /// extension. + + bool doesExist(const std::string& file) const; + ///< \return Does a file with the given name exist? + const Files::PathContainer& getPaths() const; private: diff --git a/files/opencs.desktop b/files/opencs.desktop new file mode 100644 index 0000000000..f6aad5b097 --- /dev/null +++ b/files/opencs.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=OpenMW Content Editor +GenericName=Content Editor +Comment=A replacement for the Morrowind Construction Set. +TryExec=opencs +Exec=opencs +Icon=opencs +Categories=Game;RolePlaying;