1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-04-10 15:45:37 +00:00

Merge branch 'topicloop' into 'master'

Detect service refusal in constant time

See merge request OpenMW/openmw!3053
This commit is contained in:
psi29a 2023-05-23 08:20:55 +00:00
commit 30305d7bea
3 changed files with 40 additions and 52 deletions

View File

@ -159,12 +159,12 @@ namespace MWDialogue
Filter filter(actor, mChoice, mTalkedTo); Filter filter(actor, mChoice, mTalkedTo);
for (MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin(); it != dialogs.end(); ++it) for (const ESM::Dialogue& dialogue : dialogs)
{ {
if (it->mType == ESM::Dialogue::Greeting) if (dialogue.mType == ESM::Dialogue::Greeting)
{ {
// Search a response (we do not accept a fallback to "Info refusal" here) // Search a response (we do not accept a fallback to "Info refusal" here)
if (const ESM::DialInfo* info = filter.search(*it, false)) if (const ESM::DialInfo* info = filter.search(dialogue, false).second)
{ {
creatureStats.talkedToPlayer(); creatureStats.talkedToPlayer();
@ -176,7 +176,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(), mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(), mActor);
callback->addResponse({}, Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); callback->addResponse({}, Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript(info->mResultScript, mActor); executeScript(info->mResultScript, mActor);
mLastTopic = it->mId; mLastTopic = dialogue.mId;
addTopicsFromText(info->mResponse); addTopicsFromText(info->mResponse);
@ -291,11 +291,11 @@ namespace MWDialogue
const ESM::Dialogue& dialogue = *dialogues.find(topic); const ESM::Dialogue& dialogue = *dialogues.find(topic);
const ESM::DialInfo* info = filter.search(dialogue, true); const auto [responseTopic, info] = filter.search(dialogue, true);
if (info) if (info)
{ {
std::string title; std::string_view title;
if (dialogue.mType == ESM::Dialogue::Persuasion) if (dialogue.mType == ESM::Dialogue::Persuasion)
{ {
// Determine GMST from dialogue topic. GMSTs are: // Determine GMST from dialogue topic. GMSTs are:
@ -320,15 +320,8 @@ namespace MWDialogue
{ {
// Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info // 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. // refusal group, in which case it should not be added to the journal.
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); if (responseTopic == &dialogue)
iter != dialogue.mInfo.end(); ++iter) MWBase::Environment::get().getJournal()->addTopic(topic, info->mId, mActor);
{
if (iter->mId == info->mId)
{
MWBase::Environment::get().getJournal()->addTopic(topic, info->mId, mActor);
break;
}
}
} }
mLastTopic = topic; mLastTopic = topic;
@ -363,7 +356,7 @@ namespace MWDialogue
{ {
if (dialog.mType == ESM::Dialogue::Topic) if (dialog.mType == ESM::Dialogue::Topic)
{ {
const auto* answer = filter.search(dialog, true); const auto* answer = filter.search(dialog, true).second;
const auto& topicId = dialog.mId; const auto& topicId = dialog.mId;
if (answer != nullptr) if (answer != nullptr)
@ -477,9 +470,10 @@ namespace MWDialogue
if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting) if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting)
{ {
if (const ESM::DialInfo* info = filter.search(*dialogue, true)) const auto [responseTopic, info] = filter.search(*dialogue, true);
if (info)
{ {
std::string text = info->mResponse; const std::string& text = info->mResponse;
addTopicsFromText(text); addTopicsFromText(text);
mChoice = -1; mChoice = -1;
@ -493,15 +487,8 @@ namespace MWDialogue
{ {
// Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the // 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 // Info refusal group, in which case it should not be added to the journal
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin(); if (responseTopic == dialogue)
iter != dialogue->mInfo.end(); ++iter) MWBase::Environment::get().getJournal()->addTopic(mLastTopic, info->mId, mActor);
{
if (iter->mId == info->mId)
{
MWBase::Environment::get().getJournal()->addTopic(mLastTopic, info->mId, mActor);
break;
}
}
} }
executeScript(info->mResultScript, mActor); executeScript(info->mResultScript, mActor);
@ -609,10 +596,10 @@ namespace MWDialogue
const ESM::Dialogue& dialogue = *dialogues.find(ESM::RefId::stringRefId("Service Refusal")); const ESM::Dialogue& dialogue = *dialogues.find(ESM::RefId::stringRefId("Service Refusal"));
std::vector<const ESM::DialInfo*> infos = filter.list(dialogue, false, false, true); std::vector<Filter::Response> infos = filter.list(dialogue, false, false, true);
if (!infos.empty()) if (!infos.empty())
{ {
const ESM::DialInfo* info = infos[0]; const ESM::DialInfo* info = infos[0].second;
addTopicsFromText(info->mResponse); addTopicsFromText(info->mResponse);
@ -656,7 +643,7 @@ namespace MWDialogue
const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);
Filter filter(actor, 0, creatureStats.hasTalkedToPlayer()); Filter filter(actor, 0, creatureStats.hasTalkedToPlayer());
const ESM::DialInfo* info = filter.search(*dial, false); const ESM::DialInfo* info = filter.search(*dial, false).second;
if (info != nullptr) if (info != nullptr)
{ {
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
@ -697,10 +684,9 @@ namespace MWDialogue
ESM::DialogueState state; ESM::DialogueState state;
state.load(reader); state.load(reader);
for (std::vector<ESM::RefId>::const_iterator iter(state.mKnownTopics.begin()); for (const auto& knownTopic : state.mKnownTopics)
iter != state.mKnownTopics.end(); ++iter) if (store.get<ESM::Dialogue>().search(knownTopic))
if (store.get<ESM::Dialogue>().search(*iter)) mKnownTopics.insert(knownTopic);
mKnownTopics.insert(*iter);
mChangedFactionReaction = state.mChangedFactionReaction; mChangedFactionReaction = state.mChangedFactionReaction;
} }

View File

@ -678,12 +678,13 @@ MWDialogue::Filter::Filter(const MWWorld::Ptr& actor, int choice, bool talkedToP
{ {
} }
const ESM::DialInfo* MWDialogue::Filter::search(const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const MWDialogue::Filter::Response MWDialogue::Filter::search(
const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const
{ {
std::vector<const ESM::DialInfo*> suitableInfos = list(dialogue, fallbackToInfoRefusal, false); auto suitableInfos = list(dialogue, fallbackToInfoRefusal, false);
if (suitableInfos.empty()) if (suitableInfos.empty())
return nullptr; return {};
else else
return suitableInfos[0]; return suitableInfos[0];
} }
@ -693,22 +694,21 @@ bool MWDialogue::Filter::couldPotentiallyMatch(const ESM::DialInfo& info) const
return testActor(info) && matchesStaticFilters(info, mActor); return testActor(info) && matchesStaticFilters(info, mActor);
} }
std::vector<const ESM::DialInfo*> MWDialogue::Filter::list( std::vector<MWDialogue::Filter::Response> MWDialogue::Filter::list(
const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const
{ {
std::vector<const ESM::DialInfo*> infos; std::vector<MWDialogue::Filter::Response> infos;
bool infoRefusal = false; bool infoRefusal = false;
// Iterate over topic responses to find a matching one // Iterate over topic responses to find a matching one
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter != dialogue.mInfo.end(); for (const auto& info : dialogue.mInfo)
++iter)
{ {
if (testActor(*iter) && testPlayer(*iter) && testSelectStructs(*iter)) if (testActor(info) && testPlayer(info) && testSelectStructs(info))
{ {
if (testDisposition(*iter, invertDisposition)) if (testDisposition(info, invertDisposition))
{ {
infos.push_back(&*iter); infos.emplace_back(&dialogue, &info);
if (!searchAll) if (!searchAll)
break; break;
} }
@ -726,12 +726,11 @@ std::vector<const ESM::DialInfo*> MWDialogue::Filter::list(
const ESM::Dialogue& infoRefusalDialogue = *dialogues.find(ESM::RefId::stringRefId("Info Refusal")); const ESM::Dialogue& infoRefusalDialogue = *dialogues.find(ESM::RefId::stringRefId("Info Refusal"));
for (ESM::Dialogue::InfoContainer::const_iterator iter = infoRefusalDialogue.mInfo.begin(); for (const auto& info : infoRefusalDialogue.mInfo)
iter != infoRefusalDialogue.mInfo.end(); ++iter) if (testActor(info) && testPlayer(info) && testSelectStructs(info)
if (testActor(*iter) && testPlayer(*iter) && testSelectStructs(*iter) && testDisposition(info, invertDisposition))
&& testDisposition(*iter, invertDisposition))
{ {
infos.push_back(&*iter); infos.emplace_back(&dialogue, &info);
if (!searchAll) if (!searchAll)
break; break;
} }

View File

@ -1,6 +1,7 @@
#ifndef GAME_MWDIALOGUE_FILTER_H #ifndef GAME_MWDIALOGUE_FILTER_H
#define GAME_MWDIALOGUE_FILTER_H #define GAME_MWDIALOGUE_FILTER_H
#include <utility>
#include <vector> #include <vector>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -52,10 +53,12 @@ namespace MWDialogue
const MWWorld::Ptr& actor, const ESM::RefId& factionId, int rank) const; const MWWorld::Ptr& actor, const ESM::RefId& factionId, int rank) const;
public: public:
using Response = std::pair<const ESM::Dialogue*, const ESM::DialInfo*>;
Filter(const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); Filter(const MWWorld::Ptr& actor, int choice, bool talkedToPlayer);
std::vector<const ESM::DialInfo*> list(const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, std::vector<Response> list(const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, bool searchAll,
bool searchAll, bool invertDisposition = false) const; bool invertDisposition = false) const;
///< List all infos that could be used on the given actor, using the current runtime state of the actor. ///< List all infos that could be used on the given actor, using the current runtime state of the actor.
/// \note If fallbackToInfoRefusal is used, the returned DialInfo might not be from the supplied ESM::Dialogue. /// \note If fallbackToInfoRefusal is used, the returned DialInfo might not be from the supplied ESM::Dialogue.
@ -63,7 +66,7 @@ namespace MWDialogue
///< Check if this INFO could potentially be said by the given actor, ignoring runtime state filters and ///< Check if this INFO could potentially be said by the given actor, ignoring runtime state filters and
///< ignoring player filters. ///< ignoring player filters.
const ESM::DialInfo* search(const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; Response search(const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;
///< Get a matching response for the requested dialogue. ///< Get a matching response for the requested dialogue.
/// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition. /// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition.
}; };