From 947f3cf13ce8bec733f0c8cca84b62865752d602 Mon Sep 17 00:00:00 2001 From: fredzio Date: Mon, 13 May 2019 22:32:46 +0200 Subject: [PATCH] Optionally change color of keywords in the dialogue window based on the next answer: - if the answer was already heard, apply "color topic exhaused" setting - if the answer was never heard, and the current actor is specified in the dialog, apply "color topic specific" - otherwise, do nothing special --- CHANGELOG.md | 1 + apps/openmw/mwbase/dialoguemanager.hpp | 9 +++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 55 ++++++++++++++++--- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 5 ++ apps/openmw/mwdialogue/filter.cpp | 12 ---- apps/openmw/mwdialogue/filter.hpp | 3 - apps/openmw/mwgui/dialogue.cpp | 30 ++++++++-- apps/openmw/mwgui/dialogue.hpp | 2 + .../source/reference/modding/settings/GUI.rst | 26 +++++++++ files/settings-default.cfg | 8 +++ 10 files changed, 123 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77b126d85..da480ab67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -225,6 +225,7 @@ Bug #5350: An attempt to launch magic bolt causes "AL error invalid value" error Bug #5352: Light source items' duration is decremented while they aren't visible Feature #1724: Handle AvoidNode + Feature #2159: "Graying out" exhausted dialogue topics Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls Feature #3442: Default values for fallbacks from ini file diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 2ecab1c4c..e25762f32 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -53,6 +53,8 @@ namespace MWBase virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) = 0; + virtual bool inJournal (const std::string& topicId, const std::string& infoId) = 0; + virtual void addTopic (const std::string& topic) = 0; virtual void addChoice (const std::string& text,int choice) = 0; @@ -68,7 +70,14 @@ namespace MWBase virtual void goodbyeSelected() = 0; virtual void questionAnswered (int answer, ResponseCallback* callback) = 0; + enum TopicType + { + Specific = 1, + Exhausted = 2 + }; + virtual std::list getAvailableTopics() = 0; + virtual int getTopicFlag(const std::string&) = 0; virtual bool checkServiceRefused (ResponseCallback* callback) = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 21bc067d7..3e0cbbad2 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -228,6 +228,30 @@ namespace MWDialogue } } + bool DialogueManager::inJournal (const std::string& topicId, const std::string& infoId) + { + const MWDialogue::Topic *topicHistory = nullptr; + MWBase::Journal *journal = MWBase::Environment::get().getJournal(); + for (auto it = journal->topicBegin(); it != journal->topicEnd(); ++it) + { + if (it->first == topicId) + { + topicHistory = &it->second; + break; + } + } + + if (!topicHistory) + return false; + + for(const auto& topic : *topicHistory) + { + if (topic.mInfoId == infoId) + return true; + } + return false; + } + void DialogueManager::executeTopic (const std::string& topic, ResponseCallback* callback) { Filter filter (mActor, mChoice, mTalkedTo); @@ -300,22 +324,34 @@ namespace MWDialogue mActorKnownTopics.clear(); - const MWWorld::Store &dialogs = - MWBase::Environment::get().getWorld()->getStore().get(); + const auto& dialogs = MWBase::Environment::get().getWorld()->getStore().get(); Filter filter (mActor, -1, mTalkedTo); - for (MWWorld::Store::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter) + for (const auto& dialog : dialogs) { - if (iter->mType == ESM::Dialogue::Topic) + if (dialog.mType == ESM::Dialogue::Topic) { - if (filter.responseAvailable (*iter)) + const auto* answer = filter.search(dialog, true); + auto topicId = Misc::StringUtils::lowerCase(dialog.mId); + + if (answer != nullptr) { - mActorKnownTopics.insert (iter->mId); + int flag = 0; + if(!inJournal(topicId, answer->mId)) + { + // Does this dialogue contains some actor-specific answer? + if (answer->mActor == mActor.getCellRef().getRefId()) + flag |= MWBase::DialogueManager::TopicType::Specific; + } + else + flag |= MWBase::DialogueManager::TopicType::Exhausted; + mActorKnownTopics.insert (dialog.mId); + mActorKnownTopicsFlag[dialog.mId] = flag; } + } } - } std::list DialogueManager::getAvailableTopics() @@ -336,6 +372,11 @@ namespace MWDialogue return keywordList; } + int DialogueManager::getTopicFlag(const std::string& topicId) + { + return mActorKnownTopicsFlag[topicId]; + } + void DialogueManager::keywordSelected (const std::string& keyword, ResponseCallback* callback) { if(!mIsInChoice) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index fad086183..23dd68e91 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -30,6 +31,7 @@ namespace MWDialogue ModFactionReactionMap mChangedFactionReaction; std::set mActorKnownTopics; + std::unordered_map mActorKnownTopicsFlag; Translation::Storage& mTranslationDataStorage; MWScript::CompilerContext mCompilerContext; @@ -71,6 +73,9 @@ namespace MWDialogue virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback); std::list getAvailableTopics(); + int getTopicFlag(const std::string& topicId) final; + + bool inJournal (const std::string& topicId, const std::string& infoId) final; virtual void addTopic (const std::string& topic); diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 042ccb019..a3c326ab8 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -681,15 +681,3 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue return infos; } - -bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const -{ - for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); - iter!=dialogue.mInfo.end(); ++iter) - { - if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) - return true; - } - - return false; -} diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index 4e2ebe6e5..d2747d59a 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -66,9 +66,6 @@ namespace MWDialogue const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; ///< Get a matching response for the requested dialogue. /// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition. - - bool responseAvailable (const ESM::Dialogue& dialogue) const; - ///< Does a matching response exist? (disposition is ignored for this check) }; } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index bb40bea33..1dcd8d5ea 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -339,6 +339,7 @@ namespace MWGui mTopicsList->adjustSize(); updateHistory(); + updateTopicFormat(); mCurrentWindowSize = _sender->getSize(); } @@ -448,7 +449,6 @@ namespace MWGui setTitle(mPtr.getClass().getName(mPtr)); updateTopics(); - updateTopicsPane(); // force update for new services updateDisposition(); restock(); @@ -489,8 +489,6 @@ namespace MWGui return; mIsCompanion = isCompanion(); mKeywords = keyWords; - - updateTopicsPane(); } void DialogueWindow::updateTopicsPane() @@ -540,15 +538,16 @@ namespace MWGui mTopicsList->addSeparator(); - for(std::string& keyword : mKeywords) + for(const auto& keyword : mKeywords) { + std::string topicId = Misc::StringUtils::lowerCase(keyword); mTopicsList->addItem(keyword); Topic* t = new Topic(keyword); t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated); - mTopicLinks[Misc::StringUtils::lowerCase(keyword)] = t; + mTopicLinks[topicId] = t; - mKeywordSearch.seed(Misc::StringUtils::lowerCase(keyword), intptr_t(t)); + mKeywordSearch.seed(topicId, intptr_t(t)); } mTopicsList->adjustSize(); @@ -734,9 +733,28 @@ namespace MWGui updateHistory(); } + void DialogueWindow::updateTopicFormat() + { + std::string specialColour = Settings::Manager::getString("color topic specific", "GUI"); + std::string oldColour = Settings::Manager::getString("color topic exhausted", "GUI"); + + for (const std::string& keyword : mKeywords) + { + int flag = MWBase::Environment::get().getDialogueManager()->getTopicFlag(keyword); + MyGUI::Button* button = mTopicsList->getItemWidget(keyword); + + if (!specialColour.empty() && flag & MWBase::DialogueManager::TopicType::Specific) + button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(specialColour)); + else if (!oldColour.empty() && flag & MWBase::DialogueManager::TopicType::Exhausted) + button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(oldColour)); + } + } + void DialogueWindow::updateTopics() { setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()); + updateTopicsPane(); + updateTopicFormat(); } bool DialogueWindow::isCompanion() diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 2c3fb1a44..d9c26ef20 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -186,6 +186,8 @@ namespace MWGui std::unique_ptr mCallback; std::unique_ptr mGreetingCallback; + + void updateTopicFormat(); }; } #endif diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index c8f4e16f8..b881221b3 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -140,3 +140,29 @@ The alpha value is currently ignored. This setting can only be configured by editing the settings configuration file. This setting has no effect if the crosshair setting in the HUD Settings Section is false. This setting has no effect if the show owned setting in the Game Settings Section is false. + +color topic specific +-------------------- + +:Type: RGBA floating point +:Range: 0.0 to 1.0 +:Default: empty + +This setting overrides the color of keywords in the dialogue topic window. +The value is composed of four floating point values representing the red, green, blue and alpha channels. +The alpha value is currently ignored. + +The color is overriden if the actor is about to give an answer that is unique to him (that is, dialogue with their object ID in the Actor field) that wasn't seen yet. + +color topic exhausted +--------------------- + +:Type: RGBA floating point +:Range: 0.0 to 1.0 +:Default: empty + +This setting overrides the color of keywords in the dialogue topic window. +The value is composed of four floating point values representing the red, green, blue and alpha channels. +The alpha value is currently ignored. + +The color is overridden if the next actor responses to the topic keyword has already been seen by the player. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6703e7732..3203d6743 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -190,6 +190,14 @@ color crosshair owned = 1.0 0.15 0.15 1.0 # Controls whether Arrow keys, Movement keys, Tab/Shift-Tab and Spacebar/Enter/Activate may be used to navigate GUI buttons. keyboard navigation = true +# The color of dialogue topic keywords that gives unique actor responses +# Format R G B A or empty for default +color topic specific = + +# The color of dialogue topic keywords that gives already read responses +# Format R G B A or empty for default +color topic exhausted = + [HUD] # Displays the crosshair or reticle when not in GUI mode.