You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/components/esm3/dialoguecondition.cpp

207 lines
7.1 KiB
C++

#include "dialoguecondition.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "variant.hpp"
#include <components/debug/debuglog.hpp>
#include <components/misc/concepts.hpp>
#include <components/misc/strings/conversion.hpp>
namespace ESM
{
std::optional<DialogueCondition> 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<int>(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<int>(rule[0]) << " in INFO " << context;
condition.mIndex = 0;
}
if (rule[1] == '1')
{
int function = Misc::StringUtils::toNumeric<int>(std::string_view{ rule }.substr(2, 2), -1);
if (function >= Function_FacReactionLowest && function <= Function_PcWerewolfKills)
condition.mFunction = static_cast<Function>(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<int>(rule[1]) << " in INFO "
<< context;
return {};
}
condition.mComparison = static_cast<Comparison>(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<char>(mIndex + '0');
const auto appendVariableType = [&]() {
if (variant.getType() == VT_Float)
rule += "fX";
else
{
int32_t value = variant.getInteger();
if (static_cast<int16_t>(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<int>(mFunction));
if (result.ec != std::errc())
{
Log(Debug::Error) << "Failed to save SCVR rule";
return;
}
}
rule += static_cast<char>(mComparison);
rule += mVariable;
esm.writeHNString("SCVR", rule);
variant.write(esm, Variant::Format_Info);
}
}