1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 06:49:55 +00:00
openmw-tes3mp/apps/openmw/mwdialogue/dialoguemanagerimp.cpp
scrawl 29556a1802 More consistent wording of errors/warnings
A Warning indicates a potential problem in the content file(s) that the user told OpenMW to load. E.g. this might cause an object to not display at all or as intended, however the rest of the game will run fine.

An Error, however, is more likely to be a bug with the engine itself - it means that basic assumptions have been violated and the engine might not run correctly anymore.

The above mostly applies to errors/warnings during game-play; startup issues are handled differently: when a file is completely invalid/corrupted to the point that the engine can not start, that might cause messages that are worded as Error due to the severity of the issue but are not necessarily the engine's fault.

Hopefully, being a little more consistent here will alleviate confusion among users as to when a log message should be reported and to whom.
2017-03-04 21:48:31 +01:00

748 lines
27 KiB
C++

#include "dialoguemanagerimp.hpp"
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <list>
#include <components/esm/loaddial.hpp>
#include <components/esm/loadinfo.hpp>
#include <components/esm/dialoguestate.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/compiler/exception.hpp>
#include <components/compiler/errorhandler.hpp>
#include <components/compiler/scanner.hpp>
#include <components/compiler/locals.hpp>
#include <components/compiler/output.hpp>
#include <components/compiler/scriptparser.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/defines.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwgui/dialogue.hpp"
#include "../mwscript/compilercontext.hpp"
#include "../mwscript/interpretercontext.hpp"
#include "../mwscript/extensions.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "filter.hpp"
#include "hypertextparser.hpp"
namespace MWDialogue
{
DialogueManager::DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage) :
mTranslationDataStorage(translationDataStorage)
, mCompilerContext (MWScript::CompilerContext::Type_Dialogue)
, mErrorStream(std::cout.rdbuf())
, mErrorHandler(mErrorStream)
, mTalkedTo(false)
, mTemporaryDispositionChange(0.f)
, mPermanentDispositionChange(0.f)
{
mChoice = -1;
mIsInChoice = false;
mCompilerContext.setExtensions (&extensions);
const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin();
for (; it != dialogs.end(); ++it)
{
mDialogueMap[Misc::StringUtils::lowerCase(it->mId)] = *it;
}
}
void DialogueManager::clear()
{
mKnownTopics.clear();
mTalkedTo = false;
mTemporaryDispositionChange = 0;
mPermanentDispositionChange = 0;
}
void DialogueManager::addTopic (const std::string& topic)
{
mKnownTopics.insert( Misc::StringUtils::lowerCase(topic) );
}
void DialogueManager::parseText (const std::string& text)
{
std::vector<HyperTextParser::Token> hypertext = HyperTextParser::parseHyperText(text);
for (std::vector<HyperTextParser::Token>::iterator tok = hypertext.begin(); tok != hypertext.end(); ++tok)
{
std::string topicId = Misc::StringUtils::lowerCase(tok->mText);
if (tok->isExplicitLink())
{
// calculation of standard form for all hyperlinks
size_t asterisk_count = HyperTextParser::removePseudoAsterisks(topicId);
for(; asterisk_count > 0; --asterisk_count)
topicId.append("*");
topicId = mTranslationDataStorage.topicStandardForm(topicId);
}
if (tok->isImplicitKeyword() && mTranslationDataStorage.hasTranslation())
continue;
if (mActorKnownTopics.count( topicId ))
mKnownTopics.insert( topicId );
}
updateTopics();
}
void DialogueManager::startDialogue (const MWWorld::Ptr& actor)
{
updateGlobals();
// Dialogue with dead actor (e.g. through script) should not be allowed.
if (actor.getClass().getCreatureStats(actor).isDead())
return;
mLastTopic = "";
mPermanentDispositionChange = 0;
mTemporaryDispositionChange = 0;
mChoice = -1;
mIsInChoice = false;
mActor = actor;
MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats (actor);
mTalkedTo = creatureStats.hasTalkedToPlayer();
mActorKnownTopics.clear();
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
// If the dialogue window was already open, keep the existing history
bool resetHistory = (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue));
win->startDialogue(actor, actor.getClass().getName (actor), resetHistory);
//greeting
const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
Filter filter (actor, mChoice, mTalkedTo);
for (MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin(); it != dialogs.end(); ++it)
{
if(it->mType == ESM::Dialogue::Greeting)
{
// Search a response (we do not accept a fallback to "Info refusal" here)
if (const ESM::DialInfo *info = filter.search (*it, false))
{
//initialise the GUI
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue);
creatureStats.talkedToPlayer();
if (!info->mSound.empty())
{
// TODO play sound
}
// first topics update so that parseText knows the keywords to highlight
updateTopics();
parseText (info->mResponse);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript);
mLastTopic = Misc::StringUtils::lowerCase(it->mId);
// update topics again to accommodate changes resulting from executeScript
updateTopics();
return;
}
}
}
// No greetings found. The dialogue window should not be shown.
// If this is a companion, we must show the companion window directly (used by BM_bear_be_unique).
bool isCompanion = !mActor.getClass().getScript(mActor).empty()
&& mActor.getRefData().getLocals().getIntVar(mActor.getClass().getScript(mActor), "companion");
if (isCompanion)
MWBase::Environment::get().getWindowManager()->showCompanionWindow(mActor);
}
bool DialogueManager::compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code)
{
bool success = true;
try
{
mErrorHandler.reset();
mErrorHandler.setContext("[dialogue script]");
std::istringstream input (cmd + "\n");
Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions());
Compiler::Locals locals;
std::string actorScript = mActor.getClass().getScript (mActor);
if (!actorScript.empty())
{
// grab local variables from actor's script, if available.
locals = MWBase::Environment::get().getScriptManager()->getLocals (actorScript);
}
Compiler::ScriptParser parser(mErrorHandler,mCompilerContext, locals, false);
scanner.scan (parser);
if (!mErrorHandler.isGood())
success = false;
if (success)
parser.getCode (code);
}
catch (const Compiler::SourceException& /* error */)
{
// error has already been reported via error handler
success = false;
}
catch (const std::exception& error)
{
std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl;
success = false;
}
if (!success)
{
std::cerr
<< "Warning: compiling failed (dialogue script)" << std::endl
<< cmd
<< std::endl << std::endl;
}
return success;
}
void DialogueManager::executeScript (const std::string& script)
{
std::vector<Interpreter::Type_Code> code;
if(compile(script,code))
{
try
{
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
Interpreter::Interpreter interpreter;
MWScript::installOpcodes (interpreter);
interpreter.run (&code[0], code.size(), interpreterContext);
}
catch (const std::exception& error)
{
std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl;
}
}
}
void DialogueManager::executeTopic (const std::string& topic)
{
Filter filter (mActor, mChoice, mTalkedTo);
const MWWorld::Store<ESM::Dialogue> &dialogues =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
const ESM::Dialogue& dialogue = *dialogues.find (topic);
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
const ESM::DialInfo* info = filter.search(dialogue, true);
if (info)
{
parseText (info->mResponse);
std::string title;
if (dialogue.mType==ESM::Dialogue::Persuasion)
{
// Determine GMST from dialogue topic. GMSTs are:
// sAdmireSuccess, sAdmireFail, sIntimidateSuccess, sIntimidateFail,
// sTauntSuccess, sTauntFail, sBribeSuccess, sBribeFail
std::string modifiedTopic = "s" + topic;
modifiedTopic.erase (std::remove (modifiedTopic.begin(), modifiedTopic.end(), ' '), modifiedTopic.end());
const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
title = gmsts.find (modifiedTopic)->getString();
}
else
title = topic;
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title);
if (dialogue.mType == ESM::Dialogue::Topic)
{
// Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group,
// in which case it should not be added to the journal.
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin();
iter!=dialogue.mInfo.end(); ++iter)
{
if (iter->mId == info->mId)
{
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor);
break;
}
}
}
executeScript (info->mResultScript);
mLastTopic = topic;
}
else
{
// no response found, print a fallback text
win->addResponse ("", topic);
}
}
void DialogueManager::updateGlobals()
{
MWBase::Environment::get().getWorld()->updateDialogueGlobals();
}
void DialogueManager::updateTopics()
{
updateGlobals();
std::list<std::string> keywordList;
int choice = mChoice;
mChoice = -1;
mActorKnownTopics.clear();
const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
Filter filter (mActor, mChoice, mTalkedTo);
for (MWWorld::Store<ESM::Dialogue>::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter)
{
if (iter->mType == ESM::Dialogue::Topic)
{
if (filter.responseAvailable (*iter))
{
std::string lower = Misc::StringUtils::lowerCase(iter->mId);
mActorKnownTopics.insert (lower);
//does the player know the topic?
if (mKnownTopics.count(lower))
{
keywordList.push_back (iter->mId);
}
}
}
}
// check the available services of this actor
int services = 0;
if (mActor.getTypeName() == typeid(ESM::NPC).name())
{
MWWorld::LiveCellRef<ESM::NPC>* ref = mActor.get<ESM::NPC>();
if (ref->mBase->mHasAI)
services = ref->mBase->mAiData.mServices;
}
else if (mActor.getTypeName() == typeid(ESM::Creature).name())
{
MWWorld::LiveCellRef<ESM::Creature>* ref = mActor.get<ESM::Creature>();
if (ref->mBase->mHasAI)
services = ref->mBase->mAiData.mServices;
}
int windowServices = 0;
if (services & ESM::NPC::Weapon
|| services & ESM::NPC::Armor
|| services & ESM::NPC::Clothing
|| services & ESM::NPC::Books
|| services & ESM::NPC::Ingredients
|| services & ESM::NPC::Picks
|| services & ESM::NPC::Probes
|| services & ESM::NPC::Lights
|| services & ESM::NPC::Apparatus
|| services & ESM::NPC::RepairItem
|| services & ESM::NPC::Misc)
windowServices |= MWGui::DialogueWindow::Service_Trade;
if((mActor.getTypeName() == typeid(ESM::NPC).name() && !mActor.get<ESM::NPC>()->mBase->getTransport().empty())
|| (mActor.getTypeName() == typeid(ESM::Creature).name() && !mActor.get<ESM::Creature>()->mBase->getTransport().empty()))
windowServices |= MWGui::DialogueWindow::Service_Travel;
if (services & ESM::NPC::Spells)
windowServices |= MWGui::DialogueWindow::Service_BuySpells;
if (services & ESM::NPC::Spellmaking)
windowServices |= MWGui::DialogueWindow::Service_CreateSpells;
if (services & ESM::NPC::Training)
windowServices |= MWGui::DialogueWindow::Service_Training;
if (services & ESM::NPC::Enchanting)
windowServices |= MWGui::DialogueWindow::Service_Enchant;
if (services & ESM::NPC::Repair)
windowServices |= MWGui::DialogueWindow::Service_Repair;
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->setServices (windowServices);
// sort again, because the previous sort was case-sensitive
keywordList.sort(Misc::StringUtils::ciLess);
win->setKeywords(keywordList);
mChoice = choice;
}
void DialogueManager::keywordSelected (const std::string& keyword)
{
if(!mIsInChoice)
{
if(mDialogueMap.find(keyword) != mDialogueMap.end())
{
if (mDialogueMap[keyword].mType == ESM::Dialogue::Topic)
{
executeTopic (keyword);
}
}
}
updateTopics();
}
bool DialogueManager::isInChoice() const
{
return mIsInChoice;
}
void DialogueManager::goodbyeSelected()
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
// Apply disposition change to NPC's base disposition
if (mActor.getClass().isNpc())
{
// Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate)
float curDisp = static_cast<float>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false));
if (curDisp + mPermanentDispositionChange < 0)
mPermanentDispositionChange = -curDisp;
MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor);
npcStats.setBaseDisposition(static_cast<int>(npcStats.getBaseDisposition() + mPermanentDispositionChange));
}
mPermanentDispositionChange = 0;
mTemporaryDispositionChange = 0;
}
void DialogueManager::questionAnswered (int answer)
{
mChoice = answer;
if (mDialogueMap.find(mLastTopic) != mDialogueMap.end())
{
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))
{
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));
// Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group,
// in which case it should not be added to the journal.
for (ESM::Dialogue::InfoContainer::const_iterator iter = mDialogueMap[mLastTopic].mInfo.begin();
iter!=mDialogueMap[mLastTopic].mInfo.end(); ++iter)
{
if (iter->mId == info->mId)
{
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor);
break;
}
}
executeScript (info->mResultScript);
}
else
{
mChoice = -1;
mIsInChoice = false;
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices();
}
}
}
updateTopics();
}
void DialogueManager::askQuestion (const std::string& question, int choice)
{
mIsInChoice = true;
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->addChoice(question, choice);
}
void DialogueManager::goodbye()
{
mIsInChoice = true;
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->goodbye();
}
void DialogueManager::persuade(int type)
{
bool success;
float temp, perm;
MWBase::Environment::get().getMechanicsManager()->getPersuasionDispositionChange(
mActor, MWBase::MechanicsManager::PersuasionType(type),
success, temp, perm);
mTemporaryDispositionChange += temp;
mPermanentDispositionChange += perm;
// change temp disposition so that final disposition is between 0...100
float curDisp = static_cast<float>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false));
if (curDisp + mTemporaryDispositionChange < 0)
mTemporaryDispositionChange = -curDisp;
else if (curDisp + mTemporaryDispositionChange > 100)
mTemporaryDispositionChange = 100 - curDisp;
MWWorld::Ptr player = MWMechanics::getPlayer();
player.getClass().skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1);
if (success)
{
int gold=0;
if (type == MWBase::MechanicsManager::PT_Bribe10)
gold = 10;
else if (type == MWBase::MechanicsManager::PT_Bribe100)
gold = 100;
else if (type == MWBase::MechanicsManager::PT_Bribe1000)
gold = 1000;
if (gold)
{
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, gold, player);
mActor.getClass().getContainerStore(mActor).add(MWWorld::ContainerStore::sGoldId, gold, mActor);
}
}
std::string text;
if (type == MWBase::MechanicsManager::PT_Admire)
text = "Admire";
else if (type == MWBase::MechanicsManager::PT_Taunt)
text = "Taunt";
else if (type == MWBase::MechanicsManager::PT_Intimidate)
text = "Intimidate";
else{
text = "Bribe";
}
executeTopic (text + (success ? " Success" : " Fail"));
}
int DialogueManager::getTemporaryDispositionChange() const
{
return static_cast<int>(mTemporaryDispositionChange);
}
void DialogueManager::applyDispositionChange(int delta)
{
mTemporaryDispositionChange += delta;
}
bool DialogueManager::checkServiceRefused()
{
Filter filter (mActor, mChoice, mTalkedTo);
const MWWorld::Store<ESM::Dialogue> &dialogues =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal");
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, false, false, true);
if (!infos.empty())
{
const ESM::DialInfo* info = infos[0];
parseText (info->mResponse);
const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext),
gmsts.find ("sServiceRefusal")->getString());
executeScript (info->mResultScript);
return true;
}
return false;
}
void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic) const
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(!sndMgr->sayDone(actor))
{
// Actor is already saying something.
return;
}
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Dialogue *dial = store.get<ESM::Dialogue>().find(topic);
const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);
Filter filter(actor, 0, creatureStats.hasTalkedToPlayer());
const ESM::DialInfo *info = filter.search(*dial, false);
if(info != NULL)
{
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
if(winMgr->getSubtitlesEnabled())
winMgr->messageBox(info->mResponse);
if (!info->mSound.empty())
sndMgr->say(actor, info->mSound);
}
}
int DialogueManager::countSavedGameRecords() const
{
return 1; // known topics
}
void DialogueManager::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
{
ESM::DialogueState state;
for (std::set<std::string>::const_iterator iter (mKnownTopics.begin());
iter!=mKnownTopics.end(); ++iter)
{
state.mKnownTopics.push_back (*iter);
}
state.mChangedFactionReaction = mChangedFactionReaction;
writer.startRecord (ESM::REC_DIAS);
state.save (writer);
writer.endRecord (ESM::REC_DIAS);
}
void DialogueManager::readRecord (ESM::ESMReader& reader, uint32_t type)
{
if (type==ESM::REC_DIAS)
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
ESM::DialogueState state;
state.load (reader);
for (std::vector<std::string>::const_iterator iter (state.mKnownTopics.begin());
iter!=state.mKnownTopics.end(); ++iter)
if (store.get<ESM::Dialogue>().search (*iter))
mKnownTopics.insert (*iter);
mChangedFactionReaction = state.mChangedFactionReaction;
}
}
void DialogueManager::modFactionReaction(const std::string &faction1, const std::string &faction2, int diff)
{
std::string fact1 = Misc::StringUtils::lowerCase(faction1);
std::string fact2 = Misc::StringUtils::lowerCase(faction2);
// Make sure the factions exist
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2);
int newValue = getFactionReaction(faction1, faction2) + diff;
std::map<std::string, int>& map = mChangedFactionReaction[fact1];
map[fact2] = newValue;
}
void DialogueManager::setFactionReaction(const std::string &faction1, const std::string &faction2, int absolute)
{
std::string fact1 = Misc::StringUtils::lowerCase(faction1);
std::string fact2 = Misc::StringUtils::lowerCase(faction2);
// Make sure the factions exist
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2);
std::map<std::string, int>& map = mChangedFactionReaction[fact1];
map[fact2] = absolute;
}
int DialogueManager::getFactionReaction(const std::string &faction1, const std::string &faction2) const
{
std::string fact1 = Misc::StringUtils::lowerCase(faction1);
std::string fact2 = Misc::StringUtils::lowerCase(faction2);
ModFactionReactionMap::const_iterator map = mChangedFactionReaction.find(fact1);
if (map != mChangedFactionReaction.end() && map->second.find(fact2) != map->second.end())
return map->second.at(fact2);
const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);
std::map<std::string, int>::const_iterator it = faction->mReactions.begin();
for (; it != faction->mReactions.end(); ++it)
{
if (Misc::StringUtils::ciEqual(it->first, fact2))
return it->second;
}
return 0;
}
void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const
{
if (actor == mActor && !mLastTopic.empty())
{
MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse(
mLastTopic, actor.getClass().getName(actor));
}
}
}