diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 6def8b4a42..83b4a486e1 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -1,5 +1,6 @@ #include "labels.hpp" +#include #include #include #include @@ -572,13 +573,14 @@ std::string_view enchantTypeLabel(int idx) std::string_view ruleFunction(int idx) { - if (idx >= 0 && idx <= 72) + if (idx >= ESM::DialogueCondition::Function_FacReactionLowest + && idx <= ESM::DialogueCondition::Function_PcWerewolfKills) { static constexpr std::string_view ruleFunctions[] = { - "Reaction Low", - "Reaction High", + "Lowest Faction Reaction", + "Highest Faction Reaction", "Rank Requirement", - "NPC? Reputation", + "NPC Reputation", "Health Percent", "Player Reputation", "NPC Level", @@ -648,6 +650,7 @@ std::string_view ruleFunction(int idx) "Flee", "Should Attack", "Werewolf", + "Werewolf Kills", }; return ruleFunctions[idx]; } diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 4526b891e2..cd38dadf3f 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -57,112 +57,82 @@ namespace std::cout << " Cell Name: " << p.mCellName << std::endl; } - std::string ruleString(const ESM::DialInfo::SelectStruct& ss) + std::string ruleString(const ESM::DialogueCondition& ss) { - std::string rule = ss.mSelectRule; + std::string_view type_str = "INVALID"; + std::string_view func_str; - if (rule.length() < 5) - return "INVALID"; - - char type = rule[1]; - char indicator = rule[2]; - - std::string type_str = "INVALID"; - std::string func_str = Misc::StringUtils::format("INVALID=%s", rule.substr(1, 3)); - int func = Misc::StringUtils::toNumeric(rule.substr(2, 2), 0); - - switch (type) + switch (ss.mFunction) { - case '1': - type_str = "Function"; - func_str = std::string(ruleFunction(func)); + case ESM::DialogueCondition::Function_Global: + type_str = "Global"; + func_str = ss.mVariable; break; - case '2': - if (indicator == 's') - type_str = "Global short"; - else if (indicator == 'l') - type_str = "Global long"; - else if (indicator == 'f') - type_str = "Global float"; + case ESM::DialogueCondition::Function_Local: + type_str = "Local"; + func_str = ss.mVariable; break; - case '3': - if (indicator == 's') - type_str = "Local short"; - else if (indicator == 'l') - type_str = "Local long"; - else if (indicator == 'f') - type_str = "Local float"; + case ESM::DialogueCondition::Function_Journal: + type_str = "Journal"; + func_str = ss.mVariable; break; - case '4': - if (indicator == 'J') - type_str = "Journal"; + case ESM::DialogueCondition::Function_Item: + type_str = "Item count"; + func_str = ss.mVariable; break; - case '5': - if (indicator == 'I') - type_str = "Item type"; + case ESM::DialogueCondition::Function_Dead: + type_str = "Dead"; + func_str = ss.mVariable; break; - case '6': - if (indicator == 'D') - type_str = "NPC Dead"; + case ESM::DialogueCondition::Function_NotId: + type_str = "Not ID"; + func_str = ss.mVariable; break; - case '7': - if (indicator == 'X') - type_str = "Not ID"; + case ESM::DialogueCondition::Function_NotFaction: + type_str = "Not Faction"; + func_str = ss.mVariable; break; - case '8': - if (indicator == 'F') - type_str = "Not Faction"; + case ESM::DialogueCondition::Function_NotClass: + type_str = "Not Class"; + func_str = ss.mVariable; break; - case '9': - if (indicator == 'C') - type_str = "Not Class"; + case ESM::DialogueCondition::Function_NotRace: + type_str = "Not Race"; + func_str = ss.mVariable; break; - case 'A': - if (indicator == 'R') - type_str = "Not Race"; + case ESM::DialogueCondition::Function_NotCell: + type_str = "Not Cell"; + func_str = ss.mVariable; break; - case 'B': - if (indicator == 'L') - type_str = "Not Cell"; - break; - case 'C': - if (indicator == 's') - type_str = "Not Local"; + case ESM::DialogueCondition::Function_NotLocal: + type_str = "Not Local"; + func_str = ss.mVariable; break; default: + type_str = "Function"; + func_str = ruleFunction(ss.mFunction); break; } - // Append the variable name to the function string if any. - if (type != '1') - func_str = rule.substr(5); - - // In the previous switch, we assumed that the second char was X - // for all types not qual to one. If this wasn't true, go back to - // the error message. - if (type != '1' && rule[3] != 'X') - func_str = Misc::StringUtils::format("INVALID=%s", rule.substr(1, 3)); - - char oper = rule[4]; - std::string oper_str = "??"; - switch (oper) + std::string_view oper_str = "??"; + switch (ss.mComparison) { - case '0': + case ESM::DialogueCondition::Comp_Eq: oper_str = "=="; break; - case '1': + case ESM::DialogueCondition::Comp_Ne: oper_str = "!="; break; - case '2': + case ESM::DialogueCondition::Comp_Gt: oper_str = "> "; break; - case '3': + case ESM::DialogueCondition::Comp_Ge: oper_str = ">="; break; - case '4': + case ESM::DialogueCondition::Comp_Ls: oper_str = "< "; break; - case '5': + case ESM::DialogueCondition::Comp_Le: oper_str = "<="; break; default: @@ -170,7 +140,7 @@ namespace } std::ostringstream stream; - stream << ss.mValue; + std::visit([&](auto value) { stream << value; }, ss.mValue); std::string result = Misc::StringUtils::format("%-12s %-32s %2s %s", type_str, func_str, oper_str, stream.str()); @@ -842,7 +812,7 @@ namespace EsmTool << std::endl; std::cout << " Type: " << dialogTypeLabel(mData.mData.mType) << std::endl; - for (const ESM::DialInfo::SelectStruct& rule : mData.mSelects) + for (const auto& rule : mData.mSelects) std::cout << " Select Rule: " << ruleString(rule) << std::endl; if (!mData.mResultScript.empty()) diff --git a/apps/opencs/model/tools/topicinfocheck.cpp b/apps/opencs/model/tools/topicinfocheck.cpp index ba99a33a28..fab90d951a 100644 --- a/apps/opencs/model/tools/topicinfocheck.cpp +++ b/apps/opencs/model/tools/topicinfocheck.cpp @@ -171,10 +171,9 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message // Check info conditions - for (std::vector::const_iterator it = topicInfo.mSelects.begin(); - it != topicInfo.mSelects.end(); ++it) + for (const auto& select : topicInfo.mSelects) { - verifySelectStruct((*it), id, messages); + verifySelectStruct(select, id, messages); } } @@ -308,49 +307,15 @@ bool CSMTools::TopicInfoCheckStage::verifyItem( } bool CSMTools::TopicInfoCheckStage::verifySelectStruct( - const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) + const ESM::DialogueCondition& select, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { CSMWorld::ConstInfoSelectWrapper infoCondition(select); - if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None) + if (select.mFunction == ESM::DialogueCondition::Function_None) { messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error); return false; } - else if (!infoCondition.variantTypeIsValid()) - { - std::ostringstream stream; - stream << "Value of condition '" << infoCondition.toString() << "' has invalid "; - - switch (select.mValue.getType()) - { - case ESM::VT_None: - stream << "None"; - break; - case ESM::VT_Short: - stream << "Short"; - break; - case ESM::VT_Int: - stream << "Int"; - break; - case ESM::VT_Long: - stream << "Long"; - break; - case ESM::VT_Float: - stream << "Float"; - break; - case ESM::VT_String: - stream << "String"; - break; - default: - stream << "unknown"; - break; - } - stream << " type"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); - return false; - } else if (infoCondition.conditionIsAlwaysTrue()) { messages.add( @@ -365,48 +330,48 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct( } // Id checks - if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global - && !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mGlobals, id, messages)) + if (select.mFunction == ESM::DialogueCondition::Function_Global + && !verifyId(ESM::RefId::stringRefId(select.mVariable), mGlobals, id, messages)) { return false; } - else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal - && !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mJournals, id, messages)) + else if (select.mFunction == ESM::DialogueCondition::Function_Journal + && !verifyId(ESM::RefId::stringRefId(select.mVariable), mJournals, id, messages)) { return false; } - else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item - && !verifyItem(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages)) + else if (select.mFunction == ESM::DialogueCondition::Function_Item + && !verifyItem(ESM::RefId::stringRefId(select.mVariable), id, messages)) { return false; } - else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead - && !verifyActor(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages)) + else if (select.mFunction == ESM::DialogueCondition::Function_Dead + && !verifyActor(ESM::RefId::stringRefId(select.mVariable), id, messages)) { return false; } - else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId - && !verifyActor(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages)) + else if (select.mFunction == ESM::DialogueCondition::Function_NotId + && !verifyActor(ESM::RefId::stringRefId(select.mVariable), id, messages)) { return false; } - else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction - && !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mFactions, id, messages)) + else if (select.mFunction == ESM::DialogueCondition::Function_NotFaction + && !verifyId(ESM::RefId::stringRefId(select.mVariable), mFactions, id, messages)) { return false; } - else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass - && !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mClasses, id, messages)) + else if (select.mFunction == ESM::DialogueCondition::Function_NotClass + && !verifyId(ESM::RefId::stringRefId(select.mVariable), mClasses, id, messages)) { return false; } - else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace - && !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mRaces, id, messages)) + else if (select.mFunction == ESM::DialogueCondition::Function_NotRace + && !verifyId(ESM::RefId::stringRefId(select.mVariable), mRaces, id, messages)) { return false; } - else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell - && !verifyCell(infoCondition.getVariableName(), id, messages)) + else if (select.mFunction == ESM::DialogueCondition::Function_NotCell + && !verifyCell(select.mVariable, id, messages)) { return false; } diff --git a/apps/opencs/model/tools/topicinfocheck.hpp b/apps/opencs/model/tools/topicinfocheck.hpp index 1bb2fc61dc..3069fbc0ff 100644 --- a/apps/opencs/model/tools/topicinfocheck.hpp +++ b/apps/opencs/model/tools/topicinfocheck.hpp @@ -84,7 +84,7 @@ namespace CSMTools const ESM::RefId& name, int rank, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); bool verifyItem(const ESM::RefId& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); bool verifySelectStruct( - const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); + const ESM::DialogueCondition& select, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); template diff --git a/apps/opencs/model/world/infoselectwrapper.cpp b/apps/opencs/model/world/infoselectwrapper.cpp index fb1adf16d4..2e9ed6e150 100644 --- a/apps/opencs/model/world/infoselectwrapper.cpp +++ b/apps/opencs/model/world/infoselectwrapper.cpp @@ -6,16 +6,9 @@ #include -const size_t CSMWorld::ConstInfoSelectWrapper::RuleMinSize = 5; - -const size_t CSMWorld::ConstInfoSelectWrapper::FunctionPrefixOffset = 1; -const size_t CSMWorld::ConstInfoSelectWrapper::FunctionIndexOffset = 2; -const size_t CSMWorld::ConstInfoSelectWrapper::RelationIndexOffset = 4; -const size_t CSMWorld::ConstInfoSelectWrapper::VarNameOffset = 5; - const char* CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[] = { - "Rank Low", - "Rank High", + "Faction Reaction Low", + "Faction Reaction High", "Rank Requirement", "Reputation", "Health Percent", @@ -72,7 +65,7 @@ const char* CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[] = { "PC Endurance", "PC Personality", "PC Luck", - "PC Corpus", + "PC Corprus", "Weather", "PC Vampire", "Level", @@ -112,55 +105,40 @@ const char* CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings[] = { nullptr, }; -const char* CSMWorld::ConstInfoSelectWrapper::ComparisonEnumStrings[] = { - "Boolean", - "Integer", - "Numeric", - nullptr, -}; - -// static functions - -std::string CSMWorld::ConstInfoSelectWrapper::convertToString(FunctionName name) +namespace { - if (name < Function_None) - return FunctionEnumStrings[name]; - else + std::string_view convertToString(ESM::DialogueCondition::Function name) + { + if (name < ESM::DialogueCondition::Function_None) + return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[name]; return "(Invalid Data: Function)"; -} + } -std::string CSMWorld::ConstInfoSelectWrapper::convertToString(RelationType type) -{ - if (type < Relation_None) - return RelationEnumStrings[type]; - else + std::string_view convertToString(ESM::DialogueCondition::Comparison type) + { + if (type != ESM::DialogueCondition::Comp_None) + return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings[type - ESM::DialogueCondition::Comp_Eq]; return "(Invalid Data: Relation)"; -} - -std::string CSMWorld::ConstInfoSelectWrapper::convertToString(ComparisonType type) -{ - if (type < Comparison_None) - return ComparisonEnumStrings[type]; - else - return "(Invalid Data: Comparison)"; + } } // ConstInfoSelectWrapper -CSMWorld::ConstInfoSelectWrapper::ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select) +CSMWorld::ConstInfoSelectWrapper::ConstInfoSelectWrapper(const ESM::DialogueCondition& select) : mConstSelect(select) { - readRule(); + updateHasVariable(); + updateComparisonType(); } -CSMWorld::ConstInfoSelectWrapper::FunctionName CSMWorld::ConstInfoSelectWrapper::getFunctionName() const +ESM::DialogueCondition::Function CSMWorld::ConstInfoSelectWrapper::getFunctionName() const { - return mFunctionName; + return mConstSelect.mFunction; } -CSMWorld::ConstInfoSelectWrapper::RelationType CSMWorld::ConstInfoSelectWrapper::getRelationType() const +ESM::DialogueCondition::Comparison CSMWorld::ConstInfoSelectWrapper::getRelationType() const { - return mRelationType; + return mConstSelect.mComparison; } CSMWorld::ConstInfoSelectWrapper::ComparisonType CSMWorld::ConstInfoSelectWrapper::getComparisonType() const @@ -175,24 +153,21 @@ bool CSMWorld::ConstInfoSelectWrapper::hasVariable() const const std::string& CSMWorld::ConstInfoSelectWrapper::getVariableName() const { - return mVariableName; + return mConstSelect.mVariable; } bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue() const { - if (!variantTypeIsValid()) - return false; - if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer) { - if (mConstSelect.mValue.getType() == ESM::VT_Float) + if (std::holds_alternative(mConstSelect.mValue)) return conditionIsAlwaysTrue(getConditionFloatRange(), getValidIntRange()); else return conditionIsAlwaysTrue(getConditionIntRange(), getValidIntRange()); } else if (mComparisonType == Comparison_Numeric) { - if (mConstSelect.mValue.getType() == ESM::VT_Float) + if (std::holds_alternative(mConstSelect.mValue)) return conditionIsAlwaysTrue(getConditionFloatRange(), getValidFloatRange()); else return conditionIsAlwaysTrue(getConditionIntRange(), getValidFloatRange()); @@ -203,19 +178,16 @@ bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue() const bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue() const { - if (!variantTypeIsValid()) - return false; - if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer) { - if (mConstSelect.mValue.getType() == ESM::VT_Float) + if (std::holds_alternative(mConstSelect.mValue)) return conditionIsNeverTrue(getConditionFloatRange(), getValidIntRange()); else return conditionIsNeverTrue(getConditionIntRange(), getValidIntRange()); } else if (mComparisonType == Comparison_Numeric) { - if (mConstSelect.mValue.getType() == ESM::VT_Float) + if (std::holds_alternative(mConstSelect.mValue)) return conditionIsNeverTrue(getConditionFloatRange(), getValidFloatRange()); else return conditionIsNeverTrue(getConditionIntRange(), getValidFloatRange()); @@ -224,170 +196,36 @@ bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue() const return false; } -bool CSMWorld::ConstInfoSelectWrapper::variantTypeIsValid() const -{ - return (mConstSelect.mValue.getType() == ESM::VT_Int || mConstSelect.mValue.getType() == ESM::VT_Float); -} - -const ESM::Variant& CSMWorld::ConstInfoSelectWrapper::getVariant() const -{ - return mConstSelect.mValue; -} - std::string CSMWorld::ConstInfoSelectWrapper::toString() const { std::ostringstream stream; - stream << convertToString(mFunctionName) << " "; + stream << convertToString(getFunctionName()) << " "; if (mHasVariable) - stream << mVariableName << " "; + stream << getVariableName() << " "; - stream << convertToString(mRelationType) << " "; + stream << convertToString(getRelationType()) << " "; - switch (mConstSelect.mValue.getType()) - { - case ESM::VT_Int: - stream << mConstSelect.mValue.getInteger(); - break; - - case ESM::VT_Float: - stream << mConstSelect.mValue.getFloat(); - break; - - default: - stream << "(Invalid value type)"; - break; - } + std::visit([&](auto value) { stream << value; }, mConstSelect.mValue); return stream.str(); } -void CSMWorld::ConstInfoSelectWrapper::readRule() -{ - if (mConstSelect.mSelectRule.size() < RuleMinSize) - throw std::runtime_error("InfoSelectWrapper: rule is to small"); - - readFunctionName(); - readRelationType(); - readVariableName(); - updateHasVariable(); - updateComparisonType(); -} - -void CSMWorld::ConstInfoSelectWrapper::readFunctionName() -{ - char functionPrefix = mConstSelect.mSelectRule[FunctionPrefixOffset]; - std::string functionIndex = mConstSelect.mSelectRule.substr(FunctionIndexOffset, 2); - int convertedIndex = -1; - - // Read in function index, form ## from 00 .. 73, skip leading zero - if (functionIndex[0] == '0') - functionIndex = functionIndex[1]; - - std::stringstream stream; - stream << functionIndex; - stream >> convertedIndex; - - switch (functionPrefix) - { - case '1': - if (convertedIndex >= 0 && convertedIndex <= 73) - mFunctionName = static_cast(convertedIndex); - else - mFunctionName = Function_None; - break; - - case '2': - mFunctionName = Function_Global; - break; - case '3': - mFunctionName = Function_Local; - break; - case '4': - mFunctionName = Function_Journal; - break; - case '5': - mFunctionName = Function_Item; - break; - case '6': - mFunctionName = Function_Dead; - break; - case '7': - mFunctionName = Function_NotId; - break; - case '8': - mFunctionName = Function_NotFaction; - break; - case '9': - mFunctionName = Function_NotClass; - break; - case 'A': - mFunctionName = Function_NotRace; - break; - case 'B': - mFunctionName = Function_NotCell; - break; - case 'C': - mFunctionName = Function_NotLocal; - break; - default: - mFunctionName = Function_None; - break; - } -} - -void CSMWorld::ConstInfoSelectWrapper::readRelationType() -{ - char relationIndex = mConstSelect.mSelectRule[RelationIndexOffset]; - - switch (relationIndex) - { - case '0': - mRelationType = Relation_Equal; - break; - case '1': - mRelationType = Relation_NotEqual; - break; - case '2': - mRelationType = Relation_Greater; - break; - case '3': - mRelationType = Relation_GreaterOrEqual; - break; - case '4': - mRelationType = Relation_Less; - break; - case '5': - mRelationType = Relation_LessOrEqual; - break; - default: - mRelationType = Relation_None; - } -} - -void CSMWorld::ConstInfoSelectWrapper::readVariableName() -{ - if (mConstSelect.mSelectRule.size() >= VarNameOffset) - mVariableName = mConstSelect.mSelectRule.substr(VarNameOffset); - else - mVariableName.clear(); -} - void CSMWorld::ConstInfoSelectWrapper::updateHasVariable() { - switch (mFunctionName) + switch (getFunctionName()) { - case Function_Global: - case Function_Local: - case Function_Journal: - case Function_Item: - case Function_Dead: - case Function_NotId: - case Function_NotFaction: - case Function_NotClass: - case Function_NotRace: - case Function_NotCell: - case Function_NotLocal: + case ESM::DialogueCondition::Function_Global: + case ESM::DialogueCondition::Function_Local: + case ESM::DialogueCondition::Function_Journal: + case ESM::DialogueCondition::Function_Item: + case ESM::DialogueCondition::Function_Dead: + case ESM::DialogueCondition::Function_NotId: + case ESM::DialogueCondition::Function_NotFaction: + case ESM::DialogueCondition::Function_NotClass: + case ESM::DialogueCondition::Function_NotRace: + case ESM::DialogueCondition::Function_NotCell: + case ESM::DialogueCondition::Function_NotLocal: mHasVariable = true; break; @@ -399,103 +237,103 @@ void CSMWorld::ConstInfoSelectWrapper::updateHasVariable() void CSMWorld::ConstInfoSelectWrapper::updateComparisonType() { - switch (mFunctionName) + switch (getFunctionName()) { // Boolean - case Function_NotId: - case Function_NotFaction: - case Function_NotClass: - case Function_NotRace: - case Function_NotCell: - case Function_PcExpelled: - case Function_PcCommonDisease: - case Function_PcBlightDisease: - case Function_SameSex: - case Function_SameRace: - case Function_SameFaction: - case Function_Detected: - case Function_Alarmed: - case Function_PcCorpus: - case Function_PcVampire: - case Function_Attacked: - case Function_TalkedToPc: - case Function_ShouldAttack: - case Function_Werewolf: + case ESM::DialogueCondition::Function_NotId: + case ESM::DialogueCondition::Function_NotFaction: + case ESM::DialogueCondition::Function_NotClass: + case ESM::DialogueCondition::Function_NotRace: + case ESM::DialogueCondition::Function_NotCell: + case ESM::DialogueCondition::Function_PcExpelled: + case ESM::DialogueCondition::Function_PcCommonDisease: + case ESM::DialogueCondition::Function_PcBlightDisease: + case ESM::DialogueCondition::Function_SameSex: + case ESM::DialogueCondition::Function_SameRace: + case ESM::DialogueCondition::Function_SameFaction: + case ESM::DialogueCondition::Function_Detected: + case ESM::DialogueCondition::Function_Alarmed: + case ESM::DialogueCondition::Function_PcCorprus: + case ESM::DialogueCondition::Function_PcVampire: + case ESM::DialogueCondition::Function_Attacked: + case ESM::DialogueCondition::Function_TalkedToPc: + case ESM::DialogueCondition::Function_ShouldAttack: + case ESM::DialogueCondition::Function_Werewolf: mComparisonType = Comparison_Boolean; break; // Integer - case Function_Journal: - case Function_Item: - case Function_Dead: - case Function_RankLow: - case Function_RankHigh: - case Function_RankRequirement: - case Function_Reputation: - case Function_PcReputation: - case Function_PcLevel: - case Function_PcStrength: - case Function_PcBlock: - case Function_PcArmorer: - case Function_PcMediumArmor: - case Function_PcHeavyArmor: - case Function_PcBluntWeapon: - case Function_PcLongBlade: - case Function_PcAxe: - case Function_PcSpear: - case Function_PcAthletics: - case Function_PcEnchant: - case Function_PcDestruction: - case Function_PcAlteration: - case Function_PcIllusion: - case Function_PcConjuration: - case Function_PcMysticism: - case Function_PcRestoration: - case Function_PcAlchemy: - case Function_PcUnarmored: - case Function_PcSecurity: - case Function_PcSneak: - case Function_PcAcrobatics: - case Function_PcLightArmor: - case Function_PcShortBlade: - case Function_PcMarksman: - case Function_PcMerchantile: - case Function_PcSpeechcraft: - case Function_PcHandToHand: - case Function_PcGender: - case Function_PcClothingModifier: - case Function_PcCrimeLevel: - case Function_FactionRankDifference: - case Function_Choice: - case Function_PcIntelligence: - case Function_PcWillpower: - case Function_PcAgility: - case Function_PcSpeed: - case Function_PcEndurance: - case Function_PcPersonality: - case Function_PcLuck: - case Function_Weather: - case Function_Level: - case Function_CreatureTarget: - case Function_FriendHit: - case Function_Fight: - case Function_Hello: - case Function_Alarm: - case Function_Flee: - case Function_PcWerewolfKills: + case ESM::DialogueCondition::Function_Journal: + case ESM::DialogueCondition::Function_Item: + case ESM::DialogueCondition::Function_Dead: + case ESM::DialogueCondition::Function_FacReactionLowest: + case ESM::DialogueCondition::Function_FacReactionHighest: + case ESM::DialogueCondition::Function_RankRequirement: + case ESM::DialogueCondition::Function_Reputation: + case ESM::DialogueCondition::Function_PcReputation: + case ESM::DialogueCondition::Function_PcLevel: + case ESM::DialogueCondition::Function_PcStrength: + case ESM::DialogueCondition::Function_PcBlock: + case ESM::DialogueCondition::Function_PcArmorer: + case ESM::DialogueCondition::Function_PcMediumArmor: + case ESM::DialogueCondition::Function_PcHeavyArmor: + case ESM::DialogueCondition::Function_PcBluntWeapon: + case ESM::DialogueCondition::Function_PcLongBlade: + case ESM::DialogueCondition::Function_PcAxe: + case ESM::DialogueCondition::Function_PcSpear: + case ESM::DialogueCondition::Function_PcAthletics: + case ESM::DialogueCondition::Function_PcEnchant: + case ESM::DialogueCondition::Function_PcDestruction: + case ESM::DialogueCondition::Function_PcAlteration: + case ESM::DialogueCondition::Function_PcIllusion: + case ESM::DialogueCondition::Function_PcConjuration: + case ESM::DialogueCondition::Function_PcMysticism: + case ESM::DialogueCondition::Function_PcRestoration: + case ESM::DialogueCondition::Function_PcAlchemy: + case ESM::DialogueCondition::Function_PcUnarmored: + case ESM::DialogueCondition::Function_PcSecurity: + case ESM::DialogueCondition::Function_PcSneak: + case ESM::DialogueCondition::Function_PcAcrobatics: + case ESM::DialogueCondition::Function_PcLightArmor: + case ESM::DialogueCondition::Function_PcShortBlade: + case ESM::DialogueCondition::Function_PcMarksman: + case ESM::DialogueCondition::Function_PcMerchantile: + case ESM::DialogueCondition::Function_PcSpeechcraft: + case ESM::DialogueCondition::Function_PcHandToHand: + case ESM::DialogueCondition::Function_PcGender: + case ESM::DialogueCondition::Function_PcClothingModifier: + case ESM::DialogueCondition::Function_PcCrimeLevel: + case ESM::DialogueCondition::Function_FactionRankDifference: + case ESM::DialogueCondition::Function_Choice: + case ESM::DialogueCondition::Function_PcIntelligence: + case ESM::DialogueCondition::Function_PcWillpower: + case ESM::DialogueCondition::Function_PcAgility: + case ESM::DialogueCondition::Function_PcSpeed: + case ESM::DialogueCondition::Function_PcEndurance: + case ESM::DialogueCondition::Function_PcPersonality: + case ESM::DialogueCondition::Function_PcLuck: + case ESM::DialogueCondition::Function_Weather: + case ESM::DialogueCondition::Function_Level: + case ESM::DialogueCondition::Function_CreatureTarget: + case ESM::DialogueCondition::Function_FriendHit: + case ESM::DialogueCondition::Function_Fight: + case ESM::DialogueCondition::Function_Hello: + case ESM::DialogueCondition::Function_Alarm: + case ESM::DialogueCondition::Function_Flee: + case ESM::DialogueCondition::Function_PcWerewolfKills: mComparisonType = Comparison_Integer; break; // Numeric - case Function_Global: - case Function_Local: - case Function_NotLocal: - - case Function_Health_Percent: - case Function_PcHealthPercent: - case Function_PcMagicka: - case Function_PcFatigue: - case Function_PcHealth: + case ESM::DialogueCondition::Function_Global: + case ESM::DialogueCondition::Function_Local: + case ESM::DialogueCondition::Function_NotLocal: + + case ESM::DialogueCondition::Function_Health_Percent: + case ESM::DialogueCondition::Function_PcHealthPercent: + case ESM::DialogueCondition::Function_PcMagicka: + case ESM::DialogueCondition::Function_PcFatigue: + case ESM::DialogueCondition::Function_PcHealth: mComparisonType = Comparison_Numeric; break; @@ -511,15 +349,15 @@ std::pair CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() con const int IntMin = std::numeric_limits::min(); const std::pair InvalidRange(IntMax, IntMin); - int value = mConstSelect.mValue.getInteger(); + int value = std::get(mConstSelect.mValue); - switch (mRelationType) + switch (getRelationType()) { - case Relation_Equal: - case Relation_NotEqual: + case ESM::DialogueCondition::Comp_Eq: + case ESM::DialogueCondition::Comp_Ne: return std::pair(value, value); - case Relation_Greater: + case ESM::DialogueCondition::Comp_Gt: if (value == IntMax) { return InvalidRange; @@ -530,10 +368,10 @@ std::pair CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() con } break; - case Relation_GreaterOrEqual: + case ESM::DialogueCondition::Comp_Ge: return std::pair(value, IntMax); - case Relation_Less: + case ESM::DialogueCondition::Comp_Ls: if (value == IntMin) { return InvalidRange; @@ -543,7 +381,7 @@ std::pair CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() con return std::pair(IntMin, value - 1); } - case Relation_LessOrEqual: + case ESM::DialogueCondition::Comp_Le: return std::pair(IntMin, value); default: @@ -557,24 +395,24 @@ std::pair CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange const float FloatMin = -std::numeric_limits::infinity(); const float Epsilon = std::numeric_limits::epsilon(); - float value = mConstSelect.mValue.getFloat(); + float value = std::get(mConstSelect.mValue); - switch (mRelationType) + switch (getRelationType()) { - case Relation_Equal: - case Relation_NotEqual: + case ESM::DialogueCondition::Comp_Eq: + case ESM::DialogueCondition::Comp_Ne: return std::pair(value, value); - case Relation_Greater: + case ESM::DialogueCondition::Comp_Gt: return std::pair(value + Epsilon, FloatMax); - case Relation_GreaterOrEqual: + case ESM::DialogueCondition::Comp_Ge: return std::pair(value, FloatMax); - case Relation_Less: + case ESM::DialogueCondition::Comp_Ls: return std::pair(FloatMin, value - Epsilon); - case Relation_LessOrEqual: + case ESM::DialogueCondition::Comp_Le: return std::pair(FloatMin, value); default: @@ -587,120 +425,120 @@ std::pair CSMWorld::ConstInfoSelectWrapper::getValidIntRange() const const int IntMax = std::numeric_limits::max(); const int IntMin = std::numeric_limits::min(); - switch (mFunctionName) + switch (getFunctionName()) { // Boolean - case Function_NotId: - case Function_NotFaction: - case Function_NotClass: - case Function_NotRace: - case Function_NotCell: - case Function_PcExpelled: - case Function_PcCommonDisease: - case Function_PcBlightDisease: - case Function_SameSex: - case Function_SameRace: - case Function_SameFaction: - case Function_Detected: - case Function_Alarmed: - case Function_PcCorpus: - case Function_PcVampire: - case Function_Attacked: - case Function_TalkedToPc: - case Function_ShouldAttack: - case Function_Werewolf: + case ESM::DialogueCondition::Function_NotId: + case ESM::DialogueCondition::Function_NotFaction: + case ESM::DialogueCondition::Function_NotClass: + case ESM::DialogueCondition::Function_NotRace: + case ESM::DialogueCondition::Function_NotCell: + case ESM::DialogueCondition::Function_PcExpelled: + case ESM::DialogueCondition::Function_PcCommonDisease: + case ESM::DialogueCondition::Function_PcBlightDisease: + case ESM::DialogueCondition::Function_SameSex: + case ESM::DialogueCondition::Function_SameRace: + case ESM::DialogueCondition::Function_SameFaction: + case ESM::DialogueCondition::Function_Detected: + case ESM::DialogueCondition::Function_Alarmed: + case ESM::DialogueCondition::Function_PcCorprus: + case ESM::DialogueCondition::Function_PcVampire: + case ESM::DialogueCondition::Function_Attacked: + case ESM::DialogueCondition::Function_TalkedToPc: + case ESM::DialogueCondition::Function_ShouldAttack: + case ESM::DialogueCondition::Function_Werewolf: return std::pair(0, 1); // Integer - case Function_RankLow: - case Function_RankHigh: - case Function_Reputation: - case Function_PcReputation: - case Function_Journal: + case ESM::DialogueCondition::Function_FacReactionLowest: + case ESM::DialogueCondition::Function_FacReactionHighest: + case ESM::DialogueCondition::Function_Reputation: + case ESM::DialogueCondition::Function_PcReputation: + case ESM::DialogueCondition::Function_Journal: return std::pair(IntMin, IntMax); - case Function_Item: - case Function_Dead: - case Function_PcLevel: - case Function_PcStrength: - case Function_PcBlock: - case Function_PcArmorer: - case Function_PcMediumArmor: - case Function_PcHeavyArmor: - case Function_PcBluntWeapon: - case Function_PcLongBlade: - case Function_PcAxe: - case Function_PcSpear: - case Function_PcAthletics: - case Function_PcEnchant: - case Function_PcDestruction: - case Function_PcAlteration: - case Function_PcIllusion: - case Function_PcConjuration: - case Function_PcMysticism: - case Function_PcRestoration: - case Function_PcAlchemy: - case Function_PcUnarmored: - case Function_PcSecurity: - case Function_PcSneak: - case Function_PcAcrobatics: - case Function_PcLightArmor: - case Function_PcShortBlade: - case Function_PcMarksman: - case Function_PcMerchantile: - case Function_PcSpeechcraft: - case Function_PcHandToHand: - case Function_PcClothingModifier: - case Function_PcCrimeLevel: - case Function_Choice: - case Function_PcIntelligence: - case Function_PcWillpower: - case Function_PcAgility: - case Function_PcSpeed: - case Function_PcEndurance: - case Function_PcPersonality: - case Function_PcLuck: - case Function_Level: - case Function_PcWerewolfKills: + case ESM::DialogueCondition::Function_Item: + case ESM::DialogueCondition::Function_Dead: + case ESM::DialogueCondition::Function_PcLevel: + case ESM::DialogueCondition::Function_PcStrength: + case ESM::DialogueCondition::Function_PcBlock: + case ESM::DialogueCondition::Function_PcArmorer: + case ESM::DialogueCondition::Function_PcMediumArmor: + case ESM::DialogueCondition::Function_PcHeavyArmor: + case ESM::DialogueCondition::Function_PcBluntWeapon: + case ESM::DialogueCondition::Function_PcLongBlade: + case ESM::DialogueCondition::Function_PcAxe: + case ESM::DialogueCondition::Function_PcSpear: + case ESM::DialogueCondition::Function_PcAthletics: + case ESM::DialogueCondition::Function_PcEnchant: + case ESM::DialogueCondition::Function_PcDestruction: + case ESM::DialogueCondition::Function_PcAlteration: + case ESM::DialogueCondition::Function_PcIllusion: + case ESM::DialogueCondition::Function_PcConjuration: + case ESM::DialogueCondition::Function_PcMysticism: + case ESM::DialogueCondition::Function_PcRestoration: + case ESM::DialogueCondition::Function_PcAlchemy: + case ESM::DialogueCondition::Function_PcUnarmored: + case ESM::DialogueCondition::Function_PcSecurity: + case ESM::DialogueCondition::Function_PcSneak: + case ESM::DialogueCondition::Function_PcAcrobatics: + case ESM::DialogueCondition::Function_PcLightArmor: + case ESM::DialogueCondition::Function_PcShortBlade: + case ESM::DialogueCondition::Function_PcMarksman: + case ESM::DialogueCondition::Function_PcMerchantile: + case ESM::DialogueCondition::Function_PcSpeechcraft: + case ESM::DialogueCondition::Function_PcHandToHand: + case ESM::DialogueCondition::Function_PcClothingModifier: + case ESM::DialogueCondition::Function_PcCrimeLevel: + case ESM::DialogueCondition::Function_Choice: + case ESM::DialogueCondition::Function_PcIntelligence: + case ESM::DialogueCondition::Function_PcWillpower: + case ESM::DialogueCondition::Function_PcAgility: + case ESM::DialogueCondition::Function_PcSpeed: + case ESM::DialogueCondition::Function_PcEndurance: + case ESM::DialogueCondition::Function_PcPersonality: + case ESM::DialogueCondition::Function_PcLuck: + case ESM::DialogueCondition::Function_Level: + case ESM::DialogueCondition::Function_PcWerewolfKills: return std::pair(0, IntMax); - case Function_Fight: - case Function_Hello: - case Function_Alarm: - case Function_Flee: + case ESM::DialogueCondition::Function_Fight: + case ESM::DialogueCondition::Function_Hello: + case ESM::DialogueCondition::Function_Alarm: + case ESM::DialogueCondition::Function_Flee: return std::pair(0, 100); - case Function_Weather: + case ESM::DialogueCondition::Function_Weather: return std::pair(0, 9); - case Function_FriendHit: + case ESM::DialogueCondition::Function_FriendHit: return std::pair(0, 4); - case Function_RankRequirement: + case ESM::DialogueCondition::Function_RankRequirement: return std::pair(0, 3); - case Function_CreatureTarget: + case ESM::DialogueCondition::Function_CreatureTarget: return std::pair(0, 2); - case Function_PcGender: + case ESM::DialogueCondition::Function_PcGender: return std::pair(0, 1); - case Function_FactionRankDifference: + case ESM::DialogueCondition::Function_FactionRankDifference: return std::pair(-9, 9); // Numeric - case Function_Global: - case Function_Local: - case Function_NotLocal: + case ESM::DialogueCondition::Function_Global: + case ESM::DialogueCondition::Function_Local: + case ESM::DialogueCondition::Function_NotLocal: return std::pair(IntMin, IntMax); - case Function_PcMagicka: - case Function_PcFatigue: - case Function_PcHealth: + case ESM::DialogueCondition::Function_PcMagicka: + case ESM::DialogueCondition::Function_PcFatigue: + case ESM::DialogueCondition::Function_PcHealth: return std::pair(0, IntMax); - case Function_Health_Percent: - case Function_PcHealthPercent: + case ESM::DialogueCondition::Function_Health_Percent: + case ESM::DialogueCondition::Function_PcHealthPercent: return std::pair(0, 100); default: @@ -713,21 +551,21 @@ std::pair CSMWorld::ConstInfoSelectWrapper::getValidFloatRange() c const float FloatMax = std::numeric_limits::infinity(); const float FloatMin = -std::numeric_limits::infinity(); - switch (mFunctionName) + switch (getFunctionName()) { // Numeric - case Function_Global: - case Function_Local: - case Function_NotLocal: + case ESM::DialogueCondition::Function_Global: + case ESM::DialogueCondition::Function_Local: + case ESM::DialogueCondition::Function_NotLocal: return std::pair(FloatMin, FloatMax); - case Function_PcMagicka: - case Function_PcFatigue: - case Function_PcHealth: + case ESM::DialogueCondition::Function_PcMagicka: + case ESM::DialogueCondition::Function_PcFatigue: + case ESM::DialogueCondition::Function_PcHealth: return std::pair(0, FloatMax); - case Function_Health_Percent: - case Function_PcHealthPercent: + case ESM::DialogueCondition::Function_Health_Percent: + case ESM::DialogueCondition::Function_PcHealthPercent: return std::pair(0, 100); default: @@ -768,19 +606,19 @@ template bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue( std::pair conditionRange, std::pair validRange) const { - switch (mRelationType) + switch (getRelationType()) { - case Relation_Equal: + case ESM::DialogueCondition::Comp_Eq: return false; - case Relation_NotEqual: + case ESM::DialogueCondition::Comp_Ne: // If value is not within range, it will always be true return !rangeContains(conditionRange.first, validRange); - case Relation_Greater: - case Relation_GreaterOrEqual: - case Relation_Less: - case Relation_LessOrEqual: + case ESM::DialogueCondition::Comp_Gt: + case ESM::DialogueCondition::Comp_Ge: + case ESM::DialogueCondition::Comp_Ls: + case ESM::DialogueCondition::Comp_Le: // If the valid range is completely within the condition range, it will always be true return rangeFullyContains(conditionRange, validRange); @@ -795,18 +633,18 @@ template bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue( std::pair conditionRange, std::pair validRange) const { - switch (mRelationType) + switch (getRelationType()) { - case Relation_Equal: + case ESM::DialogueCondition::Comp_Eq: return !rangeContains(conditionRange.first, validRange); - case Relation_NotEqual: + case ESM::DialogueCondition::Comp_Ne: return false; - case Relation_Greater: - case Relation_GreaterOrEqual: - case Relation_Less: - case Relation_LessOrEqual: + case ESM::DialogueCondition::Comp_Gt: + case ESM::DialogueCondition::Comp_Ge: + case ESM::DialogueCondition::Comp_Ls: + case ESM::DialogueCondition::Comp_Le: // If ranges do not overlap, it will never be true return !rangesOverlap(conditionRange, validRange); @@ -817,153 +655,47 @@ bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue( return false; } +QVariant CSMWorld::ConstInfoSelectWrapper::getValue() const +{ + return std::visit([](auto value) { return QVariant(value); }, mConstSelect.mValue); +} + // InfoSelectWrapper -CSMWorld::InfoSelectWrapper::InfoSelectWrapper(ESM::DialInfo::SelectStruct& select) +CSMWorld::InfoSelectWrapper::InfoSelectWrapper(ESM::DialogueCondition& select) : CSMWorld::ConstInfoSelectWrapper(select) , mSelect(select) { } -void CSMWorld::InfoSelectWrapper::setFunctionName(FunctionName name) +void CSMWorld::InfoSelectWrapper::setFunctionName(ESM::DialogueCondition::Function name) { - mFunctionName = name; + mSelect.mFunction = name; updateHasVariable(); updateComparisonType(); + if (getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric + && std::holds_alternative(mSelect.mValue)) + { + mSelect.mValue = std::visit([](auto value) { return static_cast(value); }, mSelect.mValue); + } } -void CSMWorld::InfoSelectWrapper::setRelationType(RelationType type) +void CSMWorld::InfoSelectWrapper::setRelationType(ESM::DialogueCondition::Comparison type) { - mRelationType = type; + mSelect.mComparison = type; } void CSMWorld::InfoSelectWrapper::setVariableName(const std::string& name) { - mVariableName = name; + mSelect.mVariable = name; } -void CSMWorld::InfoSelectWrapper::setDefaults() +void CSMWorld::InfoSelectWrapper::setValue(int value) { - if (!variantTypeIsValid()) - mSelect.mValue.setType(ESM::VT_Int); - - switch (mComparisonType) - { - case Comparison_Boolean: - setRelationType(Relation_Equal); - mSelect.mValue.setInteger(1); - break; - - case Comparison_Integer: - case Comparison_Numeric: - setRelationType(Relation_Greater); - mSelect.mValue.setInteger(0); - break; - - default: - // Do nothing - break; - } - - update(); -} - -void CSMWorld::InfoSelectWrapper::update() -{ - std::ostringstream stream; - - // Leading 0 - stream << '0'; - - // Write Function - - bool writeIndex = false; - size_t functionIndex = static_cast(mFunctionName); - - switch (mFunctionName) - { - case Function_None: - stream << '0'; - break; - case Function_Global: - stream << '2'; - break; - case Function_Local: - stream << '3'; - break; - case Function_Journal: - stream << '4'; - break; - case Function_Item: - stream << '5'; - break; - case Function_Dead: - stream << '6'; - break; - case Function_NotId: - stream << '7'; - break; - case Function_NotFaction: - stream << '8'; - break; - case Function_NotClass: - stream << '9'; - break; - case Function_NotRace: - stream << 'A'; - break; - case Function_NotCell: - stream << 'B'; - break; - case Function_NotLocal: - stream << 'C'; - break; - default: - stream << '1'; - writeIndex = true; - break; - } - - if (writeIndex && functionIndex < 10) // leading 0 - stream << '0' << functionIndex; - else if (writeIndex) - stream << functionIndex; - else - stream << "00"; - - // Write Relation - switch (mRelationType) - { - case Relation_Equal: - stream << '0'; - break; - case Relation_NotEqual: - stream << '1'; - break; - case Relation_Greater: - stream << '2'; - break; - case Relation_GreaterOrEqual: - stream << '3'; - break; - case Relation_Less: - stream << '4'; - break; - case Relation_LessOrEqual: - stream << '5'; - break; - default: - stream << '0'; - break; - } - - if (mHasVariable) - stream << mVariableName; - - mSelect.mSelectRule = stream.str(); + mSelect.mValue = value; } -ESM::Variant& CSMWorld::InfoSelectWrapper::getVariant() +void CSMWorld::InfoSelectWrapper::setValue(float value) { - return mSelect.mValue; + mSelect.mValue = value; } diff --git a/apps/opencs/model/world/infoselectwrapper.hpp b/apps/opencs/model/world/infoselectwrapper.hpp index 4ecdc676dc..b3b5abe462 100644 --- a/apps/opencs/model/world/infoselectwrapper.hpp +++ b/apps/opencs/model/world/infoselectwrapper.hpp @@ -7,133 +7,13 @@ #include -namespace ESM -{ - class Variant; -} +#include namespace CSMWorld { - // ESM::DialInfo::SelectStruct.mSelectRule - // 012345... - // ^^^ ^^ - // ||| || - // ||| |+------------- condition variable string - // ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc - // ||+---------------- function index (encoded, where function == '1') - // |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc - // +------------------ unknown - // - - // Wrapper for DialInfo::SelectStruct class ConstInfoSelectWrapper { public: - // Order matters - enum FunctionName - { - Function_RankLow = 0, - Function_RankHigh, - Function_RankRequirement, - Function_Reputation, - Function_Health_Percent, - Function_PcReputation, - Function_PcLevel, - Function_PcHealthPercent, - Function_PcMagicka, - Function_PcFatigue, - Function_PcStrength, - Function_PcBlock, - Function_PcArmorer, - Function_PcMediumArmor, - Function_PcHeavyArmor, - Function_PcBluntWeapon, - Function_PcLongBlade, - Function_PcAxe, - Function_PcSpear, - Function_PcAthletics, - Function_PcEnchant, - Function_PcDestruction, - Function_PcAlteration, - Function_PcIllusion, - Function_PcConjuration, - Function_PcMysticism, - Function_PcRestoration, - Function_PcAlchemy, - Function_PcUnarmored, - Function_PcSecurity, - Function_PcSneak, - Function_PcAcrobatics, - Function_PcLightArmor, - Function_PcShortBlade, - Function_PcMarksman, - Function_PcMerchantile, - Function_PcSpeechcraft, - Function_PcHandToHand, - Function_PcGender, - Function_PcExpelled, - Function_PcCommonDisease, - Function_PcBlightDisease, - Function_PcClothingModifier, - Function_PcCrimeLevel, - Function_SameSex, - Function_SameRace, - Function_SameFaction, - Function_FactionRankDifference, - Function_Detected, - Function_Alarmed, - Function_Choice, - Function_PcIntelligence, - Function_PcWillpower, - Function_PcAgility, - Function_PcSpeed, - Function_PcEndurance, - Function_PcPersonality, - Function_PcLuck, - Function_PcCorpus, - Function_Weather, - Function_PcVampire, - Function_Level, - Function_Attacked, - Function_TalkedToPc, - Function_PcHealth, - Function_CreatureTarget, - Function_FriendHit, - Function_Fight, - Function_Hello, - Function_Alarm, - Function_Flee, - Function_ShouldAttack, - Function_Werewolf, - Function_PcWerewolfKills = 73, - - Function_Global, - Function_Local, - Function_Journal, - Function_Item, - Function_Dead, - Function_NotId, - Function_NotFaction, - Function_NotClass, - Function_NotRace, - Function_NotCell, - Function_NotLocal, - - Function_None - }; - - enum RelationType - { - Relation_Equal, - Relation_NotEqual, - Relation_Greater, - Relation_GreaterOrEqual, - Relation_Less, - Relation_LessOrEqual, - - Relation_None - }; - enum ComparisonType { Comparison_Boolean, @@ -143,25 +23,13 @@ namespace CSMWorld Comparison_None }; - static const size_t RuleMinSize; - - static const size_t FunctionPrefixOffset; - static const size_t FunctionIndexOffset; - static const size_t RelationIndexOffset; - static const size_t VarNameOffset; - static const char* FunctionEnumStrings[]; static const char* RelationEnumStrings[]; - static const char* ComparisonEnumStrings[]; - - static std::string convertToString(FunctionName name); - static std::string convertToString(RelationType type); - static std::string convertToString(ComparisonType type); - ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select); + ConstInfoSelectWrapper(const ESM::DialogueCondition& select); - FunctionName getFunctionName() const; - RelationType getRelationType() const; + ESM::DialogueCondition::Function getFunctionName() const; + ESM::DialogueCondition::Comparison getRelationType() const; ComparisonType getComparisonType() const; bool hasVariable() const; @@ -169,17 +37,12 @@ namespace CSMWorld bool conditionIsAlwaysTrue() const; bool conditionIsNeverTrue() const; - bool variantTypeIsValid() const; - const ESM::Variant& getVariant() const; + QVariant getValue() const; std::string toString() const; protected: - void readRule(); - void readFunctionName(); - void readRelationType(); - void readVariableName(); void updateHasVariable(); void updateComparisonType(); @@ -207,38 +70,29 @@ namespace CSMWorld template bool conditionIsNeverTrue(std::pair conditionRange, std::pair validRange) const; - FunctionName mFunctionName; - RelationType mRelationType; ComparisonType mComparisonType; bool mHasVariable; - std::string mVariableName; private: - const ESM::DialInfo::SelectStruct& mConstSelect; + const ESM::DialogueCondition& mConstSelect; }; - // Wrapper for DialInfo::SelectStruct that can modify the wrapped select struct + // Wrapper for DialogueCondition that can modify the wrapped select struct class InfoSelectWrapper : public ConstInfoSelectWrapper { public: - InfoSelectWrapper(ESM::DialInfo::SelectStruct& select); + InfoSelectWrapper(ESM::DialogueCondition& select); // Wrapped SelectStruct will not be modified until update() is called - void setFunctionName(FunctionName name); - void setRelationType(RelationType type); + void setFunctionName(ESM::DialogueCondition::Function name); + void setRelationType(ESM::DialogueCondition::Comparison type); void setVariableName(const std::string& name); - - // Modified wrapped SelectStruct - void update(); - - // This sets properties based on the function name to its defaults and updates the wrapped object - void setDefaults(); - - ESM::Variant& getVariant(); + void setValue(int value); + void setValue(float value); private: - ESM::DialInfo::SelectStruct& mSelect; + ESM::DialogueCondition& mSelect; void writeRule(); }; diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 0d90ea0d68..c844c5a18f 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -538,13 +538,11 @@ namespace CSMWorld { Info info = record.get(); - std::vector& conditions = info.mSelects; + auto& conditions = info.mSelects; // default row - ESM::DialInfo::SelectStruct condStruct; - condStruct.mSelectRule = "01000"; - condStruct.mValue = ESM::Variant(); - condStruct.mValue.setType(ESM::VT_Int); + ESM::DialogueCondition condStruct; + condStruct.mIndex = conditions.size(); conditions.insert(conditions.begin() + position, condStruct); @@ -555,7 +553,7 @@ namespace CSMWorld { Info info = record.get(); - std::vector& conditions = info.mSelects; + auto& conditions = info.mSelects; if (rowToRemove < 0 || rowToRemove >= static_cast(conditions.size())) throw std::runtime_error("index out of range"); @@ -569,8 +567,8 @@ namespace CSMWorld { Info info = record.get(); - info.mSelects = static_cast>&>(nestedTable) - .mNestedTable; + info.mSelects + = static_cast>&>(nestedTable).mNestedTable; record.setModified(info); } @@ -578,14 +576,14 @@ namespace CSMWorld NestedTableWrapperBase* InfoConditionAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring - return new NestedTableWrapper>(record.get().mSelects); + return new NestedTableWrapper>(record.get().mSelects); } QVariant InfoConditionAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { Info info = record.get(); - std::vector& conditions = info.mSelects; + auto& conditions = info.mSelects; if (subRowIndex < 0 || subRowIndex >= static_cast(conditions.size())) throw std::runtime_error("index out of range"); @@ -607,23 +605,11 @@ namespace CSMWorld } case 2: { - return infoSelectWrapper.getRelationType(); + return infoSelectWrapper.getRelationType() - ESM::DialogueCondition::Comp_Eq; } case 3: { - switch (infoSelectWrapper.getVariant().getType()) - { - case ESM::VT_Int: - { - return infoSelectWrapper.getVariant().getInteger(); - } - case ESM::VT_Float: - { - return infoSelectWrapper.getVariant().getFloat(); - } - default: - return QVariant(); - } + return infoSelectWrapper.getValue(); } default: throw std::runtime_error("Info condition subcolumn index out of range"); @@ -635,7 +621,7 @@ namespace CSMWorld { Info info = record.get(); - std::vector& conditions = info.mSelects; + auto& conditions = info.mSelects; if (subRowIndex < 0 || subRowIndex >= static_cast(conditions.size())) throw std::runtime_error("index out of range"); @@ -647,27 +633,18 @@ namespace CSMWorld { case 0: // Function { - infoSelectWrapper.setFunctionName(static_cast(value.toInt())); - - if (infoSelectWrapper.getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric - && infoSelectWrapper.getVariant().getType() != ESM::VT_Int) - { - infoSelectWrapper.getVariant().setType(ESM::VT_Int); - } - - infoSelectWrapper.update(); + infoSelectWrapper.setFunctionName(static_cast(value.toInt())); break; } case 1: // Variable { infoSelectWrapper.setVariableName(value.toString().toUtf8().constData()); - infoSelectWrapper.update(); break; } case 2: // Relation { - infoSelectWrapper.setRelationType(static_cast(value.toInt())); - infoSelectWrapper.update(); + infoSelectWrapper.setRelationType( + static_cast(value.toInt() + ESM::DialogueCondition::Comp_Eq)); break; } case 3: // Value @@ -679,13 +656,11 @@ namespace CSMWorld // QVariant seems to have issues converting 0 if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0) { - infoSelectWrapper.getVariant().setType(ESM::VT_Int); - infoSelectWrapper.getVariant().setInteger(value.toInt()); + infoSelectWrapper.setValue(value.toInt()); } else if (value.toFloat(&conversionResult) && conversionResult) { - infoSelectWrapper.getVariant().setType(ESM::VT_Float); - infoSelectWrapper.getVariant().setFloat(value.toFloat()); + infoSelectWrapper.setValue(value.toFloat()); } break; } @@ -694,8 +669,7 @@ namespace CSMWorld { if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0) { - infoSelectWrapper.getVariant().setType(ESM::VT_Int); - infoSelectWrapper.getVariant().setInteger(value.toInt()); + infoSelectWrapper.setValue(value.toInt()); } break; } diff --git a/apps/opencs/view/world/idcompletiondelegate.cpp b/apps/opencs/view/world/idcompletiondelegate.cpp index 3e26ed9250..e39be392f4 100644 --- a/apps/opencs/view/world/idcompletiondelegate.cpp +++ b/apps/opencs/view/world/idcompletiondelegate.cpp @@ -46,41 +46,41 @@ QWidget* CSVWorld::IdCompletionDelegate::createEditor(QWidget* parent, const QSt switch (conditionFunction) { - case CSMWorld::ConstInfoSelectWrapper::Function_Global: + case ESM::DialogueCondition::Function_Global: { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_GlobalVariable); } - case CSMWorld::ConstInfoSelectWrapper::Function_Journal: + case ESM::DialogueCondition::Function_Journal: { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Journal); } - case CSMWorld::ConstInfoSelectWrapper::Function_Item: + case ESM::DialogueCondition::Function_Item: { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Referenceable); } - case CSMWorld::ConstInfoSelectWrapper::Function_Dead: - case CSMWorld::ConstInfoSelectWrapper::Function_NotId: + case ESM::DialogueCondition::Function_Dead: + case ESM::DialogueCondition::Function_NotId: { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Referenceable); } - case CSMWorld::ConstInfoSelectWrapper::Function_NotFaction: + case ESM::DialogueCondition::Function_NotFaction: { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Faction); } - case CSMWorld::ConstInfoSelectWrapper::Function_NotClass: + case ESM::DialogueCondition::Function_NotClass: { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Class); } - case CSMWorld::ConstInfoSelectWrapper::Function_NotRace: + case ESM::DialogueCondition::Function_NotRace: { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Race); } - case CSMWorld::ConstInfoSelectWrapper::Function_NotCell: + case ESM::DialogueCondition::Function_NotCell: { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Cell); } - case CSMWorld::ConstInfoSelectWrapper::Function_Local: - case CSMWorld::ConstInfoSelectWrapper::Function_NotLocal: + case ESM::DialogueCondition::Function_Local: + case ESM::DialogueCondition::Function_NotLocal: { return new CSVWidget::DropLineEdit(display, parent); } diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 8b67ea28b3..295d690ce5 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -30,16 +30,16 @@ namespace { bool matchesStaticFilters(const MWDialogue::SelectWrapper& select, const MWWorld::Ptr& actor) { - const ESM::RefId selectId = ESM::RefId::stringRefId(select.getName()); - if (select.getFunction() == MWDialogue::SelectWrapper::Function_NotId) + const ESM::RefId selectId = select.getId(); + if (select.getFunction() == ESM::DialogueCondition::Function_NotId) return actor.getCellRef().getRefId() != selectId; if (actor.getClass().isNpc()) { - if (select.getFunction() == MWDialogue::SelectWrapper::Function_NotFaction) + if (select.getFunction() == ESM::DialogueCondition::Function_NotFaction) return actor.getClass().getPrimaryFaction(actor) != selectId; - else if (select.getFunction() == MWDialogue::SelectWrapper::Function_NotClass) + else if (select.getFunction() == ESM::DialogueCondition::Function_NotClass) return actor.get()->mBase->mClass != selectId; - else if (select.getFunction() == MWDialogue::SelectWrapper::Function_NotRace) + else if (select.getFunction() == ESM::DialogueCondition::Function_NotRace) return actor.get()->mBase->mRace != selectId; } return true; @@ -47,7 +47,7 @@ namespace bool matchesStaticFilters(const ESM::DialInfo& info, const MWWorld::Ptr& actor) { - for (const ESM::DialInfo::SelectStruct& select : info.mSelects) + for (const auto& select : info.mSelects) { MWDialogue::SelectWrapper wrapper = select; if (wrapper.getType() == MWDialogue::SelectWrapper::Type_Boolean) @@ -62,7 +62,7 @@ namespace } else if (wrapper.getType() == MWDialogue::SelectWrapper::Type_Numeric) { - if (wrapper.getFunction() == MWDialogue::SelectWrapper::Function_Local) + if (wrapper.getFunction() == ESM::DialogueCondition::Function_Local) { const ESM::RefId& scriptName = actor.getClass().getScript(actor); if (scriptName.empty()) @@ -207,9 +207,8 @@ bool MWDialogue::Filter::testPlayer(const ESM::DialInfo& info) const bool MWDialogue::Filter::testSelectStructs(const ESM::DialInfo& info) const { - for (std::vector::const_iterator iter(info.mSelects.begin()); - iter != info.mSelects.end(); ++iter) - if (!testSelectStruct(*iter)) + for (const auto& select : info.mSelects) + if (!testSelectStruct(select)) return false; return true; @@ -270,11 +269,11 @@ bool MWDialogue::Filter::testSelectStruct(const SelectWrapper& select) const // If the actor is a creature, we pass all conditions only applicable to NPCs. return true; - if (select.getFunction() == SelectWrapper::Function_Choice && mChoice == -1) + if (select.getFunction() == ESM::DialogueCondition::Function_Choice && mChoice == -1) // If not currently in a choice, we reject all conditions that test against choices. return false; - if (select.getFunction() == SelectWrapper::Function_Weather + if (select.getFunction() == ESM::DialogueCondition::Function_Weather && !(MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior())) // Reject weather conditions in interior cells @@ -305,29 +304,31 @@ bool MWDialogue::Filter::testSelectStructNumeric(const SelectWrapper& select) co { switch (select.getFunction()) { - case SelectWrapper::Function_Global: + case ESM::DialogueCondition::Function_Global: // internally all globals are float :( return select.selectCompare(MWBase::Environment::get().getWorld()->getGlobalFloat(select.getName())); - case SelectWrapper::Function_Local: + case ESM::DialogueCondition::Function_Local: { return testFunctionLocal(select); } - case SelectWrapper::Function_NotLocal: + case ESM::DialogueCondition::Function_NotLocal: { return !testFunctionLocal(select); } - case SelectWrapper::Function_PcHealthPercent: + case ESM::DialogueCondition::Function_PcHealthPercent: { MWWorld::Ptr player = MWMechanics::getPlayer(); return select.selectCompare( static_cast(player.getClass().getCreatureStats(player).getHealth().getRatio() * 100)); } - case SelectWrapper::Function_PcDynamicStat: + case ESM::DialogueCondition::Function_PcMagicka: + case ESM::DialogueCondition::Function_PcFatigue: + case ESM::DialogueCondition::Function_PcHealth: { MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -336,7 +337,7 @@ bool MWDialogue::Filter::testSelectStructNumeric(const SelectWrapper& select) co return select.selectCompare(value); } - case SelectWrapper::Function_HealthPercent: + case ESM::DialogueCondition::Function_Health_Percent: { return select.selectCompare( static_cast(mActor.getClass().getCreatureStats(mActor).getHealth().getRatio() * 100)); @@ -354,27 +355,29 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons switch (select.getFunction()) { - case SelectWrapper::Function_Journal: + case ESM::DialogueCondition::Function_Journal: - return MWBase::Environment::get().getJournal()->getJournalIndex(ESM::RefId::stringRefId(select.getName())); + return MWBase::Environment::get().getJournal()->getJournalIndex(select.getId()); - case SelectWrapper::Function_Item: + case ESM::DialogueCondition::Function_Item: { MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); - return store.count(ESM::RefId::stringRefId(select.getName())); + return store.count(select.getId()); } - case SelectWrapper::Function_Dead: + case ESM::DialogueCondition::Function_Dead: - return MWBase::Environment::get().getMechanicsManager()->countDeaths( - ESM::RefId::stringRefId(select.getName())); + return MWBase::Environment::get().getMechanicsManager()->countDeaths(select.getId()); - case SelectWrapper::Function_Choice: + case ESM::DialogueCondition::Function_Choice: return mChoice; - case SelectWrapper::Function_AiSetting: + case ESM::DialogueCondition::Function_Fight: + case ESM::DialogueCondition::Function_Hello: + case ESM::DialogueCondition::Function_Alarm: + case ESM::DialogueCondition::Function_Flee: { int argument = select.getArgument(); if (argument < 0 || argument > 3) @@ -387,32 +390,65 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons .getAiSetting(static_cast(argument)) .getModified(false); } - case SelectWrapper::Function_PcAttribute: + case ESM::DialogueCondition::Function_PcStrength: + case ESM::DialogueCondition::Function_PcIntelligence: + case ESM::DialogueCondition::Function_PcWillpower: + case ESM::DialogueCondition::Function_PcAgility: + case ESM::DialogueCondition::Function_PcSpeed: + case ESM::DialogueCondition::Function_PcEndurance: + case ESM::DialogueCondition::Function_PcPersonality: + case ESM::DialogueCondition::Function_PcLuck: { ESM::RefId attribute = ESM::Attribute::indexToRefId(select.getArgument()); return player.getClass().getCreatureStats(player).getAttribute(attribute).getModified(); } - case SelectWrapper::Function_PcSkill: + case ESM::DialogueCondition::Function_PcBlock: + case ESM::DialogueCondition::Function_PcArmorer: + case ESM::DialogueCondition::Function_PcMediumArmor: + case ESM::DialogueCondition::Function_PcHeavyArmor: + case ESM::DialogueCondition::Function_PcBluntWeapon: + case ESM::DialogueCondition::Function_PcLongBlade: + case ESM::DialogueCondition::Function_PcAxe: + case ESM::DialogueCondition::Function_PcSpear: + case ESM::DialogueCondition::Function_PcAthletics: + case ESM::DialogueCondition::Function_PcEnchant: + case ESM::DialogueCondition::Function_PcDestruction: + case ESM::DialogueCondition::Function_PcAlteration: + case ESM::DialogueCondition::Function_PcIllusion: + case ESM::DialogueCondition::Function_PcConjuration: + case ESM::DialogueCondition::Function_PcMysticism: + case ESM::DialogueCondition::Function_PcRestoration: + case ESM::DialogueCondition::Function_PcAlchemy: + case ESM::DialogueCondition::Function_PcUnarmored: + case ESM::DialogueCondition::Function_PcSecurity: + case ESM::DialogueCondition::Function_PcSneak: + case ESM::DialogueCondition::Function_PcAcrobatics: + case ESM::DialogueCondition::Function_PcLightArmor: + case ESM::DialogueCondition::Function_PcShortBlade: + case ESM::DialogueCondition::Function_PcMarksman: + case ESM::DialogueCondition::Function_PcMerchantile: + case ESM::DialogueCondition::Function_PcSpeechcraft: + case ESM::DialogueCondition::Function_PcHandToHand: { ESM::RefId skill = ESM::Skill::indexToRefId(select.getArgument()); return static_cast(player.getClass().getNpcStats(player).getSkill(skill).getModified()); } - case SelectWrapper::Function_FriendlyHit: + case ESM::DialogueCondition::Function_FriendHit: { int hits = mActor.getClass().getCreatureStats(mActor).getFriendlyHits(); return hits > 4 ? 4 : hits; } - case SelectWrapper::Function_PcLevel: + case ESM::DialogueCondition::Function_PcLevel: return player.getClass().getCreatureStats(player).getLevel(); - case SelectWrapper::Function_PcGender: + case ESM::DialogueCondition::Function_PcGender: return player.get()->mBase->isMale() ? 0 : 1; - case SelectWrapper::Function_PcClothingModifier: + case ESM::DialogueCondition::Function_PcClothingModifier: { const MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); @@ -429,11 +465,11 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons return value; } - case SelectWrapper::Function_PcCrimeLevel: + case ESM::DialogueCondition::Function_PcCrimeLevel: return player.getClass().getNpcStats(player).getBounty(); - case SelectWrapper::Function_RankRequirement: + case ESM::DialogueCondition::Function_RankRequirement: { const ESM::RefId& faction = mActor.getClass().getPrimaryFaction(mActor); if (faction.empty()) @@ -455,23 +491,23 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons return result; } - case SelectWrapper::Function_Level: + case ESM::DialogueCondition::Function_Level: return mActor.getClass().getCreatureStats(mActor).getLevel(); - case SelectWrapper::Function_PCReputation: + case ESM::DialogueCondition::Function_PcReputation: return player.getClass().getNpcStats(player).getReputation(); - case SelectWrapper::Function_Weather: + case ESM::DialogueCondition::Function_Weather: return MWBase::Environment::get().getWorld()->getCurrentWeather(); - case SelectWrapper::Function_Reputation: + case ESM::DialogueCondition::Function_Reputation: return mActor.getClass().getNpcStats(mActor).getReputation(); - case SelectWrapper::Function_FactionRankDiff: + case ESM::DialogueCondition::Function_FactionRankDifference: { const ESM::RefId& faction = mActor.getClass().getPrimaryFaction(mActor); @@ -483,14 +519,14 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons return rank - npcRank; } - case SelectWrapper::Function_WerewolfKills: + case ESM::DialogueCondition::Function_PcWerewolfKills: return player.getClass().getNpcStats(player).getWerewolfKills(); - case SelectWrapper::Function_RankLow: - case SelectWrapper::Function_RankHigh: + case ESM::DialogueCondition::Function_FacReactionLowest: + case ESM::DialogueCondition::Function_FacReactionHighest: { - bool low = select.getFunction() == SelectWrapper::Function_RankLow; + bool low = select.getFunction() == ESM::DialogueCondition::Function_FacReactionLowest; const ESM::RefId& factionId = mActor.getClass().getPrimaryFaction(mActor); @@ -513,7 +549,7 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons return value; } - case SelectWrapper::Function_CreatureTargetted: + case ESM::DialogueCondition::Function_CreatureTarget: { MWWorld::Ptr target; @@ -540,53 +576,49 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con switch (select.getFunction()) { - case SelectWrapper::Function_False: + case ESM::DialogueCondition::Function_NotId: - return false; - - case SelectWrapper::Function_NotId: - - return !(mActor.getCellRef().getRefId() == ESM::RefId::stringRefId(select.getName())); + return mActor.getCellRef().getRefId() != select.getId(); - case SelectWrapper::Function_NotFaction: + case ESM::DialogueCondition::Function_NotFaction: - return !(mActor.getClass().getPrimaryFaction(mActor) == ESM::RefId::stringRefId(select.getName())); + return mActor.getClass().getPrimaryFaction(mActor) != select.getId(); - case SelectWrapper::Function_NotClass: + case ESM::DialogueCondition::Function_NotClass: - return !(mActor.get()->mBase->mClass == ESM::RefId::stringRefId(select.getName())); + return mActor.get()->mBase->mClass != select.getId(); - case SelectWrapper::Function_NotRace: + case ESM::DialogueCondition::Function_NotRace: - return !(mActor.get()->mBase->mRace == ESM::RefId::stringRefId(select.getName())); + return mActor.get()->mBase->mRace != select.getId(); - case SelectWrapper::Function_NotCell: + case ESM::DialogueCondition::Function_NotCell: { std::string_view actorCell = MWBase::Environment::get().getWorld()->getCellName(mActor.getCell()); - return !Misc::StringUtils::ciStartsWith(actorCell, select.getName()); + return !Misc::StringUtils::ciStartsWith(actorCell, select.getCellName()); } - case SelectWrapper::Function_SameGender: + case ESM::DialogueCondition::Function_SameSex: return (player.get()->mBase->mFlags & ESM::NPC::Female) == (mActor.get()->mBase->mFlags & ESM::NPC::Female); - case SelectWrapper::Function_SameRace: + case ESM::DialogueCondition::Function_SameRace: return mActor.get()->mBase->mRace == player.get()->mBase->mRace; - case SelectWrapper::Function_SameFaction: + case ESM::DialogueCondition::Function_SameFaction: return player.getClass().getNpcStats(player).isInFaction(mActor.getClass().getPrimaryFaction(mActor)); - case SelectWrapper::Function_PcCommonDisease: + case ESM::DialogueCondition::Function_PcCommonDisease: return player.getClass().getCreatureStats(player).hasCommonDisease(); - case SelectWrapper::Function_PcBlightDisease: + case ESM::DialogueCondition::Function_PcBlightDisease: return player.getClass().getCreatureStats(player).hasBlightDisease(); - case SelectWrapper::Function_PcCorprus: + case ESM::DialogueCondition::Function_PcCorprus: return player.getClass() .getCreatureStats(player) @@ -595,7 +627,7 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con .getMagnitude() != 0; - case SelectWrapper::Function_PcExpelled: + case ESM::DialogueCondition::Function_PcExpelled: { const ESM::RefId& faction = mActor.getClass().getPrimaryFaction(mActor); @@ -605,7 +637,7 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con return player.getClass().getNpcStats(player).getExpelled(faction); } - case SelectWrapper::Function_PcVampire: + case ESM::DialogueCondition::Function_PcVampire: return player.getClass() .getCreatureStats(player) @@ -614,27 +646,27 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con .getMagnitude() > 0; - case SelectWrapper::Function_TalkedToPc: + case ESM::DialogueCondition::Function_TalkedToPc: return mTalkedToPlayer; - case SelectWrapper::Function_Alarmed: + case ESM::DialogueCondition::Function_Alarmed: return mActor.getClass().getCreatureStats(mActor).isAlarmed(); - case SelectWrapper::Function_Detected: + case ESM::DialogueCondition::Function_Detected: return MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, mActor); - case SelectWrapper::Function_Attacked: + case ESM::DialogueCondition::Function_Attacked: return mActor.getClass().getCreatureStats(mActor).getAttacked(); - case SelectWrapper::Function_ShouldAttack: + case ESM::DialogueCondition::Function_ShouldAttack: return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor, MWMechanics::getPlayer()); - case SelectWrapper::Function_Werewolf: + case ESM::DialogueCondition::Function_Werewolf: return mActor.getClass().getNpcStats(mActor).isWerewolf(); diff --git a/apps/openmw/mwdialogue/selectwrapper.cpp b/apps/openmw/mwdialogue/selectwrapper.cpp index 94f7f73097..02c9d29b59 100644 --- a/apps/openmw/mwdialogue/selectwrapper.cpp +++ b/apps/openmw/mwdialogue/selectwrapper.cpp @@ -10,431 +10,264 @@ namespace { template - bool selectCompareImp(char comp, T1 value1, T2 value2) + bool selectCompareImp(ESM::DialogueCondition::Comparison comp, T1 value1, T2 value2) { switch (comp) { - case '0': + case ESM::DialogueCondition::Comp_Eq: return value1 == value2; - case '1': + case ESM::DialogueCondition::Comp_Ne: return value1 != value2; - case '2': + case ESM::DialogueCondition::Comp_Gt: return value1 > value2; - case '3': + case ESM::DialogueCondition::Comp_Ge: return value1 >= value2; - case '4': + case ESM::DialogueCondition::Comp_Ls: return value1 < value2; - case '5': + case ESM::DialogueCondition::Comp_Le: return value1 <= value2; + default: + throw std::runtime_error("unknown compare type in dialogue info select"); } - - throw std::runtime_error("unknown compare type in dialogue info select"); } template - bool selectCompareImp(const ESM::DialInfo::SelectStruct& select, T value1) - { - if (select.mValue.getType() == ESM::VT_Int) - { - return selectCompareImp(select.mSelectRule[4], value1, select.mValue.getInteger()); - } - else if (select.mValue.getType() == ESM::VT_Float) - { - return selectCompareImp(select.mSelectRule[4], value1, select.mValue.getFloat()); - } - else - throw std::runtime_error("unsupported variable type in dialogue info select"); - } -} - -MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::decodeFunction() const -{ - const int index = Misc::StringUtils::toNumeric(mSelect.mSelectRule.substr(2, 2), 0); - - switch (index) + bool selectCompareImp(const ESM::DialogueCondition& select, T value1) { - case 0: - return Function_RankLow; - case 1: - return Function_RankHigh; - case 2: - return Function_RankRequirement; - case 3: - return Function_Reputation; - case 4: - return Function_HealthPercent; - case 5: - return Function_PCReputation; - case 6: - return Function_PcLevel; - case 7: - return Function_PcHealthPercent; - case 8: - case 9: - return Function_PcDynamicStat; - case 10: - return Function_PcAttribute; - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - case 26: - case 27: - case 28: - case 29: - case 30: - case 31: - case 32: - case 33: - case 34: - case 35: - case 36: - case 37: - return Function_PcSkill; - case 38: - return Function_PcGender; - case 39: - return Function_PcExpelled; - case 40: - return Function_PcCommonDisease; - case 41: - return Function_PcBlightDisease; - case 42: - return Function_PcClothingModifier; - case 43: - return Function_PcCrimeLevel; - case 44: - return Function_SameGender; - case 45: - return Function_SameRace; - case 46: - return Function_SameFaction; - case 47: - return Function_FactionRankDiff; - case 48: - return Function_Detected; - case 49: - return Function_Alarmed; - case 50: - return Function_Choice; - case 51: - case 52: - case 53: - case 54: - case 55: - case 56: - case 57: - return Function_PcAttribute; - case 58: - return Function_PcCorprus; - case 59: - return Function_Weather; - case 60: - return Function_PcVampire; - case 61: - return Function_Level; - case 62: - return Function_Attacked; - case 63: - return Function_TalkedToPc; - case 64: - return Function_PcDynamicStat; - case 65: - return Function_CreatureTargetted; - case 66: - return Function_FriendlyHit; - case 67: - case 68: - case 69: - case 70: - return Function_AiSetting; - case 71: - return Function_ShouldAttack; - case 72: - return Function_Werewolf; - case 73: - return Function_WerewolfKills; + return std::visit( + [&](auto value) { return selectCompareImp(select.mComparison, value1, value); }, select.mValue); } - - return Function_False; } -MWDialogue::SelectWrapper::SelectWrapper(const ESM::DialInfo::SelectStruct& select) +MWDialogue::SelectWrapper::SelectWrapper(const ESM::DialogueCondition& select) : mSelect(select) { } -MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::getFunction() const +ESM::DialogueCondition::Function MWDialogue::SelectWrapper::getFunction() const { - char type = mSelect.mSelectRule[1]; - - switch (type) - { - case '1': - return decodeFunction(); - case '2': - return Function_Global; - case '3': - return Function_Local; - case '4': - return Function_Journal; - case '5': - return Function_Item; - case '6': - return Function_Dead; - case '7': - return Function_NotId; - case '8': - return Function_NotFaction; - case '9': - return Function_NotClass; - case 'A': - return Function_NotRace; - case 'B': - return Function_NotCell; - case 'C': - return Function_NotLocal; - } - - return Function_None; + return mSelect.mFunction; } int MWDialogue::SelectWrapper::getArgument() const { - if (mSelect.mSelectRule[1] != '1') - return 0; - - int index = 0; - - std::istringstream(mSelect.mSelectRule.substr(2, 2)) >> index; - - switch (index) + switch (mSelect.mFunction) { // AI settings - case 67: + case ESM::DialogueCondition::Function_Fight: return 1; - case 68: + case ESM::DialogueCondition::Function_Hello: return 0; - case 69: + case ESM::DialogueCondition::Function_Alarm: return 3; - case 70: + case ESM::DialogueCondition::Function_Flee: return 2; // attributes - case 10: + case ESM::DialogueCondition::Function_PcStrength: return 0; - case 51: + case ESM::DialogueCondition::Function_PcIntelligence: return 1; - case 52: + case ESM::DialogueCondition::Function_PcWillpower: return 2; - case 53: + case ESM::DialogueCondition::Function_PcAgility: return 3; - case 54: + case ESM::DialogueCondition::Function_PcSpeed: return 4; - case 55: + case ESM::DialogueCondition::Function_PcEndurance: return 5; - case 56: + case ESM::DialogueCondition::Function_PcPersonality: return 6; - case 57: + case ESM::DialogueCondition::Function_PcLuck: return 7; // skills - case 11: + case ESM::DialogueCondition::Function_PcBlock: return 0; - case 12: + case ESM::DialogueCondition::Function_PcArmorer: return 1; - case 13: + case ESM::DialogueCondition::Function_PcMediumArmor: return 2; - case 14: + case ESM::DialogueCondition::Function_PcHeavyArmor: return 3; - case 15: + case ESM::DialogueCondition::Function_PcBluntWeapon: return 4; - case 16: + case ESM::DialogueCondition::Function_PcLongBlade: return 5; - case 17: + case ESM::DialogueCondition::Function_PcAxe: return 6; - case 18: + case ESM::DialogueCondition::Function_PcSpear: return 7; - case 19: + case ESM::DialogueCondition::Function_PcAthletics: return 8; - case 20: + case ESM::DialogueCondition::Function_PcEnchant: return 9; - case 21: + case ESM::DialogueCondition::Function_PcDestruction: return 10; - case 22: + case ESM::DialogueCondition::Function_PcAlteration: return 11; - case 23: + case ESM::DialogueCondition::Function_PcIllusion: return 12; - case 24: + case ESM::DialogueCondition::Function_PcConjuration: return 13; - case 25: + case ESM::DialogueCondition::Function_PcMysticism: return 14; - case 26: + case ESM::DialogueCondition::Function_PcRestoration: return 15; - case 27: + case ESM::DialogueCondition::Function_PcAlchemy: return 16; - case 28: + case ESM::DialogueCondition::Function_PcUnarmored: return 17; - case 29: + case ESM::DialogueCondition::Function_PcSecurity: return 18; - case 30: + case ESM::DialogueCondition::Function_PcSneak: return 19; - case 31: + case ESM::DialogueCondition::Function_PcAcrobatics: return 20; - case 32: + case ESM::DialogueCondition::Function_PcLightArmor: return 21; - case 33: + case ESM::DialogueCondition::Function_PcShortBlade: return 22; - case 34: + case ESM::DialogueCondition::Function_PcMarksman: return 23; - case 35: + case ESM::DialogueCondition::Function_PcMerchantile: return 24; - case 36: + case ESM::DialogueCondition::Function_PcSpeechcraft: return 25; - case 37: + case ESM::DialogueCondition::Function_PcHandToHand: return 26; // dynamic stats - case 8: + case ESM::DialogueCondition::Function_PcMagicka: return 1; - case 9: + case ESM::DialogueCondition::Function_PcFatigue: return 2; - case 64: + case ESM::DialogueCondition::Function_PcHealth: + return 0; + default: return 0; } - - return 0; } MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const { - static const Function integerFunctions[] = { - Function_Journal, - Function_Item, - Function_Dead, - Function_Choice, - Function_AiSetting, - Function_PcAttribute, - Function_PcSkill, - Function_FriendlyHit, - Function_PcLevel, - Function_PcGender, - Function_PcClothingModifier, - Function_PcCrimeLevel, - Function_RankRequirement, - Function_Level, - Function_PCReputation, - Function_Weather, - Function_Reputation, - Function_FactionRankDiff, - Function_WerewolfKills, - Function_RankLow, - Function_RankHigh, - Function_CreatureTargetted, - // end marker - Function_None, - }; - - static const Function numericFunctions[] = { - Function_Global, - Function_Local, - Function_NotLocal, - Function_PcDynamicStat, - Function_PcHealthPercent, - Function_HealthPercent, - // end marker - Function_None, - }; - - static const Function booleanFunctions[] = { - Function_False, - Function_SameGender, - Function_SameRace, - Function_SameFaction, - Function_PcCommonDisease, - Function_PcBlightDisease, - Function_PcCorprus, - Function_PcExpelled, - Function_PcVampire, - Function_TalkedToPc, - Function_Alarmed, - Function_Detected, - Function_Attacked, - Function_ShouldAttack, - Function_Werewolf, - // end marker - Function_None, - }; - - static const Function invertedBooleanFunctions[] = { - Function_NotId, - Function_NotFaction, - Function_NotClass, - Function_NotRace, - Function_NotCell, - // end marker - Function_None, - }; - - Function function = getFunction(); - - for (int i = 0; integerFunctions[i] != Function_None; ++i) - if (integerFunctions[i] == function) + switch (mSelect.mFunction) + { + case ESM::DialogueCondition::Function_Journal: + case ESM::DialogueCondition::Function_Item: + case ESM::DialogueCondition::Function_Dead: + case ESM::DialogueCondition::Function_Choice: + case ESM::DialogueCondition::Function_Fight: + case ESM::DialogueCondition::Function_Hello: + case ESM::DialogueCondition::Function_Alarm: + case ESM::DialogueCondition::Function_Flee: + case ESM::DialogueCondition::Function_PcStrength: + case ESM::DialogueCondition::Function_PcIntelligence: + case ESM::DialogueCondition::Function_PcWillpower: + case ESM::DialogueCondition::Function_PcAgility: + case ESM::DialogueCondition::Function_PcSpeed: + case ESM::DialogueCondition::Function_PcEndurance: + case ESM::DialogueCondition::Function_PcPersonality: + case ESM::DialogueCondition::Function_PcLuck: + case ESM::DialogueCondition::Function_PcBlock: + case ESM::DialogueCondition::Function_PcArmorer: + case ESM::DialogueCondition::Function_PcMediumArmor: + case ESM::DialogueCondition::Function_PcHeavyArmor: + case ESM::DialogueCondition::Function_PcBluntWeapon: + case ESM::DialogueCondition::Function_PcLongBlade: + case ESM::DialogueCondition::Function_PcAxe: + case ESM::DialogueCondition::Function_PcSpear: + case ESM::DialogueCondition::Function_PcAthletics: + case ESM::DialogueCondition::Function_PcEnchant: + case ESM::DialogueCondition::Function_PcDestruction: + case ESM::DialogueCondition::Function_PcAlteration: + case ESM::DialogueCondition::Function_PcIllusion: + case ESM::DialogueCondition::Function_PcConjuration: + case ESM::DialogueCondition::Function_PcMysticism: + case ESM::DialogueCondition::Function_PcRestoration: + case ESM::DialogueCondition::Function_PcAlchemy: + case ESM::DialogueCondition::Function_PcUnarmored: + case ESM::DialogueCondition::Function_PcSecurity: + case ESM::DialogueCondition::Function_PcSneak: + case ESM::DialogueCondition::Function_PcAcrobatics: + case ESM::DialogueCondition::Function_PcLightArmor: + case ESM::DialogueCondition::Function_PcShortBlade: + case ESM::DialogueCondition::Function_PcMarksman: + case ESM::DialogueCondition::Function_PcMerchantile: + case ESM::DialogueCondition::Function_PcSpeechcraft: + case ESM::DialogueCondition::Function_PcHandToHand: + case ESM::DialogueCondition::Function_FriendHit: + case ESM::DialogueCondition::Function_PcLevel: + case ESM::DialogueCondition::Function_PcGender: + case ESM::DialogueCondition::Function_PcClothingModifier: + case ESM::DialogueCondition::Function_PcCrimeLevel: + case ESM::DialogueCondition::Function_RankRequirement: + case ESM::DialogueCondition::Function_Level: + case ESM::DialogueCondition::Function_PcReputation: + case ESM::DialogueCondition::Function_Weather: + case ESM::DialogueCondition::Function_Reputation: + case ESM::DialogueCondition::Function_FactionRankDifference: + case ESM::DialogueCondition::Function_PcWerewolfKills: + case ESM::DialogueCondition::Function_FacReactionLowest: + case ESM::DialogueCondition::Function_FacReactionHighest: + case ESM::DialogueCondition::Function_CreatureTarget: return Type_Integer; - - for (int i = 0; numericFunctions[i] != Function_None; ++i) - if (numericFunctions[i] == function) + case ESM::DialogueCondition::Function_Global: + case ESM::DialogueCondition::Function_Local: + case ESM::DialogueCondition::Function_NotLocal: + case ESM::DialogueCondition::Function_PcHealth: + case ESM::DialogueCondition::Function_PcMagicka: + case ESM::DialogueCondition::Function_PcFatigue: + case ESM::DialogueCondition::Function_PcHealthPercent: + case ESM::DialogueCondition::Function_Health_Percent: return Type_Numeric; - - for (int i = 0; booleanFunctions[i] != Function_None; ++i) - if (booleanFunctions[i] == function) + case ESM::DialogueCondition::Function_SameSex: + case ESM::DialogueCondition::Function_SameRace: + case ESM::DialogueCondition::Function_SameFaction: + case ESM::DialogueCondition::Function_PcCommonDisease: + case ESM::DialogueCondition::Function_PcBlightDisease: + case ESM::DialogueCondition::Function_PcCorprus: + case ESM::DialogueCondition::Function_PcExpelled: + case ESM::DialogueCondition::Function_PcVampire: + case ESM::DialogueCondition::Function_TalkedToPc: + case ESM::DialogueCondition::Function_Alarmed: + case ESM::DialogueCondition::Function_Detected: + case ESM::DialogueCondition::Function_Attacked: + case ESM::DialogueCondition::Function_ShouldAttack: + case ESM::DialogueCondition::Function_Werewolf: return Type_Boolean; - - for (int i = 0; invertedBooleanFunctions[i] != Function_None; ++i) - if (invertedBooleanFunctions[i] == function) + case ESM::DialogueCondition::Function_NotId: + case ESM::DialogueCondition::Function_NotFaction: + case ESM::DialogueCondition::Function_NotClass: + case ESM::DialogueCondition::Function_NotRace: + case ESM::DialogueCondition::Function_NotCell: return Type_Inverted; - - return Type_None; + default: + return Type_None; + }; } bool MWDialogue::SelectWrapper::isNpcOnly() const { - static const Function functions[] = { - Function_NotFaction, - Function_NotClass, - Function_NotRace, - Function_SameGender, - Function_SameRace, - Function_SameFaction, - Function_RankRequirement, - Function_Reputation, - Function_FactionRankDiff, - Function_Werewolf, - Function_WerewolfKills, - Function_RankLow, - Function_RankHigh, - // end marker - Function_None, - }; - - Function function = getFunction(); - - for (int i = 0; functions[i] != Function_None; ++i) - if (functions[i] == function) + switch (mSelect.mFunction) + { + case ESM::DialogueCondition::Function_NotFaction: + case ESM::DialogueCondition::Function_NotClass: + case ESM::DialogueCondition::Function_NotRace: + case ESM::DialogueCondition::Function_SameSex: + case ESM::DialogueCondition::Function_SameRace: + case ESM::DialogueCondition::Function_SameFaction: + case ESM::DialogueCondition::Function_RankRequirement: + case ESM::DialogueCondition::Function_Reputation: + case ESM::DialogueCondition::Function_FactionRankDifference: + case ESM::DialogueCondition::Function_Werewolf: + case ESM::DialogueCondition::Function_PcWerewolfKills: + case ESM::DialogueCondition::Function_FacReactionLowest: + case ESM::DialogueCondition::Function_FacReactionHighest: return true; - - return false; + default: + return false; + } } bool MWDialogue::SelectWrapper::selectCompare(int value) const @@ -454,5 +287,15 @@ bool MWDialogue::SelectWrapper::selectCompare(bool value) const std::string MWDialogue::SelectWrapper::getName() const { - return Misc::StringUtils::lowerCase(std::string_view(mSelect.mSelectRule).substr(5)); + return Misc::StringUtils::lowerCase(mSelect.mVariable); +} + +std::string_view MWDialogue::SelectWrapper::getCellName() const +{ + return mSelect.mVariable; +} + +ESM::RefId MWDialogue::SelectWrapper::getId() const +{ + return ESM::RefId::stringRefId(mSelect.mVariable); } diff --git a/apps/openmw/mwdialogue/selectwrapper.hpp b/apps/openmw/mwdialogue/selectwrapper.hpp index 0d376d957c..d831b6cea0 100644 --- a/apps/openmw/mwdialogue/selectwrapper.hpp +++ b/apps/openmw/mwdialogue/selectwrapper.hpp @@ -7,62 +7,9 @@ namespace MWDialogue { class SelectWrapper { - const ESM::DialInfo::SelectStruct& mSelect; + const ESM::DialogueCondition& mSelect; public: - enum Function - { - Function_None, - Function_False, - Function_Journal, - Function_Item, - Function_Dead, - Function_NotId, - Function_NotFaction, - Function_NotClass, - Function_NotRace, - Function_NotCell, - Function_NotLocal, - Function_Local, - Function_Global, - Function_SameGender, - Function_SameRace, - Function_SameFaction, - Function_Choice, - Function_PcCommonDisease, - Function_PcBlightDisease, - Function_PcCorprus, - Function_AiSetting, - Function_PcAttribute, - Function_PcSkill, - Function_PcExpelled, - Function_PcVampire, - Function_FriendlyHit, - Function_TalkedToPc, - Function_PcLevel, - Function_PcHealthPercent, - Function_PcDynamicStat, - Function_PcGender, - Function_PcClothingModifier, - Function_PcCrimeLevel, - Function_RankRequirement, - Function_HealthPercent, - Function_Level, - Function_PCReputation, - Function_Weather, - Function_Reputation, - Function_Alarmed, - Function_FactionRankDiff, - Function_Detected, - Function_Attacked, - Function_ShouldAttack, - Function_CreatureTargetted, - Function_Werewolf, - Function_WerewolfKills, - Function_RankLow, - Function_RankHigh - }; - enum Type { Type_None, @@ -72,13 +19,10 @@ namespace MWDialogue Type_Inverted }; - private: - Function decodeFunction() const; - public: - SelectWrapper(const ESM::DialInfo::SelectStruct& select); + SelectWrapper(const ESM::DialogueCondition& select); - Function getFunction() const; + ESM::DialogueCondition::Function getFunction() const; int getArgument() const; @@ -95,6 +39,10 @@ namespace MWDialogue std::string getName() const; ///< Return case-smashed name. + + std::string_view getCellName() const; + + ESM::RefId getId() const; }; } diff --git a/apps/openmw_test_suite/esm3/testsaveload.cpp b/apps/openmw_test_suite/esm3/testsaveload.cpp index 2629442563..afffbbb3d6 100644 --- a/apps/openmw_test_suite/esm3/testsaveload.cpp +++ b/apps/openmw_test_suite/esm3/testsaveload.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -603,6 +604,83 @@ namespace ESM EXPECT_EQ(result.mIcon, record.mIcon); } + TEST_P(Esm3SaveLoadRecordTest, infoShouldNotChange) + { + DialInfo record = { + .mData = { + .mType = ESM::Dialogue::Topic, + .mDisposition = 1, + .mRank = 2, + .mGender = ESM::DialInfo::NA, + .mPCrank = 3, + }, + .mSelects = { + ESM::DialogueCondition{ + .mVariable = {}, + .mValue = 42, + .mIndex = 0, + .mFunction = ESM::DialogueCondition::Function_Level, + .mComparison = ESM::DialogueCondition::Comp_Eq + }, + ESM::DialogueCondition{ + .mVariable = generateRandomString(32), + .mValue = 0, + .mIndex = 1, + .mFunction = ESM::DialogueCondition::Function_NotLocal, + .mComparison = ESM::DialogueCondition::Comp_Eq + }, + }, + .mId = generateRandomRefId(32), + .mPrev = generateRandomRefId(32), + .mNext = generateRandomRefId(32), + .mActor = generateRandomRefId(32), + .mRace = generateRandomRefId(32), + .mClass = generateRandomRefId(32), + .mFaction = generateRandomRefId(32), + .mPcFaction = generateRandomRefId(32), + .mCell = generateRandomRefId(32), + .mSound = generateRandomString(32), + .mResponse = generateRandomString(32), + .mResultScript = generateRandomString(32), + .mFactionLess = false, + .mQuestStatus = ESM::DialInfo::QS_None, + }; + + DialInfo result; + saveAndLoadRecord(record, GetParam(), result); + + EXPECT_EQ(result.mData.mType, record.mData.mType); + EXPECT_EQ(result.mData.mDisposition, record.mData.mDisposition); + EXPECT_EQ(result.mData.mRank, record.mData.mRank); + EXPECT_EQ(result.mData.mGender, record.mData.mGender); + EXPECT_EQ(result.mData.mPCrank, record.mData.mPCrank); + EXPECT_EQ(result.mId, record.mId); + EXPECT_EQ(result.mPrev, record.mPrev); + EXPECT_EQ(result.mNext, record.mNext); + EXPECT_EQ(result.mActor, record.mActor); + EXPECT_EQ(result.mRace, record.mRace); + EXPECT_EQ(result.mClass, record.mClass); + EXPECT_EQ(result.mFaction, record.mFaction); + EXPECT_EQ(result.mPcFaction, record.mPcFaction); + EXPECT_EQ(result.mCell, record.mCell); + EXPECT_EQ(result.mSound, record.mSound); + EXPECT_EQ(result.mResponse, record.mResponse); + EXPECT_EQ(result.mResultScript, record.mResultScript); + EXPECT_EQ(result.mFactionLess, record.mFactionLess); + EXPECT_EQ(result.mQuestStatus, record.mQuestStatus); + EXPECT_EQ(result.mSelects.size(), record.mSelects.size()); + for (size_t i = 0; i < result.mSelects.size(); ++i) + { + const auto& resultS = result.mSelects[i]; + const auto& recordS = record.mSelects[i]; + EXPECT_EQ(resultS.mVariable, recordS.mVariable); + EXPECT_EQ(resultS.mValue, recordS.mValue); + EXPECT_EQ(resultS.mIndex, recordS.mIndex); + EXPECT_EQ(resultS.mFunction, recordS.mFunction); + EXPECT_EQ(resultS.mComparison, recordS.mComparison); + } + } + INSTANTIATE_TEST_SUITE_P(FormatVersions, Esm3SaveLoadRecordTest, ValuesIn(getFormats())); } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 7ac01ef169..68411be2fc 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -181,7 +181,7 @@ add_component_dir (esm3 inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile aisequence magiceffects custommarkerstate stolenitems transport animationstate controlsstate mappings readerscache - infoorder timestamp formatversion landrecorddata selectiongroup + infoorder timestamp formatversion landrecorddata selectiongroup dialoguecondition ) add_component_dir (esmterrain diff --git a/components/esm3/dialoguecondition.cpp b/components/esm3/dialoguecondition.cpp new file mode 100644 index 0000000000..a6a28307c2 --- /dev/null +++ b/components/esm3/dialoguecondition.cpp @@ -0,0 +1,206 @@ +#include "dialoguecondition.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" +#include "variant.hpp" + +#include +#include +#include + +namespace ESM +{ + std::optional DialogueCondition::load(ESMReader& esm, ESM::RefId context) + { + std::string rule = esm.getHString(); + ESM::Variant variant; + variant.read(esm, Variant::Format_Info); + if (rule.size() < 5) + { + Log(Debug::Warning) << "Found invalid SCVR rule of size " << rule.size() << " in INFO " << context; + return {}; + } + if (rule[4] < '0' || rule[4] > '5') + { + Log(Debug::Warning) << "Found invalid SCVR comparison operator " << static_cast(rule[4]) << " in INFO " + << context; + return {}; + } + DialogueCondition condition; + if (rule[0] >= '0' && rule[0] <= '9') + condition.mIndex = rule[0] - '0'; + else + { + Log(Debug::Info) << "Found invalid SCVR index " << static_cast(rule[0]) << " in INFO " << context; + condition.mIndex = 0; + } + if (rule[1] == '1') + { + int function = Misc::StringUtils::toNumeric(std::string_view{ rule }.substr(2, 2), -1); + if (function >= Function_FacReactionLowest && function <= Function_PcWerewolfKills) + condition.mFunction = static_cast(function); + else + { + Log(Debug::Warning) << "Encountered invalid SCVR function index " << function << " in INFO " << context; + return {}; + } + } + else if ((rule[1] > '1' && rule[1] <= '9') || (rule[1] >= 'A' && rule[1] <= 'C')) + { + if (rule.size() == 5) + { + Log(Debug::Warning) << "Missing variable for SCVR of type " << rule[1] << " in INFO " << context; + return {}; + } + bool malformed = rule[3] != 'X'; + if (rule[1] == '2') + { + condition.mFunction = Function_Global; + malformed |= rule[2] != 'f' && rule[2] != 'l' && rule[2] != 's'; + } + else if (rule[1] == '3') + { + condition.mFunction = Function_Local; + malformed |= rule[2] != 'f' && rule[2] != 'l' && rule[2] != 's'; + } + else if (rule[1] == '4') + { + condition.mFunction = Function_Journal; + malformed |= rule[2] != 'J'; + } + else if (rule[1] == '5') + { + condition.mFunction = Function_Item; + malformed |= rule[2] != 'I'; + } + else if (rule[1] == '6') + { + condition.mFunction = Function_Dead; + malformed |= rule[2] != 'D'; + } + else if (rule[1] == '7') + { + condition.mFunction = Function_NotId; + malformed |= rule[2] != 'X'; + } + else if (rule[1] == '8') + { + condition.mFunction = Function_NotFaction; + malformed |= rule[2] != 'F'; + } + else if (rule[1] == '9') + { + condition.mFunction = Function_NotClass; + malformed |= rule[2] != 'C'; + } + else if (rule[1] == 'A') + { + condition.mFunction = Function_NotRace; + malformed |= rule[2] != 'R'; + } + else if (rule[1] == 'B') + { + condition.mFunction = Function_NotCell; + malformed |= rule[2] != 'L'; + } + else if (rule[1] == 'C') + { + condition.mFunction = Function_NotLocal; + malformed |= rule[2] != 'f' && rule[2] != 'l' && rule[2] != 's'; + } + if (malformed) + Log(Debug::Info) << "Found malformed SCVR rule in INFO " << context; + } + else + { + Log(Debug::Warning) << "Found invalid SCVR function " << static_cast(rule[1]) << " in INFO " + << context; + return {}; + } + condition.mComparison = static_cast(rule[4]); + condition.mVariable = rule.substr(5); + if (variant.getType() == VT_Int) + condition.mValue = variant.getInteger(); + else if (variant.getType() == VT_Float) + condition.mValue = variant.getFloat(); + else + { + Log(Debug::Warning) << "Found invalid SCVR variant " << variant.getType() << " in INFO " << context; + return {}; + } + return condition; + } + + void DialogueCondition::save(ESMWriter& esm) const + { + auto variant = std::visit([](auto value) { return ESM::Variant(value); }, mValue); + if (variant.getType() != VT_Float) + variant.setType(VT_Int); + std::string rule; + rule.reserve(5 + mVariable.size()); + rule += static_cast(mIndex + '0'); + const auto appendVariableType = [&]() { + if (variant.getType() == VT_Float) + rule += "fX"; + else + { + int32_t value = variant.getInteger(); + if (static_cast(value) == value) + rule += "sX"; + else + rule += "lX"; + } + }; + if (mFunction == Function_Global) + { + rule += '2'; + appendVariableType(); + } + else if (mFunction == Function_Local) + { + rule += '3'; + appendVariableType(); + } + else if (mFunction == Function_Journal) + rule += "4JX"; + else if (mFunction == Function_Item) + rule += "5IX"; + else if (mFunction == Function_Dead) + rule += "6DX"; + else if (mFunction == Function_NotId) + rule += "7XX"; + else if (mFunction == Function_NotFaction) + rule += "8FX"; + else if (mFunction == Function_NotClass) + rule += "9CX"; + else if (mFunction == Function_NotRace) + rule += "ARX"; + else if (mFunction == Function_NotCell) + rule += "BLX"; + else if (mFunction == Function_NotLocal) + { + rule += 'C'; + appendVariableType(); + } + else + { + rule += "100"; + char* start = rule.data() + rule.size(); + char* end = start; + if (mFunction < Function_PcStrength) + start--; + else + start -= 2; + auto result = std::to_chars(start, end, static_cast(mFunction)); + if (result.ec != std::errc()) + { + Log(Debug::Error) << "Failed to save SCVR rule"; + return; + } + } + rule += static_cast(mComparison); + rule += mVariable; + esm.writeHNString("SCVR", rule); + variant.write(esm, Variant::Format_Info); + } +} diff --git a/components/esm3/dialoguecondition.hpp b/components/esm3/dialoguecondition.hpp new file mode 100644 index 0000000000..c06d50b601 --- /dev/null +++ b/components/esm3/dialoguecondition.hpp @@ -0,0 +1,134 @@ +#ifndef OPENMW_ESM3_DIALOGUECONDITION_H +#define OPENMW_ESM3_DIALOGUECONDITION_H + +#include +#include +#include +#include + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + struct DialogueCondition + { + enum Function : std::int8_t + { + Function_FacReactionLowest = 0, + Function_FacReactionHighest, + Function_RankRequirement, + Function_Reputation, + Function_Health_Percent, + Function_PcReputation, + Function_PcLevel, + Function_PcHealthPercent, + Function_PcMagicka, + Function_PcFatigue, + Function_PcStrength, + Function_PcBlock, + Function_PcArmorer, + Function_PcMediumArmor, + Function_PcHeavyArmor, + Function_PcBluntWeapon, + Function_PcLongBlade, + Function_PcAxe, + Function_PcSpear, + Function_PcAthletics, + Function_PcEnchant, + Function_PcDestruction, + Function_PcAlteration, + Function_PcIllusion, + Function_PcConjuration, + Function_PcMysticism, + Function_PcRestoration, + Function_PcAlchemy, + Function_PcUnarmored, + Function_PcSecurity, + Function_PcSneak, + Function_PcAcrobatics, + Function_PcLightArmor, + Function_PcShortBlade, + Function_PcMarksman, + Function_PcMerchantile, + Function_PcSpeechcraft, + Function_PcHandToHand, + Function_PcGender, + Function_PcExpelled, + Function_PcCommonDisease, + Function_PcBlightDisease, + Function_PcClothingModifier, + Function_PcCrimeLevel, + Function_SameSex, + Function_SameRace, + Function_SameFaction, + Function_FactionRankDifference, + Function_Detected, + Function_Alarmed, + Function_Choice, + Function_PcIntelligence, + Function_PcWillpower, + Function_PcAgility, + Function_PcSpeed, + Function_PcEndurance, + Function_PcPersonality, + Function_PcLuck, + Function_PcCorprus, + Function_Weather, + Function_PcVampire, + Function_Level, + Function_Attacked, + Function_TalkedToPc, + Function_PcHealth, + Function_CreatureTarget, + Function_FriendHit, + Function_Fight, + Function_Hello, + Function_Alarm, + Function_Flee, + Function_ShouldAttack, + Function_Werewolf, + Function_PcWerewolfKills = 73, + + Function_Global, + Function_Local, + Function_Journal, + Function_Item, + Function_Dead, + Function_NotId, + Function_NotFaction, + Function_NotClass, + Function_NotRace, + Function_NotCell, + Function_NotLocal, + + Function_None, // Editor only + }; + + enum Comparison : char + { + Comp_Eq = '0', + Comp_Ne = '1', + Comp_Gt = '2', + Comp_Ge = '3', + Comp_Ls = '4', + Comp_Le = '5', + + Comp_None = ' ', // Editor only + }; + + std::string mVariable; + std::variant mValue = 0; + std::uint8_t mIndex = 0; + Function mFunction = Function_None; + Comparison mComparison = Comp_None; + + static std::optional load(ESMReader& esm, ESM::RefId context); + + void save(ESMWriter& esm) const; + }; +} + +#endif diff --git a/components/esm3/loadinfo.cpp b/components/esm3/loadinfo.cpp index 714d59fef4..8b1147ed45 100644 --- a/components/esm3/loadinfo.cpp +++ b/components/esm3/loadinfo.cpp @@ -66,10 +66,9 @@ namespace ESM break; case fourCC("SCVR"): { - SelectStruct ss; - ss.mSelectRule = esm.getHString(); - ss.mValue.read(esm, Variant::Format_Info); - mSelects.push_back(ss); + auto filter = DialogueCondition::load(esm, mId); + if (filter) + mSelects.emplace_back(std::move(*filter)); break; } case fourCC("BNAM"): @@ -120,11 +119,8 @@ namespace ESM esm.writeHNOCString("SNAM", mSound); esm.writeHNOString("NAME", mResponse); - for (std::vector::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) - { - esm.writeHNString("SCVR", it->mSelectRule); - it->mValue.write(esm, Variant::Format_Info); - } + for (const auto& rule : mSelects) + rule.save(esm); esm.writeHNOString("BNAM", mResultScript); diff --git a/components/esm3/loadinfo.hpp b/components/esm3/loadinfo.hpp index c2756e8d9c..a3fb4abffa 100644 --- a/components/esm3/loadinfo.hpp +++ b/components/esm3/loadinfo.hpp @@ -4,8 +4,10 @@ #include #include -#include "components/esm/defs.hpp" -#include "components/esm/refid.hpp" +#include +#include + +#include "dialoguecondition.hpp" #include "variant.hpp" namespace ESM @@ -47,13 +49,6 @@ namespace ESM }; // 12 bytes DATAstruct mData; - // The rules for whether or not we will select this dialog item. - struct SelectStruct - { - std::string mSelectRule; // This has a complicated format - Variant mValue; - }; - // Journal quest indices (introduced with the quest system in Tribunal) enum QuestStatus { @@ -65,7 +60,7 @@ namespace ESM // Rules for when to include this item in the final list of options // visible to the player. - std::vector mSelects; + std::vector mSelects; // Id of this, previous and next INFO items RefId mId, mPrev, mNext;