Refactor dialogue GUI to talk to the dialogue manager, not the other way around and not both ways.

- Fix memory leaks in DialogueWindow
- Fix Link objects being deleted from their own event handler
pull/303/head
scrawl 7 years ago
parent 476bec41c5
commit 2ce79e07a4

@ -2,6 +2,8 @@
#define GAME_MWBASE_DIALOGUEMANAGER_H #define GAME_MWBASE_DIALOGUEMANAGER_H
#include <string> #include <string>
#include <vector>
#include <list>
#include <stdint.h> #include <stdint.h>
@ -42,24 +44,29 @@ namespace MWBase
virtual bool isInChoice() const = 0; virtual bool isInChoice() const = 0;
virtual void startDialogue (const MWWorld::Ptr& actor) = 0; typedef std::pair<std::string, std::string> Response; // title, text
virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response) = 0;
virtual void addTopic (const std::string& topic) = 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<std::pair<std::string, int> >& getChoices() = 0;
virtual bool isGoodbye() = 0;
virtual void goodbye() = 0; virtual void goodbye() = 0;
virtual void say(const MWWorld::Ptr &actor, const std::string &topic) = 0; virtual void say(const MWWorld::Ptr &actor, const std::string &topic) = 0;
//calbacks for the GUI virtual Response keywordSelected (const std::string& keyword) = 0;
virtual void keywordSelected (const std::string& keyword) = 0;
virtual void goodbyeSelected() = 0; virtual void goodbyeSelected() = 0;
virtual void questionAnswered (int answer) = 0; virtual Response questionAnswered (int answer) = 0;
virtual std::list<std::string> 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; virtual int getTemporaryDispositionChange () const = 0;
/// @note This change is temporary and gets discarded when dialogue ends. /// @note This change is temporary and gets discarded when dialogue ends.

@ -140,7 +140,6 @@ namespace MWBase
virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0; 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 /// \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::InventoryWindow* getInventoryWindow() = 0;
virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::CountDialog* getCountDialog() = 0;
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;

@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <list> #include <list>
#include <iostream>
#include <components/esm/loaddial.hpp> #include <components/esm/loaddial.hpp>
#include <components/esm/loadinfo.hpp> #include <components/esm/loadinfo.hpp>
@ -33,8 +34,6 @@
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwgui/dialogue.hpp"
#include "../mwscript/compilercontext.hpp" #include "../mwscript/compilercontext.hpp"
#include "../mwscript/interpretercontext.hpp" #include "../mwscript/interpretercontext.hpp"
#include "../mwscript/extensions.hpp" #include "../mwscript/extensions.hpp"
@ -59,6 +58,7 @@ namespace MWDialogue
{ {
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
mGoodbye = false;
mCompilerContext.setExtensions (&extensions); mCompilerContext.setExtensions (&extensions);
} }
@ -99,17 +99,15 @@ namespace MWDialogue
if (mActorKnownTopics.count( topicId )) if (mActorKnownTopics.count( topicId ))
mKnownTopics.insert( topicId ); mKnownTopics.insert( topicId );
} }
updateTopics();
} }
void DialogueManager::startDialogue (const MWWorld::Ptr& actor) bool DialogueManager::startDialogue (const MWWorld::Ptr& actor, Response& response)
{ {
updateGlobals(); updateGlobals();
// Dialogue with dead actor (e.g. through script) should not be allowed. // Dialogue with dead actor (e.g. through script) should not be allowed.
if (actor.getClass().getCreatureStats(actor).isDead()) if (actor.getClass().getCreatureStats(actor).isDead())
return; return false;
mLastTopic = ""; mLastTopic = "";
mPermanentDispositionChange = 0; mPermanentDispositionChange = 0;
@ -117,6 +115,8 @@ namespace MWDialogue
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
mGoodbye = false;
mChoices.clear();
mActor = actor; mActor = actor;
@ -125,8 +125,6 @@ namespace MWDialogue
mActorKnownTopics.clear(); mActorKnownTopics.clear();
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
//greeting //greeting
const MWWorld::Store<ESM::Dialogue> &dialogs = const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
@ -140,10 +138,6 @@ namespace MWDialogue
// Search a response (we do not accept a fallback to "Info refusal" here) // Search a response (we do not accept a fallback to "Info refusal" here)
if (const ESM::DialInfo *info = filter.search (*it, false)) 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(); creatureStats.talkedToPlayer();
if (!info->mSound.empty()) if (!info->mSound.empty())
@ -152,29 +146,23 @@ namespace MWDialogue
} }
// first topics update so that parseText knows the keywords to highlight // first topics update so that parseText knows the keywords to highlight
updateTopics(); updateActorKnownTopics();
parseText (info->mResponse); parseText (info->mResponse);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); 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); executeScript (info->mResultScript, mActor);
mLastTopic = it->mId; mLastTopic = it->mId;
// update topics again to accommodate changes resulting from executeScript // update topics again to accommodate changes resulting from executeScript
updateTopics(); updateActorKnownTopics();
return; return true;
} }
} }
} }
return false;
// 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);
} }
bool DialogueManager::compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor) bool DialogueManager::compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& 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); Filter filter (mActor, mChoice, mTalkedTo);
const MWWorld::Store<ESM::Dialogue> &dialogues = const MWWorld::Store<ESM::Dialogue> &dialogues =
@ -261,8 +250,6 @@ namespace MWDialogue
const ESM::Dialogue& dialogue = *dialogues.find (topic); const ESM::Dialogue& dialogue = *dialogues.find (topic);
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
const ESM::DialInfo* info = filter.search(dialogue, true); const ESM::DialInfo* info = filter.search(dialogue, true);
if (info) if (info)
{ {
@ -287,7 +274,7 @@ namespace MWDialogue
title = topic; title = topic;
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); 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) if (dialogue.mType == ESM::Dialogue::Topic)
{ {
@ -308,11 +295,7 @@ namespace MWDialogue
mLastTopic = topic; mLastTopic = topic;
} }
else return response;
{
// no response found, print a fallback text
win->addResponse ("", topic);
}
} }
const ESM::Dialogue *DialogueManager::searchDialogue(const std::string& id) const ESM::Dialogue *DialogueManager::searchDialogue(const std::string& id)
@ -325,11 +308,10 @@ namespace MWDialogue
MWBase::Environment::get().getWorld()->updateDialogueGlobals(); MWBase::Environment::get().getWorld()->updateDialogueGlobals();
} }
void DialogueManager::updateTopics() void DialogueManager::updateActorKnownTopics()
{ {
updateGlobals(); updateGlobals();
std::list<std::string> keywordList;
mActorKnownTopics.clear(); mActorKnownTopics.clear();
const MWWorld::Store<ESM::Dialogue> &dialogs = const MWWorld::Store<ESM::Dialogue> &dialogs =
@ -345,35 +327,42 @@ namespace MWDialogue
{ {
std::string lower = Misc::StringUtils::lowerCase(iter->mId); std::string lower = Misc::StringUtils::lowerCase(iter->mId);
mActorKnownTopics.insert (lower); 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<std::string> DialogueManager::getAvailableTopics()
{
updateActorKnownTopics();
std::list<std::string> 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 // sort again, because the previous sort was case-sensitive
keywordList.sort(Misc::StringUtils::ciLess); 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) if(!mIsInChoice)
{ {
const ESM::Dialogue* dialogue = searchDialogue(keyword); const ESM::Dialogue* dialogue = searchDialogue(keyword);
if (dialogue && dialogue->mType == ESM::Dialogue::Topic) if (dialogue && dialogue->mType == ESM::Dialogue::Topic)
{ {
executeTopic (keyword); response = executeTopic (keyword);
} }
} }
return response;
updateTopics();
} }
bool DialogueManager::isInChoice() const bool DialogueManager::isInChoice() const
@ -383,8 +372,6 @@ namespace MWDialogue
void DialogueManager::goodbyeSelected() void DialogueManager::goodbyeSelected()
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
// Apply disposition change to NPC's base disposition // Apply disposition change to NPC's base disposition
if (mActor.getClass().isNpc()) if (mActor.getClass().isNpc())
{ {
@ -400,9 +387,10 @@ namespace MWDialogue
mTemporaryDispositionChange = 0; mTemporaryDispositionChange = 0;
} }
void DialogueManager::questionAnswered (int answer) DialogueManager::Response DialogueManager::questionAnswered (int answer)
{ {
mChoice = answer; mChoice = answer;
DialogueManager::Response response;
const ESM::Dialogue* dialogue = searchDialogue(mLastTopic); const ESM::Dialogue* dialogue = searchDialogue(mLastTopic);
if (dialogue) if (dialogue)
@ -418,10 +406,10 @@ namespace MWDialogue
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); mChoices.clear();
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); 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, // 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. // in which case it should not be added to the journal.
@ -441,32 +429,39 @@ namespace MWDialogue
{ {
mChoice = -1; mChoice = -1;
mIsInChoice = false; 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; mIsInChoice = true;
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); mChoices.push_back(std::make_pair(text, choice));
win->addChoice(question, choice);
} }
void DialogueManager::goodbye() const std::vector<std::pair<std::string, int> >& 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; bool success;
float temp, perm; float temp, perm;
@ -515,7 +510,7 @@ namespace MWDialogue
text = "Bribe"; text = "Bribe";
} }
executeTopic (text + (success ? " Success" : " Fail")); return executeTopic (text + (success ? " Success" : " Fail"));
} }
int DialogueManager::getTemporaryDispositionChange() const int DialogueManager::getTemporaryDispositionChange() const
@ -528,7 +523,7 @@ namespace MWDialogue
mTemporaryDispositionChange += delta; mTemporaryDispositionChange += delta;
} }
bool DialogueManager::checkServiceRefused() bool DialogueManager::checkServiceRefused(Response& response)
{ {
Filter filter (mActor, mChoice, mTalkedTo); Filter filter (mActor, mChoice, mTalkedTo);
@ -536,7 +531,6 @@ namespace MWDialogue
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal"); const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal");
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, false, false, true); std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, false, false, true);
if (!infos.empty()) if (!infos.empty())
@ -550,8 +544,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), response = Response(gmsts.find ("sServiceRefusal")->getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
gmsts.find ("sServiceRefusal")->getString());
executeScript (info->mResultScript, mActor); executeScript (info->mResultScript, mActor);
return true; return true;

@ -41,19 +41,22 @@ namespace MWDialogue
int mChoice; int mChoice;
std::string mLastTopic; // last topic ID, lowercase std::string mLastTopic; // last topic ID, lowercase
bool mIsInChoice; bool mIsInChoice;
bool mGoodbye;
std::vector<std::pair<std::string, int> > mChoices;
float mTemporaryDispositionChange; float mTemporaryDispositionChange;
float mPermanentDispositionChange; float mPermanentDispositionChange;
void parseText (const std::string& text); void parseText (const std::string& text);
void updateTopics(); void updateActorKnownTopics();
void updateGlobals(); void updateGlobals();
bool compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor); bool compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor);
void executeScript (const std::string& script, 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); const ESM::Dialogue* searchDialogue(const std::string& id);
@ -65,24 +68,29 @@ namespace MWDialogue
virtual bool isInChoice() const; virtual bool isInChoice() const;
virtual void startDialogue (const MWWorld::Ptr& actor); virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response);
std::list<std::string> getAvailableTopics();
virtual void addTopic (const std::string& topic); 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<std::pair<std::string, int> >& getChoices();
virtual bool isGoodbye();
virtual void goodbye(); virtual void goodbye();
virtual bool checkServiceRefused (); virtual bool checkServiceRefused (Response& response);
virtual void say(const MWWorld::Ptr &actor, const std::string &topic); virtual void say(const MWWorld::Ptr &actor, const std::string &topic);
//calbacks for the GUI //calbacks for the GUI
virtual void keywordSelected (const std::string& keyword); virtual Response keywordSelected (const std::string& keyword);
virtual void goodbyeSelected(); 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; virtual int getTemporaryDispositionChange () const;
/// @note This change is temporary and gets discarded when dialogue ends. /// @note This change is temporary and gets discarded when dialogue ends.

@ -68,7 +68,9 @@ namespace MWGui
else /*if (sender == mBribe1000Button)*/ else /*if (sender == mBribe1000Button)*/
type = MWBase::MechanicsManager::PT_Bribe1000; 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); setVisible(false);
} }
@ -214,30 +216,26 @@ namespace MWGui
void Choice::activated() void Choice::activated()
{ {
MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId); eventChoiceActivated(mChoiceId);
} }
void Topic::activated() void Topic::activated()
{ {
MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->keywordSelected(mTopicId); eventTopicActivated(mTopicId);
} }
void Goodbye::activated() void Goodbye::activated()
{ {
MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); eventActivated();
} }
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------
DialogueWindow::DialogueWindow() DialogueWindow::DialogueWindow()
: WindowBase("openmw_dialogue_window.layout") : WindowBase("openmw_dialogue_window.layout")
, mEnabled(false)
, mGoodbye(false) , mGoodbye(false)
, mPersuasionDialog() , mPersuasionDialog()
{ {
@ -245,13 +243,14 @@ namespace MWGui
center(); center();
mPersuasionDialog.setVisible(false); mPersuasionDialog.setVisible(false);
mPersuasionDialog.eventPersuadeMsg += MyGUI::newDelegate(this, &DialogueWindow::onPersuadeResult);
//History view //History view
getWidget(mHistory, "History"); getWidget(mHistory, "History");
//Topics list //Topics list
getWidget(mTopicsList, "TopicsList"); getWidget(mTopicsList, "TopicsList");
mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectListItem);
getWidget(mGoodbyeButton, "ByeButton"); getWidget(mGoodbyeButton, "ByeButton");
mGoodbyeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); mGoodbyeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
@ -269,15 +268,27 @@ namespace MWGui
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); mMainWidget->castType<MyGUI::Window>()->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() void DialogueWindow::onTradeComplete()
{ {
addResponse(MyGUI::LanguageManager::getInstance().replaceTags("#{sBarterDialog5}")); addResponse("", MyGUI::LanguageManager::getInstance().replaceTags("#{sBarterDialog5}"));
} }
bool DialogueWindow::exit() bool DialogueWindow::exit()
{ {
if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) if ((MWBase::Environment::get().getDialogueManager()->isInChoice()))
&& !mGoodbye)
{ {
return false; return false;
} }
@ -315,9 +326,9 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); 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; return;
int separatorPos = 0; int separatorPos = 0;
@ -328,17 +339,18 @@ namespace MWGui
} }
if (id >= separatorPos) if (id >= separatorPos)
MWBase::Environment::get().getDialogueManager()->keywordSelected(topic); onTopicActivated(topic);
else else
{ {
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWBase::DialogueManager::Response response;
if (topic == gmst.find("sPersuasion")->getString()) if (topic == gmst.find("sPersuasion")->getString())
mPersuasionDialog.setVisible(true); mPersuasionDialog.setVisible(true);
else if (topic == gmst.find("sCompanionShare")->getString()) else if (topic == gmst.find("sCompanionShare")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr); 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()) if (topic == gmst.find("sBarter")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);
@ -355,21 +367,30 @@ namespace MWGui
else if (topic == gmst.find("sRepair")->getString()) else if (topic == gmst.find("sRepair")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr); 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); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);
mGoodbye = false; mGoodbye = false;
mEnabled = true;
bool sameActor = (mPtr == actor); bool sameActor = (mPtr == actor);
mPtr = actor; mPtr = actor;
mTopicsList->setEnabled(true); mTopicsList->setEnabled(true);
setTitle(npcName); setTitle(mPtr.getClass().getName(mPtr));
clearChoices();
mTopicsList->clear(); mTopicsList->clear();
@ -381,12 +402,13 @@ namespace MWGui
} }
for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it) for (std::vector<Link*>::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(); mLinks.clear();
updateDisposition(); updateDisposition();
restock(); restock();
addResponse(response.first, response.second, false);
} }
void DialogueWindow::restock() 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<std::string> keyWords) void DialogueWindow::setKeywords(std::list<std::string> keyWords)
{ {
mTopicsList->clear(); mTopicsList->clear();
for (std::map<std::string, Link*>::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it) for (std::map<std::string, Link*>::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it)
delete it->second; mDeleteLater.push_back(it->second);
mTopicLinks.clear(); mTopicLinks.clear();
mKeywordSearch.clear(); mKeywordSearch.clear();
@ -416,9 +445,6 @@ namespace MWGui
bool travel = (mPtr.getTypeName() == typeid(ESM::NPC).name() && !mPtr.get<ESM::NPC>()->mBase->getTransport().empty()) bool travel = (mPtr.getTypeName() == typeid(ESM::NPC).name() && !mPtr.get<ESM::NPC>()->mBase->getTransport().empty())
|| (mPtr.getTypeName() == typeid(ESM::Creature).name() && !mPtr.get<ESM::Creature>()->mBase->getTransport().empty()); || (mPtr.getTypeName() == typeid(ESM::Creature).name() && !mPtr.get<ESM::Creature>()->mBase->getTransport().empty());
bool isCompanion = !mPtr.getClass().getScript(mPtr).empty()
&& mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion");
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
@ -446,7 +472,7 @@ namespace MWGui
if (services & ESM::NPC::Repair) if (services & ESM::NPC::Repair)
mTopicsList->addItem(gmst.find("sRepair")->getString()); mTopicsList->addItem(gmst.find("sRepair")->getString());
if (isCompanion) if (isCompanion())
mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); mTopicsList->addItem(gmst.find("sCompanionShare")->getString());
if (mTopicsList->getItemCount() > 0) if (mTopicsList->getItemCount() > 0)
@ -458,6 +484,7 @@ namespace MWGui
mTopicsList->addItem(*it); mTopicsList->addItem(*it);
Topic* t = new Topic(*it); Topic* t = new Topic(*it);
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t; mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t;
mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t));
@ -491,9 +518,11 @@ namespace MWGui
typesetter->sectionBreak(9); typesetter->sectionBreak(9);
// choices // choices
const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
for (std::vector<std::pair<std::string, int> >::iterator it = mChoices.begin(); it != mChoices.end(); ++it) mChoices = MWBase::Environment::get().getDialogueManager()->getChoices();
for (std::vector<std::pair<std::string, int> >::const_iterator it = mChoices.begin(); it != mChoices.end(); ++it)
{ {
Choice* link = new Choice(it->second); Choice* link = new Choice(it->second);
link->eventChoiceActivated += MyGUI::newDelegate(this, &DialogueWindow::onChoiceActivated);
mLinks.push_back(link); mLinks.push_back(link);
typesetter->lineBreak(); typesetter->lineBreak();
@ -503,9 +532,11 @@ namespace MWGui
typesetter->write(questionStyle, to_utf8_span(it->first.c_str())); typesetter->write(questionStyle, to_utf8_span(it->first.c_str()));
} }
mGoodbye = MWBase::Environment::get().getDialogueManager()->isGoodbye();
if (mGoodbye) if (mGoodbye)
{ {
Goodbye* link = new Goodbye(); Goodbye* link = new Goodbye();
link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated);
mLinks.push_back(link); mLinks.push_back(link);
std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString(); std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString();
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver, BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver,
@ -550,32 +581,40 @@ namespace MWGui
reinterpret_cast<Link*>(link)->activated(); reinterpret_cast<Link*>(link)->activated();
} }
void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) void DialogueWindow::onTopicActivated(const std::string &topicId)
{ {
mHistory->setPosition(0, static_cast<int>(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)); MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->questionAnswered(id);
updateHistory(); addResponse(response.first, response.second);
} }
void DialogueWindow::addMessageBox(const std::string& text) void DialogueWindow::onGoodbyeActivated()
{ {
mHistoryContents.push_back(new Message(text)); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
updateHistory(); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
resetReference();
}
void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos)
{
mHistory->setPosition(0, static_cast<int>(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(); updateHistory();
updateTopics();
} }
void DialogueWindow::clearChoices() void DialogueWindow::addMessageBox(const std::string& text)
{ {
mChoices.clear(); mHistoryContents.push_back(new Message(text));
updateHistory(); updateHistory();
} }
@ -608,13 +647,6 @@ namespace MWGui
} }
} }
void DialogueWindow::goodbye()
{
mGoodbye = true;
mEnabled = false;
updateHistory();
}
void DialogueWindow::onReferenceUnavailable() void DialogueWindow::onReferenceUnavailable()
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
@ -623,6 +655,30 @@ namespace MWGui
void DialogueWindow::onFrame(float dt) void DialogueWindow::onFrame(float dt)
{ {
checkReferenceAvailable(); checkReferenceAvailable();
if (mPtr.isEmpty())
return;
updateDisposition(); 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);
} }
} }

@ -8,6 +8,8 @@
#include "../mwdialogue/keywordsearch.hpp" #include "../mwdialogue/keywordsearch.hpp"
#include <MyGUI_Delegate.h>
namespace Gui namespace Gui
{ {
class MWList; class MWList;
@ -20,14 +22,14 @@ namespace MWGui
namespace MWGui namespace MWGui
{ {
class DialogueHistoryViewModel;
class BookPage;
class PersuasionDialog : public WindowModal class PersuasionDialog : public WindowModal
{ {
public: public:
PersuasionDialog(); PersuasionDialog();
typedef MyGUI::delegates::CMultiDelegate2<const std::string&, const std::string&> EventHandle_Result;
EventHandle_Result eventPersuadeMsg;
virtual void onOpen(); virtual void onOpen();
private: private:
@ -53,6 +55,8 @@ namespace MWGui
struct Topic : Link struct Topic : Link
{ {
typedef MyGUI::delegates::CMultiDelegate1<const std::string&> EventHandle_TopicId;
EventHandle_TopicId eventTopicActivated;
Topic(const std::string& id) : mTopicId(id) {} Topic(const std::string& id) : mTopicId(id) {}
std::string mTopicId; std::string mTopicId;
virtual void activated (); virtual void activated ();
@ -60,6 +64,8 @@ namespace MWGui
struct Choice : Link struct Choice : Link
{ {
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_ChoiceId;
EventHandle_ChoiceId eventChoiceActivated;
Choice(int id) : mChoiceId(id) {} Choice(int id) : mChoiceId(id) {}
int mChoiceId; int mChoiceId;
virtual void activated (); virtual void activated ();
@ -67,6 +73,8 @@ namespace MWGui
struct Goodbye : Link struct Goodbye : Link
{ {
typedef MyGUI::delegates::CMultiDelegate0 Event_Activated;
Event_Activated eventActivated;
virtual void activated (); virtual void activated ();
}; };
@ -98,6 +106,7 @@ namespace MWGui
{ {
public: public:
DialogueWindow(); DialogueWindow();
~DialogueWindow();
void onTradeComplete(); void onTradeComplete();
@ -108,25 +117,29 @@ namespace MWGui
void notifyLinkClicked (TypesetBook::InteractiveId link); void notifyLinkClicked (TypesetBook::InteractiveId link);
void startDialogue(MWWorld::Ptr actor, std::string npcName); void setPtr(const MWWorld::Ptr& actor);
void setKeywords(std::list<std::string> keyWord); void setKeywords(std::list<std::string> 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 addMessageBox(const std::string& text);
void addChoice(const std::string& choice, int id);
void clearChoices();
void goodbye();
void onFrame(float dt); void onFrame(float dt);
void clear() { resetReference(); } void clear() { resetReference(); }
protected: 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 onByeClicked(MyGUI::Widget* _sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onMouseWheel(MyGUI::Widget* _sender, int _rel);
void onWindowResize(MyGUI::Window* _sender); 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); void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos);
@ -137,17 +150,19 @@ namespace MWGui
private: private:
void updateDisposition(); void updateDisposition();
void restock(); void restock();
void deleteLater();
bool mEnabled; bool mEnabled;
bool mGoodbye;
std::vector<DialogueText*> mHistoryContents; std::vector<DialogueText*> mHistoryContents;
std::vector<std::pair<std::string, int> > mChoices; std::vector<std::pair<std::string, int> > mChoices;
bool mGoodbye;
std::vector<Link*> mLinks; std::vector<Link*> mLinks;
std::map<std::string, Link*> mTopicLinks; std::map<std::string, Link*> mTopicLinks;
std::vector<Link*> mDeleteLater;
KeywordSearchT mKeywordSearch; KeywordSearchT mKeywordSearch;
BookPage* mHistory; BookPage* mHistory;

@ -353,6 +353,7 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
return; return;
} }
} }

@ -323,6 +323,7 @@ namespace MWGui
onCancelButtonClicked(mCancelButton); onCancelButtonClicked(mCancelButton);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
return; return;
} }
} }

@ -197,6 +197,7 @@ namespace MWGui
// go back to game mode // go back to game mode
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
} }
void TrainingWindow::onFrame(float dt) void TrainingWindow::onFrame(float dt)

@ -178,6 +178,7 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
// Teleports any followers, too. // Teleports any followers, too.
MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); MWWorld::ActionTeleport action(interior ? cellname : "", pos, true);

@ -1288,7 +1288,6 @@ namespace MWGui
mConsole->executeFile (path); mConsole->executeFile (path);
} }
MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; }
MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; }
MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; }
MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }

@ -179,7 +179,6 @@ namespace MWGui
virtual bool isAllowed(GuiWindow wnd) const; virtual bool isAllowed(GuiWindow wnd) const;
/// \todo investigate, if we really need to expose every single lousy UI element to the outside world /// \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::InventoryWindow* getInventoryWindow();
virtual MWGui::CountDialog* getCountDialog(); virtual MWGui::CountDialog* getCountDialog();
virtual MWGui::ConfirmationDialog* getConfirmationDialog(); virtual MWGui::ConfirmationDialog* getConfirmationDialog();

@ -11,6 +11,7 @@
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/journal.hpp" #include "../mwbase/journal.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
@ -116,7 +117,7 @@ namespace MWScript
runtime.pop(); runtime.pop();
arg0 = arg0 -1; arg0 = arg0 -1;
} }
dialogue->askQuestion(question,choice); dialogue->addChoice(question,choice);
} }
} }
}; };
@ -133,7 +134,7 @@ namespace MWScript
if (!ptr.getRefData().isEnabled()) if (!ptr.getRefData().isEnabled())
return; return;
MWBase::Environment::get().getDialogueManager()->startDialogue (ptr); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, ptr);
} }
}; };

@ -1,7 +1,7 @@
#include "actiontalk.hpp" #include "actiontalk.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp"
namespace MWWorld namespace MWWorld
{ {
@ -9,6 +9,6 @@ namespace MWWorld
void ActionTalk::executeImp (const Ptr& actor) void ActionTalk::executeImp (const Ptr& actor)
{ {
MWBase::Environment::get().getDialogueManager()->startDialogue (getTarget()); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, getTarget());
} }
} }

Loading…
Cancel
Save