diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 18f249e56..c928fa940 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -2,6 +2,8 @@ #define GAME_MWBASE_DIALOGUEMANAGER_H #include +#include +#include #include @@ -42,24 +44,29 @@ namespace MWBase virtual bool isInChoice() const = 0; - virtual void startDialogue (const MWWorld::Ptr& actor) = 0; + typedef std::pair Response; // title, text + virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response) = 0; virtual void addTopic (const std::string& topic) = 0; - virtual void askQuestion (const std::string& question,int choice) = 0; + virtual void addChoice (const std::string& text,int choice) = 0; + virtual const std::vector >& getChoices() = 0; + + virtual bool isGoodbye() = 0; virtual void goodbye() = 0; virtual void say(const MWWorld::Ptr &actor, const std::string &topic) = 0; - //calbacks for the GUI - virtual void keywordSelected (const std::string& keyword) = 0; + virtual Response keywordSelected (const std::string& keyword) = 0; virtual void goodbyeSelected() = 0; - virtual void questionAnswered (int answer) = 0; + virtual Response questionAnswered (int answer) = 0; + + virtual std::list getAvailableTopics() = 0; - virtual bool checkServiceRefused () = 0; + virtual bool checkServiceRefused (Response& response) = 0; - virtual void persuade (int type) = 0; + virtual Response persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; /// @note This change is temporary and gets discarded when dialogue ends. diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 51a17d380..d8bce67e4 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -140,7 +140,6 @@ namespace MWBase virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0; /// \todo investigate, if we really need to expose every single lousy UI element to the outside world - virtual MWGui::DialogueWindow* getDialogueWindow() = 0; virtual MWGui::InventoryWindow* getInventoryWindow() = 0; virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index ffa26e211..4ed3b82a9 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -33,8 +34,6 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwgui/dialogue.hpp" - #include "../mwscript/compilercontext.hpp" #include "../mwscript/interpretercontext.hpp" #include "../mwscript/extensions.hpp" @@ -59,6 +58,7 @@ namespace MWDialogue { mChoice = -1; mIsInChoice = false; + mGoodbye = false; mCompilerContext.setExtensions (&extensions); } @@ -99,17 +99,15 @@ namespace MWDialogue if (mActorKnownTopics.count( topicId )) mKnownTopics.insert( topicId ); } - - updateTopics(); } - void DialogueManager::startDialogue (const MWWorld::Ptr& actor) + bool DialogueManager::startDialogue (const MWWorld::Ptr& actor, Response& response) { updateGlobals(); // Dialogue with dead actor (e.g. through script) should not be allowed. if (actor.getClass().getCreatureStats(actor).isDead()) - return; + return false; mLastTopic = ""; mPermanentDispositionChange = 0; @@ -117,6 +115,8 @@ namespace MWDialogue mChoice = -1; mIsInChoice = false; + mGoodbye = false; + mChoices.clear(); mActor = actor; @@ -125,8 +125,6 @@ namespace MWDialogue mActorKnownTopics.clear(); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - //greeting const MWWorld::Store &dialogs = MWBase::Environment::get().getWorld()->getStore().get(); @@ -140,10 +138,6 @@ namespace MWDialogue // Search a response (we do not accept a fallback to "Info refusal" here) if (const ESM::DialInfo *info = filter.search (*it, false)) { - //initialise the GUI - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); - win->startDialogue(actor, actor.getClass().getName (actor)); - creatureStats.talkedToPlayer(); if (!info->mSound.empty()) @@ -152,29 +146,23 @@ namespace MWDialogue } // first topics update so that parseText knows the keywords to highlight - updateTopics(); + updateActorKnownTopics(); parseText (info->mResponse); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), "", false); + response = Response ("", Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript, mActor); mLastTopic = it->mId; // update topics again to accommodate changes resulting from executeScript - updateTopics(); + updateActorKnownTopics(); - return; + return true; } } } - - // No greetings found. The dialogue window should not be shown. - // If this is a companion, we must show the companion window directly (used by BM_bear_be_unique). - bool isCompanion = !mActor.getClass().getScript(mActor).empty() - && mActor.getRefData().getLocals().getIntVar(mActor.getClass().getScript(mActor), "companion"); - if (isCompanion) - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion, mActor); + return false; } bool DialogueManager::compile (const std::string& cmd, std::vector& code, const MWWorld::Ptr& actor) @@ -252,8 +240,9 @@ namespace MWDialogue } } - void DialogueManager::executeTopic (const std::string& topic) + DialogueManager::Response DialogueManager::executeTopic (const std::string& topic) { + DialogueManager::Response response; Filter filter (mActor, mChoice, mTalkedTo); const MWWorld::Store &dialogues = @@ -261,8 +250,6 @@ namespace MWDialogue const ESM::Dialogue& dialogue = *dialogues.find (topic); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - const ESM::DialInfo* info = filter.search(dialogue, true); if (info) { @@ -287,7 +274,7 @@ namespace MWDialogue title = topic; MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); + response = Response(title, Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); if (dialogue.mType == ESM::Dialogue::Topic) { @@ -308,11 +295,7 @@ namespace MWDialogue mLastTopic = topic; } - else - { - // no response found, print a fallback text - win->addResponse ("…", topic); - } + return response; } const ESM::Dialogue *DialogueManager::searchDialogue(const std::string& id) @@ -325,11 +308,10 @@ namespace MWDialogue MWBase::Environment::get().getWorld()->updateDialogueGlobals(); } - void DialogueManager::updateTopics() + void DialogueManager::updateActorKnownTopics() { updateGlobals(); - std::list keywordList; mActorKnownTopics.clear(); const MWWorld::Store &dialogs = @@ -345,35 +327,42 @@ namespace MWDialogue { std::string lower = Misc::StringUtils::lowerCase(iter->mId); mActorKnownTopics.insert (lower); - - //does the player know the topic? - if (mKnownTopics.count(lower)) - { - keywordList.push_back (iter->mId); - } } } } - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + } + + std::list DialogueManager::getAvailableTopics() + { + updateActorKnownTopics(); + + std::list keywordList; + + for (const std::string& topic : mActorKnownTopics) + { + //does the player know the topic? + if (mKnownTopics.count(Misc::StringUtils::lowerCase(topic))) + keywordList.push_back(topic); + } // sort again, because the previous sort was case-sensitive keywordList.sort(Misc::StringUtils::ciLess); - win->setKeywords(keywordList); + return keywordList; } - void DialogueManager::keywordSelected (const std::string& keyword) + DialogueManager::Response DialogueManager::keywordSelected (const std::string& keyword) { + Response response; if(!mIsInChoice) { const ESM::Dialogue* dialogue = searchDialogue(keyword); if (dialogue && dialogue->mType == ESM::Dialogue::Topic) { - executeTopic (keyword); + response = executeTopic (keyword); } } - - updateTopics(); + return response; } bool DialogueManager::isInChoice() const @@ -383,8 +372,6 @@ namespace MWDialogue void DialogueManager::goodbyeSelected() { - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); - // Apply disposition change to NPC's base disposition if (mActor.getClass().isNpc()) { @@ -400,9 +387,10 @@ namespace MWDialogue mTemporaryDispositionChange = 0; } - void DialogueManager::questionAnswered (int answer) + DialogueManager::Response DialogueManager::questionAnswered (int answer) { mChoice = answer; + DialogueManager::Response response; const ESM::Dialogue* dialogue = searchDialogue(mLastTopic); if (dialogue) @@ -418,10 +406,10 @@ namespace MWDialogue mChoice = -1; mIsInChoice = false; - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); + mChoices.clear(); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext)); + response = Response("", Interpreter::fixDefinesDialog(text, interpreterContext)); // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, // in which case it should not be added to the journal. @@ -441,32 +429,39 @@ namespace MWDialogue { mChoice = -1; mIsInChoice = false; - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); + mChoices.clear(); } } } - updateTopics(); + updateActorKnownTopics(); + return response; } - void DialogueManager::askQuestion (const std::string& question, int choice) + void DialogueManager::addChoice (const std::string& text, int choice) { mIsInChoice = true; - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->addChoice(question, choice); + mChoices.push_back(std::make_pair(text, choice)); } - void DialogueManager::goodbye() + const std::vector >& DialogueManager::getChoices() { - mIsInChoice = true; + return mChoices; + } - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + bool DialogueManager::isGoodbye() + { + return mGoodbye; + } - win->goodbye(); + void DialogueManager::goodbye() + { + mIsInChoice = false; + mGoodbye = true; } - void DialogueManager::persuade(int type) + DialogueManager::Response DialogueManager::persuade(int type) { bool success; float temp, perm; @@ -515,7 +510,7 @@ namespace MWDialogue text = "Bribe"; } - executeTopic (text + (success ? " Success" : " Fail")); + return executeTopic (text + (success ? " Success" : " Fail")); } int DialogueManager::getTemporaryDispositionChange() const @@ -528,7 +523,7 @@ namespace MWDialogue mTemporaryDispositionChange += delta; } - bool DialogueManager::checkServiceRefused() + bool DialogueManager::checkServiceRefused(Response& response) { Filter filter (mActor, mChoice, mTalkedTo); @@ -536,7 +531,6 @@ namespace MWDialogue MWBase::Environment::get().getWorld()->getStore().get(); const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal"); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); std::vector infos = filter.list (dialogue, false, false, true); if (!infos.empty()) @@ -550,8 +544,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), - gmsts.find ("sServiceRefusal")->getString()); + response = Response(gmsts.find ("sServiceRefusal")->getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript, mActor); return true; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 81bd2f2b9..d9c622120 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -41,19 +41,22 @@ namespace MWDialogue int mChoice; std::string mLastTopic; // last topic ID, lowercase bool mIsInChoice; + bool mGoodbye; + + std::vector > mChoices; float mTemporaryDispositionChange; float mPermanentDispositionChange; void parseText (const std::string& text); - void updateTopics(); + void updateActorKnownTopics(); void updateGlobals(); bool compile (const std::string& cmd, std::vector& code, const MWWorld::Ptr& actor); void executeScript (const std::string& script, const MWWorld::Ptr& actor); - void executeTopic (const std::string& topic); + Response executeTopic (const std::string& topic); const ESM::Dialogue* searchDialogue(const std::string& id); @@ -65,24 +68,29 @@ namespace MWDialogue virtual bool isInChoice() const; - virtual void startDialogue (const MWWorld::Ptr& actor); + virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response); + + std::list getAvailableTopics(); virtual void addTopic (const std::string& topic); - virtual void askQuestion (const std::string& question,int choice); + virtual void addChoice (const std::string& text,int choice); + const std::vector >& getChoices(); + + virtual bool isGoodbye(); virtual void goodbye(); - virtual bool checkServiceRefused (); + virtual bool checkServiceRefused (Response& response); virtual void say(const MWWorld::Ptr &actor, const std::string &topic); //calbacks for the GUI - virtual void keywordSelected (const std::string& keyword); + virtual Response keywordSelected (const std::string& keyword); virtual void goodbyeSelected(); - virtual void questionAnswered (int answer); + virtual Response questionAnswered (int answer); - virtual void persuade (int type); + virtual Response persuade (int type); virtual int getTemporaryDispositionChange () const; /// @note This change is temporary and gets discarded when dialogue ends. diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 869b83928..a7f18561e 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -68,7 +68,9 @@ namespace MWGui else /*if (sender == mBribe1000Button)*/ type = MWBase::MechanicsManager::PT_Bribe1000; - MWBase::Environment::get().getDialogueManager()->persuade(type); + MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->persuade(type); + + eventPersuadeMsg(response.first, response.second); setVisible(false); } @@ -214,30 +216,26 @@ namespace MWGui void Choice::activated() { - MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); - MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId); + eventChoiceActivated(mChoiceId); } void Topic::activated() { - MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); - MWBase::Environment::get().getDialogueManager()->keywordSelected(mTopicId); + eventTopicActivated(mTopicId); } void Goodbye::activated() { - MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + eventActivated(); } // -------------------------------------------------------------------------------------------------- DialogueWindow::DialogueWindow() : WindowBase("openmw_dialogue_window.layout") - , mEnabled(false) , mGoodbye(false) , mPersuasionDialog() { @@ -245,13 +243,14 @@ namespace MWGui center(); mPersuasionDialog.setVisible(false); + mPersuasionDialog.eventPersuadeMsg += MyGUI::newDelegate(this, &DialogueWindow::onPersuadeResult); //History view getWidget(mHistory, "History"); //Topics list getWidget(mTopicsList, "TopicsList"); - mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); + mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectListItem); getWidget(mGoodbyeButton, "ByeButton"); mGoodbyeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); @@ -269,15 +268,27 @@ namespace MWGui mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); } + DialogueWindow::~DialogueWindow() + { + mPersuasionDialog.eventPersuadeMsg.clear(); + + deleteLater(); + for (Link* link : mLinks) + delete link; + for (auto link : mTopicLinks) + delete link.second; + for (auto history : mHistoryContents) + delete history; + } + void DialogueWindow::onTradeComplete() { - addResponse(MyGUI::LanguageManager::getInstance().replaceTags("#{sBarterDialog5}")); + addResponse("", MyGUI::LanguageManager::getInstance().replaceTags("#{sBarterDialog5}")); } bool DialogueWindow::exit() { - if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) - && !mGoodbye) + if ((MWBase::Environment::get().getDialogueManager()->isInChoice())) { return false; } @@ -315,9 +326,9 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } - void DialogueWindow::onSelectTopic(const std::string& topic, int id) + void DialogueWindow::onSelectListItem(const std::string& topic, int id) { - if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) + if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice()) return; int separatorPos = 0; @@ -328,17 +339,18 @@ namespace MWGui } if (id >= separatorPos) - MWBase::Environment::get().getDialogueManager()->keywordSelected(topic); + onTopicActivated(topic); else { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWBase::DialogueManager::Response response; if (topic == gmst.find("sPersuasion")->getString()) mPersuasionDialog.setVisible(true); else if (topic == gmst.find("sCompanionShare")->getString()) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr); - else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused()) + else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(response)) { if (topic == gmst.find("sBarter")->getString()) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr); @@ -355,21 +367,30 @@ namespace MWGui else if (topic == gmst.find("sRepair")->getString()) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr); } + else + addResponse(response.first, response.second); } } - void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) + void DialogueWindow::setPtr(const MWWorld::Ptr& actor) { + MWBase::DialogueManager::Response response; + if (!MWBase::Environment::get().getDialogueManager()->startDialogue(actor, response)) + { + // No greetings found. The dialogue window should not be shown. + // If this is a companion, we must show the companion window directly (used by BM_bear_be_unique). + if (isCompanion()) + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion, mPtr); + return; + } + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); mGoodbye = false; - mEnabled = true; bool sameActor = (mPtr == actor); mPtr = actor; mTopicsList->setEnabled(true); - setTitle(npcName); - - clearChoices(); + setTitle(mPtr.getClass().getName(mPtr)); mTopicsList->clear(); @@ -381,12 +402,13 @@ namespace MWGui } for (std::vector::iterator it = mLinks.begin(); it != mLinks.end(); ++it) - delete (*it); + mDeleteLater.push_back(*it); // Links are not deleted right away to prevent issues with event handlers mLinks.clear(); updateDisposition(); - restock(); + + addResponse(response.first, response.second, false); } void DialogueWindow::restock() @@ -403,11 +425,18 @@ namespace MWGui } } + void DialogueWindow::deleteLater() + { + for (Link* link : mDeleteLater) + delete link; + mDeleteLater.clear(); + } + void DialogueWindow::setKeywords(std::list keyWords) { mTopicsList->clear(); for (std::map::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it) - delete it->second; + mDeleteLater.push_back(it->second); mTopicLinks.clear(); mKeywordSearch.clear(); @@ -416,9 +445,6 @@ namespace MWGui bool travel = (mPtr.getTypeName() == typeid(ESM::NPC).name() && !mPtr.get()->mBase->getTransport().empty()) || (mPtr.getTypeName() == typeid(ESM::Creature).name() && !mPtr.get()->mBase->getTransport().empty()); - bool isCompanion = !mPtr.getClass().getScript(mPtr).empty() - && mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion"); - const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); @@ -446,7 +472,7 @@ namespace MWGui if (services & ESM::NPC::Repair) mTopicsList->addItem(gmst.find("sRepair")->getString()); - if (isCompanion) + if (isCompanion()) mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); if (mTopicsList->getItemCount() > 0) @@ -458,6 +484,7 @@ namespace MWGui mTopicsList->addItem(*it); Topic* t = new Topic(*it); + t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated); mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t; mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); @@ -491,9 +518,11 @@ namespace MWGui typesetter->sectionBreak(9); // choices const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); - for (std::vector >::iterator it = mChoices.begin(); it != mChoices.end(); ++it) + mChoices = MWBase::Environment::get().getDialogueManager()->getChoices(); + for (std::vector >::const_iterator it = mChoices.begin(); it != mChoices.end(); ++it) { Choice* link = new Choice(it->second); + link->eventChoiceActivated += MyGUI::newDelegate(this, &DialogueWindow::onChoiceActivated); mLinks.push_back(link); typesetter->lineBreak(); @@ -503,9 +532,11 @@ namespace MWGui typesetter->write(questionStyle, to_utf8_span(it->first.c_str())); } + mGoodbye = MWBase::Environment::get().getDialogueManager()->isGoodbye(); if (mGoodbye) { Goodbye* link = new Goodbye(); + link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated); mLinks.push_back(link); std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString(); BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver, @@ -550,32 +581,40 @@ namespace MWGui reinterpret_cast(link)->activated(); } - void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) + void DialogueWindow::onTopicActivated(const std::string &topicId) { - mHistory->setPosition(0, static_cast(pos) * -1); + MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId); + addResponse(response.first, response.second); } - void DialogueWindow::addResponse(const std::string &text, const std::string &title, bool needMargin) + void DialogueWindow::onChoiceActivated(int id) { - mHistoryContents.push_back(new Response(text, title, needMargin)); - updateHistory(); + MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->questionAnswered(id); + addResponse(response.first, response.second); } - void DialogueWindow::addMessageBox(const std::string& text) + void DialogueWindow::onGoodbyeActivated() { - mHistoryContents.push_back(new Message(text)); - updateHistory(); + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); + resetReference(); + } + + void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) + { + mHistory->setPosition(0, static_cast(pos) * -1); } - void DialogueWindow::addChoice(const std::string& choice, int id) + void DialogueWindow::addResponse(const std::string &title, const std::string &text, bool needMargin) { - mChoices.push_back(std::make_pair(choice, id)); + mHistoryContents.push_back(new Response(text, title, needMargin)); updateHistory(); + updateTopics(); } - void DialogueWindow::clearChoices() + void DialogueWindow::addMessageBox(const std::string& text) { - mChoices.clear(); + mHistoryContents.push_back(new Message(text)); updateHistory(); } @@ -608,13 +647,6 @@ namespace MWGui } } - void DialogueWindow::goodbye() - { - mGoodbye = true; - mEnabled = false; - updateHistory(); - } - void DialogueWindow::onReferenceUnavailable() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); @@ -623,6 +655,30 @@ namespace MWGui void DialogueWindow::onFrame(float dt) { checkReferenceAvailable(); + if (mPtr.isEmpty()) + return; + updateDisposition(); + deleteLater(); + + if (mChoices != MWBase::Environment::get().getDialogueManager()->getChoices() + || mGoodbye != MWBase::Environment::get().getDialogueManager()->isGoodbye()) + updateHistory(); + } + + void DialogueWindow::updateTopics() + { + setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()); + } + + bool DialogueWindow::isCompanion() + { + return !mPtr.getClass().getScript(mPtr).empty() + && mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion"); + } + + void DialogueWindow::onPersuadeResult(const std::string &title, const std::string &text) + { + addResponse(title, text); } } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 83f1eb1de..c83906588 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -8,6 +8,8 @@ #include "../mwdialogue/keywordsearch.hpp" +#include + namespace Gui { class MWList; @@ -20,14 +22,14 @@ namespace MWGui namespace MWGui { - class DialogueHistoryViewModel; - class BookPage; - class PersuasionDialog : public WindowModal { public: PersuasionDialog(); + typedef MyGUI::delegates::CMultiDelegate2 EventHandle_Result; + EventHandle_Result eventPersuadeMsg; + virtual void onOpen(); private: @@ -53,6 +55,8 @@ namespace MWGui struct Topic : Link { + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_TopicId; + EventHandle_TopicId eventTopicActivated; Topic(const std::string& id) : mTopicId(id) {} std::string mTopicId; virtual void activated (); @@ -60,6 +64,8 @@ namespace MWGui struct Choice : Link { + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ChoiceId; + EventHandle_ChoiceId eventChoiceActivated; Choice(int id) : mChoiceId(id) {} int mChoiceId; virtual void activated (); @@ -67,6 +73,8 @@ namespace MWGui struct Goodbye : Link { + typedef MyGUI::delegates::CMultiDelegate0 Event_Activated; + Event_Activated eventActivated; virtual void activated (); }; @@ -98,6 +106,7 @@ namespace MWGui { public: DialogueWindow(); + ~DialogueWindow(); void onTradeComplete(); @@ -108,25 +117,29 @@ namespace MWGui void notifyLinkClicked (TypesetBook::InteractiveId link); - void startDialogue(MWWorld::Ptr actor, std::string npcName); + void setPtr(const MWWorld::Ptr& actor); + void setKeywords(std::list keyWord); - void addResponse (const std::string& text, const std::string& title="", bool needMargin = true); + void addResponse (const std::string& title, const std::string& text, bool needMargin = true); void addMessageBox(const std::string& text); - void addChoice(const std::string& choice, int id); - void clearChoices(); - - void goodbye(); void onFrame(float dt); void clear() { resetReference(); } protected: - void onSelectTopic(const std::string& topic, int id); + void updateTopics(); + bool isCompanion(); + + void onPersuadeResult(const std::string& title, const std::string& text); + void onSelectListItem(const std::string& topic, int id); void onByeClicked(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onWindowResize(MyGUI::Window* _sender); + void onTopicActivated(const std::string& topicId); + void onChoiceActivated(int id); + void onGoodbyeActivated(); void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos); @@ -137,17 +150,19 @@ namespace MWGui private: void updateDisposition(); void restock(); + void deleteLater(); bool mEnabled; - bool mGoodbye; - std::vector mHistoryContents; std::vector > mChoices; + bool mGoodbye; std::vector mLinks; std::map mTopicLinks; + std::vector mDeleteLater; + KeywordSearchT mKeywordSearch; BookPage* mHistory; diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 74d80d292..6b0b33a0a 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -353,6 +353,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); return; } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 341167ab7..939f70b59 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -323,6 +323,7 @@ namespace MWGui onCancelButtonClicked(mCancelButton); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); return; } } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 120ab5cba..1081ee81e 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -197,6 +197,7 @@ namespace MWGui // go back to game mode MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void TrainingWindow::onFrame(float dt) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 02df6bd8e..c9f19ad92 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -178,6 +178,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); // Teleports any followers, too. MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 85f57a75e..f5825f08d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1288,7 +1288,6 @@ namespace MWGui mConsole->executeFile (path); } - MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 53b2dc33a..f6b2707ad 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -179,7 +179,6 @@ namespace MWGui virtual bool isAllowed(GuiWindow wnd) const; /// \todo investigate, if we really need to expose every single lousy UI element to the outside world - virtual MWGui::DialogueWindow* getDialogueWindow(); virtual MWGui::InventoryWindow* getInventoryWindow(); virtual MWGui::CountDialog* getCountDialog(); virtual MWGui::ConfirmationDialog* getConfirmationDialog(); diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index fcb7e8f3b..21d8d469b 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -11,6 +11,7 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" @@ -116,7 +117,7 @@ namespace MWScript runtime.pop(); arg0 = arg0 -1; } - dialogue->askQuestion(question,choice); + dialogue->addChoice(question,choice); } } }; @@ -133,7 +134,7 @@ namespace MWScript if (!ptr.getRefData().isEnabled()) return; - MWBase::Environment::get().getDialogueManager()->startDialogue (ptr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, ptr); } }; diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index 051380ff5..c7bb6a26e 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -1,7 +1,7 @@ #include "actiontalk.hpp" #include "../mwbase/environment.hpp" -#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWWorld { @@ -9,6 +9,6 @@ namespace MWWorld void ActionTalk::executeImp (const Ptr& actor) { - MWBase::Environment::get().getDialogueManager()->startDialogue (getTarget()); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, getTarget()); } }