mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-28 21:07:59 +03:00
Lua scripts configuration in omwaddon
This commit is contained in:
parent
58fd560ce9
commit
a70d5831c5
26 changed files with 700 additions and 236 deletions
|
@ -16,19 +16,26 @@ namespace LuaUtil
|
|||
{"GLOBAL", ESM::LuaScriptCfg::sGlobal},
|
||||
{"CUSTOM", ESM::LuaScriptCfg::sCustom},
|
||||
{"PLAYER", ESM::LuaScriptCfg::sPlayer},
|
||||
{"ACTIVATOR", ESM::LuaScriptCfg::sActivator},
|
||||
{"ARMOR", ESM::LuaScriptCfg::sArmor},
|
||||
{"BOOK", ESM::LuaScriptCfg::sBook},
|
||||
{"CLOTHING", ESM::LuaScriptCfg::sClothing},
|
||||
{"CONTAINER", ESM::LuaScriptCfg::sContainer},
|
||||
{"CREATURE", ESM::LuaScriptCfg::sCreature},
|
||||
{"DOOR", ESM::LuaScriptCfg::sDoor},
|
||||
{"INGREDIENT", ESM::LuaScriptCfg::sIngredient},
|
||||
{"LIGHT", ESM::LuaScriptCfg::sLight},
|
||||
{"MISC_ITEM", ESM::LuaScriptCfg::sMiscItem},
|
||||
{"NPC", ESM::LuaScriptCfg::sNPC},
|
||||
{"POTION", ESM::LuaScriptCfg::sPotion},
|
||||
{"WEAPON", ESM::LuaScriptCfg::sWeapon},
|
||||
};
|
||||
|
||||
const std::map<std::string, ESM::RecNameInts, std::less<>> typeTagsByName{
|
||||
{"ACTIVATOR", ESM::REC_ACTI},
|
||||
{"ARMOR", ESM::REC_ARMO},
|
||||
{"BOOK", ESM::REC_BOOK},
|
||||
{"CLOTHING", ESM::REC_CLOT},
|
||||
{"CONTAINER", ESM::REC_CONT},
|
||||
{"CREATURE", ESM::REC_CREA},
|
||||
{"DOOR", ESM::REC_DOOR},
|
||||
{"INGREDIENT", ESM::REC_INGR},
|
||||
{"LIGHT", ESM::REC_LIGH},
|
||||
{"MISC_ITEM", ESM::REC_MISC},
|
||||
{"NPC", ESM::REC_NPC_},
|
||||
{"POTION", ESM::REC_ALCH},
|
||||
{"WEAPON", ESM::REC_WEAP},
|
||||
{"APPARATUS", ESM::REC_APPA},
|
||||
{"LOCKPICK", ESM::REC_LOCK},
|
||||
{"PROBE", ESM::REC_PROB},
|
||||
{"REPAIR", ESM::REC_REPA},
|
||||
};
|
||||
|
||||
bool isSpace(char c)
|
||||
|
@ -37,43 +44,69 @@ namespace LuaUtil
|
|||
}
|
||||
}
|
||||
|
||||
const std::vector<int> ScriptsConfiguration::sEmpty;
|
||||
|
||||
void ScriptsConfiguration::init(ESM::LuaScriptsCfg cfg)
|
||||
{
|
||||
mScripts.clear();
|
||||
mScriptsByFlag.clear();
|
||||
mPathToIndex.clear();
|
||||
|
||||
// Find duplicates; only the last occurrence will be used.
|
||||
// Find duplicates; only the last occurrence will be used (unless `sMerge` flag is used).
|
||||
// Search for duplicates is case insensitive.
|
||||
std::vector<bool> skip(cfg.mScripts.size(), false);
|
||||
for (int i = cfg.mScripts.size() - 1; i >= 0; --i)
|
||||
{
|
||||
auto [_, inserted] = mPathToIndex.insert_or_assign(
|
||||
Misc::StringUtils::lowerCase(cfg.mScripts[i].mScriptPath), -1);
|
||||
if (!inserted || cfg.mScripts[i].mFlags == 0)
|
||||
skip[i] = true;
|
||||
}
|
||||
mPathToIndex.clear();
|
||||
int index = 0;
|
||||
for (size_t i = 0; i < cfg.mScripts.size(); ++i)
|
||||
{
|
||||
if (skip[i])
|
||||
const ESM::LuaScriptCfg& script = cfg.mScripts[i];
|
||||
bool global = script.mFlags & ESM::LuaScriptCfg::sGlobal;
|
||||
if (global && (script.mFlags & ~ESM::LuaScriptCfg::sMerge) != ESM::LuaScriptCfg::sGlobal)
|
||||
throw std::runtime_error(std::string("Global script can not have local flags: ") + script.mScriptPath);
|
||||
if (global && (!script.mTypes.empty() || !script.mRecords.empty() || !script.mRefs.empty()))
|
||||
throw std::runtime_error(std::string(
|
||||
"Global script can not have per-type and per-object configuration") + script.mScriptPath);
|
||||
auto [it, inserted] = mPathToIndex.emplace(
|
||||
Misc::StringUtils::lowerCase(script.mScriptPath), i);
|
||||
if (inserted)
|
||||
continue;
|
||||
ESM::LuaScriptCfg& s = cfg.mScripts[i];
|
||||
mPathToIndex[s.mScriptPath] = index; // Stored paths are case sensitive.
|
||||
ESM::LuaScriptCfg::Flags flags = s.mFlags;
|
||||
ESM::LuaScriptCfg::Flags flag = 1;
|
||||
while (flags != 0)
|
||||
ESM::LuaScriptCfg& oldScript = cfg.mScripts[it->second];
|
||||
if (global != bool(oldScript.mFlags & ESM::LuaScriptCfg::sGlobal))
|
||||
throw std::runtime_error(std::string("Flags mismatch for ") + script.mScriptPath);
|
||||
if (script.mFlags & ESM::LuaScriptCfg::sMerge)
|
||||
{
|
||||
if (flags & flag)
|
||||
mScriptsByFlag[flag].push_back(index);
|
||||
flags &= ~flag;
|
||||
flag = flag << 1;
|
||||
oldScript.mFlags |= (script.mFlags & ~ESM::LuaScriptCfg::sMerge);
|
||||
if (!script.mInitializationData.empty())
|
||||
oldScript.mInitializationData = script.mInitializationData;
|
||||
oldScript.mTypes.insert(oldScript.mTypes.end(), script.mTypes.begin(), script.mTypes.end());
|
||||
oldScript.mRecords.insert(oldScript.mRecords.end(), script.mRecords.begin(), script.mRecords.end());
|
||||
oldScript.mRefs.insert(oldScript.mRefs.end(), script.mRefs.begin(), script.mRefs.end());
|
||||
skip[i] = true;
|
||||
}
|
||||
else
|
||||
skip[it->second] = true;
|
||||
}
|
||||
|
||||
// Filter duplicates
|
||||
for (size_t i = 0; i < cfg.mScripts.size(); ++i)
|
||||
{
|
||||
if (!skip[i])
|
||||
mScripts.push_back(std::move(cfg.mScripts[i]));
|
||||
}
|
||||
|
||||
// Initialize mappings
|
||||
mPathToIndex.clear();
|
||||
for (int i = 0; i < static_cast<int>(mScripts.size()); ++i)
|
||||
{
|
||||
const ESM::LuaScriptCfg& s = mScripts[i];
|
||||
mPathToIndex[s.mScriptPath] = i; // Stored paths are case sensitive.
|
||||
for (uint32_t t : s.mTypes)
|
||||
mScriptsPerType[t].push_back(i);
|
||||
for (const ESM::LuaScriptCfg::PerRecordCfg& r : s.mRecords)
|
||||
{
|
||||
std::string_view data = r.mInitializationData.empty() ? s.mInitializationData : r.mInitializationData;
|
||||
mScriptsPerRecordId[r.mRecordId].push_back(DetailedConf{i, r.mAttach, data});
|
||||
}
|
||||
for (const ESM::LuaScriptCfg::PerRefCfg& r : s.mRefs)
|
||||
{
|
||||
std::string_view data = r.mInitializationData.empty() ? s.mInitializationData : r.mInitializationData;
|
||||
mScriptsPerRefNum[ESM::RefNum{r.mRefnumIndex, r.mRefnumContentFile}].push_back(DetailedConf{i, r.mAttach, data});
|
||||
}
|
||||
mScripts.push_back(std::move(s));
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,14 +119,50 @@ namespace LuaUtil
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::vector<int>& ScriptsConfiguration::getListByFlag(ESM::LuaScriptCfg::Flags type) const
|
||||
ScriptIdsWithInitializationData ScriptsConfiguration::getConfByFlag(ESM::LuaScriptCfg::Flags flag) const
|
||||
{
|
||||
assert(std::bitset<64>(type).count() <= 1);
|
||||
auto it = mScriptsByFlag.find(type);
|
||||
if (it != mScriptsByFlag.end())
|
||||
return it->second;
|
||||
else
|
||||
return sEmpty;
|
||||
ScriptIdsWithInitializationData res;
|
||||
for (size_t id = 0; id < mScripts.size(); ++id)
|
||||
{
|
||||
const ESM::LuaScriptCfg& script = mScripts[id];
|
||||
if (script.mFlags & flag)
|
||||
res[id] = script.mInitializationData;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ScriptIdsWithInitializationData ScriptsConfiguration::getLocalConf(
|
||||
uint32_t type, std::string_view recordId, ESM::RefNum refnum) const
|
||||
{
|
||||
ScriptIdsWithInitializationData res;
|
||||
auto typeIt = mScriptsPerType.find(type);
|
||||
if (typeIt != mScriptsPerType.end())
|
||||
for (int scriptId : typeIt->second)
|
||||
res[scriptId] = mScripts[scriptId].mInitializationData;
|
||||
auto recordIt = mScriptsPerRecordId.find(recordId);
|
||||
if (recordIt != mScriptsPerRecordId.end())
|
||||
{
|
||||
for (const DetailedConf& d : recordIt->second)
|
||||
{
|
||||
if (d.mAttach)
|
||||
res[d.mScriptId] = d.mInitializationData;
|
||||
else
|
||||
res.erase(d.mScriptId);
|
||||
}
|
||||
}
|
||||
if (!refnum.hasContentFile())
|
||||
return res;
|
||||
auto refIt = mScriptsPerRefNum.find(refnum);
|
||||
if (refIt == mScriptsPerRefNum.end())
|
||||
return res;
|
||||
for (const DetailedConf& d : refIt->second)
|
||||
{
|
||||
if (d.mAttach)
|
||||
res[d.mScriptId] = d.mInitializationData;
|
||||
else
|
||||
res.erase(d.mScriptId);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void parseOMWScripts(ESM::LuaScriptsCfg& cfg, std::string_view data)
|
||||
|
@ -117,53 +186,69 @@ namespace LuaUtil
|
|||
throw std::runtime_error(Misc::StringUtils::format(
|
||||
"Lua script should have suffix '.lua', got: %s", std::string(line.substr(0, 300))));
|
||||
|
||||
// Split flags and script path
|
||||
// Split tags and script path
|
||||
size_t semicolonPos = line.find(':');
|
||||
if (semicolonPos == std::string::npos)
|
||||
throw std::runtime_error(Misc::StringUtils::format("No flags found in: %s", std::string(line)));
|
||||
std::string_view flagsStr = line.substr(0, semicolonPos);
|
||||
std::string_view tagsStr = line.substr(0, semicolonPos);
|
||||
std::string_view scriptPath = line.substr(semicolonPos + 1);
|
||||
while (isSpace(scriptPath[0]))
|
||||
scriptPath = scriptPath.substr(1);
|
||||
|
||||
// Parse flags
|
||||
ESM::LuaScriptCfg::Flags flags = 0;
|
||||
size_t flagsPos = 0;
|
||||
ESM::LuaScriptCfg& script = cfg.mScripts.emplace_back();
|
||||
script.mScriptPath = std::string(scriptPath);
|
||||
script.mFlags = 0;
|
||||
|
||||
// Parse tags
|
||||
size_t tagsPos = 0;
|
||||
while (true)
|
||||
{
|
||||
while (flagsPos < flagsStr.size() && (isSpace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ','))
|
||||
flagsPos++;
|
||||
size_t startPos = flagsPos;
|
||||
while (flagsPos < flagsStr.size() && !isSpace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',')
|
||||
flagsPos++;
|
||||
if (startPos == flagsPos)
|
||||
while (tagsPos < tagsStr.size() && (isSpace(tagsStr[tagsPos]) || tagsStr[tagsPos] == ','))
|
||||
tagsPos++;
|
||||
size_t startPos = tagsPos;
|
||||
while (tagsPos < tagsStr.size() && !isSpace(tagsStr[tagsPos]) && tagsStr[tagsPos] != ',')
|
||||
tagsPos++;
|
||||
if (startPos == tagsPos)
|
||||
break;
|
||||
std::string_view flagName = flagsStr.substr(startPos, flagsPos - startPos);
|
||||
auto it = flagsByName.find(flagName);
|
||||
std::string_view tagName = tagsStr.substr(startPos, tagsPos - startPos);
|
||||
auto it = flagsByName.find(tagName);
|
||||
auto typesIt = typeTagsByName.find(tagName);
|
||||
if (it != flagsByName.end())
|
||||
flags |= it->second;
|
||||
script.mFlags |= it->second;
|
||||
else if (typesIt != typeTagsByName.end())
|
||||
script.mTypes.push_back(typesIt->second);
|
||||
else
|
||||
throw std::runtime_error(Misc::StringUtils::format("Unknown flag '%s' in: %s",
|
||||
std::string(flagName), std::string(line)));
|
||||
throw std::runtime_error(Misc::StringUtils::format("Unknown tag '%s' in: %s",
|
||||
std::string(tagName), std::string(line)));
|
||||
}
|
||||
if ((flags & ESM::LuaScriptCfg::sGlobal) && flags != ESM::LuaScriptCfg::sGlobal)
|
||||
throw std::runtime_error("Global script can not have local flags");
|
||||
|
||||
cfg.mScripts.push_back(ESM::LuaScriptCfg{std::string(scriptPath), "", flags});
|
||||
}
|
||||
}
|
||||
|
||||
std::string scriptCfgToString(const ESM::LuaScriptCfg& script)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (script.mFlags & ESM::LuaScriptCfg::sMerge)
|
||||
ss << "+ ";
|
||||
for (const auto& [flagName, flag] : flagsByName)
|
||||
{
|
||||
if (script.mFlags & flag)
|
||||
ss << flagName << " ";
|
||||
}
|
||||
for (uint32_t type : script.mTypes)
|
||||
{
|
||||
for (const auto& [tagName, t] : typeTagsByName)
|
||||
{
|
||||
if (type == t)
|
||||
ss << tagName << " ";
|
||||
}
|
||||
}
|
||||
ss << ": " << script.mScriptPath;
|
||||
if (!script.mInitializationData.empty())
|
||||
ss << " (with data, " << script.mInitializationData.size() << " bytes)";
|
||||
ss << " ; data " << script.mInitializationData.size() << " bytes";
|
||||
if (!script.mRecords.empty())
|
||||
ss << " ; " << script.mRecords.size() << " records";
|
||||
if (!script.mRefs.empty())
|
||||
ss << " ; " << script.mRefs.size() << " objects";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue