diff --git a/CMakeLists.txt b/CMakeLists.txt index ca3fe0eeb..668fcd83a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/mouselook.cpp ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp + ${LIBDIR}/openengine/ogre/atlas.cpp ) set(OENGINE_GUI ${LIBDIR}/openengine/gui/events.cpp diff --git a/Daedric Font License.txt b/Daedric Font License.txt new file mode 100644 index 000000000..a1553d0b0 --- /dev/null +++ b/Daedric Font License.txt @@ -0,0 +1,10 @@ +Dongle's Oblivion Daedric font set +http://www.uesp.net/wiki/Lore:Daedric_Alphabet#Daedric_Font + +--------------------------------------------------- + +This was done entirely as a personal project. Bethesda Softworks graciously granted me the permission for it. I am not connected with them in any way. +You may freely use these fonts to create anything you'd like. You may re-distribute the fonts freely, over the Internet, or by any other means. Always keep the .zip file intact, and this read me included. +Please do not modify and redistribute the fonts without my permission. +You may NOT sell any of these fonts under any circumstances. This includes putting them on compilation font CDs for sale, putting them in a "members only" pay-area of a website, or any other means of financial gain connected in ANY way with the redistribution of any of these fonts. +You have my permission to create and sell any artwork made with these fonts, however you may need to contact Bethesda Softworks before doing so. diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 79ea262e6..d7c229cf9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,8 @@ add_openmw_dir (mwinput add_openmw_dir (mwgui layouts text_input widgets race class birth review window_manager console dialogue dialogue_history window_base stats_window messagebox journalwindow charactercreation - map_window window_pinnable_base cursorreplace tooltips + map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list + formatting ) add_openmw_dir (mwdialogue @@ -46,7 +47,7 @@ add_openmw_dir (mwsound add_openmw_dir (mwworld refdata world physicssystem scene globals class action nullaction actionteleport containerstore actiontalk actiontake manualref player cellfunctors - cells localscripts customdata weather inventorystore ptr + cells localscripts customdata weather inventorystore ptr actionread ) add_openmw_dir (mwclass diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 12a5956a9..85b0557b1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -158,13 +158,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) OMW::Engine::~Engine() { - delete MWBase::Environment::get().getInputManager(); - delete MWBase::Environment::get().getSoundManager(); - delete MWBase::Environment::get().getMechanicsManager(); - delete MWBase::Environment::get().getDialogueManager(); - delete MWBase::Environment::get().getJournal(); - delete MWBase::Environment::get().getScriptManager(); - delete MWBase::Environment::get().getWorld(); + mEnvironment.cleanup(); delete mScriptContext; delete mOgre; } @@ -200,6 +194,12 @@ void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path) Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); } +void OMW::Engine::addZipResource (const boost::filesystem::path& path) +{ + mOgre->getRoot()->addResourceLocation (path.string(), "Zip", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, false); +} + void OMW::Engine::enableFSStrict(bool fsStrict) { mFSStrict = fsStrict; @@ -287,6 +287,8 @@ void OMW::Engine::go() settings.loadDefault(localdefault); else if (boost::filesystem::exists(globaldefault)) settings.loadDefault(globaldefault); + else + throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); // load user settings if they exist, otherwise just load the default settings as user settings const std::string settingspath = mCfgMgr.getUserPath().string() + "/settings.cfg"; @@ -320,6 +322,7 @@ void OMW::Engine::go() addResourcesDirectory(mResDir / "water"); addResourcesDirectory(mResDir / "gbuffer"); addResourcesDirectory(mResDir / "shadows"); + addZipResource(mResDir / "mygui" / "Obliviontt.zip"); // Create the window mOgre->createWindow("OpenMW"); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 09d6bc820..cf1ef3b9c 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -62,6 +62,7 @@ namespace OMW /// \brief Main engine class, that brings together all the components of OpenMW class Engine : private Ogre::FrameListener { + MWBase::Environment mEnvironment; std::string mEncoding; Files::PathContainer mDataDirs; boost::filesystem::path mResDir; @@ -77,7 +78,6 @@ namespace OMW std::string mFocusName; std::map mFallbackMap; - MWBase::Environment mEnvironment; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; @@ -91,9 +91,11 @@ namespace OMW /// add resources directory /// \note This function works recursively. - void addResourcesDirectory (const boost::filesystem::path& path); + /// add a .zip resource + void addZipResource (const boost::filesystem::path& path); + /// Load all BSA files in data directory. void loadBSA(); diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 8e9a9cfce..792e240f4 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -3,6 +3,19 @@ #include +#include "../mwinput/inputmanager.hpp" + +#include "../mwscript/scriptmanager.hpp" + +#include "../mwsound/soundmanager.hpp" + +#include "../mwworld/world.hpp" + +#include "../mwdialogue/dialoguemanager.hpp" +#include "../mwdialogue/journal.hpp" + +#include "../mwmechanics/mechanicsmanager.hpp" + MWBase::Environment *MWBase::Environment::sThis = 0; MWBase::Environment::Environment() @@ -15,6 +28,7 @@ MWBase::Environment::Environment() MWBase::Environment::~Environment() { + cleanup(); sThis = 0; } @@ -116,6 +130,30 @@ float MWBase::Environment::getFrameDuration() const return mFrameDuration; } +void MWBase::Environment::cleanup() +{ + delete mInputManager; + mInputManager = 0; + + delete mSoundManager; + mSoundManager = 0; + + delete mMechanicsManager; + mMechanicsManager = 0; + + delete mDialogueManager; + mDialogueManager = 0; + + delete mJournal; + mJournal = 0; + + delete mScriptManager; + mScriptManager = 0; + + delete mWorld; + mWorld = 0; +} + const MWBase::Environment& MWBase::Environment::get() { assert (sThis); diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index a010e7faa..521beee0a 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -43,7 +43,7 @@ namespace MWBase /// /// This class allows each mw-subsystem to access any others subsystem's top-level manager class. /// - /// \attention Environment does not take ownership of the manager class instances it is handed over in + /// \attention Environment takes ownership of the manager class instances it is handed over in /// the set* functions. class Environment { @@ -108,6 +108,9 @@ namespace MWBase float getFrameDuration() const; + void cleanup(); + ///< Delete all mw*-subsystems. + static const Environment& get(); ///< Return instance of this class. }; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index ab659b480..2409f95e4 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -8,7 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" -#include "../mwworld/actiontake.hpp" +#include "../mwworld/actionread.hpp" #include "../mwworld/world.hpp" #include "../mwrender/objects.hpp" @@ -60,12 +60,8 @@ namespace MWClass boost::shared_ptr Book::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - // TODO implement reading - - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + new MWWorld::ActionRead (ptr)); } std::string Book::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 282fba354..7baf589c4 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -883,4 +883,11 @@ namespace MWDialogue } return factionID; } + + void DialogueManager::goodbye() + { + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + + win->goodbye(); + } } diff --git a/apps/openmw/mwdialogue/dialoguemanager.hpp b/apps/openmw/mwdialogue/dialoguemanager.hpp index a3e37987d..992175c0c 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.hpp +++ b/apps/openmw/mwdialogue/dialoguemanager.hpp @@ -56,6 +56,8 @@ namespace MWDialogue void askQuestion(std::string question,int choice); + void goodbye(); + ///get the faction of the actor you are talking with std::string getFaction(); diff --git a/apps/openmw/mwdialogue/journal.cpp b/apps/openmw/mwdialogue/journal.cpp index e0245406e..eb828a76c 100644 --- a/apps/openmw/mwdialogue/journal.cpp +++ b/apps/openmw/mwdialogue/journal.cpp @@ -6,6 +6,8 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/messagebox.hpp" +#include "../mwworld/world.hpp" + namespace MWDialogue { Quest& Journal::getQuest (const std::string& id) @@ -38,7 +40,7 @@ namespace MWDialogue quest.addEntry (entry, *MWBase::Environment::get().getWorld()); // we are doing slicing on purpose here std::vector empty; - std::string notification = "Your Journal has been updated."; + std::string notification = MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sJournalEntry")->str; MWBase::Environment::get().getWindowManager()->messageBox (notification, empty); } diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp new file mode 100644 index 000000000..8de45984c --- /dev/null +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -0,0 +1,140 @@ +#include "bookwindow.hpp" + +#include "formatting.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwinput/inputmanager.hpp" +#include "../mwsound/soundmanager.hpp" +#include "../mwworld/actiontake.hpp" + +#include + +using namespace MWGui; + +BookWindow::BookWindow (WindowManager& parWindowManager) : + WindowBase("openmw_book_layout.xml", parWindowManager) +{ + getWidget(mCloseButton, "CloseButton"); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); + + getWidget(mTakeButton, "TakeButton"); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); + + getWidget(mNextPageButton, "NextPageBTN"); + mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); + + getWidget(mPrevPageButton, "PrevPageBTN"); + mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); + + getWidget(mLeftPageNumber, "LeftPageNumber"); + getWidget(mRightPageNumber, "RightPageNumber"); + + getWidget(mLeftPage, "LeftPage"); + getWidget(mRightPage, "RightPage"); + + center(); +} + +void BookWindow::clearPages() +{ + for (std::vector::iterator it=mPages.begin(); + it!=mPages.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mPages.clear(); +} + +void BookWindow::open (MWWorld::Ptr book) +{ + mBook = book; + + clearPages(); + mCurrentPage = 0; + + MWBase::Environment::get().getSoundManager()->playSound3D (book, "book open", 1.0, 1.0); + + ESMS::LiveCellRef *ref = + mBook.get(); + + BookTextParser parser; + std::vector results = parser.split(ref->base->text, mLeftPage->getSize().width, mLeftPage->getSize().height); + + int i=0; + for (std::vector::iterator it=results.begin(); + it!=results.end(); ++it) + { + MyGUI::Widget* parent; + if (i%2 == 0) + parent = mLeftPage; + else + parent = mRightPage; + + MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); + parser.parse(*it, pageWidget, mLeftPage->getSize().width); + mPages.push_back(pageWidget); + ++i; + } + + updatePages(); +} + +void BookWindow::onCloseButtonClicked (MyGUI::Widget* _sender) +{ + MWBase::Environment::get().getSoundManager()->playSound3D (mBook, "book close", 1.0, 1.0); + + MWBase::Environment::get().getInputManager()->setGuiMode(MWGui::GM_Game); +} + +void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender) +{ + MWBase::Environment::get().getSoundManager()->playSound3D (mBook, "Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); + + MWWorld::ActionTake take(mBook); + take.execute(); + + MWBase::Environment::get().getInputManager()->setGuiMode (GM_Game); +} + +void BookWindow::onNextPageButtonClicked (MyGUI::Widget* _sender) +{ + if ((mCurrentPage+1)*2 < mPages.size()) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); + + ++mCurrentPage; + + updatePages(); + } +} + +void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* _sender) +{ + if (mCurrentPage > 0) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); + + --mCurrentPage; + + updatePages(); + } +} + +void BookWindow::updatePages() +{ + mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); + mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); + + unsigned int i=0; + for (std::vector::iterator it = mPages.begin(); + it != mPages.end(); ++it) + { + if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) + (*it)->setVisible(true); + else + { + (*it)->setVisible(false); + } + ++i; + } +} diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp new file mode 100644 index 000000000..fcea1d11f --- /dev/null +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -0,0 +1,44 @@ +#ifndef MWGUI_BOOKWINDOW_H +#define MWGUI_BOOKWINDOW_H + +#include "window_base.hpp" + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + class BookWindow : public WindowBase + { + public: + BookWindow(WindowManager& parWindowManager); + void open(MWWorld::Ptr book); + + protected: + void onNextPageButtonClicked (MyGUI::Widget* _sender); + void onPrevPageButtonClicked (MyGUI::Widget* _sender); + void onCloseButtonClicked (MyGUI::Widget* _sender); + void onTakeButtonClicked (MyGUI::Widget* _sender); + + void updatePages(); + void clearPages(); + + private: + MyGUI::Button* mCloseButton; + MyGUI::Button* mTakeButton; + MyGUI::Button* mNextPageButton; + MyGUI::Button* mPrevPageButton; + MyGUI::TextBox* mLeftPageNumber; + MyGUI::TextBox* mRightPageNumber; + MyGUI::Widget* mLeftPage; + MyGUI::Widget* mRightPage; + + unsigned int mCurrentPage; // 0 is first page + std::vector mPages; + + MWWorld::Ptr mBook; + }; + +} + +#endif + diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 10c4cdcb4..7a86ba9ac 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -183,6 +183,7 @@ void CharacterCreation::spawnDialog(const char id) if (mCreateClassDialog) mWM->removeDialog(mCreateClassDialog); mCreateClassDialog = new CreateClassDialog(*mWM); + mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); mCreateClassDialog->open(); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 9f1fc5d2a..e654f7c90 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -111,6 +111,7 @@ PickClassDialog::PickClassDialog(WindowManager& parWindowManager) MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); + backButton->setCaption(mWindowManager.getGameSettingString("sBack", "")); MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); @@ -431,6 +432,7 @@ CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager) MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); + backButton->setCaption(mWindowManager.getGameSettingString("sBack", "")); MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); diff --git a/apps/openmw/mwgui/cursorreplace.cpp b/apps/openmw/mwgui/cursorreplace.cpp index 2079538fc..e66f54326 100644 --- a/apps/openmw/mwgui/cursorreplace.cpp +++ b/apps/openmw/mwgui/cursorreplace.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -13,4 +14,6 @@ CursorReplace::CursorReplace() OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90); OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45); OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45); + + OEngine::Render::Atlas::createFromFile("atlas1.cfg", "mwgui1", "textures\\"); } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 0238446eb..45163017a 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -2,6 +2,7 @@ #include "dialogue_history.hpp" #include "window_manager.hpp" #include "widgets.hpp" +#include "list.hpp" #include "components/esm_store/store.hpp" #include "../mwbase/environment.hpp" #include "../mwdialogue/dialoguemanager.hpp" @@ -38,6 +39,7 @@ std::string::size_type find_str_ci(const std::string& str, const std::string& su DialogueWindow::DialogueWindow(WindowManager& parWindowManager) : WindowBase("openmw_dialogue_window_layout.xml", parWindowManager) + , mEnabled(true) { // Centre dialog center(); @@ -56,17 +58,19 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager) //Topics list getWidget(topicsList, "TopicsList"); - topicsList->setScrollVisible(true); //topicsList->eventListSelectAccept += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); - topicsList->eventListMouseItemActivate += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); + topicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); //topicsList->eventListChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); MyGUI::ButtonPtr byeButton; getWidget(byeButton, "ByeButton"); byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); + byeButton->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGoodbye")->str); getWidget(pDispositionBar, "Disposition"); getWidget(pDispositionText,"DispositionText"); + + static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); } void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) @@ -79,6 +83,10 @@ void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) size_t cursorPosition = t->getCursorPosition(lastPressed); MyGUI::UString color = history->getColorAtPos(cursorPosition); + + if (!mEnabled && color == "#572D21") + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + if(color != "#B29154") { UString key = history->getColorTextAt(cursorPosition); @@ -88,6 +96,11 @@ void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) } } +void DialogueWindow::onWindowResize(MyGUI::Window* _sender) +{ + topicsList->adjustSize(); +} + void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (history->getVScrollPosition() - _rel*0.3 < 0) @@ -98,7 +111,7 @@ void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) void DialogueWindow::open() { - topicsList->removeAllItems(); + topicsList->clear(); pTopicsText.clear(); history->eraseText(0,history->getTextLength()); updateOptions(); @@ -110,23 +123,24 @@ void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); } -void DialogueWindow::onSelectTopic(MyGUI::ListBox* _sender, size_t _index) +void DialogueWindow::onSelectTopic(std::string topic) { - if (_index == MyGUI::ITEM_NONE) - return; - std::string topic = _sender->getItemNameAt(_index); + if (!mEnabled) return; + MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); } void DialogueWindow::startDialogue(std::string npcName) { + mEnabled = true; + topicsList->setEnabled(true); static_cast(mMainWidget)->setCaption(npcName); adjustWindowCaption(); } void DialogueWindow::setKeywords(std::list keyWords) { - topicsList->removeAllItems(); + topicsList->clear(); for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); it++) { topicsList->addItem(*it); @@ -135,9 +149,9 @@ void DialogueWindow::setKeywords(std::list keyWords) void DialogueWindow::removeKeyword(std::string keyWord) { - if(topicsList->findItemIndexWith(keyWord) != MyGUI::ITEM_NONE) + if(topicsList->hasItem(keyWord)) { - topicsList->removeItemAt(topicsList->findItemIndexWith(keyWord)); + topicsList->removeItem(keyWord); pTopicsText.erase(keyWord); } } @@ -211,7 +225,7 @@ void DialogueWindow::askQuestion(std::string question) void DialogueWindow::updateOptions() { //Clear the list of topics - topicsList->removeAllItems(); + topicsList->clear(); pTopicsText.clear(); history->eraseText(0,history->getTextLength()); @@ -220,3 +234,10 @@ void DialogueWindow::updateOptions() pDispositionText->eraseText(0,pDispositionText->getTextLength()); pDispositionText->addText("#B29154"+std::string("40/100")+"#B29154"); } + +void DialogueWindow::goodbye() +{ + history->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGoodbye")->str); + topicsList->setEnabled(false); + mEnabled = false; +} diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 5921ca57a..a29e73799 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -7,6 +7,11 @@ namespace MWGui { class WindowManager; + + namespace Widgets + { + class MWList; + } } /* @@ -40,12 +45,14 @@ namespace MWGui void addText(std::string text); void addTitle(std::string text); void askQuestion(std::string question); + void goodbye(); protected: - void onSelectTopic(MyGUI::ListBox* _sender, size_t _index); + void onSelectTopic(std::string topic); void onByeClicked(MyGUI::Widget* _sender); void onHistoryClicked(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void onWindowResize(MyGUI::Window* _sender); private: void updateOptions(); @@ -54,8 +61,10 @@ namespace MWGui */ std::string parseText(std::string text); + bool mEnabled; + DialogueHistory* history; - MyGUI::ListBox* topicsList; + Widgets::MWList* topicsList; MyGUI::ProgressPtr pDispositionBar; MyGUI::EditPtr pDispositionText; std::map pTopicsText;// this map links keyword and "real" text. diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp new file mode 100644 index 000000000..53c23c25d --- /dev/null +++ b/apps/openmw/mwgui/formatting.cpp @@ -0,0 +1,331 @@ +#include "formatting.hpp" + +#include +#include + +using namespace MWGui; + +namespace +{ + int convertFromHex(std::string hex) + { + int value = 0; + + int a = 0; + int b = hex.length() - 1; + for (; b >= 0; a++, b--) + { + if (hex[b] >= '0' && hex[b] <= '9') + { + value += (hex[b] - '0') * (1 << (a * 4)); + } + else + { + switch (hex[b]) + { + case 'A': + case 'a': + value += 10 * (1 << (a * 4)); + break; + + case 'B': + case 'b': + value += 11 * (1 << (a * 4)); + break; + + case 'C': + case 'c': + value += 12 * (1 << (a * 4)); + break; + + case 'D': + case 'd': + value += 13 * (1 << (a * 4)); + break; + + case 'E': + case 'e': + value += 14 * (1 << (a * 4)); + break; + + case 'F': + case 'f': + value += 15 * (1 << (a * 4)); + break; + + default: + throw std::runtime_error("invalid character in hex number"); + break; + } + } + } + + return value; + } +} + +std::vector BookTextParser::split(std::string text, const int width, const int height) +{ + std::vector result; + + boost::algorithm::replace_all(text, "
", "\n"); + boost::algorithm::replace_all(text, "

", "\n\n"); + + const int spacing = 48; + + while (text.size() > 0) + { + // read in characters until we have exceeded the size, or run out of text + int currentWidth = 0; + int currentHeight = 0; + std::string currentText; + std::string currentWord; + + unsigned int i=0; + while (currentHeight <= height-spacing && i', i) == std::string::npos) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + + if (text.size() > i+4 && text.substr(i, 4) == "', i)-i), false); + currentHeight += (mHeight-h); + currentWidth = 0; + } + else if (text.size() > i+5 && text.substr(i, 5) == "', i)-i)); + currentHeight += 18; // keep this in sync with the font size + currentWidth = 0; + } + else if (text.size() > i+4 && text.substr(i, 4) == "', i)-i)); + currentHeight += 18; // keep this in sync with the font size + currentWidth = 0; + } + + currentText += text.substr(i, text.find('>', i)-i+1); + i = text.find('>', i); + } + else if (text[i] == '\n') + { + currentHeight += 18; // keep this in sync with the font size + currentWidth = 0; + currentWord = ""; + currentText += text[i]; + } + else if (text[i] == ' ') + { + currentWidth += 3; // keep this in sync with the font's SpaceWidth property + currentWord = ""; + currentText += text[i]; + } + else + { + currentWidth += + MyGUI::FontManager::getInstance().getByName (mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont) + ->getGlyphInfo(static_cast(text[i]))->width; + currentWord += text[i]; + currentText += text[i]; + } + + if (currentWidth > width) + { + currentHeight += 18; // keep this in sync with the font size + currentWidth = 0; + + // add size of the current word + unsigned int j=0; + while (jgetGlyphInfo(static_cast(currentWord[j]))->width; + ++j; + } + } + + ++i; + } + if (currentHeight > height-spacing) + { + // remove the last word + currentText.erase(currentText.size()-currentWord.size(), currentText.size()); + } + + result.push_back(currentText); + text.erase(0, currentText.size()); + } + + return result; +} + +MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) +{ + mParent = parent; + mWidth = width; + mHeight = 0; + + assert(mParent); + while (mParent->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + } + + boost::algorithm::replace_all(text, "
", "\n"); + boost::algorithm::replace_all(text, "

", "\n\n"); + + // remove leading newlines + //while (text[0] == '\n') + // text.erase(0); + + // remove trailing " + if (text[text.size()-1] == '\"') + text.erase(text.size()-1); + + parseSubText(text); + return MyGUI::IntSize(mWidth, mHeight); +} + +void BookTextParser::parseImage(std::string tag, bool createWidget) +{ + int src_start = tag.find("SRC=")+5; + std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start); + + // fix texture extension to .dds + if (image.size() > 4) + { + image[image.size()-3] = 'd'; + image[image.size()-2] = 'd'; + image[image.size()-1] = 's'; + } + + int width_start = tag.find("WIDTH=")+7; + int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); + + int height_start = tag.find("HEIGHT=")+8; + int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); + + if (createWidget) + { + MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", + MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, + mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + box->setImageTexture("bookart\\" + image); + box->setProperty("NeedMouse", "false"); + } + + mWidth = std::max(mWidth, width); + mHeight += height; +} + +void BookTextParser::parseDiv(std::string tag) +{ + if (tag.find("ALIGN=") == std::string::npos) + return; + + int align_start = tag.find("ALIGN=")+7; + std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); + if (align == "CENTER") + mTextStyle.mTextAlign = MyGUI::Align::HCenter; + else if (align == "LEFT") + mTextStyle.mTextAlign = MyGUI::Align::Left; +} + +void BookTextParser::parseFont(std::string tag) +{ + if (tag.find("COLOR=") != std::string::npos) + { + int color_start = tag.find("COLOR=")+7; + std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); + + mTextStyle.mColour = MyGUI::Colour( + convertFromHex(color.substr(0, 2))/255.0, + convertFromHex(color.substr(2, 2))/255.0, + convertFromHex(color.substr(4, 2))/255.0); + } + if (tag.find("FACE=") != std::string::npos) + { + int face_start = tag.find("FACE=")+6; + std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); + + if (face != "Magic Cards") + mTextStyle.mFont = face; + } + if (tag.find("SIZE=") != std::string::npos) + { + /// \todo + } +} + +void BookTextParser::parseSubText(std::string text) +{ + if (text[0] == '<') + { + if (text.find('>') == std::string::npos) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + + if (text.size() > 4 && text.substr(0, 4) == "'))); + else if (text.size() > 5 && text.substr(0, 5) == "'))); + else if (text.size() > 4 && text.substr(0, 4) == "'))); + + text.erase(0, text.find('>')+1); + } + + bool tagFound = false; + std::string realText; // real text, without tags + unsigned int i=0; + for (; i= text.size()) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + ++i; + c = text[i]; + } + continue; + } + else + { + tagFound = true; + break; + } + } + else + realText += c; + } + + MyGUI::EditBox* box = mParent->createWidget("NormalText", + MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, + mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + box->setProperty("Static", "true"); + box->setProperty("MultiLine", "true"); + box->setProperty("WordWrap", "true"); + box->setProperty("NeedMouse", "false"); + box->setMaxTextLength(realText.size()); + box->setTextAlign(mTextStyle.mTextAlign); + box->setTextColour(mTextStyle.mColour); + box->setFontName(mTextStyle.mFont); + box->setCaption(realText); + box->setSize(box->getSize().width, box->getTextSize().height); + mHeight += box->getTextSize().height; + + if (tagFound) + { + parseSubText(text.substr(i, text.size())); + } +} diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp new file mode 100644 index 000000000..a1e115491 --- /dev/null +++ b/apps/openmw/mwgui/formatting.hpp @@ -0,0 +1,56 @@ +#ifndef MWGUI_FORMATTING_H +#define MWGUI_FORMATTING_H + +#include + +namespace MWGui +{ + struct TextStyle + { + TextStyle() : + mColour(0,0,0) + , mFont("Default") + , mTextSize(16) + , mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top) + { + } + + MyGUI::Colour mColour; + std::string mFont; + int mTextSize; + MyGUI::Align mTextAlign; + }; + + /// \brief utilities for parsing book/scroll text as mygui widgets + class BookTextParser + { + public: + /** + * Parse markup as MyGUI widgets + * @param markup to parse + * @param parent for the created widgets + * @param maximum width + * @return size of the created widgets + */ + MyGUI::IntSize parse(std::string text, MyGUI::Widget* parent, const int width); + + /** + * Split the specified text into pieces that fit in the area specified by width and height parameters + */ + std::vector split(std::string text, const int width, const int height); + + protected: + void parseSubText(std::string text); + + void parseImage(std::string tag, bool createWidget=true); + void parseDiv(std::string tag); + void parseFont(std::string tag); + private: + MyGUI::Widget* mParent; + int mWidth; // maximum width + int mHeight; // current height + TextStyle mTextStyle; + }; +} + +#endif diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index e67eda777..e4c38496d 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -82,6 +82,7 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager) : WindowBase("openmw_journal_layout.xml", parWindowManager) , lastPos(0) + , mVisible(false) { //setCoord(0,0,498, 342); center(); @@ -109,9 +110,15 @@ MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager) //std::list list = formatText("OpenMW rgh dsfg sqef srg ZT uzql n ZLIEHRF LQSJH GLOIjf qjfmj hslkdgn jlkdjhg qlr isgli shli uhs fiuh qksf cg ksjnf lkqsnbf ksbf sbfkl zbf kuyzflkj sbgdfkj zlfh ozhjfmo hzmfh lizuf rty qzt ezy tkyEZT RYYJ DG fgh is an open-source implementation of the game engine found in the game Morrowind. This is a dumb test text msodjbg smojg smoig fiiinnn"); //std::list list = formatText(); //displayLeftText(list.front()); +} - MyGUI::WindowPtr t = static_cast(mMainWidget); - t->eventWindowChangeCoord += MyGUI::newDelegate(this, &JournalWindow::onWindowResize); +void MWGui::JournalWindow::setVisible(bool visible) +{ + if (mVisible && !visible) + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); + mVisible = visible; + + mMainWidget->setVisible(visible); } void MWGui::JournalWindow::open() @@ -159,10 +166,6 @@ void MWGui::JournalWindow::open() } } -void MWGui::JournalWindow::onWindowResize(MyGUI::Window* window) -{ -} - void MWGui::JournalWindow::displayLeftText(std::string text) { mLeftTextWidget->eraseText(0,mLeftTextWidget->getTextLength()); diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 4656c7a48..cacdb9414 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -18,16 +18,9 @@ namespace MWGui JournalWindow(WindowManager& parWindowManager); void open(); + virtual void setVisible(bool visible); // only used to play close sound + private: - enum ColorStyle - { - CS_Sub, - CS_Normal, - CS_Super - }; - - void onWindowResize(MyGUI::Window* window); - void displayLeftText(std::string text); void displayRightText(std::string text); @@ -50,6 +43,7 @@ namespace MWGui std::vector leftPages; std::vector rightPages; int mPageNumber; //store the number of the current left page + bool mVisible; }; } diff --git a/apps/openmw/mwgui/list.cpp b/apps/openmw/mwgui/list.cpp new file mode 100644 index 000000000..d66cc6f89 --- /dev/null +++ b/apps/openmw/mwgui/list.cpp @@ -0,0 +1,121 @@ +#include "list.hpp" + +#include + +using namespace MWGui; +using namespace MWGui::Widgets; + +MWList::MWList() : + mClient(0) + , mScrollView(0) + , mItemHeight(0) +{ +} + +void MWList::initialiseOverride() +{ + Base::initialiseOverride(); + + assignWidget(mClient, "Client"); + if (mClient == 0) + mClient = this; + + mScrollView = mClient->createWidgetReal( + "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), + MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); +} + +void MWList::addItem(const std::string& name) +{ + mItems.push_back(name); + + redraw(); +} + +void MWList::adjustSize() +{ + redraw(); +} + +void MWList::redraw(bool scrollbarShown) +{ + const int _scrollBarWidth = 24; // fetch this from skin? + const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; + const int spacing = 3; + + while (mScrollView->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); + } + + mItemHeight = 0; + for (std::vector::const_iterator it=mItems.begin(); + it!=mItems.end(); ++it) + { + MyGUI::Button* button = mScrollView->createWidget( + "MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), + MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it)); + button->setCaption((*it)); + button->getSubWidgetText()->setWordWrap(true); + button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); + button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); + + int height = button->getTextSize().height; + button->setSize(MyGUI::IntSize(button->getSize().width, height)); + + mItemHeight += height + spacing; + } + mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); + mScrollView->setViewOffset(MyGUI::IntPoint(0,0)); + + if (!scrollbarShown && mItemHeight > mClient->getSize().height) + redraw(true); +} + +bool MWList::hasItem(const std::string& name) +{ + return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); +} + +unsigned int MWList::getItemCount() +{ + return mItems.size(); +} + +std::string MWList::getItemNameAt(unsigned int at) +{ + assert(at < mItems.size() && "List item out of bounds"); + return mItems[at]; +} + +void MWList::removeItem(const std::string& name) +{ + assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() ); + mItems.erase( std::find(mItems.begin(), mItems.end(), name) ); + + redraw(); +} + +void MWList::clear() +{ + mItems.clear(); + + redraw(); +} + +void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel) +{ + //NB view offset is negative + if (mScrollView->getViewOffset().top + _rel*0.3 > 0) + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3)); +} + +void MWList::onItemSelected(MyGUI::Widget* _sender) +{ + std::string name = static_cast(_sender)->getCaption(); + + eventItemSelected(name); +} diff --git a/apps/openmw/mwgui/list.hpp b/apps/openmw/mwgui/list.hpp new file mode 100644 index 000000000..a2e9afcd1 --- /dev/null +++ b/apps/openmw/mwgui/list.hpp @@ -0,0 +1,59 @@ +#ifndef MWGUI_LIST_HPP +#define MWGUI_LIST_HPP + +#include + +namespace MWGui +{ + namespace Widgets + { + /** + * \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 + */ + class MWList : public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(MWList) + public: + MWList(); + + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_String; + + /** + * Event: Item selected with the mouse. + * signature: void method(std::string itemName) + */ + EventHandle_String eventItemSelected; + + /** + * Call after the size of the list changed + */ + void adjustSize(); + + void addItem(const std::string& name); + void removeItem(const std::string& name); + bool hasItem(const std::string& name); + unsigned int getItemCount(); + std::string getItemNameAt(unsigned int at); + void clear(); + + protected: + void initialiseOverride(); + + void redraw(bool scrollbarShown = false); + + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void onItemSelected(MyGUI::Widget* _sender); + + private: + MyGUI::ScrollView* mScrollView; + MyGUI::Widget* mClient; + + std::vector mItems; + + int mItemHeight; // height of all items + }; + } +} + +#endif diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 55f0952ce..fa31bb1c4 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -12,12 +12,12 @@ namespace MWGui GM_Console, // Console mode GM_Journal, // Journal mode - // None of the following are implemented yet + GM_Scroll, // Read scroll + GM_Book, // Read book GM_Dialogue, // NPC interaction GM_Barter, GM_Rest, - // .. more here .. // Startup character creation dialogs GM_Name, diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 7dfe514de..81a87b07b 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -248,7 +248,7 @@ MyGUI::TextBox* ReviewDialog::addValueItem(const std::string text, const std::st skillNameWidget = skillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Default); skillNameWidget->setCaption(text); - skillValueWidget = skillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Default); + skillValueWidget = skillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); skillValueWidget->setCaption(value); skillValueWidget->_setWidgetState(state); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp new file mode 100644 index 000000000..38e2f77c1 --- /dev/null +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -0,0 +1,61 @@ +#include "scrollwindow.hpp" + +#include "formatting.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwinput/inputmanager.hpp" +#include "../mwworld/actiontake.hpp" +#include "../mwsound/soundmanager.hpp" + +using namespace MWGui; + +ScrollWindow::ScrollWindow (WindowManager& parWindowManager) : + WindowBase("openmw_scroll_layout.xml", parWindowManager) +{ + getWidget(mTextView, "TextView"); + + getWidget(mCloseButton, "CloseButton"); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); + + getWidget(mTakeButton, "TakeButton"); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); + + center(); +} + +void ScrollWindow::open (MWWorld::Ptr scroll) +{ + MWBase::Environment::get().getSoundManager()->playSound3D (scroll, "scroll", 1.0, 1.0); + + mScroll = scroll; + + ESMS::LiveCellRef *ref = + mScroll.get(); + + BookTextParser parser; + MyGUI::IntSize size = parser.parse(ref->base->text, mTextView, 390); + + if (size.height > mTextView->getSize().height) + mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); + else + mTextView->setCanvasSize(410, mTextView->getSize().height); + + mTextView->setViewOffset(MyGUI::IntPoint(0,0)); +} + +void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) +{ + MWBase::Environment::get().getSoundManager()->playSound3D (mScroll, "scroll", 1.0, 1.0); + + MWBase::Environment::get().getInputManager()->setGuiMode (GM_Game); +} + +void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) +{ + MWBase::Environment::get().getSoundManager()->playSound3D (mScroll, "Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); + + MWWorld::ActionTake take(mScroll); + take.execute(); + + MWBase::Environment::get().getInputManager()->setGuiMode (GM_Game); +} diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp new file mode 100644 index 000000000..918a3d3ef --- /dev/null +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -0,0 +1,30 @@ +#ifndef MWGUI_SCROLLWINDOW_H +#define MWGUI_SCROLLWINDOW_H + +#include "window_base.hpp" + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + class ScrollWindow : public WindowBase + { + public: + ScrollWindow (WindowManager& parWindowManager); + void open (MWWorld::Ptr scroll); + + protected: + void onCloseButtonClicked (MyGUI::Widget* _sender); + void onTakeButtonClicked (MyGUI::Widget* _sender); + + private: + MyGUI::Button* mCloseButton; + MyGUI::Button* mTakeButton; + MyGUI::ScrollView* mTextView; + + MWWorld::Ptr mScroll; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index b007fc1d4..8b688984f 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -230,7 +230,9 @@ void StatsWindow::setBirthSign (const std::string& signId) void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::ImageBox* separator = skillClientWidget->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); + MyGUI::ImageBox* separator = skillClientWidget->createWidget("MW_HLine", + MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); skillWidgets.push_back(separator); coord1.top += separator->getHeight(); @@ -239,7 +241,9 @@ void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::TextBox* groupWidget = skillClientWidget->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); + MyGUI::TextBox* groupWidget = skillClientWidget->createWidget("SandBrightText", + MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); groupWidget->setCaption(label); skillWidgets.push_back(groupWidget); @@ -251,12 +255,12 @@ MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::st { MyGUI::TextBox *skillNameWidget, *skillValueWidget; - skillNameWidget = skillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Default); + skillNameWidget = skillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); skillNameWidget->setCaption(text); skillNameWidget->setUserString("ToolTipType", "Text"); skillNameWidget->setUserString("ToolTipText", tooltip); - skillValueWidget = skillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Default); + skillValueWidget = skillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); skillValueWidget->setUserString("ToolTipType", "Text"); skillValueWidget->setUserString("ToolTipText", tooltip); skillValueWidget->setCaption(value); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 90b5baa67..1dbe90968 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -31,13 +31,10 @@ void ToolTips::onFrame(float frameDuration) { /// \todo Store a MWWorld::Ptr in the widget user data, retrieve it here and construct a tooltip dynamically - /// \todo we are destroying/creating the tooltip widgets every frame here, - /// because the tooltip might change (e.g. when trap is activated) - /// is there maybe a better way (listener when the object changes)? - for (size_t i=0; igetChildCount(); ++i) - { - mDynamicToolTipBox->_destroyChildWidget(mDynamicToolTipBox->getChildAt(i)); - } + MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox); + mDynamicToolTipBox = mMainWidget->createWidget("HUD_Box", + IntCoord(0, 0, mMainWidget->getCoord().width, mMainWidget->getCoord().height), + Align::Stretch, "DynamicToolTipBox"); const IntSize &viewSize = RenderManager::getInstance().getViewSize(); @@ -56,7 +53,6 @@ void ToolTips::onFrame(float frameDuration) std::string text = focus->getUserString("ToolTipText"); ToolTipInfo info; - if (type == "") { mDynamicToolTipBox->setVisible(false); @@ -64,7 +60,7 @@ void ToolTips::onFrame(float frameDuration) } else if (type == "Text") { - info.caption = text; + info.text = text; } else if (type == "CaptionText") { @@ -107,8 +103,8 @@ void ToolTips::onFrame(float frameDuration) // adjust tooltip size to fit its content, position it above the crosshair /// \todo Slide the tooltip along the bounding box of the focused object (like in Morrowind) - setCoord(viewSize.width/2 - (tooltipSize.width)/2.f, - viewSize.height/2 - (tooltipSize.height) - 32, + setCoord(std::max(0, viewSize.width/2 - (tooltipSize.width)/2), + std::max(0, viewSize.height/2 - (tooltipSize.height) - 32), tooltipSize.width, tooltipSize.height); } @@ -200,8 +196,8 @@ IntSize ToolTips::createToolTip(const ToolTipInfo& info) const IntPoint padding(8, 8); - const int imageCaptionHPadding = 8; - const int imageCaptionVPadding = 4; + const int imageCaptionHPadding = (caption != "" ? 8 : 0); + const int imageCaptionVPadding = (caption != "" ? 4 : 0); std::string realImage = "icons\\" + image; findImageExtension(realImage); @@ -211,7 +207,7 @@ IntSize ToolTips::createToolTip(const ToolTipInfo& info) captionWidget->setCaption(caption); IntSize captionSize = captionWidget->getTextSize(); - int captionHeight = std::max(captionSize.height, imageSize); + int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), Align::Stretch, "ToolTipText"); textWidget->setProperty("Static", "true"); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 2f5af6473..0422bb0e2 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -356,18 +356,6 @@ void MWSpellEffect::updateWidgets() if (!mWindowManager) return; - // lists effects that have no magnitude (e.g. invisiblity) - /// \todo this list is probably incomplete - std::vector effectsWithoutMagnitude; - effectsWithoutMagnitude.push_back("sEffectInvisibility"); - effectsWithoutMagnitude.push_back("sEffectStuntedMagicka"); - effectsWithoutMagnitude.push_back("sEffectParalyze"); - - // lists effects that have no duration (e.g. open lock) - /// \todo this list is probably incomplete - std::vector effectsWithoutDuration; - effectsWithoutDuration.push_back("sEffectOpen"); - const ESMS::ESMStore &store = mWindowManager->getStore(); const ESM::MagicEffect *magicEffect = store.magicEffects.search(effect.effectID); if (textWidget) @@ -401,8 +389,7 @@ void MWSpellEffect::updateWidgets() spellLine += " " + mWindowManager->getGameSettingString(attributes[effect.attribute], ""); } - bool hasMagnitude = (std::find(effectsWithoutMagnitude.begin(), effectsWithoutMagnitude.end(), effectIDStr) == effectsWithoutMagnitude.end()); - if ((effect.magnMin >= 0 || effect.magnMax >= 0) && hasMagnitude) + if ((effect.magnMin >= 0 || effect.magnMax >= 0) && effectHasMagnitude(effectIDStr)) { if (effect.magnMin == effect.magnMax) spellLine += " " + boost::lexical_cast(effect.magnMin) + " " + ((effect.magnMin == 1) ? pt : pts); @@ -415,8 +402,7 @@ void MWSpellEffect::updateWidgets() // constant effects have no duration and no target if (!(mFlags & MWEffectList::EF_Constant)) { - bool hasDuration = (std::find(effectsWithoutDuration.begin(), effectsWithoutDuration.end(), effectIDStr) == effectsWithoutDuration.end()); - if (effect.duration >= 0 && hasDuration) + if (effect.duration >= 0 && effectHasDuration(effectIDStr)) { spellLine += " " + mWindowManager->getGameSettingString("sfor", "") + " " + boost::lexical_cast(effect.duration) + ((effect.duration == 1) ? sec : secs); } @@ -590,11 +576,107 @@ std::string MWSpellEffect::effectIDToString(const short effectID) names[35] ="sEffectWeaknesstoPoison"; names[30] ="sEffectWeaknesstoShock"; + // bloodmoon + names[138] ="sEffectSummonCreature01"; + names[139] ="sEffectSummonCreature02"; + names[140] ="sEffectSummonCreature03"; + names[141] ="sEffectSummonCreature04"; + names[142] ="sEffectSummonCreature05"; + + // tribunal + names[137] ="sEffectSummonFabricant"; + assert(names.find(effectID) != names.end() && "Unimplemented effect type"); return names[effectID]; } +bool MWSpellEffect::effectHasDuration(const std::string& effect) +{ + // lists effects that have no duration (e.g. open lock) + std::vector effectsWithoutDuration; + effectsWithoutDuration.push_back("sEffectOpen"); + effectsWithoutDuration.push_back("sEffectLock"); + effectsWithoutDuration.push_back("sEffectDispel"); + effectsWithoutDuration.push_back("sEffectSunDamage"); + effectsWithoutDuration.push_back("sEffectCorpus"); + effectsWithoutDuration.push_back("sEffectVampirism"); + effectsWithoutDuration.push_back("sEffectMark"); + effectsWithoutDuration.push_back("sEffectRecall"); + effectsWithoutDuration.push_back("sEffectDivineIntervention"); + effectsWithoutDuration.push_back("sEffectAlmsiviIntervention"); + effectsWithoutDuration.push_back("sEffectCureCommonDisease"); + effectsWithoutDuration.push_back("sEffectCureBlightDisease"); + effectsWithoutDuration.push_back("sEffectCureCorprusDisease"); + effectsWithoutDuration.push_back("sEffectCurePoison"); + effectsWithoutDuration.push_back("sEffectCureParalyzation"); + effectsWithoutDuration.push_back("sEffectRemoveCurse"); + effectsWithoutDuration.push_back("sEffectRestoreAttribute"); + + return (std::find(effectsWithoutDuration.begin(), effectsWithoutDuration.end(), effect) == effectsWithoutDuration.end()); +} + +bool MWSpellEffect::effectHasMagnitude(const std::string& effect) +{ + // lists effects that have no magnitude (e.g. invisiblity) + std::vector effectsWithoutMagnitude; + effectsWithoutMagnitude.push_back("sEffectInvisibility"); + effectsWithoutMagnitude.push_back("sEffectStuntedMagicka"); + effectsWithoutMagnitude.push_back("sEffectParalyze"); + effectsWithoutMagnitude.push_back("sEffectSoultrap"); + effectsWithoutMagnitude.push_back("sEffectSilence"); + effectsWithoutMagnitude.push_back("sEffectParalyze"); + effectsWithoutMagnitude.push_back("sEffectInvisibility"); + effectsWithoutMagnitude.push_back("sEffectWaterWalking"); + effectsWithoutMagnitude.push_back("sEffectWaterBreathing"); + effectsWithoutMagnitude.push_back("sEffectSummonScamp"); + effectsWithoutMagnitude.push_back("sEffectSummonClannfear"); + effectsWithoutMagnitude.push_back("sEffectSummonDaedroth"); + effectsWithoutMagnitude.push_back("sEffectSummonDremora"); + effectsWithoutMagnitude.push_back("sEffectSummonAncestralGhost"); + effectsWithoutMagnitude.push_back("sEffectSummonSkeletalMinion"); + effectsWithoutMagnitude.push_back("sEffectSummonBonewalker"); + effectsWithoutMagnitude.push_back("sEffectSummonGreaterBonewalker"); + effectsWithoutMagnitude.push_back("sEffectSummonBonelord"); + effectsWithoutMagnitude.push_back("sEffectSummonWingedTwilight"); + effectsWithoutMagnitude.push_back("sEffectSummonHunger"); + effectsWithoutMagnitude.push_back("sEffectSummonGoldenSaint"); + effectsWithoutMagnitude.push_back("sEffectSummonFlameAtronach"); + effectsWithoutMagnitude.push_back("sEffectSummonFrostAtronach"); + effectsWithoutMagnitude.push_back("sEffectSummonStormAtronach"); + effectsWithoutMagnitude.push_back("sEffectSummonCenturionSphere"); + effectsWithoutMagnitude.push_back("sEffectBoundDagger"); + effectsWithoutMagnitude.push_back("sEffectBoundLongsword"); + effectsWithoutMagnitude.push_back("sEffectBoundMace"); + effectsWithoutMagnitude.push_back("sEffectBoundBattleAxe"); + effectsWithoutMagnitude.push_back("sEffectBoundSpear"); + effectsWithoutMagnitude.push_back("sEffectBoundLongbow"); + effectsWithoutMagnitude.push_back("sEffectBoundCuirass"); + effectsWithoutMagnitude.push_back("sEffectBoundHelm"); + effectsWithoutMagnitude.push_back("sEffectBoundBoots"); + effectsWithoutMagnitude.push_back("sEffectBoundShield"); + effectsWithoutMagnitude.push_back("sEffectBoundGloves"); + effectsWithoutMagnitude.push_back("sEffectStuntedMagicka"); + effectsWithoutMagnitude.push_back("sEffectMark"); + effectsWithoutMagnitude.push_back("sEffectRecall"); + effectsWithoutMagnitude.push_back("sEffectDivineIntervention"); + effectsWithoutMagnitude.push_back("sEffectAlmsiviIntervention"); + effectsWithoutMagnitude.push_back("sEffectCureCommonDisease"); + effectsWithoutMagnitude.push_back("sEffectCureBlightDisease"); + effectsWithoutMagnitude.push_back("sEffectCureCorprusDisease"); + effectsWithoutMagnitude.push_back("sEffectCurePoison"); + effectsWithoutMagnitude.push_back("sEffectCureParalyzation"); + effectsWithoutMagnitude.push_back("sEffectRemoveCurse"); + effectsWithoutMagnitude.push_back("sEffectSummonCreature01"); + effectsWithoutMagnitude.push_back("sEffectSummonCreature02"); + effectsWithoutMagnitude.push_back("sEffectSummonCreature03"); + effectsWithoutMagnitude.push_back("sEffectSummonCreature04"); + effectsWithoutMagnitude.push_back("sEffectSummonCreature05"); + effectsWithoutMagnitude.push_back("sEffectSummonFabricant"); + + return (std::find(effectsWithoutMagnitude.begin(), effectsWithoutMagnitude.end(), effect) == effectsWithoutMagnitude.end()); +} + MWSpellEffect::~MWSpellEffect() { } diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index c0e62533d..74da7fc93 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -197,6 +197,8 @@ namespace MWGui void setFlags(int flags) { mFlags = flags; } std::string effectIDToString(const short effectID); + bool effectHasMagnitude (const std::string& effect); + bool effectHasDuration (const std::string& effect); const SpellEffectValue &getSpellEffect() const { return effect; } diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 0f2df53e1..dfac55beb 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -8,6 +8,9 @@ #include "stats_window.hpp" #include "messagebox.hpp" #include "tooltips.hpp" +#include "scrollwindow.hpp" +#include "bookwindow.hpp" +#include "list.hpp" #include "../mwmechanics/mechanicsmanager.hpp" #include "../mwinput/inputmanager.hpp" @@ -37,6 +40,8 @@ WindowManager::WindowManager( , mMessageBoxManager(NULL) , console(NULL) , mJournal(NULL) + , mBookWindow(NULL) + , mScrollWindow(NULL) , dialogueWindow(nullptr) , mCharGen(NULL) , playerClass() @@ -69,6 +74,13 @@ WindowManager::WindowManager( //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); // Get size info from the Gui object assert(gui); @@ -84,6 +96,8 @@ WindowManager::WindowManager( mMessageBoxManager = new MessageBoxManager(this); dialogueWindow = new DialogueWindow(*this); mToolTips = new ToolTips(this); + mScrollWindow = new ScrollWindow(*this); + mBookWindow = new BookWindow(*this); // The HUD is always on hud->setVisible(true); @@ -101,13 +115,6 @@ WindowManager::WindowManager( playerSkillValues.insert(std::make_pair(ESM::Skill::skillIds[i], MWMechanics::Stat())); } - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - // Set up visibility updateVisible(); } @@ -179,6 +186,8 @@ void WindowManager::updateVisible() stats->setVisible(false); console->disable(); mJournal->setVisible(false); + mScrollWindow->setVisible(false); + mBookWindow->setVisible(false); dialogueWindow->setVisible(false); // Mouse is visible whenever we're not in game mode @@ -199,6 +208,12 @@ void WindowManager::updateVisible() case GM_Console: console->enable(); break; + case GM_Scroll: + mScrollWindow->setVisible(true); + break; + case GM_Book: + mBookWindow->setVisible(true); + break; case GM_Name: case GM_Race: case GM_Class: diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index f96660715..b84c74441 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -62,6 +62,8 @@ namespace MWGui class JournalWindow; class CharacterCreation; class ToolTips; + class ScrollWindow; + class BookWindow; class TextInputDialog; class InfoBoxDialog; @@ -125,6 +127,9 @@ namespace MWGui MWGui::DialogueWindow* getDialogueWindow() {return dialogueWindow;} + MWGui::BookWindow* getBookWindow() {return mBookWindow;} + MWGui::ScrollWindow* getScrollWindow() {return mScrollWindow;} + MyGUI::Gui* getGui() const { return gui; } void wmUpdateFps(float fps, size_t triangleCount, size_t batchCount) @@ -203,6 +208,8 @@ namespace MWGui Console *console; JournalWindow* mJournal; DialogueWindow *dialogueWindow; + ScrollWindow* mScrollWindow; + BookWindow* mBookWindow; CharacterCreation* mCharGen; diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 7a56a0adf..9026cdf64 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -133,6 +133,9 @@ namespace MWInput void screenshot() { mEngine.screenshot(); + + std::vector empty; + windows.messageBox ("Screenshot saved", empty); } /* toggleInventory() is called when the user presses the button to toggle the inventory screen. */ diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index b99d55999..ec8ab59b4 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -115,6 +115,16 @@ namespace MWScript } }; + class OpGoodbye : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime& runtime) + { + MWBase::Environment::get().getDialogueManager()->goodbye(); + } + }; + const int opcodeJournal = 0x2000133; const int opcodeSetJournalIndex = 0x2000134; const int opcodeGetJournalIndex = 0x2000135; @@ -122,6 +132,7 @@ namespace MWScript const int opcodeChoice = 0x2000a; const int opcodeForceGreeting = 0x200014f; const int opcodeForceGreetingExplicit = 0x2000150; + const int opcodeGoodbye = 0x2000152; void registerExtensions (Compiler::Extensions& extensions) { @@ -133,6 +144,7 @@ namespace MWScript extensions.registerInstruction("forcegreeting","",opcodeForceGreeting); extensions.registerInstruction("forcegreeting","",opcodeForceGreeting, opcodeForceGreetingExplicit); + extensions.registerInstruction("goodbye", "", opcodeGoodbye); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -144,6 +156,7 @@ namespace MWScript interpreter.installSegment3 (opcodeChoice,new OpChoice); interpreter.installSegment5 (opcodeForceGreeting, new OpForceGreeting); interpreter.installSegment5 (opcodeForceGreetingExplicit, new OpForceGreeting); + interpreter.installSegment5 (opcodeGoodbye, new OpGoodbye); } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 378b2412a..0920c72f8 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -145,4 +145,5 @@ op 0x200014e: ModDisposition, explicit reference op 0x200014f: ForceGreeting op 0x2000150: ForceGreeting, explicit reference op 0x2000151: ToggleFullHelp -opcodes 0x2000152-0x3ffffff unused +op 0x2000152: Goodbye +opcodes 0x2000153-0x3ffffff unused diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp new file mode 100644 index 000000000..0d37e06f5 --- /dev/null +++ b/apps/openmw/mwworld/actionread.cpp @@ -0,0 +1,31 @@ +#include "actionread.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwgui/window_manager.hpp" +#include "../mwgui/bookwindow.hpp" +#include "../mwgui/scrollwindow.hpp" + +namespace MWWorld +{ + ActionRead::ActionRead (const MWWorld::Ptr& object) : mObject (object) + { + } + + void ActionRead::execute () + { + ESMS::LiveCellRef *ref = + mObject.get(); + + if (ref->base->data.isScroll) + { + MWBase::Environment::get().getWindowManager()->setGuiMode(MWGui::GM_Scroll); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->open(mObject); + } + else + { + MWBase::Environment::get().getWindowManager()->setGuiMode(MWGui::GM_Book); + MWBase::Environment::get().getWindowManager()->getBookWindow()->open(mObject); + } + } +} + diff --git a/apps/openmw/mwworld/actionread.hpp b/apps/openmw/mwworld/actionread.hpp new file mode 100644 index 000000000..a4b495f79 --- /dev/null +++ b/apps/openmw/mwworld/actionread.hpp @@ -0,0 +1,21 @@ +#ifndef GAME_MWWORLD_ACTIONREAD_H +#define GAME_MWWORLD_ACTIONREAD_H + +#include "action.hpp" +#include "ptr.hpp" + +namespace MWWorld +{ + class ActionRead : public Action + { + Ptr mObject; // book or scroll to read + + public: + /// @param book or scroll to read + ActionRead (const Ptr& object); + + virtual void execute (); + }; +} + +#endif // ACTIONOPEN_H diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 49055d2ed..ac803646e 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -9,7 +9,7 @@ configure_file("${SDIR}/black.png" "${DDIR}/black.png" COPYONLY) configure_file("${SDIR}/core.skin" "${DDIR}/core.skin" COPYONLY) configure_file("${SDIR}/core.xml" "${DDIR}/core.xml" COPYONLY) configure_file("${SDIR}/mwgui.png" "${DDIR}/mwgui.png" COPYONLY) -configure_file("${SDIR}/openmw_images.xml" "${DDIR}/openmw_images.xml" COPYONLY) +configure_file("${SDIR}/openmw_resources.xml" "${DDIR}/openmw_resources.xml" COPYONLY) configure_file("${SDIR}/openmw_settings.xml" "${DDIR}/openmw_settings.xml" COPYONLY) configure_file("${SDIR}/openmw_box.skin.xml" "${DDIR}/openmw_box.skin.xml" COPYONLY) configure_file("${SDIR}/openmw_button.skin.xml" "${DDIR}/openmw_button.skin.xml" COPYONLY) @@ -51,7 +51,12 @@ configure_file("${SDIR}/openmw_interactive_messagebox_layout.xml" "${DDIR}/openm configure_file("${SDIR}/openmw_journal_layout.xml" "${DDIR}/openmw_journal_layout.xml" COPYONLY) configure_file("${SDIR}/openmw_journal_skin.xml" "${DDIR}/openmw_journal_skin.xml" COPYONLY) configure_file("${SDIR}/openmw_tooltips.xml" "${DDIR}/openmw_tooltips.xml" COPYONLY) +configure_file("${SDIR}/openmw_scroll_layout.xml" "${DDIR}/openmw_scroll_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_scroll_skin.xml" "${DDIR}/openmw_scroll_skin.xml" COPYONLY) +configure_file("${SDIR}/openmw_book_layout.xml" "${DDIR}/openmw_book_layout.xml" COPYONLY) +configure_file("${SDIR}/atlas1.cfg" "${DDIR}/atlas1.cfg" COPYONLY) configure_file("${SDIR}/smallbars.png" "${DDIR}/smallbars.png" COPYONLY) configure_file("${SDIR}/transparent.png" "${DDIR}/transparent.png" COPYONLY) configure_file("${SDIR}/EBGaramond-Regular.ttf" "${DDIR}/EBGaramond-Regular.ttf" COPYONLY) +configure_file("${SDIR}/Obliviontt.zip" "${DDIR}/Obliviontt.zip" COPYONLY) configure_file("${SDIR}/VeraMono.ttf" "${DDIR}/VeraMono.ttf" COPYONLY) diff --git a/files/mygui/Obliviontt.zip b/files/mygui/Obliviontt.zip new file mode 100644 index 000000000..af4f809fd Binary files /dev/null and b/files/mygui/Obliviontt.zip differ diff --git a/files/mygui/atlas1.cfg b/files/mygui/atlas1.cfg new file mode 100644 index 000000000..d1e05e041 --- /dev/null +++ b/files/mygui/atlas1.cfg @@ -0,0 +1,51 @@ +[settings] + size_x = 512 + size_y = 512 + +[tx_menubook_close_idle.dds] + x = 0 + y = 0 + +[tx_menubook_close_over.dds] + x = 128 + y = 0 + +[tx_menubook_close_pressed.dds] + x = 256 + y = 0 + +[tx_menubook_take_idle.dds] + x = 384 + y = 0 + +[tx_menubook_take_over.dds] + x = 0 + y = 32 + +[tx_menubook_take_pressed.dds] + x = 128 + y = 32 + +[tx_menubook_next_idle.dds] + x = 256 + y = 32 + +[tx_menubook_next_over.dds] + x = 384 + y = 32 + +[tx_menubook_next_pressed.dds] + x = 0 + y = 64 + +[tx_menubook_prev_idle.dds] + x = 128 + y = 64 + +[tx_menubook_prev_over.dds] + x = 256 + y = 64 + +[tx_menubook_prev_pressed.dds] + x = 384 + y = 64 diff --git a/files/mygui/core.xml b/files/mygui/core.xml index 7417328cf..e1fb1b5e2 100644 --- a/files/mygui/core.xml +++ b/files/mygui/core.xml @@ -2,7 +2,7 @@ - + @@ -20,6 +20,7 @@ + diff --git a/files/mygui/openmw.font.xml b/files/mygui/openmw.font.xml index e7d0f50c8..252499a5f 100644 --- a/files/mygui/openmw.font.xml +++ b/files/mygui/openmw.font.xml @@ -6,26 +6,38 @@ - + + + + + + + + + + + + + + - @@ -37,4 +49,4 @@ - \ No newline at end of file + diff --git a/files/mygui/openmw_book_layout.xml b/files/mygui/openmw_book_layout.xml new file mode 100644 index 000000000..07ebf5030 --- /dev/null +++ b/files/mygui/openmw_book_layout.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_dialogue_window_layout.xml b/files/mygui/openmw_dialogue_window_layout.xml index b0e437074..d45de4bd4 100644 --- a/files/mygui/openmw_dialogue_window_layout.xml +++ b/files/mygui/openmw_dialogue_window_layout.xml @@ -19,7 +19,7 @@ - + diff --git a/files/mygui/openmw_images.xml b/files/mygui/openmw_images.xml deleted file mode 100644 index e149273e2..000000000 --- a/files/mygui/openmw_images.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/files/mygui/openmw_journal_layout.xml b/files/mygui/openmw_journal_layout.xml index da4fe658b..75bb5eea1 100644 --- a/files/mygui/openmw_journal_layout.xml +++ b/files/mygui/openmw_journal_layout.xml @@ -7,14 +7,14 @@ - + + - - + + - - - + + diff --git a/files/mygui/openmw_journal_skin.xml b/files/mygui/openmw_journal_skin.xml index 0ef87852f..9f82e907f 100644 --- a/files/mygui/openmw_journal_skin.xml +++ b/files/mygui/openmw_journal_skin.xml @@ -1,18 +1,6 @@ - - - - - - - - - - - - diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 02075ad1a..89cc73123 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -212,6 +212,20 @@ + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_resources.xml b/files/mygui/openmw_resources.xml new file mode 100644 index 000000000..455765aad --- /dev/null +++ b/files/mygui/openmw_resources.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_scroll_layout.xml b/files/mygui/openmw_scroll_layout.xml new file mode 100644 index 000000000..0f4a0be3e --- /dev/null +++ b/files/mygui/openmw_scroll_layout.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_scroll_skin.xml b/files/mygui/openmw_scroll_skin.xml new file mode 100644 index 000000000..39437d54e --- /dev/null +++ b/files/mygui/openmw_scroll_skin.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/files/mygui/openmw_stats_window_layout.xml b/files/mygui/openmw_stats_window_layout.xml index fd99f863e..4f3f80e89 100644 --- a/files/mygui/openmw_stats_window_layout.xml +++ b/files/mygui/openmw_stats_window_layout.xml @@ -26,34 +26,34 @@ - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/libs/openengine/ogre/atlas.cpp b/libs/openengine/ogre/atlas.cpp new file mode 100644 index 000000000..01b84afab --- /dev/null +++ b/libs/openengine/ogre/atlas.cpp @@ -0,0 +1,113 @@ +#include "atlas.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Ogre; +using namespace OEngine::Render; + +void Atlas::createFromFile (const std::string& filename, const std::string& textureName, const std::string& texturePrefix) +{ + ConfigFile file; + file.load(filename, ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, "\t:=", true); + + Root* root = Ogre::Root::getSingletonPtr(); + + SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC); + Camera* camera = sceneMgr->createCamera("AtlasCamera"); + + int width = StringConverter::parseInt(file.getSetting("size_x", "settings")); + int height = StringConverter::parseInt(file.getSetting("size_y", "settings")); + + std::vector rectangles; + int i = 0; + + ConfigFile::SectionIterator seci = file.getSectionIterator(); + while (seci.hasMoreElements()) + { + Ogre::String sectionName = seci.peekNextKey(); + seci.getNext(); + + if (sectionName == "settings" || sectionName == "") + continue; + + MaterialPtr material = MaterialManager::getSingleton().create("AtlasMaterial" + StringConverter::toString(i), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + material->getTechnique(0)->getPass(0)->setLightingEnabled(false); + material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + TextureUnitState* tus = material->getTechnique(0)->getPass(0)->createTextureUnitState(texturePrefix + sectionName); + tus->setTextureBorderColour(ColourValue(0, 0, 0, 0)); + + Rectangle2D* rect = new Rectangle2D(true); + rect->setMaterial("AtlasMaterial" + StringConverter::toString(i)); + rect->setRenderQueueGroup(RENDER_QUEUE_BACKGROUND); + + int x = StringConverter::parseInt(file.getSetting("x", sectionName)); + int y = StringConverter::parseInt(file.getSetting("y", sectionName)); + + TexturePtr texture = TextureManager::getSingleton().getByName(texturePrefix + sectionName); + if (texture.isNull()) + { + std::cerr << "OEngine::Render::Atlas: Can't find texture " << texturePrefix + sectionName << ", skipping..." << std::endl; + continue; + } + int textureWidth = texture->getWidth(); + int textureHeight = texture->getHeight(); + + float left = x/float(width) * 2 - 1; + float top = (1-(y/float(height))) * 2 - 1; + float right = ((x+textureWidth))/float(width) * 2 - 1; + float bottom = (1-((y+textureHeight)/float(height))) * 2 - 1; + rect->setCorners(left, top, right, bottom); + + // Use infinite AAB to always stay visible + AxisAlignedBox aabInf; + aabInf.setInfinite(); + rect->setBoundingBox(aabInf); + + // Attach background to the scene + SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); + node->attachObject(rect); + + rectangles.push_back(rect); + ++i; + } + + TexturePtr destTexture = TextureManager::getSingleton().createManual( + textureName, + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + width, height, + 0, + PF_FLOAT16_RGBA, + TU_RENDERTARGET); + + RenderTarget* rtt = destTexture->getBuffer()->getRenderTarget(); + rtt->setAutoUpdated(false); + Viewport* vp = rtt->addViewport(camera); + vp->setOverlaysEnabled(false); + vp->setShadowsEnabled(false); + vp->setBackgroundColour(ColourValue(0,0,0,0)); + + rtt->update(); + + // remove all the junk we've created + for (std::vector::iterator it=rectangles.begin(); + it!=rectangles.end(); ++it) + { + delete (*it); + } + while (i > 0) + { + MaterialManager::getSingleton().remove("AtlasMaterial" + StringConverter::toString(i-1)); + --i; + } + root->destroySceneManager(sceneMgr); +} diff --git a/libs/openengine/ogre/atlas.hpp b/libs/openengine/ogre/atlas.hpp new file mode 100644 index 000000000..5dcd409ca --- /dev/null +++ b/libs/openengine/ogre/atlas.hpp @@ -0,0 +1,27 @@ +#ifndef OENGINE_OGRE_ATLAS_HPP +#define OENGINE_OGRE_ATLAS_HPP + +#include + +namespace OEngine +{ +namespace Render +{ + + /// \brief Creates a texture atlas at runtime + class Atlas + { + public: + /** + * @param absolute path to file that specifies layout of the texture (positions of the textures it contains) + * @param name of the destination texture to save to (in memory) + * @param texture directory prefix + */ + static void createFromFile (const std::string& filename, const std::string& textureName, const std::string& texturePrefix="textures\\"); + }; + +} +} + +#endif +