Dialogue history rewrite WIP

actorid
scrawl 12 years ago
parent 6cd28d1156
commit 78e6dab9d2

@ -32,6 +32,7 @@ add_openmw_dir (mwgui
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
keywordsearch
)
add_openmw_dir (mwdialogue

@ -41,7 +41,7 @@ namespace MWBase
//calbacks for the GUI
virtual void keywordSelected (const std::string& keyword) = 0;
virtual void goodbyeSelected() = 0;
virtual void questionAnswered (const std::string& answer) = 0;
virtual void questionAnswered (int answer) = 0;
virtual bool checkServiceRefused () = 0;

@ -161,7 +161,7 @@ namespace MWDialogue
parseText (info->mResponse);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript);
mLastTopic = Misc::StringUtils::lowerCase(it->mId);
mLastDialogue = *info;
@ -263,6 +263,7 @@ namespace MWDialogue
parseText (info->mResponse);
std::string title;
if (dialogue.mType==ESM::Dialogue::Persuasion)
{
std::string modifiedTopic = "s" + topic;
@ -272,13 +273,13 @@ namespace MWDialogue
const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
win->addTitle (gmsts.find (modifiedTopic)->getString());
title = gmsts.find (modifiedTopic)->getString();
}
else
win->addTitle (topic);
title = topic;
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);
executeScript (info->mResultScript);
@ -289,9 +290,7 @@ namespace MWDialogue
else
{
// no response found, print a fallback text
win->addTitle (topic);
win->addText ("");
win->addResponse ("", topic);
}
}
@ -424,53 +423,42 @@ namespace MWDialogue
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 (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic
|| mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting)
if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true))
{
if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true))
{
std::string text = info->mResponse;
parseText (text);
mChoiceMap.clear();
mChoice = -1;
mIsInChoice = false;
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext));
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
executeScript (info->mResultScript);
mLastDialogue = *info;
}
std::string text = info->mResponse;
parseText (text);
mChoice = -1;
mIsInChoice = false;
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices();
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext));
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
executeScript (info->mResultScript);
mLastDialogue = *info;
}
}
updateTopics();
}
}
void DialogueManager::printError (const std::string& error)
{
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->addText(error);
updateTopics();
}
void DialogueManager::askQuestion (const std::string& question, int choice)
{
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->askQuestion(question);
mChoiceMap[Misc::StringUtils::lowerCase(question)] = choice;
win->addChoice(question, choice);
mIsInChoice = true;
}
@ -551,10 +539,10 @@ namespace MWDialogue
const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
win->addTitle (gmsts.find ("sServiceRefusal")->getString());
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);
return true;
@ -565,9 +553,7 @@ namespace MWDialogue
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{
std::vector<HyperTextToken> result;
MyGUI::UString utext(text);
size_t pos_begin, pos_end, iteration_pos = 0;
for(;;)
{

@ -30,7 +30,6 @@ namespace MWDialogue
bool mTalkedTo;
int mChoice;
std::map<std::string, int> mChoiceMap;
std::string mLastTopic;
ESM::DialInfo mLastDialogue;
bool mIsInChoice;
@ -46,8 +45,6 @@ namespace MWDialogue
bool compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code);
void executeScript (const std::string& script);
void printError (const std::string& error);
void executeTopic (const std::string& topic, bool randomResponse=false);
public:
@ -72,7 +69,7 @@ namespace MWDialogue
//calbacks for the GUI
virtual void keywordSelected (const std::string& keyword);
virtual void goodbyeSelected();
virtual void questionAnswered (const std::string& answer);
virtual void questionAnswered (int answer);
virtual void persuade (int type);
virtual int getTemporaryDispositionChange () const;

@ -120,7 +120,7 @@ struct MWGui::TypesetBookImpl : TypesetBook
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 ());
}

@ -27,7 +27,7 @@ namespace MWGui
/// it is the largest distance from the left edge to the
/// right edge. The second integer is the height of all
/// 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.

@ -18,29 +18,18 @@
#include "spellbuyingwindow.hpp"
#include "inventorywindow.hpp"
#include "travelwindow.hpp"
#include "bookpage.hpp"
/**
*Copied from the internet.
*/
namespace
{
std::string lower_string(const std::string& str)
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
{
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 lower_string(str).find(lower_string(substr),pos);
}
bool sortByLength (const std::string& left, const std::string& right)
{
return left.size() > right.size();
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
}
}
@ -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()
: WindowBase("openmw_dialogue_window.layout")
, mPersuasionDialog()
@ -129,15 +215,6 @@ namespace MWGui
//History view
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
getWidget(mTopicsList, "TopicsList");
@ -149,12 +226,20 @@ namespace MWGui
getWidget(mDispositionBar, "Disposition");
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);
}
void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
{
/*
MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText();
if(t == NULL)
return;
@ -198,19 +283,22 @@ namespace MWGui
if(color == "#572D21")
MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key));
}
*/
}
void DialogueWindow::onWindowResize(MyGUI::Window* _sender)
{
mTopicsList->adjustSize();
updateHistory();
}
void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)
{
if (mHistory->getVScrollPosition() - _rel*0.3 < 0)
mHistory->setVScrollPosition(0);
else
mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3);
if (!mScrollBar->getVisible())
return;
mScrollBar->setScrollPosition(std::min(static_cast<int>(mScrollBar->getScrollRange()-1),
std::max(0, static_cast<int>(mScrollBar->getScrollPosition() - _rel*0.3))));
onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition());
}
void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
@ -231,7 +319,7 @@ namespace MWGui
}
if (id >= separatorPos)
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic));
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic));
else
{
const MWWorld::Store<ESM::GameSetting> &gmst =
@ -296,13 +384,25 @@ namespace MWGui
mTopicsList->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();
}
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
{
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()
&& 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)
{
mTopicsList->addItem(*it);
}
mTopicsList->adjustSize();
}
void DialogueWindow::removeKeyword(std::string keyWord)
{
if(mTopicsList->hasItem(keyWord))
{
mTopicsList->removeItem(keyWord);
Topic* t = new Topic(*it);
mTopicLinks[*it] = t;
mKeywordSearch.seed(*it, intptr_t(t));
}
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();
}
updateHistory();
}
std::string DialogueWindow::parseText(const std::string& text)
@ -410,18 +479,19 @@ namespace MWGui
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);
/*
size_t historySize = 0;
if(mHistory->getClient()->getSubWidgetText() != NULL)
{
historySize = mHistory->getOnlyText().size();
}
*/
std::string result;
/*
size_t hypertextPos = 0;
for (size_t i = 0; i < hypertext.size(); ++i)
{
@ -461,38 +531,115 @@ namespace MWGui
hypertextPos += MyGUI::UString(hypertext[i].mText).length();
}
*/
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::addMessageBox(const std::string& text)
void DialogueWindow::notifyLinkClicked (TypesetBook::InteractiveId link)
{
mHistory->addDialogText("\n#FFFFFF"+text+"#B29154");
reinterpret_cast<Link*>(link)->activated();
}
void DialogueWindow::addTitle(std::string text)
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 text through the topic list.
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
// of the title through the topic list.
std::string realTitle = title;
if (realTitle != "")
{
std::string item = mTopicsList->getItemNameAt(i);
if (lower_string(item) == text)
text = item;
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
{
std::string item = mTopicsList->getItemNameAt(i);
if (Misc::StringUtils::lowerCase(item) == title)
{
realTitle = item;
break;
}
}
}
mHistory->addDialogHeading(text);
mHistoryContents.push_back(new Response(text, realTitle));
updateHistory();
}
void DialogueWindow::addMessageBox(const std::string& text)
{
//mHistoryContents.push_back(new Message(text));
updateHistory();
}
void DialogueWindow::addChoice(const std::string& choice, int id)
{
mChoices[choice] = id;
updateHistory();
}
void DialogueWindow::askQuestion(std::string question)
void DialogueWindow::clearChoices()
{
mHistory->addDialogText("#572D21"+question+"#B29154"+" ");
mChoices.clear();
updateHistory();
}
void DialogueWindow::updateOptions()
@ -500,7 +647,6 @@ namespace MWGui
//Clear the list of topics
mTopicsList->clear();
mHyperLinks.clear();
mHistory->eraseText(0, mHistory->getTextLength());
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
{
@ -513,7 +659,7 @@ namespace MWGui
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);
mEnabled = false;
}

@ -4,6 +4,10 @@
#include "windowbase.hpp"
#include "referenceinterface.hpp"
#include "bookpage.hpp"
#include "keywordsearch.hpp"
namespace MWGui
{
class WindowManager;
@ -21,7 +25,8 @@ namespace MWGui
namespace MWGui
{
class DialogueHistory;
class DialogueHistoryViewModel;
class BookPage;
class PersuasionDialog : public WindowModal
{
@ -44,6 +49,55 @@ namespace MWGui
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
{
public:
@ -52,19 +106,19 @@ namespace MWGui
// Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Dialog finished, OK button clicked.\n
signature : void method()\n
*/
EventHandle_Void eventBye;
void notifyLinkClicked (TypesetBook::InteractiveId link);
void startDialogue(MWWorld::Ptr actor, std::string npcName);
void stopDialogue();
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 addTitle(std::string text);
void askQuestion(std::string question);
void addChoice(const std::string& choice, int id);
void clearChoices();
void goodbye();
void onFrame();
@ -89,6 +143,10 @@ namespace MWGui
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
void onWindowResize(MyGUI::Window* _sender);
void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos);
void updateHistory(bool scrollbar=false);
virtual void onReferenceUnavailable();
struct HyperLink
@ -108,11 +166,22 @@ namespace MWGui
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;
MyGUI::ScrollBar* mScrollBar;
MyGUI::ProgressPtr mDispositionBar;
MyGUI::EditBox* mDispositionText;
std::stringstream mText;
PersuasionDialog mPersuasionDialog;
std::map<size_t, HyperLink> mHyperLinks;

@ -1,78 +1,11 @@
#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
{
MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos)
{
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");
}
DialogueHistoryViewModel::DialogueHistoryViewModel()
{
}
}

@ -1,19 +1,23 @@
#ifndef MWGUI_DIALOGE_HISTORY_H
#define MWGUI_DIALOGE_HISTORY_H
#include <openengine/gui/layout.hpp>
#include "keywordsearch.hpp"
#include <platform/stdint.h>
namespace MWGui
{
class DialogueHistory : public MyGUI::EditBox
class DialogueHistoryViewModel
{
MYGUI_RTTI_DERIVED( DialogueHistory )
public:
Widget* getClient() { return mClient; }
MyGUI::UString getColorAtPos(size_t _pos);
MyGUI::UString getColorTextAt(size_t _pos);
void addDialogHeading(const MyGUI::UString& parText);
void addDialogText(const MyGUI::UString& parText);
public:
DialogueHistoryViewModel();
private:
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
mutable bool mKeywordSearchLoaded;
mutable KeywordSearchT mKeywordSearch;
};
}
#endif

@ -1,17 +1,20 @@
#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/journal.hpp"
#include "../mwbase/environment.hpp"
#include "../mwdialogue/journalentry.hpp"
#include <MyGUI_LanguageManager.h>
#include <components/misc/utf8stream.hpp>
#include "keywordsearch.hpp"
#include <map>
#include <sstream>
#include <boost/make_shared.hpp>
using namespace MWGui;
@ -19,143 +22,12 @@ namespace MWGui { struct JournalViewModelImpl; }
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
{
typedef KeywordSearch <std::string, intptr_t> keyword_search_t;
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
mutable bool mKeywordSearchLoaded;
mutable keyword_search_t mKeywordSearch;
mutable KeywordSearchT mKeywordSearch;
std::locale mLocale;
@ -253,7 +125,7 @@ struct MWGui::JournalViewModelImpl : JournalViewModel
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))
{
@ -300,8 +172,8 @@ struct MWGui::JournalViewModelImpl : JournalViewModel
mutable std::string timestamp_buffer;
JournalEntryImpl (JournalViewModelImpl const * Model, iterator_t itr) :
BaseEntry <iterator_t, JournalEntry> (Model, itr)
JournalEntryImpl (JournalViewModelImpl const * model, iterator_t itr) :
BaseEntry <iterator_t, JournalEntry> (model, itr)
{}
std::string getText () const

@ -152,6 +152,10 @@ namespace
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;
if (mModel->isEmpty ())
journalBook = createEmptyJournalBook ();

@ -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();
//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::MWAttribute>("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){
unsigned int start = 0;
std::ostringstream retval;
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"/>
<!-- The Dialogue history -->
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="13 13 405 371" name="History" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
<Property key="Static" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="MultiLine" value="1" />
<Property key="VisibleVScroll" value="1" />
<!-- box for receiving mouse events -->
<Widget type="Widget" skin="" position="0 0 400 375" name="EventBox" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH"/>
<Widget type="Widget" position="13 13 391 371" 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">
</Widget>
</Widget>
<Widget type="ScrollBar" skin="MW_VScroll" position="404 13 14 371" align="ALIGN_RIGHT ALIGN_VSTRETCH" name="VScroll">
<Property key="Visible" value="false"/>
</Widget>
<!-- The disposition bar-->

Loading…
Cancel
Save