#include "infoselectwrapper.hpp" #include #include #include #include const char* CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[] = { "Faction Reaction Low", "Faction Reaction High", "Rank Requirement", "Reputation", "Health Percent", "PC Reputation", "PC Level", "PC Health Percent", "PC Magicka", "PC Fatigue", "PC Strength", "PC Block", "PC Armorer", "PC Medium Armor", "PC Heavy Armor", "PC Blunt Weapon", "PC Long Blade", "PC Axe", "PC Spear", "PC Athletics", "PC Enchant", "PC Detruction", "PC Alteration", "PC Illusion", "PC Conjuration", "PC Mysticism", "PC Restoration", "PC Alchemy", "PC Unarmored", "PC Security", "PC Sneak", "PC Acrobatics", "PC Light Armor", "PC Short Blade", "PC Marksman", "PC Merchantile", "PC Speechcraft", "PC Hand to Hand", "PC Sex", "PC Expelled", "PC Common Disease", "PC Blight Disease", "PC Clothing Modifier", "PC Crime Level", "Same Sex", "Same Race", "Same Faction", "Faction Rank Difference", "Detected", "Alarmed", "Choice", "PC Intelligence", "PC Willpower", "PC Agility", "PC Speed", "PC Endurance", "PC Personality", "PC Luck", "PC Corprus", "Weather", "PC Vampire", "Level", "Attacked", "Talked to PC", "PC Health", "Creature Target", "Friend Hit", "Fight", "Hello", "Alarm", "Flee", "Should Attack", "Werewolf", "PC Werewolf Kills", "Global", "Local", "Journal", "Item", "Dead", "Not Id", "Not Faction", "Not Class", "Not Race", "Not Cell", "Not Local", nullptr, }; const char* CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings[] = { "=", "!=", ">", ">=", "<", "<=", nullptr, }; namespace { 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_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)"; } } // ConstInfoSelectWrapper CSMWorld::ConstInfoSelectWrapper::ConstInfoSelectWrapper(const ESM::DialogueCondition& select) : mConstSelect(select) { updateHasVariable(); updateComparisonType(); } ESM::DialogueCondition::Function CSMWorld::ConstInfoSelectWrapper::getFunctionName() const { return mConstSelect.mFunction; } ESM::DialogueCondition::Comparison CSMWorld::ConstInfoSelectWrapper::getRelationType() const { return mConstSelect.mComparison; } CSMWorld::ConstInfoSelectWrapper::ComparisonType CSMWorld::ConstInfoSelectWrapper::getComparisonType() const { return mComparisonType; } bool CSMWorld::ConstInfoSelectWrapper::hasVariable() const { return mHasVariable; } const std::string& CSMWorld::ConstInfoSelectWrapper::getVariableName() const { return mConstSelect.mVariable; } bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue() const { if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer) { if (std::holds_alternative(mConstSelect.mValue)) return conditionIsAlwaysTrue(getConditionFloatRange(), getValidIntRange()); else return conditionIsAlwaysTrue(getConditionIntRange(), getValidIntRange()); } else if (mComparisonType == Comparison_Numeric) { if (std::holds_alternative(mConstSelect.mValue)) return conditionIsAlwaysTrue(getConditionFloatRange(), getValidFloatRange()); else return conditionIsAlwaysTrue(getConditionIntRange(), getValidFloatRange()); } return false; } bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue() const { if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer) { if (std::holds_alternative(mConstSelect.mValue)) return conditionIsNeverTrue(getConditionFloatRange(), getValidIntRange()); else return conditionIsNeverTrue(getConditionIntRange(), getValidIntRange()); } else if (mComparisonType == Comparison_Numeric) { if (std::holds_alternative(mConstSelect.mValue)) return conditionIsNeverTrue(getConditionFloatRange(), getValidFloatRange()); else return conditionIsNeverTrue(getConditionIntRange(), getValidFloatRange()); } return false; } std::string CSMWorld::ConstInfoSelectWrapper::toString() const { std::ostringstream stream; stream << convertToString(getFunctionName()) << " "; if (mHasVariable) stream << getVariableName() << " "; stream << convertToString(getRelationType()) << " "; std::visit([&](auto value) { stream << value; }, mConstSelect.mValue); return stream.str(); } void CSMWorld::ConstInfoSelectWrapper::updateHasVariable() { switch (getFunctionName()) { 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; default: mHasVariable = false; break; } } void CSMWorld::ConstInfoSelectWrapper::updateComparisonType() { switch (getFunctionName()) { // Boolean 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 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 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; default: mComparisonType = Comparison_None; break; } } std::pair CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() const { const int IntMax = std::numeric_limits::max(); const int IntMin = std::numeric_limits::min(); const std::pair InvalidRange(IntMax, IntMin); int value = std::get(mConstSelect.mValue); switch (getRelationType()) { case ESM::DialogueCondition::Comp_Eq: case ESM::DialogueCondition::Comp_Ne: return std::pair(value, value); case ESM::DialogueCondition::Comp_Gt: if (value == IntMax) { return InvalidRange; } else { return std::pair(value + 1, IntMax); } break; case ESM::DialogueCondition::Comp_Ge: return std::pair(value, IntMax); case ESM::DialogueCondition::Comp_Ls: if (value == IntMin) { return InvalidRange; } else { return std::pair(IntMin, value - 1); } case ESM::DialogueCondition::Comp_Le: return std::pair(IntMin, value); default: throw std::logic_error("InfoSelectWrapper: relation does not have a range"); } } std::pair CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange() const { const float FloatMax = std::numeric_limits::infinity(); const float FloatMin = -std::numeric_limits::infinity(); const float Epsilon = std::numeric_limits::epsilon(); float value = std::get(mConstSelect.mValue); switch (getRelationType()) { case ESM::DialogueCondition::Comp_Eq: case ESM::DialogueCondition::Comp_Ne: return std::pair(value, value); case ESM::DialogueCondition::Comp_Gt: return std::pair(value + Epsilon, FloatMax); case ESM::DialogueCondition::Comp_Ge: return std::pair(value, FloatMax); case ESM::DialogueCondition::Comp_Ls: return std::pair(FloatMin, value - Epsilon); case ESM::DialogueCondition::Comp_Le: return std::pair(FloatMin, value); default: throw std::logic_error("InfoSelectWrapper: given relation does not have a range"); } } std::pair CSMWorld::ConstInfoSelectWrapper::getValidIntRange() const { const int IntMax = std::numeric_limits::max(); const int IntMin = std::numeric_limits::min(); switch (getFunctionName()) { // Boolean 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 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 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 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 ESM::DialogueCondition::Function_Weather: return std::pair(0, 9); case ESM::DialogueCondition::Function_FriendHit: return std::pair(0, 4); case ESM::DialogueCondition::Function_RankRequirement: return std::pair(0, 3); case ESM::DialogueCondition::Function_CreatureTarget: return std::pair(0, 2); case ESM::DialogueCondition::Function_PcGender: return std::pair(0, 1); case ESM::DialogueCondition::Function_FactionRankDifference: return std::pair(-9, 9); // Numeric case ESM::DialogueCondition::Function_Global: case ESM::DialogueCondition::Function_Local: case ESM::DialogueCondition::Function_NotLocal: return std::pair(IntMin, IntMax); case ESM::DialogueCondition::Function_PcMagicka: case ESM::DialogueCondition::Function_PcFatigue: case ESM::DialogueCondition::Function_PcHealth: return std::pair(0, IntMax); case ESM::DialogueCondition::Function_Health_Percent: case ESM::DialogueCondition::Function_PcHealthPercent: return std::pair(0, 100); default: throw std::runtime_error("InfoSelectWrapper: function does not exist"); } } std::pair CSMWorld::ConstInfoSelectWrapper::getValidFloatRange() const { const float FloatMax = std::numeric_limits::infinity(); const float FloatMin = -std::numeric_limits::infinity(); switch (getFunctionName()) { // Numeric case ESM::DialogueCondition::Function_Global: case ESM::DialogueCondition::Function_Local: case ESM::DialogueCondition::Function_NotLocal: return std::pair(FloatMin, FloatMax); case ESM::DialogueCondition::Function_PcMagicka: case ESM::DialogueCondition::Function_PcFatigue: case ESM::DialogueCondition::Function_PcHealth: return std::pair(0, FloatMax); case ESM::DialogueCondition::Function_Health_Percent: case ESM::DialogueCondition::Function_PcHealthPercent: return std::pair(0, 100); default: throw std::runtime_error("InfoSelectWrapper: function does not exist or is not numeric"); } } template bool CSMWorld::ConstInfoSelectWrapper::rangeContains(T1 value, std::pair range) const { return (value >= range.first && value <= range.second); } template bool CSMWorld::ConstInfoSelectWrapper::rangeFullyContains( std::pair containingRange, std::pair testRange) const { return (containingRange.first <= testRange.first) && (testRange.second <= containingRange.second); } template bool CSMWorld::ConstInfoSelectWrapper::rangesOverlap(std::pair range1, std::pair range2) const { // One of the bounds of either range should fall within the other range return (range1.first <= range2.first && range2.first <= range1.second) || (range1.first <= range2.second && range2.second <= range1.second) || (range2.first <= range1.first && range1.first <= range2.second) || (range2.first <= range1.second && range1.second <= range2.second); } template bool CSMWorld::ConstInfoSelectWrapper::rangesMatch(std::pair range1, std::pair range2) const { return (range1.first == range2.first && range1.second == range2.second); } template bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue( std::pair conditionRange, std::pair validRange) const { switch (getRelationType()) { case ESM::DialogueCondition::Comp_Eq: return false; case ESM::DialogueCondition::Comp_Ne: // If value is not within range, it will always be true return !rangeContains(conditionRange.first, validRange); 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); default: throw std::logic_error("InfoCondition: operator can not be used to compare"); } return false; } template bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue( std::pair conditionRange, std::pair validRange) const { switch (getRelationType()) { case ESM::DialogueCondition::Comp_Eq: return !rangeContains(conditionRange.first, validRange); case ESM::DialogueCondition::Comp_Ne: return false; 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); default: throw std::logic_error("InfoCondition: operator can not be used to compare"); } return false; } QVariant CSMWorld::ConstInfoSelectWrapper::getValue() const { return std::visit([](auto value) { return QVariant(value); }, mConstSelect.mValue); } // InfoSelectWrapper CSMWorld::InfoSelectWrapper::InfoSelectWrapper(ESM::DialogueCondition& select) : CSMWorld::ConstInfoSelectWrapper(select) , mSelect(select) { } void CSMWorld::InfoSelectWrapper::setFunctionName(ESM::DialogueCondition::Function 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(ESM::DialogueCondition::Comparison type) { mSelect.mComparison = type; } void CSMWorld::InfoSelectWrapper::setVariableName(const std::string& name) { mSelect.mVariable = name; } void CSMWorld::InfoSelectWrapper::setValue(int value) { mSelect.mValue = value; } void CSMWorld::InfoSelectWrapper::setValue(float value) { mSelect.mValue = value; }