From 0c461f4424a0912af405530878d559ebd376c3b8 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 9 May 2015 21:21:16 +1000 Subject: [PATCH] Add TopicInfos special conditions table. --- apps/opencs/model/world/columnbase.cpp | 3 + apps/opencs/model/world/columnbase.hpp | 3 + apps/opencs/model/world/columns.cpp | 21 + apps/opencs/model/world/columns.hpp | 7 +- apps/opencs/model/world/data.cpp | 13 + .../model/world/nestedcoladapterimp.cpp | 413 ++++++++++++++++++ .../model/world/nestedcoladapterimp.hpp | 25 ++ apps/opencs/view/doc/viewmanager.cpp | 4 +- 8 files changed, 487 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 659954f48..cf125aa63 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -81,6 +81,9 @@ bool CSMWorld::ColumnBase::isId (Display display) Display_PartRefType, Display_AiPackageType, Display_YesNo, + Display_InfoCondFunc, + Display_InfoCondVar, + Display_InfoCondComp, Display_None }; diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 71c22a9f0..2d2513774 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -116,6 +116,9 @@ namespace CSMWorld Display_PartRefType, Display_AiPackageType, Display_YesNo, + Display_InfoCondFunc, + Display_InfoCondVar, + Display_InfoCondComp, //top level columns that nest other columns Display_NestedHeader diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 9076aa096..89ee6258b 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -274,6 +274,11 @@ namespace CSMWorld { ColumnId_SkillImpact, "Skills" }, { ColumnId_InfoList, "Info List" }, + { ColumnId_InfoCondition, "Info Conditions" }, + { ColumnId_InfoCondFunc, "Function" }, + { ColumnId_InfoCondVar, "Func/Variable" }, + { ColumnId_InfoCondComp, "Comp" }, + { ColumnId_InfoCondValue, "Value" }, { ColumnId_OriginalCell, "Original Cell" }, { ColumnId_UseValue1, "Use value 1" }, @@ -502,6 +507,18 @@ namespace "No", "Yes", 0 }; + static const char *sInfoCondFunc[] = + { + " ", "Function", "Global", "Local", "Journal", + "Item", "Dead", "Not ID", "Not Faction", "Not Class", + "Not Race", "Not Cell", "Not Local", 0 + }; + + static const char *sInfoCondComp[] = + { + "!=", "<", "<=", "=", ">", ">=", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -530,6 +547,10 @@ namespace case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType; case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType; case CSMWorld::Columns::ColumnId_AiWanderRepeat: return sAiWanderRepeat; + case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc; + // FIXME: don't have dynamic value enum delegate, use Display_String for now + //case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond; + case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index b87f6c53d..f971f3fd8 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -264,8 +264,13 @@ namespace CSMWorld ColumnId_SkillImpact = 240, // impact from magic effects ColumnId_InfoList = 241, + ColumnId_InfoCondition = 242, + ColumnId_InfoCondFunc = 243, + ColumnId_InfoCondVar = 244, + ColumnId_InfoCondComp = 245, + ColumnId_InfoCondValue = 246, - ColumnId_OriginalCell = 242, + ColumnId_OriginalCell = 247, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index fc4532fb0..e2fab0a25 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -242,6 +242,19 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoListAdapter ())); mTopicInfos.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_ScriptText, ColumnBase::Display_ScriptLines)); + // Special conditions + mTopicInfos.addColumn (new NestedParentColumn (Columns::ColumnId_InfoCondition)); + index = mTopicInfos.getColumns()-1; + mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoConditionAdapter ())); + mTopicInfos.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc)); + // FIXME: don't have dynamic value enum delegate, use Display_String for now + mTopicInfos.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String)); + mTopicInfos.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp)); + mTopicInfos.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Value, ColumnBase::Display_Var)); mJournalInfos.addColumn (new StringIdColumn (true)); mJournalInfos.addColumn (new RecordStateColumn); diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index d29155a47..9cd1a0a3b 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -528,4 +528,417 @@ namespace CSMWorld { return 1; // fixed at size 1 } + + // 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 + // + InfoConditionAdapter::InfoConditionAdapter () {} + + void InfoConditionAdapter::addRow(Record& record, int position) const + { + Info info = record.get(); + + std::vector& conditions = info.mSelects; + + // blank row + ESM::DialInfo::SelectStruct condStruct; + condStruct.mSelectRule = "00000"; + condStruct.mValue = ESM::Variant(); + + conditions.insert(conditions.begin()+position, condStruct); + + record.setModified (info); + } + + void InfoConditionAdapter::removeRow(Record& record, int rowToRemove) const + { + Info info = record.get(); + + std::vector& conditions = info.mSelects; + + if (rowToRemove < 0 || rowToRemove >= static_cast (conditions.size())) + throw std::runtime_error ("index out of range"); + + conditions.erase(conditions.begin()+rowToRemove); + + record.setModified (info); + } + + void InfoConditionAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + Info info = record.get(); + + info.mSelects = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (info); + } + + NestedTableWrapperBase* InfoConditionAdapter::table(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mSelects); + } + + // See the mappings in MWDialogue::SelectWrapper::getArgument + // from ESM::Attribute, ESM::Skill and MWMechanics::CreatureStats (for AI) + static const std::map sEncToInfoFunc = + { + { "00", "Rank Low" }, + { "01", "Rank High" }, + { "02", "Rank Requirement" }, + { "03", "Reputation" }, + { "04", "Health Percent" }, + { "05", "PC Reputation" }, + { "06", "PC Level" }, + { "07", "PC Health Percent" }, + { "08", "PC Magicka" }, // dynamic stat + { "09", "PC Fatigue" }, // dynamic stat + { "10", "PC Strength" }, // attrib + { "11", "PC Block" }, + { "12", "PC Armoror" }, + { "13", "PC Medium Armor" }, + { "14", "PC Heavy Armor" }, + { "15", "PC Blunt Weapon" }, + { "16", "PC Long Blade" }, + { "17", "PC Axe" }, + { "18", "PC Spear" }, + { "19", "PC Athletics" }, + { "20", "PC Enchant" }, + { "21", "PC Destruction" }, + { "22", "PC Alteration" }, + { "23", "PC Illusion" }, + { "24", "PC Conjuration" }, + { "25", "PC Mysticism" }, + { "26", "PC Restoration" }, + { "27", "PC Alchemy" }, + { "28", "PC Unarmored" }, + { "29", "PC Security" }, + { "30", "PC Sneak" }, + { "31", "PC Acrobatics" }, + { "32", "PC Light Armor" }, + { "33", "PC Short Blade" }, + { "34", "PC Marksman" }, + { "35", "PC Merchantile" }, + { "36", "PC Speechcraft" }, + { "37", "PC Hand To Hand" }, + { "38", "PC Sex" }, + { "39", "PC Expelled" }, + { "40", "PC Common Disease" }, + { "41", "PC Blight Disease" }, + { "42", "PC Clothing Modifier" }, + { "43", "PC Crime Level" }, + { "44", "Same Sex" }, + { "45", "Same Race" }, + { "46", "Same Faction" }, + { "47", "Faction Rank Difference" }, + { "48", "Detected" }, + { "49", "Alarmed" }, + { "50", "Choice" }, + { "51", "PC Intelligence" }, // attrib + { "52", "PC Willpower" }, // attrib + { "53", "PC Agility" }, // attrib + { "54", "PC Speed" }, // attrib + { "55", "PC Endurance" }, // attrib + { "56", "PC Personality" }, // attrib + { "57", "PC Luck" }, // attrib + { "58", "PC Corpus" }, + { "59", "Weather" }, + { "60", "PC Vampire" }, + { "61", "Level" }, + { "62", "Attacked" }, + { "63", "Talked To PC" }, + { "64", "PC Health" }, // dynamic stat + { "65", "Creature Target" }, + { "66", "Friend Hit" }, + { "67", "Fight" }, // AI + { "68", "Hello" }, // AI + { "69", "Alarm" }, // AI + { "70", "Flee" }, // AI + { "71", "Should Attack" }, + { "72", "Werewolf" }, + { "73", "PC Werewolf Kills" } + }; + + QVariant InfoConditionAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + Info info = record.get(); + + std::vector& conditions = info.mSelects; + + if (subRowIndex < 0 || subRowIndex >= static_cast (conditions.size())) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: + { + char condType = conditions[subRowIndex].mSelectRule[1]; + switch (condType) + { + case '1': return 1; // Function + case '2': return 2; // Global + case '3': return 3; // Local + case '4': return 4; // Journal + case '5': return 5; // Item + case '6': return 6; // Dead + case '7': return 7; // Not ID + case '8': return 8; // Not Factio + case '9': return 9; // Not Class + case 'A': return 10; // Not Race + case 'B': return 11; // Not Cell + case 'C': return 12; // Not Local + default: return QVariant(); // TODO: log an error? + } + } + case 1: + { + if (conditions[subRowIndex].mSelectRule[1] == '1') + { + // throws an exception if the encoding is not found + return sEncToInfoFunc.at(conditions[subRowIndex].mSelectRule.substr(2, 2)).c_str(); + } + else + return QString(conditions[subRowIndex].mSelectRule.substr(5).c_str()); + } + case 2: + { + char compType = conditions[subRowIndex].mSelectRule[4]; + switch (compType) + { + case '0': return 3; // = + case '1': return 0; // != + case '2': return 4; // > + case '3': return 5; // >= + case '4': return 1; // < + case '5': return 2; // <= + default: return QVariant(); // TODO: log an error? + } + } + case 3: + { + switch (conditions[subRowIndex].mValue.getType()) + { + case ESM::VT_String: + { + return QString::fromUtf8 (conditions[subRowIndex].mValue.getString().c_str()); + } + case ESM::VT_Int: + case ESM::VT_Short: + case ESM::VT_Long: + { + return conditions[subRowIndex].mValue.getInteger(); + } + case ESM::VT_Float: + { + return conditions[subRowIndex].mValue.getFloat(); + } + default: return QVariant(); + } + } + default: throw std::runtime_error("Info condition subcolumn index out of range"); + } + } + + static const std::map sInfoFuncToEnc = + { + { "Alarm", "69" }, // AI + { "Alarmed", "49" }, + { "Attacked", "62" }, + { "Choice", "50" }, + { "Creature Target", "65" }, + { "Detected", "48" }, + { "Faction Rank Difference", "47" }, + { "Fight", "67" }, // AI + { "Flee", "70" }, // AI + { "Friend Hit", "66" }, + { "Health Percent", "04" }, + { "Hello", "68" }, // AI + { "Level", "61" }, + { "PC Acrobatics", "31" }, + { "PC Agility", "53" }, // attrib + { "PC Alchemy", "27" }, + { "PC Alteration", "22" }, + { "PC Armoror", "12" }, + { "PC Athletics", "19" }, + { "PC Axe", "17" }, + { "PC Blight Disease", "41" }, + { "PC Block", "11" }, + { "PC Blunt Weapon", "15" }, + { "PC Clothing Modifier", "42" }, + { "PC Common Disease", "40" }, + { "PC Conjuration", "24" }, + { "PC Corpus", "58" }, + { "PC Crime Level", "43" }, + { "PC Destruction", "21" }, + { "PC Enchant", "20" }, + { "PC Endurance", "55" }, // attrib + { "PC Expelled", "39" }, + { "PC Fatigue", "09" }, // dynamic stat + { "PC Hand To Hand", "37" }, + { "PC Health", "64" }, // dynamic stat + { "PC Health Percent", "07" }, + { "PC Heavy Armor", "14" }, + { "PC Illusion", "23" }, + { "PC Intelligence", "51" }, // attrib + { "PC Level", "06" }, + { "PC Light Armor", "32" }, + { "PC Long Blade", "16" }, + { "PC Luck", "57" }, // attrib + { "PC Magicka", "08" }, // dynamic stat + { "PC Marksman", "34" }, + { "PC Medium Armor", "13" }, + { "PC Merchantile", "35" }, + { "PC Mysticism", "25" }, + { "PC Personality", "56" }, // attrib + { "PC Reputation", "05" }, + { "PC Restoration", "26" }, + { "PC Security", "29" }, + { "PC Sex", "38" }, + { "PC Short Blade", "33" }, + { "PC Sneak", "30" }, + { "PC Spear", "18" }, + { "PC Speechcraft", "36" }, + { "PC Speed", "54" }, // attrib + { "PC Strength", "10" }, // attrib + { "PC Unarmored", "28" }, + { "PC Vampire", "60" }, + { "PC Werewolf Kills", "73" }, + { "PC Willpower", "52" }, // attrib + { "Rank Requirement", "02" }, + { "Rank High", "01" }, + { "Rank Low", "00" }, + { "Reputation", "03" }, + { "Same Faction", "46" }, + { "Same Race", "45" }, + { "Same Sex", "44" }, + { "Should Attack", "71" }, + { "Talked To PC", "63" }, + { "Weather", "59" }, + { "Werewolf", "72" } + }; + + void InfoConditionAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + Info info = record.get(); + + std::vector& conditions = info.mSelects; + + if (subRowIndex < 0 || subRowIndex >= static_cast (conditions.size())) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: + { + // See sInfoCondFunc in columns.cpp for the enum values + switch (value.toInt()) + { + // FIXME: when these change the values of the other columns need to change + // correspondingly (and automatically) + case 1: conditions[subRowIndex].mSelectRule[1] = '1'; break; // Function + case 2: conditions[subRowIndex].mSelectRule[1] = '2'; break; // Global + case 3: conditions[subRowIndex].mSelectRule[1] = '3'; break; // Local + case 4: conditions[subRowIndex].mSelectRule[1] = '4'; break; // Journal + case 5: conditions[subRowIndex].mSelectRule[1] = '5'; break; // Item + case 6: conditions[subRowIndex].mSelectRule[1] = '6'; break; // Dead + case 7: conditions[subRowIndex].mSelectRule[1] = '7'; break; // Not ID + case 8: conditions[subRowIndex].mSelectRule[1] = '8'; break; // Not Faction + case 9: conditions[subRowIndex].mSelectRule[1] = '9'; break; // Not Class + case 10: conditions[subRowIndex].mSelectRule[1] = 'A'; break; // Not Race + case 11: conditions[subRowIndex].mSelectRule[1] = 'B'; break; // Not Cell + case 12: conditions[subRowIndex].mSelectRule[1] = 'C'; break; // Not Local + default: return; // return without saving + } + break; + } + case 1: + { + if (conditions[subRowIndex].mSelectRule[1] == '1') + { + // throws an exception if the function is not found + const std::map::const_iterator it = sInfoFuncToEnc.find( + value.toString().toUtf8().constData()); + if (it != sInfoFuncToEnc.end()) + { + std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 2); + rule.append(it->second); + rule.append(std::string(1, conditions[subRowIndex].mSelectRule[4])); + conditions[subRowIndex].mSelectRule = rule.append(value.toString().toUtf8().constData()); + } + else + return; // return without saving; TODO: maybe log an error here + } + else + { + // FIXME: validate the string values before saving, based on the current function + std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 5); + conditions[subRowIndex].mSelectRule = rule.append(value.toString().toUtf8().constData()); + } + break; + } + case 2: + { + // See sInfoCondComp in columns.cpp for the enum values + switch (value.toInt()) + { + case 0: conditions[subRowIndex].mSelectRule[4] = '1'; break; // != + case 1: conditions[subRowIndex].mSelectRule[4] = '4'; break; // < + case 2: conditions[subRowIndex].mSelectRule[4] = '5'; break; // <= + case 3: conditions[subRowIndex].mSelectRule[4] = '0'; break; // = + case 4: conditions[subRowIndex].mSelectRule[4] = '2'; break; // > + case 5: conditions[subRowIndex].mSelectRule[4] = '3'; break; // >= + default: return; // return without saving + } + break; + } + case 3: + { + switch (conditions[subRowIndex].mValue.getType()) + { + case ESM::VT_String: + { + conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData()); + break; + } + case ESM::VT_Int: + case ESM::VT_Short: + case ESM::VT_Long: + { + conditions[subRowIndex].mValue.setInteger (value.toInt()); + break; + } + case ESM::VT_Float: + { + conditions[subRowIndex].mValue.setFloat (value.toFloat()); + break; + } + default: break; + } + } + default: throw std::runtime_error("Info condition subcolumn index out of range"); + } + + record.setModified (info); + } + + int InfoConditionAdapter::getColumnsCount(const Record& record) const + { + return 4; + } + + int InfoConditionAdapter::getRowsCount(const Record& record) const + { + return static_cast(record.get().mSelects.size()); + } } diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index 96dcd943d..ea2037eb8 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -412,6 +412,31 @@ namespace CSMWorld virtual int getRowsCount(const Record& record) const; }; + + class InfoConditionAdapter : public NestedColumnAdapter + { + public: + InfoConditionAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; } #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 5908c67a1..6362f9659 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -90,7 +90,9 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false }, { CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false }, { CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false }, - { CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false } + { CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false }, + { CSMWorld::ColumnBase::Display_InfoCondFunc, CSMWorld::Columns::ColumnId_InfoCondFunc, false }, + { CSMWorld::ColumnBase::Display_InfoCondComp, CSMWorld::Columns::ColumnId_InfoCondComp, false } }; for (std::size_t i=0; i