forked from teamnwah/openmw-tes3coop
Dialogue history rewrite WIP
This commit is contained in:
parent
6cd28d1156
commit
78e6dab9d2
17 changed files with 529 additions and 381 deletions
|
@ -32,6 +32,7 @@ add_openmw_dir (mwgui
|
||||||
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog
|
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog
|
||||||
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons
|
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons
|
||||||
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
|
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
|
||||||
|
keywordsearch
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwdialogue
|
add_openmw_dir (mwdialogue
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace MWBase
|
||||||
//calbacks for the GUI
|
//calbacks for the GUI
|
||||||
virtual void 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 (const std::string& answer) = 0;
|
virtual void questionAnswered (int answer) = 0;
|
||||||
|
|
||||||
virtual bool checkServiceRefused () = 0;
|
virtual bool checkServiceRefused () = 0;
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ namespace MWDialogue
|
||||||
parseText (info->mResponse);
|
parseText (info->mResponse);
|
||||||
|
|
||||||
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
||||||
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
|
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
|
||||||
executeScript (info->mResultScript);
|
executeScript (info->mResultScript);
|
||||||
mLastTopic = Misc::StringUtils::lowerCase(it->mId);
|
mLastTopic = Misc::StringUtils::lowerCase(it->mId);
|
||||||
mLastDialogue = *info;
|
mLastDialogue = *info;
|
||||||
|
@ -263,6 +263,7 @@ namespace MWDialogue
|
||||||
|
|
||||||
parseText (info->mResponse);
|
parseText (info->mResponse);
|
||||||
|
|
||||||
|
std::string title;
|
||||||
if (dialogue.mType==ESM::Dialogue::Persuasion)
|
if (dialogue.mType==ESM::Dialogue::Persuasion)
|
||||||
{
|
{
|
||||||
std::string modifiedTopic = "s" + topic;
|
std::string modifiedTopic = "s" + topic;
|
||||||
|
@ -272,13 +273,13 @@ namespace MWDialogue
|
||||||
const MWWorld::Store<ESM::GameSetting>& gmsts =
|
const MWWorld::Store<ESM::GameSetting>& gmsts =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
win->addTitle (gmsts.find (modifiedTopic)->getString());
|
title = gmsts.find (modifiedTopic)->getString();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
win->addTitle (topic);
|
title = topic;
|
||||||
|
|
||||||
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
||||||
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
|
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title);
|
||||||
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId);
|
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId);
|
||||||
|
|
||||||
executeScript (info->mResultScript);
|
executeScript (info->mResultScript);
|
||||||
|
@ -289,9 +290,7 @@ namespace MWDialogue
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// no response found, print a fallback text
|
// no response found, print a fallback text
|
||||||
win->addTitle (topic);
|
win->addResponse ("…", topic);
|
||||||
win->addText ("…");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,53 +423,42 @@ namespace MWDialogue
|
||||||
mTemporaryDispositionChange = 0;
|
mTemporaryDispositionChange = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueManager::questionAnswered (const std::string& answer)
|
void DialogueManager::questionAnswered (int answer)
|
||||||
{
|
{
|
||||||
|
mChoice = answer;
|
||||||
|
|
||||||
if (mChoiceMap.find(answer) != mChoiceMap.end())
|
if (mDialogueMap.find(mLastTopic) != mDialogueMap.end())
|
||||||
{
|
{
|
||||||
mChoice = mChoiceMap[answer];
|
Filter filter (mActor, mChoice, mTalkedTo);
|
||||||
|
|
||||||
if (mDialogueMap.find(mLastTopic) != mDialogueMap.end())
|
if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic
|
||||||
|
|| mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting)
|
||||||
{
|
{
|
||||||
Filter filter (mActor, mChoice, mTalkedTo);
|
if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true))
|
||||||
|
|
||||||
if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic
|
|
||||||
|| mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting)
|
|
||||||
{
|
{
|
||||||
if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true))
|
std::string text = info->mResponse;
|
||||||
{
|
parseText (text);
|
||||||
std::string text = info->mResponse;
|
|
||||||
parseText (text);
|
|
||||||
|
|
||||||
mChoiceMap.clear();
|
mChoice = -1;
|
||||||
mChoice = -1;
|
mIsInChoice = false;
|
||||||
mIsInChoice = false;
|
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices();
|
||||||
|
|
||||||
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
||||||
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext));
|
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext));
|
||||||
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
|
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
|
||||||
executeScript (info->mResultScript);
|
executeScript (info->mResultScript);
|
||||||
mLastDialogue = *info;
|
mLastDialogue = *info;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTopics();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void DialogueManager::printError (const std::string& error)
|
updateTopics();
|
||||||
{
|
|
||||||
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
|
||||||
win->addText(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueManager::askQuestion (const std::string& question, int choice)
|
void DialogueManager::askQuestion (const std::string& question, int choice)
|
||||||
{
|
{
|
||||||
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
|
||||||
win->askQuestion(question);
|
win->addChoice(question, choice);
|
||||||
mChoiceMap[Misc::StringUtils::lowerCase(question)] = choice;
|
|
||||||
mIsInChoice = true;
|
mIsInChoice = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,10 +539,10 @@ namespace MWDialogue
|
||||||
const MWWorld::Store<ESM::GameSetting>& gmsts =
|
const MWWorld::Store<ESM::GameSetting>& gmsts =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
win->addTitle (gmsts.find ("sServiceRefusal")->getString());
|
|
||||||
|
|
||||||
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
||||||
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
|
|
||||||
|
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext),
|
||||||
|
gmsts.find ("sServiceRefusal")->getString());
|
||||||
|
|
||||||
executeScript (info->mResultScript);
|
executeScript (info->mResultScript);
|
||||||
return true;
|
return true;
|
||||||
|
@ -565,9 +553,7 @@ namespace MWDialogue
|
||||||
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
|
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
|
||||||
{
|
{
|
||||||
std::vector<HyperTextToken> result;
|
std::vector<HyperTextToken> result;
|
||||||
|
|
||||||
MyGUI::UString utext(text);
|
MyGUI::UString utext(text);
|
||||||
|
|
||||||
size_t pos_begin, pos_end, iteration_pos = 0;
|
size_t pos_begin, pos_end, iteration_pos = 0;
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,7 +30,6 @@ namespace MWDialogue
|
||||||
bool mTalkedTo;
|
bool mTalkedTo;
|
||||||
|
|
||||||
int mChoice;
|
int mChoice;
|
||||||
std::map<std::string, int> mChoiceMap;
|
|
||||||
std::string mLastTopic;
|
std::string mLastTopic;
|
||||||
ESM::DialInfo mLastDialogue;
|
ESM::DialInfo mLastDialogue;
|
||||||
bool mIsInChoice;
|
bool mIsInChoice;
|
||||||
|
@ -46,8 +45,6 @@ namespace MWDialogue
|
||||||
bool compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code);
|
bool compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code);
|
||||||
void executeScript (const std::string& script);
|
void executeScript (const std::string& script);
|
||||||
|
|
||||||
void printError (const std::string& error);
|
|
||||||
|
|
||||||
void executeTopic (const std::string& topic, bool randomResponse=false);
|
void executeTopic (const std::string& topic, bool randomResponse=false);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -72,7 +69,7 @@ namespace MWDialogue
|
||||||
//calbacks for the GUI
|
//calbacks for the GUI
|
||||||
virtual void keywordSelected (const std::string& keyword);
|
virtual void keywordSelected (const std::string& keyword);
|
||||||
virtual void goodbyeSelected();
|
virtual void goodbyeSelected();
|
||||||
virtual void questionAnswered (const std::string& answer);
|
virtual void questionAnswered (int answer);
|
||||||
|
|
||||||
virtual void persuade (int type);
|
virtual void persuade (int type);
|
||||||
virtual int getTemporaryDispositionChange () const;
|
virtual int getTemporaryDispositionChange () const;
|
||||||
|
|
|
@ -120,7 +120,7 @@ struct MWGui::TypesetBookImpl : TypesetBook
|
||||||
|
|
||||||
size_t pageCount () const { return mPages.size (); }
|
size_t pageCount () const { return mPages.size (); }
|
||||||
|
|
||||||
std::pair <int, int> getSize () const
|
std::pair <unsigned int, unsigned int> getSize () const
|
||||||
{
|
{
|
||||||
return std::make_pair (mRect.width (), mRect.height ());
|
return std::make_pair (mRect.width (), mRect.height ());
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace MWGui
|
||||||
/// it is the largest distance from the left edge to the
|
/// it is the largest distance from the left edge to the
|
||||||
/// right edge. The second integer is the height of all
|
/// right edge. The second integer is the height of all
|
||||||
/// text combined prior to pagination.
|
/// text combined prior to pagination.
|
||||||
virtual std::pair <int, int> getSize () const = 0;
|
virtual std::pair <unsigned int, unsigned int> getSize () const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A factory class for creating a typeset book instance.
|
/// A factory class for creating a typeset book instance.
|
||||||
|
|
|
@ -18,29 +18,18 @@
|
||||||
#include "spellbuyingwindow.hpp"
|
#include "spellbuyingwindow.hpp"
|
||||||
#include "inventorywindow.hpp"
|
#include "inventorywindow.hpp"
|
||||||
#include "travelwindow.hpp"
|
#include "travelwindow.hpp"
|
||||||
|
#include "bookpage.hpp"
|
||||||
|
|
||||||
/**
|
|
||||||
*Copied from the internet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
|
||||||
std::string lower_string(const std::string& str)
|
|
||||||
{
|
{
|
||||||
std::string lowerCase = Misc::StringUtils::lowerCase (str);
|
typedef MWGui::BookTypesetter::Utf8Point point;
|
||||||
|
|
||||||
return lowerCase;
|
point begin = reinterpret_cast <point> (text);
|
||||||
}
|
|
||||||
|
|
||||||
std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
|
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
|
||||||
{
|
|
||||||
return lower_string(str).find(lower_string(substr),pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sortByLength (const std::string& left, const std::string& right)
|
|
||||||
{
|
|
||||||
return left.size() > right.size();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +105,103 @@ namespace MWGui
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Response::Response(const std::string &text, const std::string &title)
|
||||||
|
: mTitle(title)
|
||||||
|
{
|
||||||
|
mText = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks)
|
||||||
|
{
|
||||||
|
BookTypesetter::Style* title = typesetter->createStyle("EB Garamond", MyGUI::Colour::White);
|
||||||
|
BookTypesetter::Style* body = typesetter->createStyle("EB Garamond", MyGUI::Colour::Green);
|
||||||
|
typesetter->sectionBreak(9);
|
||||||
|
if (mTitle != "")
|
||||||
|
typesetter->write(title, to_utf8_span(mTitle.c_str()));
|
||||||
|
typesetter->sectionBreak(9);
|
||||||
|
|
||||||
|
typedef std::pair<size_t, size_t> Range;
|
||||||
|
std::map<Range, intptr_t> hyperLinks;
|
||||||
|
|
||||||
|
size_t pos_begin, pos_end, iteration_pos = 0;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
pos_begin = mText.find('@', iteration_pos);
|
||||||
|
if (pos_begin != std::string::npos)
|
||||||
|
pos_end = mText.find('#', pos_begin);
|
||||||
|
|
||||||
|
if (pos_begin != std::string::npos && pos_end != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string link = mText.substr(pos_begin + 1, pos_end - pos_begin - 1);
|
||||||
|
const char specialPseudoAsteriskCharacter = 127;
|
||||||
|
std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*');
|
||||||
|
std::string topicName = MWBase::Environment::get().getWindowManager()->
|
||||||
|
getTranslationDataStorage().topicStandardForm(link);
|
||||||
|
|
||||||
|
std::string displayName = link;
|
||||||
|
MWDialogue::RemovePseudoAsterisks(displayName);
|
||||||
|
|
||||||
|
mText.replace(pos_begin, pos_end+1, displayName);
|
||||||
|
|
||||||
|
assert(topicLinks.find(topicName) != topicLinks.end());
|
||||||
|
hyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = intptr_t(topicLinks[topicName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typesetter->addContent(to_utf8_span(mText.c_str()));
|
||||||
|
|
||||||
|
for (std::map<Range, intptr_t>::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it)
|
||||||
|
{
|
||||||
|
intptr_t topicId = it->second;
|
||||||
|
BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour::Green);
|
||||||
|
const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f);
|
||||||
|
const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f);
|
||||||
|
const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f);
|
||||||
|
style = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId);
|
||||||
|
typesetter->write(style, it->first.first, it->first.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string::const_iterator i = mText.begin ();
|
||||||
|
KeywordSearchT::Match match;
|
||||||
|
while (i != mText.end () && keywordSearch->search (i, mText.end (), match))
|
||||||
|
{
|
||||||
|
if (i != match.mBeg)
|
||||||
|
addTopicLink (typesetter, 0, i - mText.begin (), match.mBeg - mText.begin ());
|
||||||
|
|
||||||
|
addTopicLink (typesetter, match.mValue, match.mBeg - mText.begin (), match.mEnd - mText.begin ());
|
||||||
|
|
||||||
|
i = match.mEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != mText.end ())
|
||||||
|
addTopicLink (typesetter, 0, i - mText.begin (), mText.size ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Response::addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end)
|
||||||
|
{
|
||||||
|
BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour::Green);
|
||||||
|
const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f);
|
||||||
|
const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f);
|
||||||
|
const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f);
|
||||||
|
if (topicId)
|
||||||
|
style = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId);
|
||||||
|
typesetter->write (style, begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Choice::activated()
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Topic::activated()
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
DialogueWindow::DialogueWindow()
|
DialogueWindow::DialogueWindow()
|
||||||
: WindowBase("openmw_dialogue_window.layout")
|
: WindowBase("openmw_dialogue_window.layout")
|
||||||
, mPersuasionDialog()
|
, mPersuasionDialog()
|
||||||
|
@ -129,15 +215,6 @@ namespace MWGui
|
||||||
|
|
||||||
//History view
|
//History view
|
||||||
getWidget(mHistory, "History");
|
getWidget(mHistory, "History");
|
||||||
mHistory->setOverflowToTheLeft(true);
|
|
||||||
mHistory->setMaxTextLength(1000000);
|
|
||||||
MyGUI::Widget* eventbox;
|
|
||||||
|
|
||||||
//An EditBox cannot receive mouse click events, so we use an
|
|
||||||
//invisible widget on top of the editbox to receive them
|
|
||||||
getWidget(eventbox, "EventBox");
|
|
||||||
eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked);
|
|
||||||
eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel);
|
|
||||||
|
|
||||||
//Topics list
|
//Topics list
|
||||||
getWidget(mTopicsList, "TopicsList");
|
getWidget(mTopicsList, "TopicsList");
|
||||||
|
@ -149,12 +226,20 @@ namespace MWGui
|
||||||
|
|
||||||
getWidget(mDispositionBar, "Disposition");
|
getWidget(mDispositionBar, "Disposition");
|
||||||
getWidget(mDispositionText,"DispositionText");
|
getWidget(mDispositionText,"DispositionText");
|
||||||
|
getWidget(mScrollBar, "VScroll");
|
||||||
|
|
||||||
|
mScrollBar->eventScrollChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onScrollbarMoved);
|
||||||
|
mHistory->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel);
|
||||||
|
|
||||||
|
BookPage::ClickCallback callback = boost::bind (&DialogueWindow::notifyLinkClicked, this, _1);
|
||||||
|
mHistory->adviseLinkClicked(callback);
|
||||||
|
|
||||||
static_cast<MyGUI::Window*>(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);
|
static_cast<MyGUI::Window*>(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
|
void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText();
|
MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText();
|
||||||
if(t == NULL)
|
if(t == NULL)
|
||||||
return;
|
return;
|
||||||
|
@ -198,19 +283,22 @@ namespace MWGui
|
||||||
if(color == "#572D21")
|
if(color == "#572D21")
|
||||||
MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key));
|
MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::onWindowResize(MyGUI::Window* _sender)
|
void DialogueWindow::onWindowResize(MyGUI::Window* _sender)
|
||||||
{
|
{
|
||||||
mTopicsList->adjustSize();
|
mTopicsList->adjustSize();
|
||||||
|
updateHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||||
{
|
{
|
||||||
if (mHistory->getVScrollPosition() - _rel*0.3 < 0)
|
if (!mScrollBar->getVisible())
|
||||||
mHistory->setVScrollPosition(0);
|
return;
|
||||||
else
|
mScrollBar->setScrollPosition(std::min(static_cast<int>(mScrollBar->getScrollRange()-1),
|
||||||
mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3);
|
std::max(0, static_cast<int>(mScrollBar->getScrollPosition() - _rel*0.3))));
|
||||||
|
onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
|
void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
|
||||||
|
@ -231,7 +319,7 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id >= separatorPos)
|
if (id >= separatorPos)
|
||||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic));
|
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||||
|
@ -296,13 +384,25 @@ namespace MWGui
|
||||||
|
|
||||||
mTopicsList->clear();
|
mTopicsList->clear();
|
||||||
mHyperLinks.clear();
|
mHyperLinks.clear();
|
||||||
mHistory->setCaption("");
|
|
||||||
|
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
|
||||||
|
delete (*it);
|
||||||
|
mHistoryContents.clear();
|
||||||
|
|
||||||
|
for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it)
|
||||||
|
delete (*it);
|
||||||
|
mLinks.clear();
|
||||||
|
|
||||||
updateOptions();
|
updateOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
delete it->second;
|
||||||
|
mTopicLinks.clear();
|
||||||
|
mKeywordSearch.clear();
|
||||||
|
|
||||||
bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty()
|
bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty()
|
||||||
&& mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion");
|
&& mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion");
|
||||||
|
@ -346,46 +446,15 @@ namespace MWGui
|
||||||
for(std::list<std::string>::iterator it = keyWords.begin(); it != keyWords.end(); ++it)
|
for(std::list<std::string>::iterator it = keyWords.begin(); it != keyWords.end(); ++it)
|
||||||
{
|
{
|
||||||
mTopicsList->addItem(*it);
|
mTopicsList->addItem(*it);
|
||||||
|
|
||||||
|
Topic* t = new Topic(*it);
|
||||||
|
mTopicLinks[*it] = t;
|
||||||
|
|
||||||
|
mKeywordSearch.seed(*it, intptr_t(t));
|
||||||
}
|
}
|
||||||
mTopicsList->adjustSize();
|
mTopicsList->adjustSize();
|
||||||
}
|
|
||||||
|
|
||||||
void DialogueWindow::removeKeyword(std::string keyWord)
|
updateHistory();
|
||||||
{
|
|
||||||
if(mTopicsList->hasItem(keyWord))
|
|
||||||
{
|
|
||||||
mTopicsList->removeItem(keyWord);
|
|
||||||
}
|
|
||||||
mTopicsList->adjustSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2)
|
|
||||||
{
|
|
||||||
size_t pos = 0;
|
|
||||||
while((pos = find_str_ci(str,keyword, pos)) != std::string::npos)
|
|
||||||
{
|
|
||||||
// do not add color if this portion of text is already colored.
|
|
||||||
{
|
|
||||||
MyGUI::TextIterator iterator (str);
|
|
||||||
MyGUI::UString colour;
|
|
||||||
while(iterator.moveNext())
|
|
||||||
{
|
|
||||||
size_t iteratorPos = iterator.getPosition();
|
|
||||||
iterator.getTagColour(colour);
|
|
||||||
if (iteratorPos == pos)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colour == color1)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
str.insert(pos,color1);
|
|
||||||
pos += color1.length();
|
|
||||||
pos += keyword.length();
|
|
||||||
str.insert(pos,color2);
|
|
||||||
pos+= color2.length();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DialogueWindow::parseText(const std::string& text)
|
std::string DialogueWindow::parseText(const std::string& text)
|
||||||
|
@ -410,18 +479,19 @@ namespace MWGui
|
||||||
separatorReached = true;
|
separatorReached = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort by length to make sure longer topics are replaced first
|
|
||||||
std::sort(topics.begin(), topics.end(), sortByLength);
|
|
||||||
|
|
||||||
std::vector<MWDialogue::HyperTextToken> hypertext = MWDialogue::ParseHyperText(text);
|
std::vector<MWDialogue::HyperTextToken> hypertext = MWDialogue::ParseHyperText(text);
|
||||||
|
|
||||||
|
/*
|
||||||
size_t historySize = 0;
|
size_t historySize = 0;
|
||||||
if(mHistory->getClient()->getSubWidgetText() != NULL)
|
if(mHistory->getClient()->getSubWidgetText() != NULL)
|
||||||
{
|
{
|
||||||
historySize = mHistory->getOnlyText().size();
|
historySize = mHistory->getOnlyText().size();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
|
/*
|
||||||
size_t hypertextPos = 0;
|
size_t hypertextPos = 0;
|
||||||
for (size_t i = 0; i < hypertext.size(); ++i)
|
for (size_t i = 0; i < hypertext.size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -461,38 +531,115 @@ namespace MWGui
|
||||||
|
|
||||||
hypertextPos += MyGUI::UString(hypertext[i].mText).length();
|
hypertextPos += MyGUI::UString(hypertext[i].mText).length();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::addText(std::string text)
|
void DialogueWindow::updateHistory(bool scrollbar)
|
||||||
{
|
{
|
||||||
mHistory->addDialogText("#B29154"+parseText(text)+"#B29154");
|
if (!scrollbar && mScrollBar->getVisible())
|
||||||
|
{
|
||||||
|
mHistory->setSize(mHistory->getSize()+MyGUI::IntSize(mScrollBar->getWidth(),0));
|
||||||
|
mScrollBar->setVisible(false);
|
||||||
|
}
|
||||||
|
if (scrollbar && !mScrollBar->getVisible())
|
||||||
|
{
|
||||||
|
mHistory->setSize(mHistory->getSize()-MyGUI::IntSize(mScrollBar->getWidth(),0));
|
||||||
|
mScrollBar->setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
BookTypesetter::Ptr typesetter = BookTypesetter::create (mHistory->getWidth(), std::numeric_limits<int>().max());
|
||||||
|
|
||||||
|
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
|
||||||
|
(*it)->write(typesetter, &mKeywordSearch, mTopicLinks);
|
||||||
|
|
||||||
|
|
||||||
|
BookTypesetter::Style* body = typesetter->createStyle("EB Garamond", MyGUI::Colour::White);
|
||||||
|
|
||||||
|
// choices
|
||||||
|
for (std::map<std::string, int>::iterator it = mChoices.begin(); it != mChoices.end(); ++it)
|
||||||
|
{
|
||||||
|
Choice* link = new Choice(it->second);
|
||||||
|
mLinks.push_back(link);
|
||||||
|
|
||||||
|
typesetter->lineBreak();
|
||||||
|
const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f);
|
||||||
|
const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f);
|
||||||
|
const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f);
|
||||||
|
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive,
|
||||||
|
TypesetBook::InteractiveId(link));
|
||||||
|
typesetter->write(questionStyle, to_utf8_span(it->first.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TypesetBook::Ptr book = typesetter->complete();
|
||||||
|
mHistory->showPage(book, 0);
|
||||||
|
size_t viewHeight = mHistory->getParent()->getHeight();
|
||||||
|
if (!scrollbar && book->getSize().second > viewHeight)
|
||||||
|
updateHistory(true);
|
||||||
|
else if (scrollbar)
|
||||||
|
{
|
||||||
|
mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second));
|
||||||
|
size_t range = book->getSize().second - viewHeight;
|
||||||
|
mScrollBar->setScrollRange(range);
|
||||||
|
mScrollBar->setScrollPosition(range-1);
|
||||||
|
onScrollbarMoved(mScrollBar, range-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no scrollbar
|
||||||
|
onScrollbarMoved(mScrollBar, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogueWindow::notifyLinkClicked (TypesetBook::InteractiveId link)
|
||||||
|
{
|
||||||
|
reinterpret_cast<Link*>(link)->activated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos)
|
||||||
|
{
|
||||||
|
mHistory->setPosition(0,-pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogueWindow::addResponse(const std::string &text, const std::string &title)
|
||||||
|
{
|
||||||
|
// This is called from the dialogue manager, so text is
|
||||||
|
// case-smashed - thus we have to retrieve the correct case
|
||||||
|
// of the title through the topic list.
|
||||||
|
std::string realTitle = title;
|
||||||
|
if (realTitle != "")
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
|
||||||
|
{
|
||||||
|
std::string item = mTopicsList->getItemNameAt(i);
|
||||||
|
if (Misc::StringUtils::lowerCase(item) == title)
|
||||||
|
{
|
||||||
|
realTitle = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mHistoryContents.push_back(new Response(text, realTitle));
|
||||||
|
updateHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::addMessageBox(const std::string& text)
|
void DialogueWindow::addMessageBox(const std::string& text)
|
||||||
{
|
{
|
||||||
mHistory->addDialogText("\n#FFFFFF"+text+"#B29154");
|
//mHistoryContents.push_back(new Message(text));
|
||||||
|
updateHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::addTitle(std::string text)
|
void DialogueWindow::addChoice(const std::string& choice, int id)
|
||||||
{
|
{
|
||||||
// This is called from the dialogue manager, so text is
|
mChoices[choice] = id;
|
||||||
// case-smashed - thus we have to retrieve the correct case
|
updateHistory();
|
||||||
// of the text through the topic list.
|
|
||||||
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
|
|
||||||
{
|
|
||||||
std::string item = mTopicsList->getItemNameAt(i);
|
|
||||||
if (lower_string(item) == text)
|
|
||||||
text = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
mHistory->addDialogHeading(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::askQuestion(std::string question)
|
void DialogueWindow::clearChoices()
|
||||||
{
|
{
|
||||||
mHistory->addDialogText("#572D21"+question+"#B29154"+" ");
|
mChoices.clear();
|
||||||
|
updateHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::updateOptions()
|
void DialogueWindow::updateOptions()
|
||||||
|
@ -500,7 +647,6 @@ namespace MWGui
|
||||||
//Clear the list of topics
|
//Clear the list of topics
|
||||||
mTopicsList->clear();
|
mTopicsList->clear();
|
||||||
mHyperLinks.clear();
|
mHyperLinks.clear();
|
||||||
mHistory->eraseText(0, mHistory->getTextLength());
|
|
||||||
|
|
||||||
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
|
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||||
{
|
{
|
||||||
|
@ -513,7 +659,7 @@ namespace MWGui
|
||||||
|
|
||||||
void DialogueWindow::goodbye()
|
void DialogueWindow::goodbye()
|
||||||
{
|
{
|
||||||
mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString());
|
//mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString());
|
||||||
mTopicsList->setEnabled(false);
|
mTopicsList->setEnabled(false);
|
||||||
mEnabled = false;
|
mEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
#include "windowbase.hpp"
|
#include "windowbase.hpp"
|
||||||
#include "referenceinterface.hpp"
|
#include "referenceinterface.hpp"
|
||||||
|
|
||||||
|
#include "bookpage.hpp"
|
||||||
|
|
||||||
|
#include "keywordsearch.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class WindowManager;
|
class WindowManager;
|
||||||
|
@ -21,7 +25,8 @@ namespace MWGui
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class DialogueHistory;
|
class DialogueHistoryViewModel;
|
||||||
|
class BookPage;
|
||||||
|
|
||||||
class PersuasionDialog : public WindowModal
|
class PersuasionDialog : public WindowModal
|
||||||
{
|
{
|
||||||
|
@ -44,6 +49,55 @@ namespace MWGui
|
||||||
void onPersuade (MyGUI::Widget* sender);
|
void onPersuade (MyGUI::Widget* sender);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Link
|
||||||
|
{
|
||||||
|
virtual ~Link() {}
|
||||||
|
virtual void activated () = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Topic : Link
|
||||||
|
{
|
||||||
|
Topic(const std::string& id) : mTopicId(id) {}
|
||||||
|
std::string mTopicId;
|
||||||
|
virtual void activated ();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Choice : Link
|
||||||
|
{
|
||||||
|
Choice(int id) : mChoiceId(id) {}
|
||||||
|
int mChoiceId;
|
||||||
|
virtual void activated ();
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
|
||||||
|
|
||||||
|
struct DialogueText
|
||||||
|
{
|
||||||
|
virtual ~DialogueText() {}
|
||||||
|
virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) = 0;
|
||||||
|
std::string mText;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Response : DialogueText
|
||||||
|
{
|
||||||
|
Response(const std::string& text, const std::string& title = "");
|
||||||
|
virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks);
|
||||||
|
void addTopicLink (BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end);
|
||||||
|
std::string mTitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Message : DialogueText
|
||||||
|
{
|
||||||
|
Message(const std::string& text);
|
||||||
|
virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Goodbye : DialogueText
|
||||||
|
{
|
||||||
|
virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks);
|
||||||
|
};
|
||||||
|
|
||||||
class DialogueWindow: public WindowBase, public ReferenceInterface
|
class DialogueWindow: public WindowBase, public ReferenceInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -52,19 +106,19 @@ namespace MWGui
|
||||||
// Events
|
// Events
|
||||||
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
|
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
|
||||||
|
|
||||||
/** Event : Dialog finished, OK button clicked.\n
|
void notifyLinkClicked (TypesetBook::InteractiveId link);
|
||||||
signature : void method()\n
|
|
||||||
*/
|
|
||||||
EventHandle_Void eventBye;
|
|
||||||
|
|
||||||
void startDialogue(MWWorld::Ptr actor, std::string npcName);
|
void startDialogue(MWWorld::Ptr actor, std::string npcName);
|
||||||
void stopDialogue();
|
void stopDialogue();
|
||||||
void setKeywords(std::list<std::string> keyWord);
|
void setKeywords(std::list<std::string> keyWord);
|
||||||
void removeKeyword(std::string keyWord);
|
|
||||||
void addText(std::string text);
|
void addResponse (const std::string& text, const std::string& title="");
|
||||||
|
|
||||||
void addMessageBox(const std::string& text);
|
void addMessageBox(const std::string& text);
|
||||||
void addTitle(std::string text);
|
|
||||||
void askQuestion(std::string question);
|
void addChoice(const std::string& choice, int id);
|
||||||
|
void clearChoices();
|
||||||
|
|
||||||
void goodbye();
|
void goodbye();
|
||||||
void onFrame();
|
void onFrame();
|
||||||
|
|
||||||
|
@ -89,6 +143,10 @@ namespace MWGui
|
||||||
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 onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos);
|
||||||
|
|
||||||
|
void updateHistory(bool scrollbar=false);
|
||||||
|
|
||||||
virtual void onReferenceUnavailable();
|
virtual void onReferenceUnavailable();
|
||||||
|
|
||||||
struct HyperLink
|
struct HyperLink
|
||||||
|
@ -108,11 +166,22 @@ namespace MWGui
|
||||||
|
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
|
|
||||||
DialogueHistory* mHistory;
|
std::vector<DialogueText*> mHistoryContents;
|
||||||
|
std::map<std::string, int> mChoices;
|
||||||
|
|
||||||
|
std::vector<Link*> mLinks;
|
||||||
|
std::map<std::string, Link*> mTopicLinks;
|
||||||
|
|
||||||
|
KeywordSearchT mKeywordSearch;
|
||||||
|
|
||||||
|
BookPage* mHistory;
|
||||||
Widgets::MWList* mTopicsList;
|
Widgets::MWList* mTopicsList;
|
||||||
|
MyGUI::ScrollBar* mScrollBar;
|
||||||
MyGUI::ProgressPtr mDispositionBar;
|
MyGUI::ProgressPtr mDispositionBar;
|
||||||
MyGUI::EditBox* mDispositionText;
|
MyGUI::EditBox* mDispositionText;
|
||||||
|
|
||||||
|
std::stringstream mText;
|
||||||
|
|
||||||
PersuasionDialog mPersuasionDialog;
|
PersuasionDialog mPersuasionDialog;
|
||||||
|
|
||||||
std::map<size_t, HyperLink> mHyperLinks;
|
std::map<size_t, HyperLink> mHyperLinks;
|
||||||
|
|
|
@ -1,78 +1,11 @@
|
||||||
#include "dialoguehistory.hpp"
|
#include "dialoguehistory.hpp"
|
||||||
|
|
||||||
#include "../mwbase/windowmanager.hpp"
|
|
||||||
|
|
||||||
#include "widgets.hpp"
|
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos)
|
DialogueHistoryViewModel::DialogueHistoryViewModel()
|
||||||
{
|
{
|
||||||
MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour());
|
}
|
||||||
MyGUI::TextIterator iterator(getCaption());
|
|
||||||
while(iterator.moveNext())
|
|
||||||
{
|
|
||||||
size_t pos = iterator.getPosition();
|
|
||||||
iterator.getTagColour(colour);
|
|
||||||
if (pos < _pos)
|
|
||||||
continue;
|
|
||||||
else if (pos == _pos)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return colour;
|
|
||||||
}
|
|
||||||
|
|
||||||
MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos)
|
|
||||||
{
|
|
||||||
bool breakOnNext = false;
|
|
||||||
MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour());
|
|
||||||
MyGUI::UString colour2 = colour;
|
|
||||||
MyGUI::TextIterator iterator(getCaption());
|
|
||||||
MyGUI::TextIterator col_start = iterator;
|
|
||||||
while(iterator.moveNext())
|
|
||||||
{
|
|
||||||
size_t pos = iterator.getPosition();
|
|
||||||
iterator.getTagColour(colour);
|
|
||||||
if(colour != colour2)
|
|
||||||
{
|
|
||||||
if(breakOnNext)
|
|
||||||
{
|
|
||||||
return getOnlyText().substr(col_start.getPosition(), iterator.getPosition()-col_start.getPosition());
|
|
||||||
}
|
|
||||||
col_start = iterator;
|
|
||||||
colour2 = colour;
|
|
||||||
}
|
|
||||||
if (pos < _pos)
|
|
||||||
continue;
|
|
||||||
else if (pos == _pos)
|
|
||||||
{
|
|
||||||
breakOnNext = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogueHistory::addDialogHeading(const MyGUI::UString& parText)
|
|
||||||
{
|
|
||||||
MyGUI::UString head("\n#D8C09A");
|
|
||||||
head.append(parText);
|
|
||||||
head.append("#B29154\n");
|
|
||||||
addText(head);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogueHistory::addDialogText(const MyGUI::UString& parText)
|
|
||||||
{
|
|
||||||
addText(parText);
|
|
||||||
addText("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
#ifndef MWGUI_DIALOGE_HISTORY_H
|
#ifndef MWGUI_DIALOGE_HISTORY_H
|
||||||
#define MWGUI_DIALOGE_HISTORY_H
|
#define MWGUI_DIALOGE_HISTORY_H
|
||||||
|
|
||||||
#include <openengine/gui/layout.hpp>
|
#include "keywordsearch.hpp"
|
||||||
|
|
||||||
|
#include <platform/stdint.h>
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class DialogueHistory : public MyGUI::EditBox
|
class DialogueHistoryViewModel
|
||||||
{
|
{
|
||||||
MYGUI_RTTI_DERIVED( DialogueHistory )
|
public:
|
||||||
public:
|
DialogueHistoryViewModel();
|
||||||
Widget* getClient() { return mClient; }
|
|
||||||
MyGUI::UString getColorAtPos(size_t _pos);
|
private:
|
||||||
MyGUI::UString getColorTextAt(size_t _pos);
|
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
|
||||||
void addDialogHeading(const MyGUI::UString& parText);
|
|
||||||
void addDialogText(const MyGUI::UString& parText);
|
mutable bool mKeywordSearchLoaded;
|
||||||
|
mutable KeywordSearchT mKeywordSearch;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
#include "journalviewmodel.hpp"
|
#include "journalviewmodel.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
|
|
||||||
|
#include <MyGUI_LanguageManager.h>
|
||||||
|
|
||||||
|
#include <components/misc/utf8stream.hpp>
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/journal.hpp"
|
#include "../mwbase/journal.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwdialogue/journalentry.hpp"
|
#include "../mwdialogue/journalentry.hpp"
|
||||||
|
|
||||||
#include <MyGUI_LanguageManager.h>
|
#include "keywordsearch.hpp"
|
||||||
|
|
||||||
#include <components/misc/utf8stream.hpp>
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <sstream>
|
|
||||||
#include <boost/make_shared.hpp>
|
|
||||||
|
|
||||||
using namespace MWGui;
|
using namespace MWGui;
|
||||||
|
|
||||||
|
@ -19,143 +22,12 @@ namespace MWGui { struct JournalViewModelImpl; }
|
||||||
|
|
||||||
static void injectMonthName (std::ostream & os, int month);
|
static void injectMonthName (std::ostream & os, int month);
|
||||||
|
|
||||||
template <typename string_t, typename value_t>
|
|
||||||
class KeywordSearch
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef typename string_t::const_iterator Point;
|
|
||||||
|
|
||||||
struct Match
|
|
||||||
{
|
|
||||||
Point mBeg;
|
|
||||||
Point mEnd;
|
|
||||||
value_t mValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
void seed (string_t keyword, value_t value)
|
|
||||||
{
|
|
||||||
seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear ()
|
|
||||||
{
|
|
||||||
mRoot.mChildren.clear ();
|
|
||||||
mRoot.mKeyword.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool search (Point beg, Point end, Match & match)
|
|
||||||
{
|
|
||||||
for (Point i = beg; i != end; ++i)
|
|
||||||
{
|
|
||||||
// check first character
|
|
||||||
typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (std::tolower (*i, mLocale));
|
|
||||||
|
|
||||||
// no match, on to next character
|
|
||||||
if (candidate == mRoot.mChildren.end ())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// see how far the match goes
|
|
||||||
Point j = i;
|
|
||||||
|
|
||||||
while ((j + 1) != end)
|
|
||||||
{
|
|
||||||
typename Entry::childen_t::iterator next = candidate->second.mChildren.find (std::tolower (*++j, mLocale));
|
|
||||||
|
|
||||||
if (next == candidate->second.mChildren.end ())
|
|
||||||
break;
|
|
||||||
|
|
||||||
candidate = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// didn't match enough to disambiguate, on to next character
|
|
||||||
if (!candidate->second.mKeyword.size ())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// match the rest of the keyword
|
|
||||||
typename string_t::const_iterator t = candidate->second.mKeyword.begin () + (j - i);
|
|
||||||
|
|
||||||
while (j != end && t != candidate->second.mKeyword.end ())
|
|
||||||
{
|
|
||||||
if (std::tolower (*j, mLocale) != std::tolower (*t, mLocale))
|
|
||||||
break;
|
|
||||||
|
|
||||||
++j, ++t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// didn't match full keyword, on to next character
|
|
||||||
if (t != candidate->second.mKeyword.end ())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// we did it, report the good news
|
|
||||||
match.mValue = candidate->second.mValue;
|
|
||||||
match.mBeg = i;
|
|
||||||
match.mEnd = j;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no match in range, report the bad news
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct Entry
|
|
||||||
{
|
|
||||||
typedef std::map <wchar_t, Entry> childen_t;
|
|
||||||
|
|
||||||
string_t mKeyword;
|
|
||||||
value_t mValue;
|
|
||||||
childen_t mChildren;
|
|
||||||
};
|
|
||||||
|
|
||||||
void seed_impl (string_t keyword, value_t value, size_t depth, Entry & entry)
|
|
||||||
{
|
|
||||||
int ch = tolower (keyword.at (depth), mLocale);
|
|
||||||
|
|
||||||
typename Entry::childen_t::iterator j = entry.mChildren.find (ch);
|
|
||||||
|
|
||||||
if (j == entry.mChildren.end ())
|
|
||||||
{
|
|
||||||
entry.mChildren [ch].mValue = /*std::move*/ (value);
|
|
||||||
entry.mChildren [ch].mKeyword = /*std::move*/ (keyword);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (j->second.mKeyword.size () > 0)
|
|
||||||
{
|
|
||||||
if (keyword == j->second.mKeyword)
|
|
||||||
throw std::runtime_error ("duplicate keyword inserted");
|
|
||||||
|
|
||||||
value_t pushValue = /*std::move*/ (j->second.mValue);
|
|
||||||
string_t pushKeyword = /*std::move*/ (j->second.mKeyword);
|
|
||||||
|
|
||||||
j->second.mKeyword.clear ();
|
|
||||||
|
|
||||||
if (depth >= pushKeyword.size ())
|
|
||||||
throw std::runtime_error ("unexpected");
|
|
||||||
|
|
||||||
if (depth+1 < pushKeyword.size())
|
|
||||||
seed_impl (/*std::move*/ (pushKeyword), /*std::move*/ (pushValue), depth+1, j->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depth+1 < keyword.size())
|
|
||||||
seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), depth+1, j->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry mRoot;
|
|
||||||
std::locale mLocale;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MWGui::JournalViewModelImpl : JournalViewModel
|
struct MWGui::JournalViewModelImpl : JournalViewModel
|
||||||
{
|
{
|
||||||
typedef KeywordSearch <std::string, intptr_t> keyword_search_t;
|
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
|
||||||
|
|
||||||
mutable bool mKeywordSearchLoaded;
|
mutable bool mKeywordSearchLoaded;
|
||||||
mutable keyword_search_t mKeywordSearch;
|
mutable KeywordSearchT mKeywordSearch;
|
||||||
|
|
||||||
std::locale mLocale;
|
std::locale mLocale;
|
||||||
|
|
||||||
|
@ -253,7 +125,7 @@ struct MWGui::JournalViewModelImpl : JournalViewModel
|
||||||
|
|
||||||
std::string::const_iterator i = utf8text.begin ();
|
std::string::const_iterator i = utf8text.begin ();
|
||||||
|
|
||||||
keyword_search_t::Match match;
|
KeywordSearchT::Match match;
|
||||||
|
|
||||||
while (i != utf8text.end () && mModel->mKeywordSearch.search (i, utf8text.end (), match))
|
while (i != utf8text.end () && mModel->mKeywordSearch.search (i, utf8text.end (), match))
|
||||||
{
|
{
|
||||||
|
@ -300,8 +172,8 @@ struct MWGui::JournalViewModelImpl : JournalViewModel
|
||||||
|
|
||||||
mutable std::string timestamp_buffer;
|
mutable std::string timestamp_buffer;
|
||||||
|
|
||||||
JournalEntryImpl (JournalViewModelImpl const * Model, iterator_t itr) :
|
JournalEntryImpl (JournalViewModelImpl const * model, iterator_t itr) :
|
||||||
BaseEntry <iterator_t, JournalEntry> (Model, itr)
|
BaseEntry <iterator_t, JournalEntry> (model, itr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string getText () const
|
std::string getText () const
|
||||||
|
|
|
@ -152,6 +152,10 @@ namespace
|
||||||
|
|
||||||
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
|
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
|
||||||
|
|
||||||
|
/// \todo Wiping the whole book layout each time the journal is opened is probably too costly for a large journal (eg 300+ pages).
|
||||||
|
/// There should be a way to keep the existing layout and append new entries to the end of it.
|
||||||
|
/// However, that still leaves the problem of having to add links to previously unknown, but now known topics, so
|
||||||
|
/// we maybe need to find another way to speed things up.
|
||||||
Book journalBook;
|
Book journalBook;
|
||||||
if (mModel->isEmpty ())
|
if (mModel->isEmpty ())
|
||||||
journalBook = createEmptyJournalBook ();
|
journalBook = createEmptyJournalBook ();
|
||||||
|
|
0
apps/openmw/mwgui/keywordsearch.cpp
Normal file
0
apps/openmw/mwgui/keywordsearch.cpp
Normal file
139
apps/openmw/mwgui/keywordsearch.hpp
Normal file
139
apps/openmw/mwgui/keywordsearch.hpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
#ifndef MWGUI_KEYWORDSEARCH_H
|
||||||
|
#define MWGUI_KEYWORDSEARCH_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <locale>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
template <typename string_t, typename value_t>
|
||||||
|
class KeywordSearch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef typename string_t::const_iterator Point;
|
||||||
|
|
||||||
|
struct Match
|
||||||
|
{
|
||||||
|
Point mBeg;
|
||||||
|
Point mEnd;
|
||||||
|
value_t mValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
void seed (string_t keyword, value_t value)
|
||||||
|
{
|
||||||
|
seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear ()
|
||||||
|
{
|
||||||
|
mRoot.mChildren.clear ();
|
||||||
|
mRoot.mKeyword.clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool search (Point beg, Point end, Match & match)
|
||||||
|
{
|
||||||
|
for (Point i = beg; i != end; ++i)
|
||||||
|
{
|
||||||
|
// check first character
|
||||||
|
typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (std::tolower (*i, mLocale));
|
||||||
|
|
||||||
|
// no match, on to next character
|
||||||
|
if (candidate == mRoot.mChildren.end ())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// see how far the match goes
|
||||||
|
Point j = i;
|
||||||
|
|
||||||
|
while ((j + 1) != end)
|
||||||
|
{
|
||||||
|
typename Entry::childen_t::iterator next = candidate->second.mChildren.find (std::tolower (*++j, mLocale));
|
||||||
|
|
||||||
|
if (next == candidate->second.mChildren.end ())
|
||||||
|
break;
|
||||||
|
|
||||||
|
candidate = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// didn't match enough to disambiguate, on to next character
|
||||||
|
if (!candidate->second.mKeyword.size ())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// match the rest of the keyword
|
||||||
|
typename string_t::const_iterator t = candidate->second.mKeyword.begin () + (j - i);
|
||||||
|
|
||||||
|
while (j != end && t != candidate->second.mKeyword.end ())
|
||||||
|
{
|
||||||
|
if (std::tolower (*j, mLocale) != std::tolower (*t, mLocale))
|
||||||
|
break;
|
||||||
|
|
||||||
|
++j, ++t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// didn't match full keyword, on to next character
|
||||||
|
if (t != candidate->second.mKeyword.end ())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// we did it, report the good news
|
||||||
|
match.mValue = candidate->second.mValue;
|
||||||
|
match.mBeg = i;
|
||||||
|
match.mEnd = j;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no match in range, report the bad news
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
typedef std::map <wchar_t, Entry> childen_t;
|
||||||
|
|
||||||
|
string_t mKeyword;
|
||||||
|
value_t mValue;
|
||||||
|
childen_t mChildren;
|
||||||
|
};
|
||||||
|
|
||||||
|
void seed_impl (string_t keyword, value_t value, size_t depth, Entry & entry)
|
||||||
|
{
|
||||||
|
int ch = tolower (keyword.at (depth), mLocale);
|
||||||
|
|
||||||
|
typename Entry::childen_t::iterator j = entry.mChildren.find (ch);
|
||||||
|
|
||||||
|
if (j == entry.mChildren.end ())
|
||||||
|
{
|
||||||
|
entry.mChildren [ch].mValue = /*std::move*/ (value);
|
||||||
|
entry.mChildren [ch].mKeyword = /*std::move*/ (keyword);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (j->second.mKeyword.size () > 0)
|
||||||
|
{
|
||||||
|
if (keyword == j->second.mKeyword)
|
||||||
|
throw std::runtime_error ("duplicate keyword inserted");
|
||||||
|
|
||||||
|
value_t pushValue = /*std::move*/ (j->second.mValue);
|
||||||
|
string_t pushKeyword = /*std::move*/ (j->second.mKeyword);
|
||||||
|
|
||||||
|
j->second.mKeyword.clear ();
|
||||||
|
|
||||||
|
if (depth >= pushKeyword.size ())
|
||||||
|
throw std::runtime_error ("unexpected");
|
||||||
|
|
||||||
|
if (depth+1 < pushKeyword.size())
|
||||||
|
seed_impl (/*std::move*/ (pushKeyword), /*std::move*/ (pushValue), depth+1, j->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth+1 < keyword.size())
|
||||||
|
seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), depth+1, j->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry mRoot;
|
||||||
|
std::locale mLocale;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -108,7 +108,6 @@ namespace MWGui
|
||||||
mGui = mGuiManager->getGui();
|
mGui = mGuiManager->getGui();
|
||||||
|
|
||||||
//Register own widgets with MyGUI
|
//Register own widgets with MyGUI
|
||||||
MyGUI::FactoryManager::getInstance().registerFactory<DialogueHistory>("Widget");
|
|
||||||
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
|
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
|
||||||
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWAttribute>("Widget");
|
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWAttribute>("Widget");
|
||||||
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSpell>("Widget");
|
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSpell>("Widget");
|
||||||
|
|
|
@ -23,7 +23,6 @@ namespace Interpreter{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context){
|
std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context){
|
||||||
|
|
||||||
unsigned int start = 0;
|
unsigned int start = 0;
|
||||||
std::ostringstream retval;
|
std::ostringstream retval;
|
||||||
for(unsigned int i = 0; i < text.length(); i++){
|
for(unsigned int i = 0; i < text.length(); i++){
|
||||||
|
|
|
@ -5,14 +5,13 @@
|
||||||
|
|
||||||
<Widget type="Widget" skin="MW_Box" position="8 8 415 381" align="Stretch" name = "Client"/>
|
<Widget type="Widget" skin="MW_Box" position="8 8 415 381" align="Stretch" name = "Client"/>
|
||||||
|
|
||||||
<!-- The Dialogue history -->
|
<Widget type="Widget" position="13 13 391 371" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
|
||||||
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="13 13 405 371" name="History" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
|
<Widget type="BookPage" skin="MW_BookPage" position="0 0 391 371" name="History" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
|
||||||
<Property key="Static" value="true"/>
|
</Widget>
|
||||||
<Property key="WordWrap" value="true"/>
|
</Widget>
|
||||||
<Property key="MultiLine" value="1" />
|
|
||||||
<Property key="VisibleVScroll" value="1" />
|
<Widget type="ScrollBar" skin="MW_VScroll" position="404 13 14 371" align="ALIGN_RIGHT ALIGN_VSTRETCH" name="VScroll">
|
||||||
<!-- box for receiving mouse events -->
|
<Property key="Visible" value="false"/>
|
||||||
<Widget type="Widget" skin="" position="0 0 400 375" name="EventBox" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH"/>
|
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
<!-- The disposition bar-->
|
<!-- The disposition bar-->
|
||||||
|
|
Loading…
Reference in a new issue