Merge branch openmw:master into mwdialogue-bindings

i-have-no-land-and-i-must-scream
trav 9 months ago
commit 807d78a0d0

@ -225,6 +225,7 @@
Feature #7875: Disable MyGUI windows snapping
Feature #7914: Do not allow to move GUI windows out of screen
Feature #7923: Don't show non-existent higher ranks for factions with fewer than 9 ranks
Feature #7932: Support two-channel normal maps
Task #5896: Do not use deprecated MyGUI properties
Task #6085: Replace boost::filesystem with std::filesystem
Task #6149: Dehardcode Lua API_REVISION

@ -347,6 +347,16 @@ add_qt_style_dlls() {
QT_STYLES[$CONFIG]="${QT_STYLES[$CONFIG]} $@"
}
declare -A QT_IMAGEFORMATS
QT_IMAGEFORMATS["Release"]=""
QT_IMAGEFORMATS["Debug"]=""
QT_IMAGEFORMATS["RelWithDebInfo"]=""
add_qt_image_dlls() {
local CONFIG=$1
shift
QT_IMAGEFORMATS[$CONFIG]="${QT_IMAGEFORMATS[$CONFIG]} $@"
}
if [ -z $PLATFORM ]; then
PLATFORM="$(uname -m)"
fi
@ -913,12 +923,13 @@ printf "Qt ${QT_VER}... "
DLLSUFFIX=""
fi
if [ "${QT_VER:0:1}" -eq "6" ]; then
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_VER:0:1}"{Core,Gui,Network,OpenGL,OpenGLWidgets,Widgets}${DLLSUFFIX}.dll
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_VER:0:1}"{Core,Gui,Network,OpenGL,OpenGLWidgets,Widgets,Svg}${DLLSUFFIX}.dll
else
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_VER:0:1}"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_VER:0:1}"{Core,Gui,Network,OpenGL,Widgets,Svg}${DLLSUFFIX}.dll
fi
add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll"
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
add_qt_image_dlls $CONFIGURATION "$(pwd)/plugins/imageformats/qsvg${DLLSUFFIX}.dll"
done
echo Done.
}
@ -1112,6 +1123,13 @@ fi
echo " $(basename $DLL)"
cp "$DLL" "${DLL_PREFIX}styles"
done
echo "- Qt Image Format DLLs..."
mkdir -p ${DLL_PREFIX}imageformats
for DLL in ${QT_IMAGEFORMATS[$CONFIGURATION]}; do
echo " $(basename $DLL)"
cp "$DLL" "${DLL_PREFIX}imageformats"
done
echo
done
#fi

@ -36,7 +36,7 @@ declare -rA GROUPED_DEPS=(
libsdl2-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libopenal-dev
libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-dev
libluajit-5.1-dev librecast-dev libsqlite3-dev ca-certificates libicu-dev
libyaml-cpp-dev
libyaml-cpp-dev libqt5svg5 libqt5svg5-dev
"
# These dependencies can alternatively be built and linked statically.

@ -234,7 +234,7 @@ else()
endif(APPLE)
if (WIN32)
option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON)
option(USE_DEBUG_CONSOLE "Whether a console should be displayed if OpenMW isn't launched from the command line. Does not affect the Release configuration." ON)
endif()
if(MSVC)
@ -249,9 +249,9 @@ find_package(LZ4 REQUIRED)
if (USE_QT)
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5)
if (QT_VERSION_MAJOR VERSION_EQUAL 5)
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL LinguistTools REQUIRED)
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL LinguistTools Svg REQUIRED)
else()
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools REQUIRED)
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools Svg REQUIRED)
endif()
message(STATUS "Using Qt${QT_VERSION}")
endif()
@ -710,9 +710,8 @@ if (WIN32)
if (USE_DEBUG_CONSOLE AND BUILD_OPENMW)
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_CONSOLE>)
elseif (BUILD_OPENMW)
# Turn off debug console, debug output will be written to visual studio output instead
# Turn off implicit console, you won't see stdout unless launching OpenMW from a command line shell or look at openmw.log
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
endif()
@ -834,6 +833,12 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE)
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
configure_file("${QT_QMACSTYLE_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}" COPYONLY)
configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${OPENCS_BUNDLE_NAME}/Contents/Resources/qt.conf" COPYONLY)
get_property(QT_QSVG_PLUGIN_PATH TARGET Qt${QT_VERSION_MAJOR}::QSvgPlugin PROPERTY LOCATION_RELEASE)
get_filename_component(QT_QSVG_PLUGIN_DIR "${QT_QSVG_PLUGIN_PATH}" DIRECTORY)
get_filename_component(QT_QSVG_PLUGIN_GROUP "${QT_QSVG_PLUGIN_DIR}" NAME)
get_filename_component(QT_QSVG_PLUGIN_NAME "${QT_QSVG_PLUGIN_PATH}" NAME)
configure_file("${QT_QSVG_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_QSVG_PLUGIN_GROUP}/${QT_QSVG_PLUGIN_NAME}" COPYONLY)
endif ()
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime)

@ -1,5 +1,6 @@
#include "labels.hpp"
#include <components/esm3/dialoguecondition.hpp>
#include <components/esm3/loadalch.hpp>
#include <components/esm3/loadbody.hpp>
#include <components/esm3/loadcell.hpp>
@ -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];
}

@ -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<int>(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())

@ -250,9 +250,9 @@ target_link_libraries(openmw-cs-lib
)
if (QT_VERSION_MAJOR VERSION_EQUAL 6)
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::OpenGLWidgets)
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::OpenGLWidgets Qt::Svg)
else()
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL)
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::Svg)
endif()
if (WIN32)

@ -81,7 +81,7 @@ int runApplication(int argc, char* argv[])
Application application(argc, argv);
application.setWindowIcon(QIcon(":./openmw-cs.png"));
application.setWindowIcon(QIcon(":openmw-cs"));
CS::Editor editor(argc, argv);
#ifdef __linux__

@ -171,10 +171,9 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
// Check info conditions
for (std::vector<ESM::DialInfo::SelectStruct>::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;
}

@ -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 <typename T>

File diff suppressed because it is too large Load Diff

@ -7,133 +7,13 @@
#include <components/esm3/loadinfo.hpp>
namespace ESM
{
class Variant;
}
#include <QVariant>
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 <typename Type1, typename Type2>
bool conditionIsNeverTrue(std::pair<Type1, Type1> conditionRange, std::pair<Type2, Type2> 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();
};

@ -538,13 +538,11 @@ namespace CSMWorld
{
Info info = record.get();
std::vector<ESM::DialInfo::SelectStruct>& 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<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
auto& conditions = info.mSelects;
if (rowToRemove < 0 || rowToRemove >= static_cast<int>(conditions.size()))
throw std::runtime_error("index out of range");
@ -569,8 +567,8 @@ namespace CSMWorld
{
Info info = record.get();
info.mSelects = static_cast<const NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct>>&>(nestedTable)
.mNestedTable;
info.mSelects
= static_cast<const NestedTableWrapper<std::vector<ESM::DialogueCondition>>&>(nestedTable).mNestedTable;
record.setModified(info);
}
@ -578,14 +576,14 @@ namespace CSMWorld
NestedTableWrapperBase* InfoConditionAdapter::table(const Record<Info>& record) const
{
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct>>(record.get().mSelects);
return new NestedTableWrapper<std::vector<ESM::DialogueCondition>>(record.get().mSelects);
}
QVariant InfoConditionAdapter::getData(const Record<Info>& record, int subRowIndex, int subColIndex) const
{
Info info = record.get();
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
auto& conditions = info.mSelects;
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(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<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
auto& conditions = info.mSelects;
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(conditions.size()))
throw std::runtime_error("index out of range");
@ -647,27 +633,18 @@ namespace CSMWorld
{
case 0: // Function
{
infoSelectWrapper.setFunctionName(static_cast<ConstInfoSelectWrapper::FunctionName>(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<ESM::DialogueCondition::Function>(value.toInt()));
break;
}
case 1: // Variable
{
infoSelectWrapper.setVariableName(value.toString().toUtf8().constData());
infoSelectWrapper.update();
break;
}
case 2: // Relation
{
infoSelectWrapper.setRelationType(static_cast<ConstInfoSelectWrapper::RelationType>(value.toInt()));
infoSelectWrapper.update();
infoSelectWrapper.setRelationType(
static_cast<ESM::DialogueCondition::Comparison>(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;
}

@ -58,7 +58,7 @@ std::string CSMWorld::TableMimeData::getIcon() const
if (tmpIcon != id.getIcon())
{
return ":/multitype.png"; // icon stolen from gnome TODO: get new icon
return ":multitype";
}
tmpIcon = id.getIcon();

@ -23,158 +23,137 @@ namespace
constexpr TypeData sNoArg[] = {
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", ":placeholder" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables",
":./global-variable.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", ":./skill.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", ":./class.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", ":./faction.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", ":./race.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", ":./sound.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", ":./script.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", ":./region.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns",
":./birthsign.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", ":./spell.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics",
":./dialogue-topics.png" },
":global-variable" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":gmst" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", ":skill" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", ":class" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", ":faction" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", ":race" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", ":sound" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", ":script" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", ":region" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", ":birthsign" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", ":spell" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", ":dialogue-topics" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals",
":./journal-topics.png" },
":journal-topics" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos",
":./dialogue-topic-infos.png" },
":dialogue-info" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos",
":./journal-topic-infos.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", ":./cell.png" },
":journal-topic-infos" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", ":cell" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments",
":./enchantment.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts",
":./body-part.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects",
":./object.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances",
":./instance.png" },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map",
":./region-map.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", ":./filter.png" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes",
":./resources-mesh" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", ":./resources-icon" },
":enchantment" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", ":body-part" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects", ":object" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances", ":instance" },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", ":region-map" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", ":filter" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", ":resources-mesh" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", ":resources-icon" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files",
":./resources-music" },
":resources-music" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files",
":resources-sound" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures",
":./resources-texture" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos",
":./resources-video" },
":resources-texture" },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":resources-video" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles",
":./debug-profile.png" },
":debug-profile" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SelectionGroup, "Selection Groups", "" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":run-log" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators",
":./sound-generator.png" },
":sound-generator" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects",
":./magic-effect.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands",
":./land-heightmap.png" },
":magic-effect" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":land-heightmap" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "Land Textures",
":./land-texture.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids",
":./pathgrid.png" },
":land-texture" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":pathgrid" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts",
":./start-script.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata",
":./metadata.png" },
":start-script" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":metadata" },
};
constexpr TypeData sIdArg[] = {
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable",
":./global-variable.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", ":./gmst.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", ":./skill.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", ":./class.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", ":./faction.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", ":./race.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", ":./sound.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", ":./script.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./region.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", ":./dialogue-topics.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal",
":./journal-topics.png" },
":global-variable" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", ":gmst" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", ":skill" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", ":class" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", ":faction" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", ":race" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", ":sound" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", ":script" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":region" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":birthsign" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":spell" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", ":dialogue-topics" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", ":journal-topics" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo",
":./dialogue-topic-infos.png" },
":dialogue-info" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo",
":./journal-topic-infos.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", ":./object.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator",
":./activator.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus",
":./apparatus.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, "Armor", ":./armor.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, "Book", ":./book.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, "Clothing", ":./clothing.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, "Container",
":./container.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, "Creature", ":./creature.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient",
":./ingredient.png" },
":journal-topic-infos" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":cell" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":cell" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", ":object" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":activator" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":potion" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":apparatus" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, "Armor", ":armor" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, "Book", ":book" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, "Clothing", ":clothing" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, "Container", ":container" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, "Creature", ":creature" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":door" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":ingredient" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList,
"Creature Levelled List", ":./levelled-creature.png" },
"Creature Levelled List", ":levelled-creature" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, "Item Levelled List",
":./levelled-item.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" },
":levelled-item" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":light" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":lockpick" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, "Miscellaneous",
":./miscellaneous.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":./npc.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":./probe.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance",
":./instance.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", ":./scene.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview",
":./record-preview.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment",
":./enchantment.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":./body-part.png" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":./resources-mesh" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":./resources-icon" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":./resources-music" },
":miscellaneous" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":npc" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":probe" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":repair" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":static" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":weapon" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", ":instance" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":filter" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", ":scene" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", ":edit-preview" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", ":enchantment" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":body-part" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":resources-mesh" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":resources-icon" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":resources-music" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File",
":./resources-sound" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture",
":./resources-texture" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":./resources-video" },
":resources-sound" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":resources-texture" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":resources-video" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile",
":./debug-profile.png" },
":debug-profile" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator",
":./sound-generator.png" },
":sound-generator" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect",
":./magic-effect.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", ":./land-heightmap.png" },
":magic-effect" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", ":land-heightmap" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "Land Texture",
":./land-texture.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", ":./pathgrid.png" },
":land-texture" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", ":pathgrid" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script",
":./start-script.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" },
":start-script" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":metadata" },
};
constexpr TypeData sIndexArg[] = {
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults,
"Verification Results", ":./menu-verify.png" },
"Verification Results", ":menu-verify" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log",
":./error-log.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search",
":./menu-search.png" },
":error-log" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", ":menu-search" },
};
struct WriteToStream

@ -71,30 +71,30 @@ void CSVDoc::View::setupFileMenu()
{
QMenu* file = menuBar()->addMenu(tr("File"));
QAction* newGame = createMenuEntry("New Game", ":./menu-new-game.png", file, "document-file-newgame");
QAction* newGame = createMenuEntry("New Game", ":menu-new-game", file, "document-file-newgame");
connect(newGame, &QAction::triggered, this, &View::newGameRequest);
QAction* newAddon = createMenuEntry("New Addon", ":./menu-new-addon.png", file, "document-file-newaddon");
QAction* newAddon = createMenuEntry("New Addon", ":menu-new-addon", file, "document-file-newaddon");
connect(newAddon, &QAction::triggered, this, &View::newAddonRequest);
QAction* open = createMenuEntry("Open", ":./menu-open.png", file, "document-file-open");
QAction* open = createMenuEntry("Open", ":menu-open", file, "document-file-open");
connect(open, &QAction::triggered, this, &View::loadDocumentRequest);
QAction* save = createMenuEntry("Save", ":./menu-save.png", file, "document-file-save");
QAction* save = createMenuEntry("Save", ":menu-save", file, "document-file-save");
connect(save, &QAction::triggered, this, &View::save);
mSave = save;
file->addSeparator();
QAction* verify = createMenuEntry("Verify", ":./menu-verify.png", file, "document-file-verify");
QAction* verify = createMenuEntry("Verify", ":menu-verify", file, "document-file-verify");
connect(verify, &QAction::triggered, this, &View::verify);
mVerify = verify;
QAction* merge = createMenuEntry("Merge", ":./menu-merge.png", file, "document-file-merge");
QAction* merge = createMenuEntry("Merge", ":menu-merge", file, "document-file-merge");
connect(merge, &QAction::triggered, this, &View::merge);
mMerge = merge;
QAction* loadErrors = createMenuEntry("Error Log", ":./error-log.png", file, "document-file-errorlog");
QAction* loadErrors = createMenuEntry("Error Log", ":error-log", file, "document-file-errorlog");
connect(loadErrors, &QAction::triggered, this, &View::loadErrorLog);
QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata");
@ -102,10 +102,10 @@ void CSVDoc::View::setupFileMenu()
file->addSeparator();
QAction* close = createMenuEntry("Close", ":./menu-close.png", file, "document-file-close");
QAction* close = createMenuEntry("Close", ":menu-close", file, "document-file-close");
connect(close, &QAction::triggered, this, &View::close);
QAction* exit = createMenuEntry("Exit", ":./menu-exit.png", file, "document-file-exit");
QAction* exit = createMenuEntry("Exit", ":menu-exit", file, "document-file-exit");
connect(exit, &QAction::triggered, this, &View::exit);
connect(this, &View::exitApplicationRequest, &mViewManager, &ViewManager::exitApplication);
@ -140,17 +140,16 @@ void CSVDoc::View::setupEditMenu()
mUndo = mDocument->getUndoStack().createUndoAction(this, tr("Undo"));
setupShortcut("document-edit-undo", mUndo);
connect(mUndo, &QAction::changed, this, &View::undoActionChanged);
mUndo->setIcon(QIcon(QString::fromStdString(":./menu-undo.png")));
mUndo->setIcon(QIcon(QString::fromStdString(":menu-undo")));
edit->addAction(mUndo);
mRedo = mDocument->getUndoStack().createRedoAction(this, tr("Redo"));
connect(mRedo, &QAction::changed, this, &View::redoActionChanged);
setupShortcut("document-edit-redo", mRedo);
mRedo->setIcon(QIcon(QString::fromStdString(":./menu-redo.png")));
mRedo->setIcon(QIcon(QString::fromStdString(":menu-redo")));
edit->addAction(mRedo);
QAction* userSettings
= createMenuEntry("Preferences", ":./menu-preferences.png", edit, "document-edit-preferences");
QAction* userSettings = createMenuEntry("Preferences", ":menu-preferences", edit, "document-edit-preferences");
connect(userSettings, &QAction::triggered, this, &View::editSettingsRequest);
QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search");
@ -161,10 +160,10 @@ void CSVDoc::View::setupViewMenu()
{
QMenu* view = menuBar()->addMenu(tr("View"));
QAction* newWindow = createMenuEntry("New View", ":./menu-new-window.png", view, "document-view-newview");
QAction* newWindow = createMenuEntry("New View", ":menu-new-window", view, "document-view-newview");
connect(newWindow, &QAction::triggered, this, &View::newView);
mShowStatusBar = createMenuEntry("Toggle Status Bar", ":./menu-status-bar.png", view, "document-view-statusbar");
mShowStatusBar = createMenuEntry("Toggle Status Bar", ":menu-status-bar", view, "document-view-statusbar");
connect(mShowStatusBar, &QAction::toggled, this, &View::toggleShowStatusBar);
mShowStatusBar->setCheckable(true);
mShowStatusBar->setChecked(CSMPrefs::get()["Windows"]["show-statusbar"].isTrue());
@ -289,7 +288,7 @@ void CSVDoc::View::setupAssetsMenu()
{
QMenu* assets = menuBar()->addMenu(tr("Assets"));
QAction* reload = createMenuEntry("Reload", ":./menu-reload.png", assets, "document-assets-reload");
QAction* reload = createMenuEntry("Reload", ":menu-reload", assets, "document-assets-reload");
connect(reload, &QAction::triggered, &mDocument->getData(), &CSMWorld::Data::assetsChanged);
assets->addSeparator();
@ -341,9 +340,9 @@ void CSVDoc::View::setupDebugMenu()
QAction* runDebug = debug->addMenu(mGlobalDebugProfileMenu);
runDebug->setText(tr("Run OpenMW"));
setupShortcut("document-debug-run", runDebug);
runDebug->setIcon(QIcon(QString::fromStdString(":./run-openmw.png")));
runDebug->setIcon(QIcon(QString::fromStdString(":run-openmw")));
QAction* stopDebug = createMenuEntry("Stop OpenMW", ":./stop-openmw.png", debug, "document-debug-shutdown");
QAction* stopDebug = createMenuEntry("Stop OpenMW", ":stop-openmw", debug, "document-debug-shutdown");
connect(stopDebug, &QAction::triggered, this, &View::stop);
mStopDebug = stopDebug;
@ -355,16 +354,16 @@ void CSVDoc::View::setupHelpMenu()
{
QMenu* help = menuBar()->addMenu(tr("Help"));
QAction* helpInfo = createMenuEntry("Help", ":/info.png", help, "document-help-help");
QAction* helpInfo = createMenuEntry("Help", ":info", help, "document-help-help");
connect(helpInfo, &QAction::triggered, this, &View::openHelp);
QAction* tutorial = createMenuEntry("Tutorial", ":/info.png", help, "document-help-tutorial");
QAction* tutorial = createMenuEntry("Tutorial", ":info", help, "document-help-tutorial");
connect(tutorial, &QAction::triggered, this, &View::tutorial);
QAction* about = createMenuEntry("About OpenMW-CS", ":./info.png", help, "document-help-about");
QAction* about = createMenuEntry("About OpenMW-CS", ":info", help, "document-help-about");
connect(about, &QAction::triggered, this, &View::infoAbout);
QAction* aboutQt = createMenuEntry("About Qt", ":./qt.png", help, "document-help-qt");
QAction* aboutQt = createMenuEntry("About Qt", ":qt", help, "document-help-qt");
connect(aboutQt, &QAction::triggered, this, &View::infoAboutQt);
}

@ -44,7 +44,7 @@ CSVFilter::EditWidget::EditWidget(CSMWorld::Data& data, QWidget* parent)
mHelpAction = new QAction(tr("Help"), this);
connect(mHelpAction, &QAction::triggered, this, &EditWidget::openHelp);
mHelpAction->setIcon(QIcon(":/info.png"));
mHelpAction->setIcon(QIcon(":info"));
addAction(mHelpAction);
auto* openHelpShortcut = new CSMPrefs::Shortcut("help", this);
openHelpShortcut->associateAction(mHelpAction);

@ -12,11 +12,10 @@ namespace CSVRender
{
// elements that are part of the actual scene
Mask_Hidden = 0x0,
Mask_Reference = 0x2,
Mask_Pathgrid = 0x4,
Mask_Water = 0x8,
Mask_Fog = 0x10,
Mask_Terrain = 0x20,
Mask_Reference = 0x1,
Mask_Pathgrid = 0x2,
Mask_Water = 0x4,
Mask_Terrain = 0x8,
// used within models
Mask_ParticleSystem = 0x100,

@ -160,7 +160,6 @@ void CSVRender::PagedWorldspaceWidget::addVisibilitySelectorButtons(CSVWidget::S
{
WorldspaceWidget::addVisibilitySelectorButtons(tool);
tool->addButton(Button_Terrain, Mask_Terrain, "Terrain");
tool->addButton(Button_Fog, Mask_Fog, "Fog", "", true);
}
void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons(CSVWidget::SceneToolMode* tool)
@ -170,9 +169,10 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons(CSVWidget::Sce
/// \todo replace EditMode with suitable subclasses
tool->addButton(new TerrainShapeMode(this, mRootNode, tool), "terrain-shape");
tool->addButton(new TerrainTextureMode(this, mRootNode, tool), "terrain-texture");
tool->addButton(
new EditMode(this, QIcon(":placeholder"), Mask_Reference, "Terrain vertex paint editing"), "terrain-vertex");
tool->addButton(new EditMode(this, QIcon(":placeholder"), Mask_Reference, "Terrain movement"), "terrain-move");
const QIcon vertexIcon = QIcon(":scenetoolbar/editing-terrain-vertex-paint");
const QIcon movementIcon = QIcon(":scenetoolbar/editing-terrain-movement");
tool->addButton(new EditMode(this, vertexIcon, Mask_Reference, "Terrain vertex paint editing"), "terrain-vertex");
tool->addButton(new EditMode(this, movementIcon, Mask_Reference, "Terrain movement"), "terrain-move");
}
void CSVRender::PagedWorldspaceWidget::handleInteractionPress(const WorldspaceHitResult& hit, InteractionType type)

@ -36,8 +36,8 @@ class QWidget;
namespace CSVRender
{
PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent)
: EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid | Mask_Terrain | Mask_Reference, getTooltip(),
parent)
: EditMode(worldspaceWidget, QIcon(":scenetoolbar/editing-pathgrid"),
Mask_Pathgrid | Mask_Terrain | Mask_Reference, getTooltip(), parent)
, mDragMode(DragMode_None)
, mFromNode(0)
, mSelectionMode(nullptr)

@ -347,7 +347,6 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons(CSVWidget:
{
WorldspaceWidget::addVisibilitySelectorButtons(tool);
tool->addButton(Button_Terrain, Mask_Terrain, "Terrain", "", true);
tool->addButton(Button_Fog, Mask_Fog, "Fog");
}
std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction()

@ -222,8 +222,7 @@ namespace CSVRender
Button_Reference = 0x1,
Button_Pathgrid = 0x2,
Button_Water = 0x4,
Button_Fog = 0x8,
Button_Terrain = 0x10
Button_Terrain = 0x8
};
virtual void addVisibilitySelectorButtons(CSVWidget::SceneToolToggle2* tool);

@ -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);
}

@ -92,7 +92,7 @@ CSVWorld::RecordButtonBar::RecordButtonBar(const CSMWorld::UniversalId& id, CSMW
if (mTable.getFeatures() & CSMWorld::IdTable::Feature_View)
{
QToolButton* viewButton = new QToolButton(this);
viewButton->setIcon(QIcon(":/cell.png"));
viewButton->setIcon(QIcon(":cell"));
viewButton->setToolTip("Open a scene view of the cell this record is located in");
buttonsLayout->addWidget(viewButton);
connect(viewButton, &QToolButton::clicked, this, &RecordButtonBar::viewRecord);

@ -385,7 +385,7 @@ CSVWorld::Table::Table(const CSMWorld::UniversalId& id, bool createAndDelete, bo
mViewAction = new QAction(tr("View"), this);
connect(mViewAction, &QAction::triggered, this, &Table::viewRecord);
mViewAction->setIcon(QIcon(":/cell.png"));
mViewAction->setIcon(QIcon(":cell"));
addAction(mViewAction);
CSMPrefs::Shortcut* viewShortcut = new CSMPrefs::Shortcut("table-view", this);
viewShortcut->associateAction(mViewAction);
@ -417,7 +417,7 @@ CSVWorld::Table::Table(const CSMWorld::UniversalId& id, bool createAndDelete, bo
mHelpAction = new QAction(tr("Help"), this);
connect(mHelpAction, &QAction::triggered, this, &Table::openHelp);
mHelpAction->setIcon(QIcon(":/info.png"));
mHelpAction->setIcon(QIcon(":info"));
addAction(mHelpAction);
CSMPrefs::Shortcut* openHelpShortcut = new CSMPrefs::Shortcut("help", this);
openHelpShortcut->associateAction(mHelpAction);

@ -149,39 +149,36 @@ namespace CSMWorld
Params{ UniversalId(UniversalId::Type_None), UniversalId::Type_None, UniversalId::Class_None,
UniversalId::ArgumentType_None, "-", "-", ":placeholder" },
Params{ UniversalId(UniversalId::Type_RegionMap), UniversalId::Type_RegionMap, UniversalId::Class_NonRecord,
UniversalId::ArgumentType_None, "Region Map", "Region Map", ":./region-map.png" },
UniversalId::ArgumentType_None, "Region Map", "Region Map", ":region-map" },
Params{ UniversalId(UniversalId::Type_RunLog), UniversalId::Type_RunLog, UniversalId::Class_Transient,
UniversalId::ArgumentType_None, "Run Log", "Run Log", ":./run-log.png" },
UniversalId::ArgumentType_None, "Run Log", "Run Log", ":run-log" },
Params{ UniversalId(UniversalId::Type_Lands), UniversalId::Type_Lands, UniversalId::Class_RecordList,
UniversalId::ArgumentType_None, "Lands", "Lands", ":./land-heightmap.png" },
UniversalId::ArgumentType_None, "Lands", "Lands", ":land-heightmap" },
Params{ UniversalId(UniversalId::Type_Icons), UniversalId::Type_Icons, UniversalId::Class_ResourceList,
UniversalId::ArgumentType_None, "Icons", "Icons", ":./resources-icon" },
UniversalId::ArgumentType_None, "Icons", "Icons", ":resources-icon" },
Params{ UniversalId(UniversalId::Type_Activator, "a"), UniversalId::Type_Activator,
UniversalId::Class_RefRecord, UniversalId::ArgumentType_Id, "Activator", "Activator: a",
":./activator.png" },
UniversalId::Class_RefRecord, UniversalId::ArgumentType_Id, "Activator", "Activator: a", ":activator" },
Params{ UniversalId(UniversalId::Type_Gmst, "b"), UniversalId::Type_Gmst, UniversalId::Class_Record,
UniversalId::ArgumentType_Id, "Game Setting", "Game Setting: b", ":./gmst.png" },
UniversalId::ArgumentType_Id, "Game Setting", "Game Setting: b", ":gmst" },
Params{ UniversalId(UniversalId::Type_Mesh, "c"), UniversalId::Type_Mesh, UniversalId::Class_Resource,
UniversalId::ArgumentType_Id, "Mesh", "Mesh: c", ":./resources-mesh" },
UniversalId::ArgumentType_Id, "Mesh", "Mesh: c", ":resources-mesh" },
Params{ UniversalId(UniversalId::Type_Scene, "d"), UniversalId::Type_Scene, UniversalId::Class_Collection,
UniversalId::ArgumentType_Id, "Scene", "Scene: d", ":./scene.png" },
UniversalId::ArgumentType_Id, "Scene", "Scene: d", ":scene" },
Params{ UniversalId(UniversalId::Type_Reference, "e"), UniversalId::Type_Reference,
UniversalId::Class_SubRecord, UniversalId::ArgumentType_Id, "Instance", "Instance: e",
":./instance.png" },
UniversalId::Class_SubRecord, UniversalId::ArgumentType_Id, "Instance", "Instance: e", ":instance" },
Params{ UniversalId(UniversalId::Type_Search, 42), UniversalId::Type_Search, UniversalId::Class_Transient,
UniversalId::ArgumentType_Index, "Global Search", "Global Search: 42", ":./menu-search.png" },
UniversalId::ArgumentType_Index, "Global Search", "Global Search: 42", ":menu-search" },
Params{ UniversalId("Instance: f"), UniversalId::Type_Reference, UniversalId::Class_SubRecord,
UniversalId::ArgumentType_Id, "Instance", "Instance: f", ":./instance.png" },
UniversalId::ArgumentType_Id, "Instance", "Instance: f", ":instance" },
Params{ UniversalId(UniversalId::Type_Reference, ESM::RefId::stringRefId("g")), UniversalId::Type_Reference,
UniversalId::Class_SubRecord, UniversalId::ArgumentType_RefId, "Instance", "Instance: g",
":./instance.png" },
UniversalId::Class_SubRecord, UniversalId::ArgumentType_RefId, "Instance", "Instance: g", ":instance" },
Params{ UniversalId(UniversalId::Type_Reference, ESM::RefId::index(ESM::REC_SKIL, 42)),
UniversalId::Type_Reference, UniversalId::Class_SubRecord, UniversalId::ArgumentType_RefId, "Instance",
"Instance: SKIL:0x2a", ":./instance.png" },
"Instance: SKIL:0x2a", ":instance" },
};
INSTANTIATE_TEST_SUITE_P(ValidParams, CSMWorldUniversalIdValidPerTypeTest, ValuesIn(validParams));

@ -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<ESM::NPC>()->mBase->mClass != selectId;
else if (select.getFunction() == MWDialogue::SelectWrapper::Function_NotRace)
else if (select.getFunction() == ESM::DialogueCondition::Function_NotRace)
return actor.get<ESM::NPC>()->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<ESM::DialInfo::SelectStruct>::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<int>(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<int>(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<MWMechanics::AiSetting>(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<int>(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<ESM::NPC>()->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<ESM::NPC>()->mBase->mClass == ESM::RefId::stringRefId(select.getName()));
return mActor.get<ESM::NPC>()->mBase->mClass != select.getId();
case SelectWrapper::Function_NotRace:
case ESM::DialogueCondition::Function_NotRace:
return !(mActor.get<ESM::NPC>()->mBase->mRace == ESM::RefId::stringRefId(select.getName()));
return mActor.get<ESM::NPC>()->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<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female)
== (mActor.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female);
case SelectWrapper::Function_SameRace:
case ESM::DialogueCondition::Function_SameRace:
return mActor.get<ESM::NPC>()->mBase->mRace == player.get<ESM::NPC>()->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();

@ -10,431 +10,264 @@
namespace
{
template <typename T1, typename T2>
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 <typename T>
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<int>(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);
}

@ -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;
};
}

@ -1,15 +1,41 @@
#include "ffmpeg_decoder.hpp"
#include <memory>
#include <algorithm>
#include <memory>
#include <stdexcept>
#include <utility>
#include <components/debug/debuglog.hpp>
#include <components/vfs/manager.hpp>
namespace MWSound
{
void AVIOContextDeleter::operator()(AVIOContext* ptr) const
{
if (ptr->buffer != nullptr)
av_freep(&ptr->buffer);
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100)
avio_context_free(&ptr);
#else
av_free(ptr);
#endif
}
void AVFormatContextDeleter::operator()(AVFormatContext* ptr) const
{
avformat_close_input(&ptr);
}
void AVCodecContextDeleter::operator()(AVCodecContext* ptr) const
{
avcodec_free_context(&ptr);
}
void AVFrameDeleter::operator()(AVFrame* ptr) const
{
av_frame_free(&ptr);
}
int FFmpeg_Decoder::readPacket(void* user_data, uint8_t* buf, int buf_size)
{
@ -75,7 +101,7 @@ namespace MWSound
return false;
std::ptrdiff_t stream_idx = mStream - mFormatCtx->streams;
while (av_read_frame(mFormatCtx, &mPacket) >= 0)
while (av_read_frame(mFormatCtx.get(), &mPacket) >= 0)
{
/* Check if the packet belongs to this stream */
if (stream_idx == mPacket.stream_index)
@ -102,12 +128,12 @@ namespace MWSound
do
{
/* Decode some data, and check for errors */
int ret = avcodec_receive_frame(mCodecCtx, mFrame);
int ret = avcodec_receive_frame(mCodecCtx.get(), mFrame.get());
if (ret == AVERROR(EAGAIN))
{
if (mPacket.size == 0 && !getNextPacket())
return false;
ret = avcodec_send_packet(mCodecCtx, &mPacket);
ret = avcodec_send_packet(mCodecCtx.get(), &mPacket);
av_packet_unref(&mPacket);
if (ret == 0)
continue;
@ -187,137 +213,95 @@ namespace MWSound
close();
mDataStream = mResourceMgr->get(fname);
if ((mFormatCtx = avformat_alloc_context()) == nullptr)
AVIOContextPtr ioCtx(avio_alloc_context(nullptr, 0, 0, this, readPacket, writePacket, seek));
if (ioCtx == nullptr)
throw std::runtime_error("Failed to allocate AVIO context");
AVFormatContext* formatCtx = avformat_alloc_context();
if (formatCtx == nullptr)
throw std::runtime_error("Failed to allocate context");
try
formatCtx->pb = ioCtx.get();
// avformat_open_input frees user supplied AVFormatContext on failure
if (avformat_open_input(&formatCtx, fname.c_str(), nullptr, nullptr) != 0)
throw std::runtime_error("Failed to open input");
AVFormatContextPtr formatCtxPtr(std::exchange(formatCtx, nullptr));
if (avformat_find_stream_info(formatCtxPtr.get(), nullptr) < 0)
throw std::runtime_error("Failed to find stream info");
AVStream** stream = nullptr;
for (size_t j = 0; j < formatCtxPtr->nb_streams; j++)
{
mFormatCtx->pb = avio_alloc_context(nullptr, 0, 0, this, readPacket, writePacket, seek);
if (!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), nullptr, nullptr) != 0)
if (formatCtxPtr->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
// "Note that a user-supplied AVFormatContext will be freed on failure".
if (mFormatCtx)
{
if (mFormatCtx->pb != nullptr)
{
if (mFormatCtx->pb->buffer != nullptr)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = nullptr;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = nullptr;
}
avformat_free_context(mFormatCtx);
}
mFormatCtx = nullptr;
throw std::runtime_error("Failed to allocate input stream");
stream = &formatCtxPtr->streams[j];
break;
}
}
if (avformat_find_stream_info(mFormatCtx, nullptr) < 0)
throw std::runtime_error("Failed to find stream info in " + fname);
if (stream == nullptr)
throw std::runtime_error("No audio streams");
for (size_t j = 0; j < mFormatCtx->nb_streams; j++)
{
if (mFormatCtx->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
mStream = &mFormatCtx->streams[j];
break;
}
}
if (!mStream)
throw std::runtime_error("No audio streams in " + fname);
const AVCodec* codec = avcodec_find_decoder((*stream)->codecpar->codec_id);
if (codec == nullptr)
throw std::runtime_error("No codec found for id " + std::to_string((*stream)->codecpar->codec_id));
const AVCodec* codec = avcodec_find_decoder((*mStream)->codecpar->codec_id);
if (!codec)
{
std::string ss = "No codec found for id " + std::to_string((*mStream)->codecpar->codec_id);
throw std::runtime_error(ss);
}
AVCodecContext* codecCtx = avcodec_alloc_context3(codec);
if (codecCtx == nullptr)
throw std::runtime_error("Failed to allocate codec context");
AVCodecContext* avctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(avctx, (*mStream)->codecpar);
avcodec_parameters_to_context(codecCtx, (*stream)->codecpar);
// This is not needed anymore above FFMpeg version 4.0
#if LIBAVCODEC_VERSION_INT < 3805796
av_codec_set_pkt_timebase(avctx, (*mStream)->time_base);
av_codec_set_pkt_timebase(avctx, (*stream)->time_base);
#endif
mCodecCtx = avctx;
if (avcodec_open2(mCodecCtx, codec, nullptr) < 0)
throw std::runtime_error(std::string("Failed to open audio codec ") + codec->long_name);
AVCodecContextPtr codecCtxPtr(std::exchange(codecCtx, nullptr));
mFrame = av_frame_alloc();
if (avcodec_open2(codecCtxPtr.get(), codec, nullptr) < 0)
throw std::runtime_error(std::string("Failed to open audio codec ") + codec->long_name);
if (mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8P)
mOutputSampleFormat = AV_SAMPLE_FMT_U8;
// FIXME: Check for AL_EXT_FLOAT32 support
// else if (mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP)
// mOutputSampleFormat = AV_SAMPLE_FMT_S16;
else
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
AVFramePtr frame(av_frame_alloc());
if (frame == nullptr)
throw std::runtime_error("Failed to allocate frame");
mOutputChannelLayout = (*mStream)->codecpar->channel_layout;
if (mOutputChannelLayout == 0)
mOutputChannelLayout = av_get_default_channel_layout(mCodecCtx->channels);
if (codecCtxPtr->sample_fmt == AV_SAMPLE_FMT_U8P)
mOutputSampleFormat = AV_SAMPLE_FMT_U8;
// FIXME: Check for AL_EXT_FLOAT32 support
// else if (codecCtxPtr->sample_fmt == AV_SAMPLE_FMT_FLT || codecCtxPtr->sample_fmt == AV_SAMPLE_FMT_FLTP)
// mOutputSampleFormat = AV_SAMPLE_FMT_S16;
else
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
mCodecCtx->channel_layout = mOutputChannelLayout;
}
catch (...)
{
if (mStream)
avcodec_free_context(&mCodecCtx);
mStream = nullptr;
mOutputChannelLayout = (*stream)->codecpar->channel_layout;
if (mOutputChannelLayout == 0)
mOutputChannelLayout = av_get_default_channel_layout(codecCtxPtr->channels);
if (mFormatCtx != nullptr)
{
if (mFormatCtx->pb->buffer != nullptr)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = nullptr;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = nullptr;
codecCtxPtr->channel_layout = mOutputChannelLayout;
avformat_close_input(&mFormatCtx);
}
}
mIoCtx = std::move(ioCtx);
mFrame = std::move(frame);
mFormatCtx = std::move(formatCtxPtr);
mCodecCtx = std::move(codecCtxPtr);
mStream = stream;
}
void FFmpeg_Decoder::close()
{
if (mStream)
avcodec_free_context(&mCodecCtx);
mStream = nullptr;
mCodecCtx.reset();
av_packet_unref(&mPacket);
av_freep(&mDataBuf);
av_frame_free(&mFrame);
mFrame.reset();
swr_free(&mSwr);
if (mFormatCtx)
{
if (mFormatCtx->pb != nullptr)
{
// mFormatCtx->pb->buffer must be freed by hand,
// if not, valgrind will show memleak, see:
//
// https://trac.ffmpeg.org/ticket/1357
//
if (mFormatCtx->pb->buffer != nullptr)
{
av_freep(&mFormatCtx->pb->buffer);
}
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100)
avio_context_free(&mFormatCtx->pb);
#else
av_freep(&mFormatCtx->pb);
#endif
}
avformat_close_input(&mFormatCtx);
}
mFormatCtx.reset();
mIoCtx.reset();
mDataStream.reset();
}
@ -436,10 +420,7 @@ namespace MWSound
FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs)
: Sound_Decoder(vfs)
, mFormatCtx(nullptr)
, mCodecCtx(nullptr)
, mStream(nullptr)
, mFrame(nullptr)
, mFrameSize(0)
, mFramePos(0)
, mNextPts(0.0)
@ -470,5 +451,4 @@ namespace MWSound
{
close();
}
}

@ -32,14 +32,43 @@ extern "C"
namespace MWSound
{
struct AVIOContextDeleter
{
void operator()(AVIOContext* ptr) const;
};
using AVIOContextPtr = std::unique_ptr<AVIOContext, AVIOContextDeleter>;
struct AVFormatContextDeleter
{
void operator()(AVFormatContext* ptr) const;
};
using AVFormatContextPtr = std::unique_ptr<AVFormatContext, AVFormatContextDeleter>;
struct AVCodecContextDeleter
{
void operator()(AVCodecContext* ptr) const;
};
using AVCodecContextPtr = std::unique_ptr<AVCodecContext, AVCodecContextDeleter>;
struct AVFrameDeleter
{
void operator()(AVFrame* ptr) const;
};
using AVFramePtr = std::unique_ptr<AVFrame, AVFrameDeleter>;
class FFmpeg_Decoder final : public Sound_Decoder
{
AVFormatContext* mFormatCtx;
AVCodecContext* mCodecCtx;
AVIOContextPtr mIoCtx;
AVFormatContextPtr mFormatCtx;
AVCodecContextPtr mCodecCtx;
AVStream** mStream;
AVPacket mPacket;
AVFrame* mFrame;
AVFramePtr mFrame;
std::size_t mFrameSize;
std::size_t mFramePos;

@ -325,6 +325,7 @@ namespace MWWorld
player.mObject.mEnabled = true;
}
MWBase::Environment::get().getWorldModel()->deregisterLiveCellRef(mPlayer);
mPlayer.load(player.mObject);
for (size_t i = 0; i < mSaveAttributes.size(); ++i)

@ -591,6 +591,12 @@ namespace MWWorld
// Must be cleared before mRendering is destroyed
if (mProjectileManager)
mProjectileManager->clear();
if (Settings::navigator().mWaitForAllJobsOnExit)
{
Log(Debug::Verbose) << "Waiting for all navmesh jobs to be done...";
mNavigator->wait(DetourNavigator::WaitConditionType::allJobsDone, nullptr);
}
}
void World::setRandomSeed(uint32_t seed)

@ -267,7 +267,6 @@ namespace
updater.wait(WaitConditionType::allJobsDone, &mListener);
updater.stop();
const std::set<TilePosition> present{
TilePosition(-2, 0),
TilePosition(-1, -1),
TilePosition(-1, 0),
TilePosition(-1, 1),
@ -278,6 +277,7 @@ namespace
TilePosition(0, 2),
TilePosition(1, -1),
TilePosition(1, 0),
TilePosition(1, 1),
};
for (int x = -5; x <= 5; ++x)
for (int y = -5; y <= 5; ++y)
@ -336,4 +336,273 @@ namespace
EXPECT_EQ(tile->mTileId, 2);
EXPECT_EQ(tile->mVersion, navMeshFormatVersion);
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, repeated_tile_updates_should_be_delayed)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
mSettings.mMaxTilesNumber = 9;
mSettings.mMinUpdateInterval = std::chrono::milliseconds(250);
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
std::map<TilePosition, ChangeType> changedTiles;
for (int x = -3; x <= 3; ++x)
for (int y = -3; y <= 3; ++y)
changedTiles.emplace(TilePosition{ x, y }, ChangeType::update);
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
{
const AsyncNavMeshUpdaterStats stats = updater.getStats();
EXPECT_EQ(stats.mJobs, 0);
EXPECT_EQ(stats.mWaiting.mDelayed, 0);
}
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
{
const AsyncNavMeshUpdaterStats stats = updater.getStats();
EXPECT_EQ(stats.mJobs, 49);
EXPECT_EQ(stats.mWaiting.mDelayed, 49);
}
updater.wait(WaitConditionType::allJobsDone, &mListener);
{
const AsyncNavMeshUpdaterStats stats = updater.getStats();
EXPECT_EQ(stats.mJobs, 0);
EXPECT_EQ(stats.mWaiting.mDelayed, 0);
}
}
struct DetourNavigatorSpatialJobQueueTest : Test
{
const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, osg::Vec3f(1, 1, 1) };
const std::shared_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItemPtr;
const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem = mNavMeshCacheItemPtr;
const std::string_view mWorldspace = "worldspace";
const TilePosition mChangedTile{ 0, 0 };
const std::chrono::steady_clock::time_point mProcessTime{};
const TilePosition mPlayerTile{ 0, 0 };
const int mMaxTiles = 9;
};
TEST_F(DetourNavigatorSpatialJobQueueTest, should_store_multiple_jobs_per_tile)
{
std::list<Job> jobs;
SpatialJobQueue queue;
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, "worldspace1", mChangedTile,
ChangeType::remove, mProcessTime));
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, "worldspace2", mChangedTile,
ChangeType::update, mProcessTime));
ASSERT_EQ(queue.size(), 2);
const auto job1 = queue.pop(mChangedTile);
ASSERT_TRUE(job1.has_value());
EXPECT_EQ((*job1)->mWorldspace, "worldspace1");
const auto job2 = queue.pop(mChangedTile);
ASSERT_TRUE(job2.has_value());
EXPECT_EQ((*job2)->mWorldspace, "worldspace2");
EXPECT_EQ(queue.size(), 0);
}
struct DetourNavigatorJobQueueTest : DetourNavigatorSpatialJobQueueTest
{
};
TEST_F(DetourNavigatorJobQueueTest, pop_should_return_nullptr_from_empty)
{
JobQueue queue;
ASSERT_FALSE(queue.hasJob());
ASSERT_FALSE(queue.pop(mPlayerTile).has_value());
}
TEST_F(DetourNavigatorJobQueueTest, push_on_change_type_remove_should_add_to_removing)
{
const std::chrono::steady_clock::time_point processTime{};
std::list<Job> jobs;
const JobIt job = jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, mChangedTile, ChangeType::remove, processTime);
JobQueue queue;
queue.push(job);
EXPECT_EQ(queue.getStats().mRemoving, 1);
}
TEST_F(DetourNavigatorJobQueueTest, pop_should_return_last_removing)
{
std::list<Job> jobs;
JobQueue queue;
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, TilePosition(0, 0),
ChangeType::remove, mProcessTime));
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, TilePosition(1, 0),
ChangeType::remove, mProcessTime));
ASSERT_TRUE(queue.hasJob());
const auto job = queue.pop(mPlayerTile);
ASSERT_TRUE(job.has_value());
EXPECT_EQ((*job)->mChangedTile, TilePosition(1, 0));
}
TEST_F(DetourNavigatorJobQueueTest, push_on_change_type_not_remove_should_add_to_updating)
{
std::list<Job> jobs;
const JobIt job = jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, mChangedTile, ChangeType::update, mProcessTime);
JobQueue queue;
queue.push(job);
EXPECT_EQ(queue.getStats().mUpdating, 1);
}
TEST_F(DetourNavigatorJobQueueTest, pop_should_return_nearest_to_player_tile)
{
std::list<Job> jobs;
JobQueue queue;
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, TilePosition(0, 0),
ChangeType::update, mProcessTime));
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, TilePosition(1, 0),
ChangeType::update, mProcessTime));
ASSERT_TRUE(queue.hasJob());
const auto job = queue.pop(TilePosition(1, 0));
ASSERT_TRUE(job.has_value());
EXPECT_EQ((*job)->mChangedTile, TilePosition(1, 0));
}
TEST_F(DetourNavigatorJobQueueTest, push_on_processing_time_more_than_now_should_add_to_delayed)
{
const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
const std::chrono::steady_clock::time_point processTime = now + std::chrono::seconds(1);
std::list<Job> jobs;
const JobIt job = jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, mChangedTile, ChangeType::update, processTime);
JobQueue queue;
queue.push(job, now);
EXPECT_EQ(queue.getStats().mDelayed, 1);
}
TEST_F(DetourNavigatorJobQueueTest, pop_should_return_when_delayed_job_is_ready)
{
const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
const std::chrono::steady_clock::time_point processTime = now + std::chrono::seconds(1);
std::list<Job> jobs;
const JobIt job = jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, mChangedTile, ChangeType::update, processTime);
JobQueue queue;
queue.push(job, now);
ASSERT_FALSE(queue.hasJob(now));
ASSERT_FALSE(queue.pop(mPlayerTile, now).has_value());
ASSERT_TRUE(queue.hasJob(processTime));
EXPECT_TRUE(queue.pop(mPlayerTile, processTime).has_value());
}
TEST_F(DetourNavigatorJobQueueTest, update_should_move_ready_delayed_to_updating)
{
const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
const std::chrono::steady_clock::time_point processTime = now + std::chrono::seconds(1);
std::list<Job> jobs;
const JobIt job = jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, mChangedTile, ChangeType::update, processTime);
JobQueue queue;
queue.push(job, now);
ASSERT_EQ(queue.getStats().mDelayed, 1);
queue.update(mPlayerTile, mMaxTiles, processTime);
EXPECT_EQ(queue.getStats().mDelayed, 0);
EXPECT_EQ(queue.getStats().mUpdating, 1);
}
TEST_F(DetourNavigatorJobQueueTest, update_should_move_ready_delayed_to_removing_when_out_of_range)
{
const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
const std::chrono::steady_clock::time_point processTime = now + std::chrono::seconds(1);
std::list<Job> jobs;
const JobIt job = jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, mChangedTile, ChangeType::update, processTime);
JobQueue queue;
queue.push(job, now);
ASSERT_EQ(queue.getStats().mDelayed, 1);
queue.update(TilePosition(10, 10), mMaxTiles, processTime);
EXPECT_EQ(queue.getStats().mDelayed, 0);
EXPECT_EQ(queue.getStats().mRemoving, 1);
EXPECT_EQ(job->mChangeType, ChangeType::remove);
}
TEST_F(DetourNavigatorJobQueueTest, update_should_move_updating_to_removing_when_out_of_range)
{
std::list<Job> jobs;
JobQueue queue;
queue.push(jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, mChangedTile, ChangeType::update, mProcessTime));
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, TilePosition(10, 10),
ChangeType::update, mProcessTime));
ASSERT_EQ(queue.getStats().mUpdating, 2);
queue.update(TilePosition(10, 10), mMaxTiles);
EXPECT_EQ(queue.getStats().mUpdating, 1);
EXPECT_EQ(queue.getStats().mRemoving, 1);
}
TEST_F(DetourNavigatorJobQueueTest, clear_should_remove_all)
{
const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
const std::chrono::steady_clock::time_point processTime = now + std::chrono::seconds(1);
std::list<Job> jobs;
const JobIt removing = jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace,
TilePosition(0, 0), ChangeType::remove, mProcessTime);
const JobIt updating = jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace,
TilePosition(1, 0), ChangeType::update, mProcessTime);
const JobIt delayed = jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, mWorldspace, TilePosition(2, 0),
ChangeType::update, processTime);
JobQueue queue;
queue.push(removing);
queue.push(updating);
queue.push(delayed, now);
ASSERT_EQ(queue.getStats().mRemoving, 1);
ASSERT_EQ(queue.getStats().mUpdating, 1);
ASSERT_EQ(queue.getStats().mDelayed, 1);
queue.clear();
EXPECT_EQ(queue.getStats().mRemoving, 0);
EXPECT_EQ(queue.getStats().mUpdating, 0);
EXPECT_EQ(queue.getStats().mDelayed, 0);
}
}

@ -1055,6 +1055,96 @@ namespace
}
}
TEST_P(DetourNavigatorUpdateTest, update_should_change_covered_area_when_player_moves_without_waiting_for_all)
{
Loading::Listener listener;
Settings settings = makeSettings();
settings.mMaxTilesNumber = 1;
settings.mWaitUntilMinDistanceToPlayer = 1;
NavigatorImpl navigator(settings, nullptr);
const AgentBounds agentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } };
ASSERT_TRUE(navigator.addAgent(agentBounds));
GetParam()(navigator);
{
auto updateGuard = navigator.makeUpdateGuard();
navigator.update(osg::Vec3f(3000, 3000, 0), updateGuard.get());
}
navigator.wait(WaitConditionType::requiredTilesPresent, &listener);
{
const auto navMesh = navigator.getNavMesh(agentBounds);
ASSERT_NE(navMesh, nullptr);
const TilePosition expectedTile(4, 4);
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
EXPECT_THAT(usedTiles, Contains(expectedTile)) << usedTiles;
}
{
auto updateGuard = navigator.makeUpdateGuard();
navigator.update(osg::Vec3f(6000, 3000, 0), updateGuard.get());
}
navigator.wait(WaitConditionType::requiredTilesPresent, &listener);
{
const auto navMesh = navigator.getNavMesh(agentBounds);
ASSERT_NE(navMesh, nullptr);
const TilePosition expectedTile(8, 4);
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
EXPECT_THAT(usedTiles, Contains(expectedTile)) << usedTiles;
}
}
TEST_P(DetourNavigatorUpdateTest, update_should_change_covered_area_when_player_moves_with_db)
{
Loading::Listener listener;
Settings settings = makeSettings();
settings.mMaxTilesNumber = 1;
settings.mWaitUntilMinDistanceToPlayer = 1;
NavigatorImpl navigator(settings, std::make_unique<NavMeshDb>(":memory:", settings.mMaxDbFileSize));
const AgentBounds agentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } };
ASSERT_TRUE(navigator.addAgent(agentBounds));
GetParam()(navigator);
{
auto updateGuard = navigator.makeUpdateGuard();
navigator.update(osg::Vec3f(3000, 3000, 0), updateGuard.get());
}
navigator.wait(WaitConditionType::requiredTilesPresent, &listener);
{
const auto navMesh = navigator.getNavMesh(agentBounds);
ASSERT_NE(navMesh, nullptr);
const TilePosition expectedTile(4, 4);
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
EXPECT_THAT(usedTiles, Contains(expectedTile)) << usedTiles;
}
{
auto updateGuard = navigator.makeUpdateGuard();
navigator.update(osg::Vec3f(6000, 3000, 0), updateGuard.get());
}
navigator.wait(WaitConditionType::requiredTilesPresent, &listener);
{
const auto navMesh = navigator.getNavMesh(agentBounds);
ASSERT_NE(navMesh, nullptr);
const TilePosition expectedTile(8, 4);
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
EXPECT_THAT(usedTiles, Contains(expectedTile)) << usedTiles;
}
}
struct AddHeightfieldSurface
{
static constexpr std::size_t sSize = 65;

@ -6,6 +6,7 @@
#include <components/esm3/esmwriter.hpp>
#include <components/esm3/loadcont.hpp>
#include <components/esm3/loaddial.hpp>
#include <components/esm3/loadinfo.hpp>
#include <components/esm3/loadregn.hpp>
#include <components/esm3/loadscpt.hpp>
#include <components/esm3/loadweap.hpp>
@ -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()));
}
}

@ -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

@ -61,6 +61,11 @@ namespace Debug
bool outRedirected = isRedirected(STD_OUTPUT_HANDLE);
bool errRedirected = isRedirected(STD_ERROR_HANDLE);
// Note: Do not spend three days reinvestigating this PowerShell bug thinking its our bug.
// https://gitlab.com/OpenMW/openmw/-/merge_requests/408#note_447467393
// The handles look valid, but GetFinalPathNameByHandleA can't tell what files they go to and writing to them
// doesn't work.
if (AttachConsole(ATTACH_PARENT_PROCESS))
{
fflush(stdout);
@ -73,16 +78,19 @@ namespace Debug
{
_wfreopen(L"CON", L"r", stdin);
freopen("CON", "r", stdin);
std::cin.clear();
}
if (!outRedirected)
{
_wfreopen(L"CON", L"w", stdout);
freopen("CON", "w", stdout);
std::cout.clear();
}
if (!errRedirected)
{
_wfreopen(L"CON", L"w", stderr);
freopen("CON", "w", stderr);
std::cerr.clear();
}
return true;
@ -256,9 +264,17 @@ namespace Debug
private:
static bool useColoredOutput()
{
// Note: cmd.exe in Win10 should support ANSI colors, but in its own way.
#if defined(_WIN32)
return 0;
if (getenv("NO_COLOR"))
return false;
DWORD mode;
if (GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &mode) && mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
return true;
// some console emulators may not use the Win32 API, so try the Unixy approach
char* term = getenv("TERM");
return term && GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_CHAR;
#else
char* term = getenv("TERM");
bool useColor = term && !getenv("NO_COLOR") && isatty(fileno(stderr));

@ -16,8 +16,9 @@
#include <osg/io_utils>
#include <boost/geometry.hpp>
#include <algorithm>
#include <numeric>
#include <optional>
#include <set>
#include <tuple>
@ -49,40 +50,6 @@ namespace DetourNavigator
return false;
}
auto getPriority(const Job& job) noexcept
{
return std::make_tuple(-static_cast<std::underlying_type_t<JobState>>(job.mState), job.mProcessTime,
job.mChangeType, job.mTryNumber, job.mDistanceToPlayer, job.mDistanceToOrigin);
}
struct LessByJobPriority
{
bool operator()(JobIt lhs, JobIt rhs) const noexcept { return getPriority(*lhs) < getPriority(*rhs); }
};
void insertPrioritizedJob(JobIt job, std::deque<JobIt>& queue)
{
const auto it = std::upper_bound(queue.begin(), queue.end(), job, LessByJobPriority{});
queue.insert(it, job);
}
auto getDbPriority(const Job& job) noexcept
{
return std::make_tuple(static_cast<std::underlying_type_t<JobState>>(job.mState), job.mChangeType,
job.mDistanceToPlayer, job.mDistanceToOrigin);
}
struct LessByJobDbPriority
{
bool operator()(JobIt lhs, JobIt rhs) const noexcept { return getDbPriority(*lhs) < getDbPriority(*rhs); }
};
void insertPrioritizedDbJob(JobIt job, std::deque<JobIt>& queue)
{
const auto it = std::upper_bound(queue.begin(), queue.end(), job, LessByJobDbPriority{});
queue.insert(it, job);
}
auto getAgentAndTile(const Job& job) noexcept
{
return std::make_tuple(job.mAgentBounds, job.mChangedTile);
@ -97,16 +64,6 @@ namespace DetourNavigator
settings.mRecast, settings.mWriteToNavMeshDb);
}
void updateJobs(std::deque<JobIt>& jobs, TilePosition playerTile, int maxTiles)
{
for (JobIt job : jobs)
{
job->mDistanceToPlayer = getManhattanDistance(job->mChangedTile, playerTile);
if (!shouldAddTile(job->mChangedTile, playerTile, maxTiles))
job->mChangeType = ChangeType::remove;
}
}
std::size_t getNextJobId()
{
static std::atomic_size_t nextJobId{ 1 };
@ -134,7 +91,7 @@ namespace DetourNavigator
}
Job::Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType,
std::chrono::steady_clock::time_point processTime)
: mId(getNextJobId())
, mAgentBounds(agentBounds)
@ -143,11 +100,148 @@ namespace DetourNavigator
, mChangedTile(changedTile)
, mProcessTime(processTime)
, mChangeType(changeType)
, mDistanceToPlayer(distanceToPlayer)
, mDistanceToOrigin(getManhattanDistance(changedTile, TilePosition{ 0, 0 }))
{
}
void SpatialJobQueue::clear()
{
mValues.clear();
mIndex.clear();
mSize = 0;
}
void SpatialJobQueue::push(JobIt job)
{
auto it = mValues.find(job->mChangedTile);
if (it == mValues.end())
{
it = mValues.emplace_hint(it, job->mChangedTile, std::deque<JobIt>());
mIndex.insert(IndexValue(IndexPoint(job->mChangedTile.x(), job->mChangedTile.y()), it));
}
it->second.push_back(job);
++mSize;
}
std::optional<JobIt> SpatialJobQueue::pop(TilePosition playerTile)
{
const IndexPoint point(playerTile.x(), playerTile.y());
const auto it = mIndex.qbegin(boost::geometry::index::nearest(point, 1));
if (it == mIndex.qend())
return std::nullopt;
const UpdatingMap::iterator mapIt = it->second;
std::deque<JobIt>& tileJobs = mapIt->second;
JobIt result = tileJobs.front();
tileJobs.pop_front();
--mSize;
if (tileJobs.empty())
{
mValues.erase(mapIt);
mIndex.remove(*it);
}
return result;
}
void SpatialJobQueue::update(TilePosition playerTile, int maxTiles, std::vector<JobIt>& removing)
{
for (auto it = mValues.begin(); it != mValues.end();)
{
if (shouldAddTile(it->first, playerTile, maxTiles))
{
++it;
continue;
}
for (JobIt job : it->second)
{
job->mChangeType = ChangeType::remove;
removing.push_back(job);
}
mSize -= it->second.size();
mIndex.remove(IndexValue(IndexPoint(it->first.x(), it->first.y()), it));
it = mValues.erase(it);
}
}
bool JobQueue::hasJob(std::chrono::steady_clock::time_point now) const
{
return !mRemoving.empty() || mUpdating.size() > 0
|| (!mDelayed.empty() && mDelayed.front()->mProcessTime <= now);
}
void JobQueue::clear()
{
mRemoving.clear();
mDelayed.clear();
mUpdating.clear();
}
void JobQueue::push(JobIt job, std::chrono::steady_clock::time_point now)
{
if (job->mProcessTime > now)
{
mDelayed.push_back(job);
return;
}
if (job->mChangeType == ChangeType::remove)
{
mRemoving.push_back(job);
return;
}
mUpdating.push(job);
}
std::optional<JobIt> JobQueue::pop(TilePosition playerTile, std::chrono::steady_clock::time_point now)
{
if (!mRemoving.empty())
{
const JobIt result = mRemoving.back();
mRemoving.pop_back();
return result;
}
if (const std::optional<JobIt> result = mUpdating.pop(playerTile))
return result;
if (mDelayed.empty() || mDelayed.front()->mProcessTime > now)
return std::nullopt;
const JobIt result = mDelayed.front();
mDelayed.pop_front();
return result;
}
void JobQueue::update(TilePosition playerTile, int maxTiles, std::chrono::steady_clock::time_point now)
{
mUpdating.update(playerTile, maxTiles, mRemoving);
while (!mDelayed.empty() && mDelayed.front()->mProcessTime <= now)
{
const JobIt job = mDelayed.front();
mDelayed.pop_front();
if (shouldAddTile(job->mChangedTile, playerTile, maxTiles))
{
mUpdating.push(job);
}
else
{
job->mChangeType = ChangeType::remove;
mRemoving.push_back(job);
}
}
}
AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings)
@ -180,48 +274,47 @@ namespace DetourNavigator
if (!playerTileChanged && changedTiles.empty())
return;
const int maxTiles
= std::min(mSettings.get().mMaxTilesNumber, navMeshCacheItem->lockConst()->getImpl().getParams()->maxTiles);
std::unique_lock lock(mMutex);
if (playerTileChanged)
updateJobs(mWaiting, playerTile, maxTiles);
{
Log(Debug::Debug) << "Player tile has been changed to " << playerTile;
mWaiting.update(playerTile, mSettings.get().mMaxTilesNumber);
}
for (const auto& [changedTile, changeType] : changedTiles)
{
if (mPushed.emplace(agentBounds, changedTile).second)
{
const auto processTime = changeType == ChangeType::update
? mLastUpdates[std::tie(agentBounds, changedTile)] + mSettings.get().mMinUpdateInterval
: std::chrono::steady_clock::time_point();
const JobIt it = mJobs.emplace(mJobs.end(), agentBounds, navMeshCacheItem, worldspace, changedTile,
changeType, getManhattanDistance(changedTile, playerTile), processTime);
const auto processTime = [&, changedTile = changedTile, changeType = changeType] {
if (changeType != ChangeType::update)
return std::chrono::steady_clock::time_point();
const auto lastUpdate = mLastUpdates.find(std::tie(agentBounds, changedTile));
if (lastUpdate == mLastUpdates.end())
return std::chrono::steady_clock::time_point();
return lastUpdate->second + mSettings.get().mMinUpdateInterval;
}();
const JobIt it = mJobs.emplace(
mJobs.end(), agentBounds, navMeshCacheItem, worldspace, changedTile, changeType, processTime);
Log(Debug::Debug) << "Post job " << it->mId << " for agent=(" << it->mAgentBounds << ")"
<< " changedTile=(" << it->mChangedTile << ") "
<< " changedTile=(" << it->mChangedTile << ")"
<< " changeType=" << it->mChangeType;
if (playerTileChanged)
mWaiting.push_back(it);
else
insertPrioritizedJob(it, mWaiting);
mWaiting.push(it);
}
}
if (playerTileChanged)
std::sort(mWaiting.begin(), mWaiting.end(), LessByJobPriority{});
Log(Debug::Debug) << "Posted " << mJobs.size() << " navigator jobs";
if (!mWaiting.empty())
if (mWaiting.hasJob())
mHasJob.notify_all();
lock.unlock();
if (playerTileChanged && mDbWorker != nullptr)
mDbWorker->updateJobs(playerTile, maxTiles);
mDbWorker->update(playerTile);
}
void AsyncNavMeshUpdater::wait(WaitConditionType waitConditionType, Loading::Listener* listener)
@ -313,7 +406,7 @@ namespace DetourNavigator
{
const std::lock_guard<std::mutex> lock(mMutex);
result.mJobs = mJobs.size();
result.mWaiting = mWaiting.size();
result.mWaiting = mWaiting.getStats();
result.mPushed = mPushed.size();
}
result.mProcessing = mProcessingTiles.lockConst()->size();
@ -335,7 +428,8 @@ namespace DetourNavigator
if (JobIt job = getNextJob(); job != mJobs.end())
{
const JobStatus status = processJob(*job);
Log(Debug::Debug) << "Processed job " << job->mId << " with status=" << status;
Log(Debug::Debug) << "Processed job " << job->mId << " with status=" << status
<< " changeType=" << job->mChangeType;
switch (status)
{
case JobStatus::Done:
@ -346,7 +440,8 @@ namespace DetourNavigator
removeJob(job);
break;
case JobStatus::Fail:
repost(job);
unlockTile(job->mId, job->mAgentBounds, job->mChangedTile);
removeJob(job);
break;
case JobStatus::MemoryCacheMiss:
{
@ -368,7 +463,9 @@ namespace DetourNavigator
JobStatus AsyncNavMeshUpdater::processJob(Job& job)
{
Log(Debug::Debug) << "Processing job " << job.mId << " by thread=" << std::this_thread::get_id();
Log(Debug::Debug) << "Processing job " << job.mId << " for agent=(" << job.mAgentBounds << ")"
<< " changedTile=(" << job.mChangedTile << ")"
<< " changeType=" << job.mChangeType << " by thread=" << std::this_thread::get_id();
const auto navMeshCacheItem = job.mNavMeshCacheItem.lock();
@ -376,12 +473,11 @@ namespace DetourNavigator
return JobStatus::Done;
const auto playerTile = *mPlayerTile.lockConst();
const int maxTiles
= std::min(mSettings.get().mMaxTilesNumber, navMeshCacheItem->lockConst()->getImpl().getParams()->maxTiles);
if (!shouldAddTile(job.mChangedTile, playerTile, maxTiles))
if (!shouldAddTile(job.mChangedTile, playerTile, mSettings.get().mMaxTilesNumber))
{
Log(Debug::Debug) << "Ignore add tile by job " << job.mId << ": too far from player";
job.mChangeType = ChangeType::remove;
navMeshCacheItem->lock()->removeTile(job.mChangedTile);
return JobStatus::Done;
}
@ -549,9 +645,8 @@ namespace DetourNavigator
bool shouldStop = false;
const auto hasJob = [&] {
shouldStop = mShouldStop;
return shouldStop
|| (!mWaiting.empty() && mWaiting.front()->mProcessTime <= std::chrono::steady_clock::now());
shouldStop = mShouldStop.load();
return shouldStop || mWaiting.hasJob();
};
if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob))
@ -564,9 +659,15 @@ namespace DetourNavigator
if (shouldStop)
return mJobs.end();
const JobIt job = mWaiting.front();
const TilePosition playerTile = *mPlayerTile.lockConst();
JobIt job = mJobs.end();
mWaiting.pop_front();
if (const std::optional<JobIt> nextJob = mWaiting.pop(playerTile))
job = *nextJob;
if (job == mJobs.end())
return job;
Log(Debug::Debug) << "Pop job " << job->mId << " by thread=" << std::this_thread::get_id();
@ -575,9 +676,9 @@ namespace DetourNavigator
if (!lockTile(job->mId, job->mAgentBounds, job->mChangedTile))
{
Log(Debug::Debug) << "Failed to lock tile by job " << job->mId << " try=" << job->mTryNumber;
++job->mTryNumber;
insertPrioritizedJob(job, mWaiting);
Log(Debug::Debug) << "Failed to lock tile by job " << job->mId;
job->mProcessTime = std::chrono::steady_clock::now() + mSettings.get().mMinUpdateInterval;
mWaiting.push(job);
return mJobs.end();
}
@ -613,26 +714,6 @@ namespace DetourNavigator
writeToFile(shared->lockConst()->getImpl(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);
}
void AsyncNavMeshUpdater::repost(JobIt job)
{
unlockTile(job->mId, job->mAgentBounds, job->mChangedTile);
if (mShouldStop || job->mTryNumber > 2)
return;
const std::lock_guard<std::mutex> lock(mMutex);
if (mPushed.emplace(job->mAgentBounds, job->mChangedTile).second)
{
++job->mTryNumber;
insertPrioritizedJob(job, mWaiting);
mHasJob.notify_all();
return;
}
mJobs.erase(job);
}
bool AsyncNavMeshUpdater::lockTile(
std::size_t jobId, const AgentBounds& agentBounds, const TilePosition& changedTile)
{
@ -677,7 +758,7 @@ namespace DetourNavigator
{
Log(Debug::Debug) << "Enqueueing job " << job->mId << " by thread=" << std::this_thread::get_id();
const std::lock_guard lock(mMutex);
insertPrioritizedJob(job, mWaiting);
mWaiting.push(job);
mHasJob.notify_all();
}
@ -691,40 +772,47 @@ namespace DetourNavigator
void DbJobQueue::push(JobIt job)
{
const std::lock_guard lock(mMutex);
insertPrioritizedDbJob(job, mJobs);
if (isWritingDbJob(*job))
++mWritingJobs;
mWriting.push_back(job);
else
++mReadingJobs;
mReading.push(job);
mHasJob.notify_all();
}
std::optional<JobIt> DbJobQueue::pop()
{
std::unique_lock lock(mMutex);
mHasJob.wait(lock, [&] { return mShouldStop || !mJobs.empty(); });
if (mJobs.empty())
const auto hasJob = [&] { return mShouldStop || mReading.size() > 0 || mWriting.size() > 0; };
mHasJob.wait(lock, hasJob);
if (mShouldStop)
return std::nullopt;
const JobIt job = mJobs.front();
mJobs.pop_front();
if (isWritingDbJob(*job))
--mWritingJobs;
else
--mReadingJobs;
if (const std::optional<JobIt> job = mReading.pop(mPlayerTile))
return job;
if (mWriting.empty())
return std::nullopt;
const JobIt job = mWriting.front();
mWriting.pop_front();
return job;
}
void DbJobQueue::update(TilePosition playerTile, int maxTiles)
void DbJobQueue::update(TilePosition playerTile)
{
const std::lock_guard lock(mMutex);
updateJobs(mJobs, playerTile, maxTiles);
std::sort(mJobs.begin(), mJobs.end(), LessByJobDbPriority{});
mPlayerTile = playerTile;
}
void DbJobQueue::stop()
{
const std::lock_guard lock(mMutex);
mJobs.clear();
mReading.clear();
mWriting.clear();
mShouldStop = true;
mHasJob.notify_all();
}
@ -732,7 +820,10 @@ namespace DetourNavigator
DbJobQueueStats DbJobQueue::getStats() const
{
const std::lock_guard lock(mMutex);
return DbJobQueueStats{ .mWritingJobs = mWritingJobs, .mReadingJobs = mReadingJobs };
return DbJobQueueStats{
.mReadingJobs = mReading.size(),
.mWritingJobs = mWriting.size(),
};
}
DbWorker::DbWorker(AsyncNavMeshUpdater& updater, std::unique_ptr<NavMeshDb>&& db, TileVersion version,
@ -761,8 +852,10 @@ namespace DetourNavigator
DbWorkerStats DbWorker::getStats() const
{
return DbWorkerStats{ .mJobs = mQueue.getStats(),
.mGetTileCount = mGetTileCount.load(std::memory_order_relaxed) };
return DbWorkerStats{
.mJobs = mQueue.getStats(),
.mGetTileCount = mGetTileCount.load(std::memory_order_relaxed),
};
}
void DbWorker::stop()

@ -14,6 +14,9 @@
#include "tileposition.hpp"
#include "waitconditiontype.hpp"
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <atomic>
#include <chrono>
#include <condition_variable>
@ -49,11 +52,8 @@ namespace DetourNavigator
const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
const std::string mWorldspace;
const TilePosition mChangedTile;
const std::chrono::steady_clock::time_point mProcessTime;
unsigned mTryNumber = 0;
std::chrono::steady_clock::time_point mProcessTime;
ChangeType mChangeType;
int mDistanceToPlayer;
const int mDistanceToOrigin;
JobState mState = JobState::Initial;
std::vector<std::byte> mInput;
std::shared_ptr<RecastMesh> mRecastMesh;
@ -61,12 +61,65 @@ namespace DetourNavigator
std::unique_ptr<PreparedNavMeshData> mGeneratedNavMeshData;
Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType,
std::chrono::steady_clock::time_point processTime);
};
using JobIt = std::list<Job>::iterator;
class SpatialJobQueue
{
public:
std::size_t size() const { return mSize; }
void clear();
void push(JobIt job);
std::optional<JobIt> pop(TilePosition playerTile);
void update(TilePosition playerTile, int maxTiles, std::vector<JobIt>& removing);
private:
using IndexPoint = boost::geometry::model::point<int, 2, boost::geometry::cs::cartesian>;
using UpdatingMap = std::map<TilePosition, std::deque<JobIt>>;
using IndexValue = std::pair<IndexPoint, UpdatingMap::iterator>;
std::size_t mSize = 0;
UpdatingMap mValues;
boost::geometry::index::rtree<IndexValue, boost::geometry::index::linear<4>> mIndex;
};
class JobQueue
{
public:
JobQueueStats getStats() const
{
return JobQueueStats{
.mRemoving = mRemoving.size(),
.mUpdating = mUpdating.size(),
.mDelayed = mDelayed.size(),
};
}
bool hasJob(std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now()) const;
void clear();
void push(JobIt job, std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now());
std::optional<JobIt> pop(
TilePosition playerTile, std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now());
void update(TilePosition playerTile, int maxTiles,
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now());
private:
std::vector<JobIt> mRemoving;
SpatialJobQueue mUpdating;
std::deque<JobIt> mDelayed;
};
enum class JobStatus
{
Done,
@ -83,7 +136,7 @@ namespace DetourNavigator
std::optional<JobIt> pop();
void update(TilePosition playerTile, int maxTiles);
void update(TilePosition playerTile);
void stop();
@ -92,10 +145,10 @@ namespace DetourNavigator
private:
mutable std::mutex mMutex;
std::condition_variable mHasJob;
std::deque<JobIt> mJobs;
SpatialJobQueue mReading;
std::deque<JobIt> mWriting;
TilePosition mPlayerTile;
bool mShouldStop = false;
std::size_t mWritingJobs = 0;
std::size_t mReadingJobs = 0;
};
class AsyncNavMeshUpdater;
@ -112,7 +165,7 @@ namespace DetourNavigator
void enqueueJob(JobIt job);
void updateJobs(TilePosition playerTile, int maxTiles) { mQueue.update(playerTile, maxTiles); }
void update(TilePosition playerTile) { mQueue.update(playerTile); }
void stop();
@ -169,7 +222,7 @@ namespace DetourNavigator
std::condition_variable mDone;
std::condition_variable mProcessed;
std::list<Job> mJobs;
std::deque<JobIt> mWaiting;
JobQueue mWaiting;
std::set<std::tuple<AgentBounds, TilePosition>> mPushed;
Misc::ScopeGuarded<TilePosition> mPlayerTile;
NavMeshTilesCache mNavMeshTilesCache;
@ -197,8 +250,6 @@ namespace DetourNavigator
void writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const;
void repost(JobIt job);
bool lockTile(std::size_t jobId, const AgentBounds& agentBounds, const TilePosition& changedTile);
void unlockTile(std::size_t jobId, const AgentBounds& agentBounds, const TilePosition& changedTile);

@ -6,15 +6,9 @@ namespace DetourNavigator
enum class ChangeType
{
remove = 0,
mixed = 1,
add = 2,
update = 3,
add = 1,
update = 2,
};
inline ChangeType addChangeType(const ChangeType current, const ChangeType add)
{
return current == add ? current : ChangeType::mixed;
}
}
#endif

@ -79,13 +79,13 @@ namespace DetourNavigator
switch (v)
{
case CollisionShapeType::Aabb:
return s << "AgentShapeType::Aabb";
return s << "CollisionShapeType::Aabb";
case CollisionShapeType::RotatingBox:
return s << "AgentShapeType::RotatingBox";
return s << "CollisionShapeType::RotatingBox";
case CollisionShapeType::Cylinder:
return s << "AgentShapeType::Cylinder";
return s << "CollisionShapeType::Cylinder";
}
return s << "AgentShapeType::" << static_cast<std::underlying_type_t<CollisionShapeType>>(v);
return s << "CollisionShapeType::" << static_cast<std::underlying_type_t<CollisionShapeType>>(v);
}
std::ostream& operator<<(std::ostream& s, const AgentBounds& v)
@ -184,8 +184,6 @@ namespace DetourNavigator
{
case ChangeType::remove:
return stream << "ChangeType::remove";
case ChangeType::mixed:
return stream << "ChangeType::mixed";
case ChangeType::add:
return stream << "ChangeType::add";
case ChangeType::update:

@ -480,15 +480,6 @@ namespace DetourNavigator
return true;
}
template <class T>
unsigned long getMinValuableBitsNumber(const T value)
{
unsigned long power = 0;
while (power < sizeof(T) * 8 && (static_cast<T>(1) << power) < value)
++power;
return power;
}
std::pair<float, float> getBoundsByZ(
const RecastMesh& recastMesh, float agentHalfExtentsZ, const RecastSettings& settings)
{
@ -528,10 +519,7 @@ namespace DetourNavigator
return { minZ, maxZ };
}
}
} // namespace DetourNavigator
namespace DetourNavigator
{
std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh,
std::string_view worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds,
const RecastSettings& settings)
@ -621,22 +609,12 @@ namespace DetourNavigator
void initEmptyNavMesh(const Settings& settings, dtNavMesh& navMesh)
{
// Max tiles and max polys affect how the tile IDs are caculated.
// There are 22 bits available for identifying a tile and a polygon.
const int polysAndTilesBits = 22;
const auto polysBits = getMinValuableBitsNumber(settings.mDetour.mMaxPolys);
if (polysBits >= polysAndTilesBits)
throw InvalidArgument("Too many polygons per tile");
const auto tilesBits = polysAndTilesBits - polysBits;
dtNavMeshParams params;
std::fill_n(params.orig, 3, 0.0f);
params.tileWidth = settings.mRecast.mTileSize * settings.mRecast.mCellSize;
params.tileHeight = settings.mRecast.mTileSize * settings.mRecast.mCellSize;
params.maxTiles = 1 << tilesBits;
params.maxPolys = 1 << polysBits;
params.maxTiles = settings.mMaxTilesNumber;
params.maxPolys = settings.mDetour.mMaxPolys;
const auto status = navMesh.init(&params);

@ -131,6 +131,15 @@ namespace DetourNavigator
function(position, tile.mVersion, *meshTile);
}
template <class Function>
void forEachTilePosition(Function&& function) const
{
for (const auto& [position, tile] : mUsedTiles)
function(position);
for (const TilePosition& position : mEmptyTiles)
function(position);
}
private:
struct Tile
{

@ -13,8 +13,6 @@
#include <DetourNavMesh.h>
#include <iterator>
namespace
{
/// Safely reset shared_ptr with definite underlying object destrutor call.
@ -179,9 +177,9 @@ namespace DetourNavigator
{
std::map<osg::Vec2i, ChangeType> tilesToPost = changedTiles;
{
const int maxTiles = mSettings.mMaxTilesNumber;
const auto locked = cached->lockConst();
const auto& navMesh = locked->getImpl();
const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles);
getTilesPositions(range, [&](const TilePosition& tile) {
if (changedTiles.find(tile) != changedTiles.end())
return;
@ -190,7 +188,11 @@ namespace DetourNavigator
if (shouldAdd && !presentInNavMesh)
tilesToPost.emplace(tile, locked->isEmptyTile(tile) ? ChangeType::update : ChangeType::add);
else if (!shouldAdd && presentInNavMesh)
tilesToPost.emplace(tile, ChangeType::mixed);
tilesToPost.emplace(tile, ChangeType::remove);
});
locked->forEachTilePosition([&](const TilePosition& tile) {
if (!shouldAddTile(tile, playerTile, maxTiles))
tilesToPost.emplace(tile, ChangeType::remove);
});
}
mAsyncNavMeshUpdater.post(agentBounds, cached, playerTile, mWorldspace, tilesToPost);

@ -3,41 +3,81 @@
#include <components/misc/constants.hpp>
#include <components/settings/values.hpp>
#include <algorithm>
#include <stdexcept>
#include <string>
namespace DetourNavigator
{
RecastSettings makeRecastSettingsFromSettingsManager()
namespace
{
RecastSettings result;
result.mBorderSize = ::Settings::navigator().mBorderSize;
result.mCellHeight = ::Settings::navigator().mCellHeight;
result.mCellSize = ::Settings::navigator().mCellSize;
result.mDetailSampleDist = ::Settings::navigator().mDetailSampleDist;
result.mDetailSampleMaxError = ::Settings::navigator().mDetailSampleMaxError;
result.mMaxClimb = Constants::sStepSizeUp;
result.mMaxSimplificationError = ::Settings::navigator().mMaxSimplificationError;
result.mMaxSlope = Constants::sMaxSlope;
result.mRecastScaleFactor = ::Settings::navigator().mRecastScaleFactor;
result.mSwimHeightScale = 0;
result.mMaxEdgeLen = ::Settings::navigator().mMaxEdgeLen;
result.mMaxVertsPerPoly = ::Settings::navigator().mMaxVertsPerPoly;
result.mRegionMergeArea = ::Settings::navigator().mRegionMergeArea;
result.mRegionMinArea = ::Settings::navigator().mRegionMinArea;
result.mTileSize = ::Settings::navigator().mTileSize;
struct NavMeshLimits
{
int mMaxTiles;
int mMaxPolys;
};
return result;
}
template <class T>
unsigned long getMinValuableBitsNumber(const T value)
{
unsigned long power = 0;
while (power < sizeof(T) * 8 && (static_cast<T>(1) << power) < value)
++power;
return power;
}
DetourSettings makeDetourSettingsFromSettingsManager()
{
DetourSettings result;
NavMeshLimits getNavMeshTileLimits(const DetourSettings& settings)
{
// Max tiles and max polys affect how the tile IDs are caculated.
// There are 22 bits available for identifying a tile and a polygon.
constexpr int polysAndTilesBits = 22;
const unsigned long polysBits = getMinValuableBitsNumber(settings.mMaxPolys);
result.mMaxNavMeshQueryNodes = ::Settings::navigator().mMaxNavMeshQueryNodes;
result.mMaxPolys = ::Settings::navigator().mMaxPolygonsPerTile;
result.mMaxPolygonPathSize = ::Settings::navigator().mMaxPolygonPathSize;
result.mMaxSmoothPathSize = ::Settings::navigator().mMaxSmoothPathSize;
if (polysBits >= polysAndTilesBits)
throw std::invalid_argument("Too many polygons per tile: " + std::to_string(settings.mMaxPolys));
return result;
const unsigned long tilesBits = polysAndTilesBits - polysBits;
return NavMeshLimits{
.mMaxTiles = static_cast<int>(1 << tilesBits),
.mMaxPolys = static_cast<int>(1 << polysBits),
};
}
RecastSettings makeRecastSettingsFromSettingsManager()
{
RecastSettings result;
result.mBorderSize = ::Settings::navigator().mBorderSize;
result.mCellHeight = ::Settings::navigator().mCellHeight;
result.mCellSize = ::Settings::navigator().mCellSize;
result.mDetailSampleDist = ::Settings::navigator().mDetailSampleDist;
result.mDetailSampleMaxError = ::Settings::navigator().mDetailSampleMaxError;
result.mMaxClimb = Constants::sStepSizeUp;
result.mMaxSimplificationError = ::Settings::navigator().mMaxSimplificationError;
result.mMaxSlope = Constants::sMaxSlope;
result.mRecastScaleFactor = ::Settings::navigator().mRecastScaleFactor;
result.mSwimHeightScale = 0;
result.mMaxEdgeLen = ::Settings::navigator().mMaxEdgeLen;
result.mMaxVertsPerPoly = ::Settings::navigator().mMaxVertsPerPoly;
result.mRegionMergeArea = ::Settings::navigator().mRegionMergeArea;
result.mRegionMinArea = ::Settings::navigator().mRegionMinArea;
result.mTileSize = ::Settings::navigator().mTileSize;
return result;
}
DetourSettings makeDetourSettingsFromSettingsManager()
{
DetourSettings result;
result.mMaxNavMeshQueryNodes = ::Settings::navigator().mMaxNavMeshQueryNodes;
result.mMaxPolys = ::Settings::navigator().mMaxPolygonsPerTile;
result.mMaxPolygonPathSize = ::Settings::navigator().mMaxPolygonPathSize;
result.mMaxSmoothPathSize = ::Settings::navigator().mMaxSmoothPathSize;
return result;
}
}
Settings makeSettingsFromSettingsManager()
@ -46,7 +86,12 @@ namespace DetourNavigator
result.mRecast = makeRecastSettingsFromSettingsManager();
result.mDetour = makeDetourSettingsFromSettingsManager();
result.mMaxTilesNumber = ::Settings::navigator().mMaxTilesNumber;
const NavMeshLimits limits = getNavMeshTileLimits(result.mDetour);
result.mDetour.mMaxPolys = limits.mMaxPolys;
result.mMaxTilesNumber = std::min(limits.mMaxTiles, ::Settings::navigator().mMaxTilesNumber.get());
result.mWaitUntilMinDistanceToPlayer = ::Settings::navigator().mWaitUntilMinDistanceToPlayer;
result.mAsyncNavMeshUpdaterThreads = ::Settings::navigator().mAsyncNavMeshUpdaterThreads;
result.mMaxNavMeshTilesCacheSize = ::Settings::navigator().mMaxNavMeshTilesCacheSize;

@ -55,10 +55,6 @@ namespace DetourNavigator
inline constexpr std::int64_t navMeshFormatVersion = 2;
RecastSettings makeRecastSettingsFromSettingsManager();
DetourSettings makeDetourSettingsFromSettingsManager();
Settings makeSettingsFromSettingsManager();
}

@ -9,16 +9,18 @@ namespace DetourNavigator
void reportStats(const AsyncNavMeshUpdaterStats& stats, unsigned int frameNumber, osg::Stats& out)
{
out.setAttribute(frameNumber, "NavMesh Jobs", static_cast<double>(stats.mJobs));
out.setAttribute(frameNumber, "NavMesh Waiting", static_cast<double>(stats.mWaiting));
out.setAttribute(frameNumber, "NavMesh Removing", static_cast<double>(stats.mWaiting.mRemoving));
out.setAttribute(frameNumber, "NavMesh Updating", static_cast<double>(stats.mWaiting.mUpdating));
out.setAttribute(frameNumber, "NavMesh Delayed", static_cast<double>(stats.mWaiting.mDelayed));
out.setAttribute(frameNumber, "NavMesh Pushed", static_cast<double>(stats.mPushed));
out.setAttribute(frameNumber, "NavMesh Processing", static_cast<double>(stats.mProcessing));
if (stats.mDb.has_value())
{
out.setAttribute(
frameNumber, "NavMesh DbJobs Write", static_cast<double>(stats.mDb->mJobs.mWritingJobs));
out.setAttribute(
frameNumber, "NavMesh DbJobs Read", static_cast<double>(stats.mDb->mJobs.mReadingJobs));
out.setAttribute(
frameNumber, "NavMesh DbJobs Write", static_cast<double>(stats.mDb->mJobs.mWritingJobs));
out.setAttribute(frameNumber, "NavMesh DbCache Get", static_cast<double>(stats.mDb->mGetTileCount));
out.setAttribute(frameNumber, "NavMesh DbCache Hit", static_cast<double>(stats.mDbGetTileHits));

@ -11,10 +11,17 @@ namespace osg
namespace DetourNavigator
{
struct JobQueueStats
{
std::size_t mRemoving = 0;
std::size_t mUpdating = 0;
std::size_t mDelayed = 0;
};
struct DbJobQueueStats
{
std::size_t mWritingJobs = 0;
std::size_t mReadingJobs = 0;
std::size_t mWritingJobs = 0;
};
struct DbWorkerStats
@ -35,7 +42,7 @@ namespace DetourNavigator
struct AsyncNavMeshUpdaterStats
{
std::size_t mJobs = 0;
std::size_t mWaiting = 0;
JobQueueStats mWaiting;
std::size_t mPushed = 0;
std::size_t mProcessing = 0;
std::size_t mDbGetTileHits = 0;

@ -10,7 +10,6 @@
#include <boost/geometry/geometry.hpp>
#include <algorithm>
#include <limits>
#include <vector>
@ -416,7 +415,7 @@ namespace DetourNavigator
if (tile == mChangedTiles.end())
mChangedTiles.emplace(tilePosition, changeType);
else
tile->second = addChangeType(tile->second, changeType);
tile->second = changeType == ChangeType::remove ? changeType : tile->second;
}
std::map<osg::Vec2i, ChangeType> TileCachedRecastMeshManager::takeChangedTiles(const UpdateGuard* guard)

@ -0,0 +1,206 @@
#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);
}
}

@ -0,0 +1,134 @@
#ifndef OPENMW_ESM3_DIALOGUECONDITION_H
#define OPENMW_ESM3_DIALOGUECONDITION_H
#include <cstdint>
#include <optional>
#include <string>
#include <variant>
#include <components/esm/refid.hpp>
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<int32_t, float> mValue = 0;
std::uint8_t mIndex = 0;
Function mFunction = Function_None;
Comparison mComparison = Comp_None;
static std::optional<DialogueCondition> load(ESMReader& esm, ESM::RefId context);
void save(ESMWriter& esm) const;
};
}
#endif

@ -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<SelectStruct>::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);

@ -4,8 +4,10 @@
#include <string>
#include <vector>
#include "components/esm/defs.hpp"
#include "components/esm/refid.hpp"
#include <components/esm/defs.hpp>
#include <components/esm/refid.hpp>
#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<SelectStruct> mSelects;
std::vector<DialogueCondition> mSelects;
// Id of this, previous and next INFO items
RefId mId, mPrev, mNext;

@ -78,8 +78,7 @@ namespace Resource
}
break;
}
// not bothering with checks for other compression formats right now, we are unlikely to ever use those
// anyway
// not bothering with checks for other compression formats right now
default:
return true;
}

@ -49,6 +49,8 @@ namespace Resource
std::vector<std::string> generateAllStatNames()
{
constexpr std::size_t itemsPerPage = 24;
constexpr std::string_view firstPage[] = {
"FrameNumber",
"",
@ -76,6 +78,8 @@ namespace Resource
"",
};
static_assert(std::size(firstPage) == itemsPerPage);
constexpr std::string_view caches[] = {
"Node",
"Shape",
@ -100,7 +104,9 @@ namespace Resource
constexpr std::string_view navMesh[] = {
"NavMesh Jobs",
"NavMesh Waiting",
"NavMesh Removing",
"NavMesh Updating",
"NavMesh Delayed",
"NavMesh Pushed",
"NavMesh Processing",
"NavMesh DbJobs Write",
@ -129,7 +135,8 @@ namespace Resource
for (std::string_view name : cellPreloader)
statNames.emplace_back(name);
statNames.emplace_back();
while (statNames.size() % itemsPerPage != 0)
statNames.emplace_back();
for (std::string_view name : navMesh)
statNames.emplace_back(name);

@ -286,4 +286,125 @@ namespace SceneUtil
mOperationQueue->add(operation);
}
GLenum computeUnsizedPixelFormat(GLenum format)
{
switch (format)
{
// Try compressed formats first, they're more likely to be used
// Generic
case GL_COMPRESSED_ALPHA_ARB:
return GL_ALPHA;
case GL_COMPRESSED_INTENSITY_ARB:
return GL_INTENSITY;
case GL_COMPRESSED_LUMINANCE_ALPHA_ARB:
return GL_LUMINANCE_ALPHA;
case GL_COMPRESSED_LUMINANCE_ARB:
return GL_LUMINANCE;
case GL_COMPRESSED_RGB_ARB:
return GL_RGB;
case GL_COMPRESSED_RGBA_ARB:
return GL_RGBA;
// S3TC
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
return GL_RGB;
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
return GL_RGBA;
// RGTC
case GL_COMPRESSED_RED_RGTC1_EXT:
case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
return GL_RED;
case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
return GL_RG;
// PVRTC
case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
return GL_RGB;
case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
return GL_RGBA;
// ETC
case GL_COMPRESSED_R11_EAC:
case GL_COMPRESSED_SIGNED_R11_EAC:
return GL_RED;
case GL_COMPRESSED_RG11_EAC:
case GL_COMPRESSED_SIGNED_RG11_EAC:
return GL_RG;
case GL_ETC1_RGB8_OES:
case GL_COMPRESSED_RGB8_ETC2:
case GL_COMPRESSED_SRGB8_ETC2:
return GL_RGB;
case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_RGBA8_ETC2_EAC:
case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
return GL_RGBA;
// ASTC
case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
return GL_RGBA;
// Plug in some holes computePixelFormat has, you never know when these could come in handy
case GL_INTENSITY4:
case GL_INTENSITY8:
case GL_INTENSITY12:
case GL_INTENSITY16:
return GL_INTENSITY;
case GL_LUMINANCE4:
case GL_LUMINANCE8:
case GL_LUMINANCE12:
case GL_LUMINANCE16:
return GL_LUMINANCE;
case GL_LUMINANCE4_ALPHA4:
case GL_LUMINANCE6_ALPHA2:
case GL_LUMINANCE8_ALPHA8:
case GL_LUMINANCE12_ALPHA4:
case GL_LUMINANCE12_ALPHA12:
case GL_LUMINANCE16_ALPHA16:
return GL_LUMINANCE_ALPHA;
}
return osg::Image::computePixelFormat(format);
}
}

@ -112,6 +112,10 @@ namespace SceneUtil
protected:
osg::ref_ptr<osg::OperationQueue> mOperationQueue;
};
// Compute the unsized format equivalent to the given pixel format
// Unlike osg::Image::computePixelFormat, this also covers compressed formats
GLenum computeUnsizedPixelFormat(GLenum format);
}
#endif

@ -63,6 +63,7 @@ namespace Settings
SettingValue<bool> mEnableNavMeshDiskCache{ mIndex, "Navigator", "enable nav mesh disk cache" };
SettingValue<bool> mWriteToNavmeshdb{ mIndex, "Navigator", "write to navmeshdb" };
SettingValue<std::uint64_t> mMaxNavmeshdbFileSize{ mIndex, "Navigator", "max navmeshdb file size" };
SettingValue<bool> mWaitForAllJobsOnExit{ mIndex, "Navigator", "wait for all jobs on exit" };
};
}

@ -26,6 +26,7 @@
#include <components/sceneutil/morphgeometry.hpp>
#include <components/sceneutil/riggeometry.hpp>
#include <components/sceneutil/riggeometryosgaextension.hpp>
#include <components/sceneutil/util.hpp>
#include <components/settings/settings.hpp>
#include <components/stereo/stereomanager.hpp>
#include <components/vfs/manager.hpp>
@ -184,6 +185,7 @@ namespace Shader
, mAdditiveBlending(false)
, mDiffuseHeight(false)
, mNormalHeight(false)
, mReconstructNormalZ(false)
, mTexStageRequiringTangents(-1)
, mSoftParticles(false)
, mNode(nullptr)
@ -429,6 +431,7 @@ namespace Shader
normalMapTex->setFilter(osg::Texture::MAG_FILTER, diffuseMap->getFilter(osg::Texture::MAG_FILTER));
normalMapTex->setMaxAnisotropy(diffuseMap->getMaxAnisotropy());
normalMapTex->setName("normalMap");
normalMap = normalMapTex;
int unit = texAttributes.size();
if (!writableStateSet)
@ -440,6 +443,21 @@ namespace Shader
mRequirements.back().mNormalHeight = normalHeight;
}
}
if (normalMap != nullptr && normalMap->getImage(0))
{
// Special handling for red-green normal maps (e.g. BC5 or R8G8)
switch (SceneUtil::computeUnsizedPixelFormat(normalMap->getImage(0)->getPixelFormat()))
{
case GL_RG:
case GL_RG_INTEGER:
{
mRequirements.back().mReconstructNormalZ = true;
mRequirements.back().mNormalHeight = false;
}
}
}
if (mAutoUseSpecularMaps && diffuseMap != nullptr && specularMap == nullptr && diffuseMap->getImage(0))
{
std::string specularMapFileName = diffuseMap->getImage(0)->getFileName();
@ -629,6 +647,7 @@ namespace Shader
defineMap["diffuseParallax"] = reqs.mDiffuseHeight ? "1" : "0";
defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0";
defineMap["reconstructNormalZ"] = reqs.mReconstructNormalZ ? "1" : "0";
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
addedState->addUniform("colorMode");

@ -110,6 +110,7 @@ namespace Shader
bool mDiffuseHeight; // true if diffuse map has height info in alpha channel
bool mNormalHeight; // true if normal map has height info in alpha channel
bool mReconstructNormalZ; // used for red-green normal maps (e.g. BC5)
// -1 == no tangents required
int mTexStageRequiringTangents;

@ -10,6 +10,7 @@
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/depth.hpp>
#include <components/sceneutil/util.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/stereo/stereomanager.hpp>
@ -271,18 +272,37 @@ namespace Terrain
stateset->addUniform(UniformCollection::value().mBlendMap);
}
bool parallax = it->mNormalMap && it->mParallax;
bool reconstructNormalZ = false;
if (it->mNormalMap)
{
stateset->setTextureAttributeAndModes(2, it->mNormalMap);
stateset->addUniform(UniformCollection::value().mNormalMap);
// Special handling for red-green normal maps (e.g. BC5 or R8G8).
const osg::Image* image = it->mNormalMap->getImage(0);
if (image)
{
switch (SceneUtil::computeUnsizedPixelFormat(image->getPixelFormat()))
{
case GL_RG:
case GL_RG_INTEGER:
{
reconstructNormalZ = true;
parallax = false;
}
}
}
}
Shader::ShaderManager::DefineMap defineMap;
defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0";
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
defineMap["parallax"] = parallax ? "1" : "0";
defineMap["writeNormals"] = (it == layers.end() - 1) ? "1" : "0";
defineMap["reconstructNormalZ"] = reconstructNormalZ ? "1" : "0";
Stereo::shaderStereoDefines(defineMap);
stateset->setAttributeAndModes(shaderManager.getProgram("terrain", defineMap));

@ -245,6 +245,16 @@ Absent pieces usually mean a bug in recast mesh tiles building.
Allows to do in-game debug.
Potentially decreases performance.
wait for all jobs on exit
-------------------------
:Type: boolean
:Range: True/False
:Default: False
Wait until all queued async navmesh jobs are processed before exiting the engine.
Useful when a benchmark generates jobs to write into navmeshdb faster than they are processed.
Expert settings
***************

@ -25,6 +25,19 @@ Content creators need to know that OpenMW uses the DX format for normal maps, an
See the section `Automatic use`_ further down below for detailed information.
The RGB channels of the normal map are used to store XYZ components of tangent space normals and the alpha channel of the normal map may be used to store a height map used for parallax.
This is different from the setup used in Bethesda games that use the traditional pipeline, which may store specular information in the alpha channel.
Special pixel formats that only store two color channels exist and are used by Bethesda games that employ a PBR-based pipeline. Compressed red-green formats are optimized for use with normal maps and suffer from far less quality degradation than S3TC-compressed normal maps of equivalent size.
OpenMW supports the use of such pixel formats. When a red-green normal map is provided, the Z component of the normal will be reconstructed based on XY components it stores.
Naturally, since these formats cannot provide an alpha channel, they do not support parallax.
Keep in mind, however, that while the necessary hardware support is widespread for compressed red-green formats, it is less ubiquitous than the support for S3TC family of compressed formats.
Should you run into the consequences of this, you might want to convert such textures into an uncompressed red-green format such as R8G8.
Be careful not to try and convert such textures into a full-color format as the previously non-existent blue channel would then be used.
Specular Mapping
################

@ -0,0 +1,50 @@
# Swedish does not actually upper-case first letter on months and weekday names, so I'm doing them lower case right now.
month1: "januari"
month2: "februari"
month3: "mars"
month4: "april"
month5: "maj"
month6: "juni"
month7: "juli"
month8: "augusti"
month9: "september"
month10: "oktober"
month11: "november"
month12: "december"
# There are no different grammatical forms of the months in Swedish
monthInGenitive1: "januari"
monthInGenitive2: "februari"
monthInGenitive3: "mars"
monthInGenitive4: "april"
monthInGenitive5: "maj"
monthInGenitive6: "juni"
monthInGenitive7: "juli"
monthInGenitive8: "augusti"
monthInGenitive9: "september"
monthInGenitive10: "oktober"
monthInGenitive11: "november"
monthInGenitive12: "december"
# Standard Swedish date format: d MMMM YYYY
# Source: http://www4.sprakochfolkminnen.se/cgi-bin/srfl/visasvar.py?sok=datum&svar=26089
# Example: "23 februari 1337"
dateFormat: "{day} {month} {year, number, :: group-off}"
# The Swedish week starts with monday actually, but whatever.
weekday1: "söndag"
weekday2: "måndag"
weekday3: "tisdag"
weekday4: "onsdag"
weekday5: "torsdag"
weekday6: "fredag"
weekday7: "lördag"
# In Swedish, as with German, we don't use AM/PM but instead a 24h clock.
# But instead of that, we could use "förmiddag" and "eftermiddag", which is basically "morning" and "afternoon"
am: "förmiddag"
pm: "eftermiddag"
day: "dag"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
xlink:href="#Main-5"
id="linearGradient2320"
x1="2.6458335"
y1="14.816667"
x2="5.8208332"
y2="14.816667"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="Main-5">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-8" />
</linearGradient>
<linearGradient
xlink:href="#Main-5"
id="linearGradient2322"
x1="3.1750002"
y1="14.816651"
x2="5.2916675"
y2="14.816651"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-5"
id="linearGradient2324"
x1="5.6885424"
y1="14.816667"
x2="6.3500004"
y2="14.816667"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-5"
id="linearGradient2326"
x1="4.17975"
y1="16.49861"
x2="5.3575182"
y2="16.49861"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-5"
id="linearGradient2328"
x1="2.2552035"
y1="15.843752"
x2="3.3670816"
y2="15.843752"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-5"
id="linearGradient2330"
x1="2.2552028"
y1="13.789582"
x2="3.3670833"
y2="13.789582"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-5"
id="linearGradient2332"
x1="4.17975"
y1="13.134723"
x2="5.3575163"
y2="13.134723"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="activator"
style="display:inline"
transform="matrix(3.7795278,0,0,3.7323517,-8.0000099,-47.301008)">
<rect
style="opacity:0;fill:#a51d2d;stroke-width:0.264583px;paint-order:fill markers stroke;stop-color:#000000"
id="rect1388"
width="4.2333331"
height="4.2333331"
x="2.1166692"
y="12.7"
rx="1.9824198e-15" />
<path
id="path1591"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2320);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 4.2333336,13.229167 a 1.5875001,1.5874996 0 0 0 -1.5875001,1.5875 1.5875001,1.5874996 0 0 0 1.5875001,1.5875 1.5875001,1.5874996 0 0 0 1.5874998,-1.5875 1.5875001,1.5874996 0 0 0 -1.5874998,-1.5875 z m 0,0.264583 A 1.3229167,1.3229167 0 0 1 5.5562504,14.816667 1.3229167,1.3229167 0 0 1 4.2333336,16.139583 1.3229167,1.3229167 0 0 1 2.9104169,14.816667 1.3229167,1.3229167 0 0 1 4.2333336,13.49375 Z" />
<path
id="path1598"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2322);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="M 4.2173138,13.758333 A 1.0583334,1.0583334 0 0 0 3.1750002,14.816667 1.0583334,1.0583334 0 0 0 4.2333336,15.875 1.0583334,1.0583334 0 0 0 5.2916674,14.816667 1.0583334,1.0583334 0 0 0 4.2333336,13.758333 a 1.0583334,1.0583334 0 0 0 -0.016021,0 z m 0.00259,0.264584 a 0.79374999,0.79374999 0 0 1 0.013427,0 0.79374999,0.79374999 0 0 1 0.7937496,0.79375 0.79374999,0.79374999 0 0 1 -0.7937496,0.79375 0.79374999,0.79374999 0 0 1 -0.7937501,-0.79375 0.79374999,0.79374999 0 0 1 0.7803143,-0.79375 z" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2324);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 5.6885424,14.2875 v 0.264584 h 0.396875 v 0.529166 h -0.396875 v 0.264584 c 0,0 0.264583,0 0.396875,0 0.264583,0 0.264583,0 0.264583,-0.264584 0,-0.176389 0,-0.529166 0,-0.529166 0,-0.264584 0,-0.264584 -0.264583,-0.264584 z"
id="path1605" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2326);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 5.1862854,16.037131 -0.251634,0.08176 0.122641,0.377451 -0.5032668,0.163521 -0.1226411,-0.377451 -0.2516344,0.08176 c 0,0 0.081761,0.251634 0.1226411,0.377451 0.081761,0.251633 0.081761,0.251633 0.333395,0.169872 0.1677562,-0.05451 0.5032672,-0.163521 0.5032672,-0.163521 0.251634,-0.08176 0.251634,-0.08176 0.169873,-0.333394 z"
id="path1607" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2328);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="M 3.3670817,16.100122 3.2115641,15.886069 2.890485,16.119347 2.5794493,15.691242 2.9005284,15.457965 2.7450107,15.243911 c 0,0 -0.2140526,0.155519 -0.321079,0.233278 -0.2140517,0.155518 -0.2140517,0.155518 -0.058533,0.369571 0.1036816,0.142701 0.3110358,0.428104 0.3110358,0.428104 0.1555176,0.214053 0.1555176,0.214053 0.3695702,0.05853 z"
id="path1609" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2330);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="M 2.7450088,14.389422 2.9005278,14.17537 2.5794483,13.942092 2.890485,13.513988 3.2115635,13.747266 3.3670834,13.533213 c 0,0 -0.2140533,-0.155518 -0.3210794,-0.233277 C 2.8319519,13.144419 2.8319519,13.144419 2.6764334,13.358476 2.572756,13.50118 2.3653976,13.78658 2.3653976,13.78658 c -0.1555189,0.214052 -0.1555189,0.214052 0.058538,0.369569 z"
id="path1611" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2332);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 4.1797501,13.26916 0.2516335,0.08176 0.1226416,-0.377452 0.5032662,0.163522 -0.122641,0.377451 0.251634,0.08176 c 0,0 0.08176,-0.251634 0.122641,-0.377451 0.08176,-0.251633 0.08176,-0.251633 -0.169878,-0.333393 -0.167758,-0.0545 -0.5032668,-0.163521 -0.5032668,-0.163521 -0.2516336,-0.08176 -0.2516336,-0.08176 -0.3333918,0.169876 z"
id="path1613" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
xlink:href="#Main-1"
id="linearGradient2316"
x1="7.4083333"
y1="15.610416"
x2="11.641667"
y2="15.610416"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="Main-1">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-1" />
</linearGradient>
<linearGradient
xlink:href="#Main-1"
id="linearGradient2318"
x1="7.4311438"
y1="13.585216"
x2="9.7828169"
y2="13.585216"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="apparatus"
style="display:inline"
transform="matrix(3.7795264,0,0,3.7795277,-27.999988,-48.000001)">
<path
id="path1548"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2316);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 7.4083332,14.2875 c 0,1.058334 10e-8,1.852084 0.7937501,2.116667 v 0.529166 h 2.6458337 v -0.529166 c 0.79375,-0.264583 0.79375,-1.058333 0.79375,-2.116667 z m 0.2645835,0.264583 h 3.7041663 c -0.0043,0.391937 -0.02302,0.776349 -0.08992,1.021643 -0.08422,0.308823 -0.201392,0.472102 -0.522965,0.579293 -0.108066,0.03605 -0.180927,0.0049 -0.180868,0.118856 V 16.66875 H 8.4666666 l 10e-8,-0.396875 c 5.96e-5,-0.113922 -0.072801,-0.0828 -0.1808676,-0.118856 C 7.9642258,16.045828 7.847058,15.882549 7.7628336,15.573726 7.6959349,15.328432 7.6772196,14.94402 7.6729167,14.552083 Z" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2318);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 8.0303333,14.440792 c 0,0 -0.2549422,-0.503321 -0.5872624,-1.214834 -0.051288,-0.109811 0.072913,-0.283564 0.1805731,-0.367422 0.1272316,-0.0991 0.335854,-0.186629 0.4261393,-0.102012 0.6564188,0.615209 1.7330335,1.691503 1.7330335,1.691503 l -0.443406,-0.08626 c 0,0 -0.8603506,-0.879948 -1.300715,-1.309642 -0.1226325,-0.119661 -0.3779304,0.08975 -0.3032537,0.272244 0.1220055,0.29815 0.5843882,1.043318 0.5843882,1.043318 z"
id="path1575" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 B

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Main-1">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-1" />
</linearGradient>
<linearGradient
xlink:href="#Main-1"
id="linearGradient2334"
x1="12.7"
y1="14.834408"
x2="16.933334"
y2="14.834408"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-1"
id="linearGradient2336"
x1="13.229167"
y1="14.816667"
x2="16.404167"
y2="14.816667"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="armor"
style="display:inline"
transform="matrix(3.7795254,0,0,3.7481122,-47.999972,-47.601024)">
<path
id="path1648"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2334);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 12.7,12.7 c -10e-7,2.645834 0.529166,3.307291 1.984375,4.233333 0.07443,0.04731 0.190154,0.04731 0.264584,0 1.455208,-0.926042 1.984375,-1.587499 1.984375,-4.233333 z m 0.264584,0.264583 h 3.704166 c 0,1.984375 -0.463021,2.869609 -1.736328,3.673161 -0.06513,0.04105 -0.16638,0.04105 -0.23151,0 -1.273308,-0.803552 -1.736328,-1.688786 -1.736328,-3.673161 z" />
<path
id="path1653"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2336);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 14.816667,16.404167 c 1.364557,-0.881951 1.49242,-1.658193 1.5875,-3.175 h -1.5875 -1.5875 c 0.09508,1.516807 0.222943,2.293049 1.5875,3.175 z m 0,-2.910417 h 1.30638 c -0.115736,1.322698 -0.265299,1.809261 -1.30638,2.588086 -1.041081,-0.778825 -1.190644,-1.265388 -1.30638,-2.588086 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Main-1">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-1" />
</linearGradient>
<linearGradient
xlink:href="#Main-1"
id="linearGradient2232"
x1="2.1166666"
y1="41.275002"
x2="6.3499999"
y2="41.275002"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="attribute"
transform="matrix(3.7795255,0,0,3.7795259,-7.9999953,-147.99994)"
style="display:inline">
<path
id="rect1081"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2232);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="M 3.9687499,39.158335 2.1166666,42.862502 2.453597,43.391668 H 6.0130694 L 6.3499999,42.862502 4.4979166,39.158335 Z m 0.1638143,0.264583 h 0.2015381 l 0.2645832,0.529167 H 3.8679809 Z m -0.396875,0.79375 h 0.995288 l 0.396875,0.79375 h -1.789038 z m -0.5291666,1.058334 h 2.0536213 l 0.396875,0.79375 H 2.8096476 Z m -0.5291667,1.058333 h 3.1119546 l 0.2361616,0.472323 -0.2046388,0.321427 h -3.175 L 2.4411946,42.805658 Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 B

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Main-1">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-1" />
</linearGradient>
<linearGradient
xlink:href="#Main-1"
id="linearGradient2222"
x1="8.2020836"
y1="40.481251"
x2="10.847917"
y2="40.481251"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-1"
id="linearGradient2224"
x1="7.4083333"
y1="42.068752"
x2="8.4666662"
y2="42.068752"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-1"
id="linearGradient2226"
x1="8.9958334"
y1="42.597919"
x2="10.054167"
y2="42.597919"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-1"
id="linearGradient2228"
x1="10.583333"
y1="42.068752"
x2="11.641667"
y2="42.068752"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-1"
id="linearGradient2230"
x1="7.4083333"
y1="40.613544"
x2="11.641667"
y2="40.613544"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="birthsign"
transform="matrix(3.7795264,0,0,3.7795278,-27.999988,-148.00001)"
style="display:inline">
<path
id="path10688"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2222);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 9.5249999,39.158335 a 1.3229166,1.3229166 0 0 0 -1.3229167,1.322916 1.3229166,1.3229166 0 0 0 1.3229167,1.322917 1.3229166,1.3229166 0 0 0 1.3229171,-1.322917 1.3229166,1.3229166 0 0 0 -1.3229171,-1.322916 z m -0.01602,0.264583 a 1.0583333,1.0583333 0 0 1 0.01602,0 1.0583333,1.0583333 0 0 1 1.0583331,1.058333 1.0583333,1.0583333 0 0 1 -1.0583331,1.058334 1.0583333,1.0583333 0 0 1 -1.0583333,-1.058334 1.0583333,1.0583333 0 0 1 1.0423133,-1.058333 z" />
<path
id="rect10697"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2224);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 7.6729166,41.539585 c -0.1465789,0 -0.2645834,0.118004 -0.2645834,0.264583 v 0.529167 c 0,0.146579 0.1180045,0.264583 0.2645834,0.264583 h 0.5291666 c 0.146579,0 0.2645834,-0.118004 0.2645834,-0.264583 v -0.529167 c 0,-0.146579 -0.1180044,-0.264583 -0.2645834,-0.264583 z m 0,0.264583 h 0.5291666 v 0.529167 H 7.6729166 Z" />
<path
id="path10718"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2226);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 9.2604166,42.068753 c -0.1465789,0 -0.2645833,0.118005 -0.2645833,0.264584 v 0.529166 c 0,0.14658 0.1180044,0.264584 0.2645833,0.264584 h 0.5291667 c 0.146579,0 0.2645837,-0.118004 0.2645837,-0.264584 v -0.529166 c 0,-0.146579 -0.1180047,-0.264584 -0.2645837,-0.264584 z m 0,0.264584 h 0.5291667 v 0.529166 H 9.2604166 Z" />
<path
id="path10720"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2228);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 10.847917,41.539587 c -0.146579,0 -0.264584,0.118005 -0.264584,0.264584 v 0.529166 c 0,0.14658 0.118005,0.264584 0.264584,0.264584 h 0.529166 c 0.146579,0 0.264584,-0.118004 0.264584,-0.264584 v -0.529166 c 0,-0.146579 -0.118005,-0.264584 -0.264584,-0.264584 z m 0,0.264584 h 0.529166 v 0.529166 h -0.529166 z" />
<path
id="path10722"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2230);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 7.9374999,40.216668 c -0.2922507,0 -0.5291667,0.177686 -0.5291667,0.396875 0,0.219189 0.236916,0.396875 0.5291667,0.396875 H 9.2604165 9.7895832 11.1125 c 0.292251,0 0.529167,-0.177686 0.529167,-0.396875 0,-0.219189 -0.236916,-0.396875 -0.529167,-0.396875 h -0.432015 v 0.264583 H 11.1125 c 0.0019,-2.4e-5 0.0038,-2.4e-5 0.0057,0 0.09451,0.0024 0.169547,0.06084 0.169499,0.132292 0,0.07309 -0.07845,0.132334 -0.175183,0.132292 h -0.0057 -1.3172328 -0.5291667 -1.3172322 -0.00568 c -0.096734,4.2e-5 -0.1751831,-0.0592 -0.1751831,-0.132292 -4.81e-5,-0.07145 0.074991,-0.130014 0.1694987,-0.132292 0.00189,-2.4e-5 0.00379,-2.4e-5 0.00568,0 h 0.432015 v -0.264583 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Main-1">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-1" />
</linearGradient>
<linearGradient
xlink:href="#Main-1"
id="linearGradient2234"
x1="12.700004"
y1="41.275002"
x2="16.662027"
y2="41.275002"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-1"
id="linearGradient2236"
x1="15.261501"
y1="42.025791"
x2="15.91278"
y2="42.025791"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="body-part"
transform="matrix(3.7795264,0,0,3.7795278,-47.999995,-148.00001)"
style="display:inline">
<path
id="path1689"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2234);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 14.305075,39.158335 c 0.660363,1.331254 0.764294,1.869768 0.764294,2.462382 0,0.424307 -0.988158,0.901957 -1.840199,0.987536 -0.18112,0.0063 -0.529166,0.09876 -0.529166,0.518832 0,0.229584 0.339402,0.264584 0.529166,0.264584 h 3.024106 c 0.686469,-0.431602 0.364641,-0.995913 0.07028,-1.802474 -0.223239,-0.779487 -0.183967,-1.148525 -0.183967,-2.43086 z m 0.432014,0.264584 h 1.137915 c 0.01315,1.138401 -0.05581,1.388856 0.167431,2.168342 0.294362,0.806564 0.370625,1.401879 0.09715,1.535824 h -2.910417 c -0.324061,0 -0.321976,-0.267568 0,-0.293523 0.859047,-0.06925 2.12235,-0.654593 2.12235,-1.212845 0.0016,-0.218358 0.106445,-0.895485 -0.614431,-2.197798 z" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2236);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 15.261501,42.27091 c 0.204639,0.05209 0.511182,-0.240606 0.55025,-0.599654 0.113481,0.139526 0.14858,0.519555 0.01464,0.635827 -0.133946,0.116272 -0.449543,0.08661 -0.564885,-0.03617 z"
id="path1693" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Red">
<stop
style="stop-color:#ff664d;stop-opacity:1;"
offset="0"
id="stop2533" />
</linearGradient>
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect3119">
<path
id="path3117"
style="opacity:1;fill:#000000;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 10.250537,66.995394 -0.0047,0.0047 c -0.02767,-0.0066 -0.211799,-0.04039 -0.4563028,0.204116 C 9.6572915,67.468749 9.6572915,67.468749 9.5249999,67.49562 9.3927082,67.468749 9.3927082,67.468749 9.2604165,67.204166 9.061979,67.005731 8.8982935,66.990872 8.8289184,66.99591 c -0.023124,0.0016 -0.035657,0.0057 -0.035657,0.0057 0,0 -0.062009,0.20257 0.2025716,0.467154 0.2645833,0.132291 0.2645841,0.132291 0.291455,0.264583 -0.026871,0.132292 -0.026872,0.132292 -0.291455,0.264583 -0.2445017,0.244505 -0.2107512,0.428639 -0.2041218,0.456304 l -0.00465,0.0047 c 0,0 0.00465,2.65e-4 0.00517,5.29e-4 1.736e-4,5.3e-4 0.00103,0.0057 0.00103,0.0057 l 0.00465,-0.0047 c 0.027721,0.0066 0.2118445,0.04033 0.4563028,-0.204123 0.1322919,-0.264584 0.1322919,-0.264584 0.2645833,-0.291455 0.1322919,0.02687 0.1322917,0.02687 0.2645833,0.291455 0.1984378,0.198435 0.3621234,0.213294 0.4314984,0.208256 0.02312,-0.0016 0.03566,-0.0057 0.03566,-0.0057 0,0 0.06201,-0.20257 -0.202572,-0.467156 -0.2645836,-0.132289 -0.2645839,-0.132291 -0.291455,-0.264583 0.026871,-0.132279 0.026873,-0.132292 0.291455,-0.264583 0.244502,-0.244505 0.210752,-0.428639 0.204122,-0.456304 l 0.0047,-0.0047 c 0,0 -0.0046,-7.94e-4 -0.0052,-0.0011 -1.47e-4,-5.29e-4 -10e-4,-0.0052 -10e-4,-0.0052 z" />
</mask>
<linearGradient
id="Main-6">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-3" />
</linearGradient>
<linearGradient
xlink:href="#Main-6"
id="linearGradient2338"
x1="17.991667"
y1="13.62678"
x2="20.108334"
y2="13.62678"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2340"
x1="19.84375"
y1="14.816667"
x2="20.372917"
y2="14.816667"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2342"
x1="17.991667"
y1="14.948958"
x2="18.25625"
y2="14.948958"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2344"
x1="21.960417"
y1="14.948958"
x2="22.225"
y2="14.948958"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2346"
x1="17.991667"
y1="15.478861"
x2="20.108334"
y2="15.478861"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2348"
x1="17.991667"
y1="16.008026"
x2="20.108334"
y2="16.008026"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2350"
x1="20.108332"
y1="13.62678"
x2="22.224998"
y2="13.62678"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2352"
x1="20.108332"
y1="16.008026"
x2="22.224998"
y2="16.008026"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2354"
x1="20.108332"
y1="15.478861"
x2="22.224998"
y2="15.478861"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="book"
style="display:inline"
transform="matrix(3.7795278,0,0,3.7795278,-67.999998,-48.000002)">
<path
id="path1701"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2338);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.108334,13.493751 h -0.264583 c -0.631361,-0.523199 -1.676055,-0.176816 -1.852084,0.264583 l 0.04547,0.264583 c 0.176029,-0.441399 1.167806,-0.797084 1.806609,-0.264583 h 0.264583 z" />
<rect
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2340);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect1714"
width="0.5291667"
height="2.3812501"
x="19.84375"
y="13.626042" />
<rect
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2342);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect1716"
width="0.26458335"
height="2.3812501"
x="17.991667"
y="13.758333" />
<rect
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2344);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect1718"
width="0.26458335"
height="2.3812501"
x="21.960417"
y="13.758333" />
<path
id="path1736"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2346);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.108334,15.345832 h -0.264583 c -0.631361,-0.523199 -1.676055,-0.176816 -1.852084,0.264583 l 0.04547,0.264583 c 0.176029,-0.441399 1.167806,-0.797084 1.806609,-0.264583 h 0.264583 z" />
<path
id="path1738"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2348);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.108334,15.874998 h -0.264583 c -0.631361,-0.523199 -1.676055,-0.176816 -1.852084,0.264583 l 0.04547,0.264583 c 0.176029,-0.441399 1.167806,-0.797084 1.806609,-0.264583 h 0.264583 z" />
<path
id="path1740"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2350);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.108331,13.493751 h 0.264583 c 0.631361,-0.523199 1.676055,-0.176816 1.852084,0.264583 l -0.04547,0.264583 c -0.176029,-0.441399 -1.167806,-0.797084 -1.806609,-0.264583 h -0.264583 z" />
<path
id="path1742"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2352);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.108331,15.874998 h 0.264583 c 0.631361,-0.523199 1.676055,-0.176816 1.852084,0.264583 l -0.04547,0.264583 C 22.003499,15.962765 21.011722,15.60708 20.372919,16.139581 h -0.264583 z" />
<path
id="path1744"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2354);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.108331,15.345832 h 0.264583 c 0.631361,-0.523199 1.676055,-0.176816 1.852084,0.264583 l -0.04547,0.264583 c -0.176029,-0.441399 -1.167806,-0.797084 -1.806609,-0.264583 h -0.264583 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48pt"
height="48pt"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="Main">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
</defs>
<g
transform="matrix(0.01348304,5.4290978e-4,-5.4290978e-4,0.01348304,-8.3169871,11.902656)"
id="g3861"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:7.41072;stroke-dasharray:none;stroke-opacity:1">
<path
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:14.1112;stroke-dasharray:none;stroke-opacity:1"
d="m 1223.0877,970.60693 c 3.4659,1.1301 69.3268,-132.17231 0.01,1.65348 22.4324,7.9982 57.8129,20.83702 84.2736,35.72149 31.47,17.7026 95.0975,62.919 130.2191,93.4513 159.6062,-258.85749 217.7622,-402.8573 250.8816,-481.45696 1.4672,-3.6726 -10.6381,-224.03518 -7.9288,-242.38296 0.1439,-0.97479 -4.6307,-160.00282 -6.6178,-156.89403 -185.4652,290.15856 -351.8616,515.32983 -450.8377,749.90768 z m -18.5993,36.81547 c -10.343,-2.1435 -12.6893,2.7546 -14.3902,6.9154 -54.017,132.1446 -235.98278,450.7428 -258.39341,511.235 42.95377,6.6425 123.61141,56.0901 148.18501,89.6848 41.8408,-48.8743 238.2435,-354.2773 326.9039,-464.9703 3.3794,-4.2192 7.1429,-9.3124 11.2187,-15.1876 -31.8717,-28.1815 -90.6253,-67.9585 -118.9834,-84.5388 -27.4402,-16.0436 -52.8034,-27.3693 -75.3438,-35.4062 -5.3875,-1.921 -14.1678,-6.1689 -19.1968,-7.7323 z"
id="path3021"
transform="matrix(0.52516433,0,0,0.52516433,961.78994,-828.86591)" />
<g
id="g3795"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:7.41072;stroke-dasharray:none;stroke-opacity:1">
<path
id="path3793"
d="m 1432.3282,180.29088 c -0.3954,-0.0244 -0.6929,-0.0105 -1.091,-0.0361 -3.8526,-0.2484 -7.8422,-0.56196 -11.8738,-0.94553 -4.0316,-0.38357 -8.1046,-0.83644 -12.1828,-1.38194 -4.0783,-0.5455 -8.1539,-1.17502 -12.1465,-1.90928 -3.9927,-0.73421 -7.9173,-1.57768 -11.692,-2.52747 -3.7746,-0.94978 -7.3947,-2.0082 -10.8191,-3.20027 -1.7122,-0.59601 -3.3824,-1.22943 -4.9822,-1.89108 -1.5999,-0.66164 -3.1295,-1.36052 -4.6004,-2.09106 -1.4709,-0.73059 -2.8749,-1.48824 -4.2004,-2.29116 -1.3254,-0.80286 -2.5823,-1.64896 -3.7458,-2.52747 -1.1634,-0.8785 -2.2335,-1.78819 -3.2184,-2.74566 -0.9849,-0.95752 -1.8831,-1.96043 -2.673,-3.00029 -0.7898,-1.03985 -1.4765,-2.12929 -2.0547,-3.25479 -0.5782,-1.12557 -1.0501,-2.29484 -1.4001,-3.50943 -0.3501,-1.21459 -0.5856,-2.47515 -0.691,-3.78214 -0.1054,-1.307 -0.083,-2.65216 0.073,-4.05487 0.1559,-1.40277 0.2212,-2.7861 0.1819,-4.12766 -0.039,-1.34156 -0.1778,-2.64605 -0.4001,-3.92761 -0.2222,-1.28157 -0.5345,-2.5231 -0.9273,-3.74578 -0.3929,-1.22268 -0.8671,-2.41726 -1.4183,-3.58216 -0.5512,-1.16484 -1.1757,-2.31025 -1.8729,-3.41847 -0.6973,-1.10817 -1.4601,-2.18399 -2.2911,-3.23659 -0.8311,-1.05266 -1.7386,-2.07483 -2.6912,-3.07302 -0.9525,-0.9982 -1.9566,-1.98266 -3.0184,-2.92756 -1.0618,-0.94484 -2.1688,-1.85296 -3.3276,-2.74566 -1.1587,-0.8927 -2.375,-1.75864 -3.6185,-2.6002 -0.5162,-0.34947 -1.1071,-0.64116 -1.6365,-0.98196 -18.7596,41.685 -48.6658,67.50321 -110.1368,78.95234 -10.0671,1.87502 -6.7445,14.14668 0,14.14662 72.5797,0 156.074,-0.34097 218.4553,-19.58348 z"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:8.21086;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3849"
d="m 1430.619,-12.707633 c -45.8854,0 -84.0461,33.091677 -91.8989,76.715697 -4.2316,15.792726 -8.9665,30.004108 -14.7104,42.767336 0.5294,0.3408 1.1203,0.63249 1.6365,0.98196 1.2435,0.84156 2.4598,1.7075 3.6185,2.6002 1.1588,0.8927 2.2658,1.80082 3.3276,2.74566 1.0618,0.9449 2.0659,1.92936 3.0184,2.92756 0.9526,0.99819 1.8601,2.02036 2.6912,3.07302 0.831,1.0526 1.5938,2.12842 2.2911,3.23658 0.6972,1.10823 1.3217,2.25364 1.8729,3.41848 0.5512,1.1649 1.0254,2.35948 1.4183,3.58216 0.3928,1.22268 0.7051,2.46421 0.9273,3.74578 0.2223,1.28156 0.3607,2.58605 0.4001,3.92761 0.039,1.34156 -0.026,2.72489 -0.1819,4.12766 -0.1558,1.40271 -0.1781,2.74787 -0.073,4.05487 0.1054,1.30699 0.3409,2.56755 0.691,3.78214 0.35,1.21459 0.8219,2.38386 1.4001,3.50942 0.5782,1.12551 1.2649,2.21495 2.0547,3.2548 0.7899,1.03986 1.6881,2.04277 2.673,3.00029 0.9849,0.95747 2.055,1.86716 3.2184,2.74566 1.1635,0.87851 2.4204,1.7246 3.7458,2.52747 1.3255,0.80292 2.7295,1.56057 4.2004,2.29116 1.4709,0.73054 3.0005,1.42942 4.6004,2.09106 1.5998,0.66165 3.27,1.29507 4.9822,1.89108 3.4244,1.19207 7.0445,2.25049 10.8191,3.20027 3.7747,0.94979 7.6993,1.79326 11.692,2.52746 3.9926,0.73426 8.0682,1.36379 12.1465,1.90929 4.0782,0.5455 8.1512,0.99837 12.1828,1.38194 4.0316,0.38356 8.0212,0.69713 11.8738,0.94553 0.3981,0.0256 0.6956,0.0122 1.091,0.0361 35.9837,-11.09972 64.9673,-28.45453 80.7161,-55.73206 6.9768,-13.08128 10.9282,-28.03414 10.9282,-43.89477 0,-51.568563 -41.7849,-93.371684 -93.3535,-93.371684 z"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:8.21086;stroke-dasharray:none;stroke-opacity:1" />
</g>
<circle
style="fill:none;fill-opacity:0.8;stroke:#920080;stroke-width:0.367521;stroke-opacity:1"
id="path1"
cx="6.0803809"
cy="6.6884193"
r="4.1580262"
transform="matrix(74.047192,-2.9815935,2.9815935,74.047192,580.36067,-906.15614)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48pt"
height="48pt"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="Main">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
</defs>
<g
id="brush-custom"
style="display:inline"
transform="translate(-96.39494,-63.540021)">
<rect
style="opacity:0;fill:#ffbe6f;stroke:#eff0f1;stroke-width:0;paint-order:stroke fill markers;stop-color:#000000"
id="rect2449"
width="12.699994"
height="12.699999"
x="95.514572"
y="63.76458" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#920080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
d="m 101.20313,66.54271 c -1.678876,0 -3.042711,1.363835 -3.042711,3.042709 8.2e-5,0.61718 0.198856,1.213246 0.548288,1.720307 -0.178268,0.276987 -0.283678,0.594905 -0.283704,0.925526 0,0.948248 0.771543,1.719791 1.719787,1.719791 0.827,1.09e-4 1.52138,-0.595452 1.67639,-1.399397 0.27684,-0.05797 0.54302,-0.155514 0.79323,-0.287321 0.4243,0.549629 1.06517,0.89282 1.76372,0.892968 1.2405,0 2.24896,-1.008459 2.24896,-2.248958 0,-1.240499 -1.00846,-2.248958 -2.24896,-2.248958 -0.0962,-1.11e-4 -0.19147,0.01262 -0.28681,0.02479 -0.39574,-1.263941 -1.55476,-2.141357 -2.88819,-2.141457 z m 0,0.264584 c 1.25466,9.5e-5 2.35238,0.839655 2.68097,2.05052 l 0.0315,0.116271 0.11886,-0.02066 c 0.11342,-0.01979 0.22851,-0.02959 0.34365,-0.02946 1.09751,0 1.98437,0.886868 1.98437,1.984375 0,1.097508 -0.88686,1.984375 -1.98437,1.984375 -0.65001,-1.37e-4 -1.2585,-0.318322 -1.62936,-0.852143 l -0.0687,-0.09922 -0.10542,0.05943 c -0.27347,0.155496 -0.57095,0.263626 -0.88057,0.31936 l -0.0946,0.01707 -0.0134,0.09509 c -0.10037,0.720193 -0.71411,1.254281 -1.44126,1.254186 -0.805258,0 -1.455209,-0.649952 -1.455209,-1.455209 2.3e-5,-0.304604 0.09566,-0.601652 0.273367,-0.849042 l 0.05581,-0.07751 -0.05684,-0.07752 c -0.348953,-0.476242 -0.536839,-1.051359 -0.536919,-1.641761 0,-1.535882 1.242243,-2.778125 2.778121,-2.778125 z"
id="path2188" />
<g
id="g3795"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:7.41072;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(0.01348304,5.4290912e-4,-5.4290912e-4,0.01348304,88.077953,75.442677)">
<path
id="path3793"
d="m 1432.3282,180.29088 c -0.3954,-0.0244 -0.6929,-0.0105 -1.091,-0.0361 -3.8526,-0.2484 -7.8422,-0.56196 -11.8738,-0.94553 -4.0316,-0.38357 -8.1046,-0.83644 -12.1828,-1.38194 -4.0783,-0.5455 -8.1539,-1.17502 -12.1465,-1.90928 -3.9927,-0.73421 -7.9173,-1.57768 -11.692,-2.52747 -3.7746,-0.94978 -7.3947,-2.0082 -10.8191,-3.20027 -1.7122,-0.59601 -3.3824,-1.22943 -4.9822,-1.89108 -1.5999,-0.66164 -3.1295,-1.36052 -4.6004,-2.09106 -1.4709,-0.73059 -2.8749,-1.48824 -4.2004,-2.29116 -1.3254,-0.80286 -2.5823,-1.64896 -3.7458,-2.52747 -1.1634,-0.8785 -2.2335,-1.78819 -3.2184,-2.74566 -0.9849,-0.95752 -1.8831,-1.96043 -2.673,-3.00029 -0.7898,-1.03985 -1.4765,-2.12929 -2.0547,-3.25479 -0.5782,-1.12557 -1.0501,-2.29484 -1.4001,-3.50943 -0.3501,-1.21459 -0.5856,-2.47515 -0.691,-3.78214 -0.1054,-1.307 -0.083,-2.65216 0.073,-4.05487 0.1559,-1.40277 0.2212,-2.7861 0.1819,-4.12766 -0.039,-1.34156 -0.1778,-2.64605 -0.4001,-3.92761 -0.2222,-1.28157 -0.5345,-2.5231 -0.9273,-3.74578 -0.3929,-1.22268 -0.8671,-2.41726 -1.4183,-3.58216 -0.5512,-1.16484 -1.1757,-2.31025 -1.8729,-3.41847 -0.6973,-1.10817 -1.4601,-2.18399 -2.2911,-3.23659 -0.8311,-1.05266 -1.7386,-2.07483 -2.6912,-3.07302 -0.9525,-0.9982 -1.9566,-1.98266 -3.0184,-2.92756 -1.0618,-0.94484 -2.1688,-1.85296 -3.3276,-2.74566 -1.1587,-0.8927 -2.375,-1.75864 -3.6185,-2.6002 -0.5162,-0.34947 -1.1071,-0.64116 -1.6365,-0.98196 -18.7596,41.685 -48.6658,67.50321 -110.1368,78.95234 -10.0671,1.87502 -6.7445,14.14668 0,14.14662 72.5797,0 156.074,-0.34097 218.4553,-19.58348 z"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:8.21086;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3849"
d="m 1430.619,-12.707633 c -45.8854,0 -84.0461,33.091677 -91.8989,76.715697 -4.2316,15.792726 -8.9665,30.004108 -14.7104,42.767336 0.5294,0.3408 1.1203,0.63249 1.6365,0.98196 1.2435,0.84156 2.4598,1.7075 3.6185,2.6002 1.1588,0.8927 2.2658,1.80082 3.3276,2.74566 1.0618,0.9449 2.0659,1.92936 3.0184,2.92756 0.9526,0.99819 1.8601,2.02036 2.6912,3.07302 0.831,1.0526 1.5938,2.12842 2.2911,3.23658 0.6972,1.10823 1.3217,2.25364 1.8729,3.41848 0.5512,1.1649 1.0254,2.35948 1.4183,3.58216 0.3928,1.22268 0.7051,2.46421 0.9273,3.74578 0.2223,1.28156 0.3607,2.58605 0.4001,3.92761 0.039,1.34156 -0.026,2.72489 -0.1819,4.12766 -0.1558,1.40271 -0.1781,2.74787 -0.073,4.05487 0.1054,1.30699 0.3409,2.56755 0.691,3.78214 0.35,1.21459 0.8219,2.38386 1.4001,3.50942 0.5782,1.12551 1.2649,2.21495 2.0547,3.2548 0.7899,1.03986 1.6881,2.04277 2.673,3.00029 0.9849,0.95747 2.055,1.86716 3.2184,2.74566 1.1635,0.87851 2.4204,1.7246 3.7458,2.52747 1.3255,0.80292 2.7295,1.56057 4.2004,2.29116 1.4709,0.73054 3.0005,1.42942 4.6004,2.09106 1.5998,0.66165 3.27,1.29507 4.9822,1.89108 3.4244,1.19207 7.0445,2.25049 10.8191,3.20027 3.7747,0.94979 7.6993,1.79326 11.692,2.52746 3.9926,0.73426 8.0682,1.36379 12.1465,1.90929 4.0782,0.5455 8.1512,0.99837 12.1828,1.38194 4.0316,0.38356 8.0212,0.69713 11.8738,0.94553 0.3981,0.0256 0.6956,0.0122 1.091,0.0361 35.9837,-11.09972 64.9673,-28.45453 80.7161,-55.73206 6.9768,-13.08128 10.9282,-28.03414 10.9282,-43.89477 0,-51.568563 -41.7849,-93.371684 -93.3535,-93.371684 z"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:8.21086;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:14.1112;stroke-dasharray:none;stroke-opacity:1"
d="m 1223.0877,970.60693 c 3.4659,1.1301 69.3268,-132.17231 0.01,1.65348 22.4324,7.9982 57.8129,20.83702 84.2736,35.72149 31.47,17.7026 95.0975,62.919 130.2191,93.4513 159.6062,-258.85749 217.7622,-402.8573 250.8816,-481.45696 1.4672,-3.6726 -10.6381,-224.03518 -7.9288,-242.38296 0.1439,-0.97479 -4.636,-160.00618 -6.6178,-156.89403 -183.2275,287.73306 -308.4059,492.56152 -450.8377,749.90768 z m -18.5993,36.81547 c -10.343,-2.1435 -12.6893,2.7546 -14.3902,6.9154 -54.017,132.1446 -235.98278,450.7428 -258.39341,511.235 42.95377,6.6425 123.61141,56.0901 148.18501,89.6848 41.8408,-48.8743 238.2435,-354.2773 326.9039,-464.9703 3.3794,-4.2192 7.1429,-9.3124 11.2187,-15.1876 -31.8717,-28.1815 -90.6253,-67.9585 -118.9834,-84.5388 -27.4402,-16.0436 -52.8034,-27.3693 -75.3438,-35.4062 -5.3875,-1.921 -14.1678,-6.1689 -19.1968,-7.7323 z"
id="path3021"
transform="matrix(0.52516428,8.6770679e-8,-8.6770679e-8,0.52516428,961.78997,-828.86587)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 B

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48pt"
height="48pt"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="Main">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Main-6">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-2" />
</linearGradient>
</defs>
<g
id="brush-point"
style="display:inline"
transform="translate(-74.612486,-65.352077)">
<path
id="path1998"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#920080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 76.975838,71.657408 c -0.144291,0 -0.792839,0.121337 -0.792839,0.265625 0,0.144291 0.648546,0.263672 0.792839,0.263672 h 1.853515 c 0.144291,0 0.263672,-0.119384 0.263672,-0.263672 0,-0.144291 -0.119379,-0.265625 -0.263672,-0.265625 z m 3.96875,-3.439453 c 0,-0.144291 -0.119384,-0.792839 -0.263672,-0.792839 -0.144291,0 -0.265625,0.648546 -0.265625,0.792839 v 1.853515 c 0,0.144291 0.121337,0.263672 0.265625,0.263672 0.144291,0 0.263672,-0.119379 0.263672,-0.263672 z m 0,5.55664 c 0,-0.14429 -0.119384,-0.263671 -0.263672,-0.263671 -0.144291,0 -0.265625,0.119378 -0.265625,0.263671 v 1.851563 c 0,0.144291 0.121337,0.794792 0.265625,0.794792 0.144291,0 0.263672,-0.650499 0.263672,-0.794792 z m 1.58789,-2.117187 c -0.14429,0 -0.263671,0.121337 -0.263671,0.265625 0,0.144291 0.119378,0.263672 0.263671,0.263672 h 1.851563 c 0.144291,0 0.794792,-0.119384 0.794792,-0.263672 0,-0.144291 -0.650499,-0.265625 -0.794792,-0.265625 z" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#920080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 80.151619,70.864439 c -0.290872,0 -0.529297,0.238426 -0.529297,0.529297 v 1.058594 c 0,0.290872 0.238426,0.529297 0.529297,0.529297 h 1.058594 c 0.290872,0 0.529297,-0.238425 0.529297,-0.529297 v -1.058594 c 0,-0.290871 -0.238425,-0.529297 -0.529297,-0.529297 z m 0,0.265625 h 1.058594 c 0.14887,0 0.263672,0.114803 0.263672,0.263672 v 1.058594 c 0,0.14887 -0.114802,0.263672 -0.263672,0.263672 h -1.058594 c -0.148869,0 -0.263672,-0.114802 -0.263672,-0.263672 v -1.058594 c 0,-0.148869 0.114804,-0.263672 0.263672,-0.263672 z"
id="path1983" />
<g
id="g3795"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:7.41072;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(0.01348304,5.4290911e-4,-5.4290911e-4,0.01348304,66.295499,77.254733)">
<path
id="path3793"
d="m 1432.3282,180.29088 c -0.3954,-0.0244 -0.6929,-0.0105 -1.091,-0.0361 -3.8526,-0.2484 -7.8422,-0.56196 -11.8738,-0.94553 -4.0316,-0.38357 -8.1046,-0.83644 -12.1828,-1.38194 -4.0783,-0.5455 -8.1539,-1.17502 -12.1465,-1.90928 -3.9927,-0.73421 -7.9173,-1.57768 -11.692,-2.52747 -3.7746,-0.94978 -7.3947,-2.0082 -10.8191,-3.20027 -1.7122,-0.59601 -3.3824,-1.22943 -4.9822,-1.89108 -1.5999,-0.66164 -3.1295,-1.36052 -4.6004,-2.09106 -1.4709,-0.73059 -2.8749,-1.48824 -4.2004,-2.29116 -1.3254,-0.80286 -2.5823,-1.64896 -3.7458,-2.52747 -1.1634,-0.8785 -2.2335,-1.78819 -3.2184,-2.74566 -0.9849,-0.95752 -1.8831,-1.96043 -2.673,-3.00029 -0.7898,-1.03985 -1.4765,-2.12929 -2.0547,-3.25479 -0.5782,-1.12557 -1.0501,-2.29484 -1.4001,-3.50943 -0.3501,-1.21459 -0.5856,-2.47515 -0.691,-3.78214 -0.1054,-1.307 -0.083,-2.65216 0.073,-4.05487 0.1559,-1.40277 0.2212,-2.7861 0.1819,-4.12766 -0.039,-1.34156 -0.1778,-2.64605 -0.4001,-3.92761 -0.2222,-1.28157 -0.5345,-2.5231 -0.9273,-3.74578 -0.3929,-1.22268 -0.8671,-2.41726 -1.4183,-3.58216 -0.5512,-1.16484 -1.1757,-2.31025 -1.8729,-3.41847 -0.6973,-1.10817 -1.4601,-2.18399 -2.2911,-3.23659 -0.8311,-1.05266 -1.7386,-2.07483 -2.6912,-3.07302 -0.9525,-0.9982 -1.9566,-1.98266 -3.0184,-2.92756 -1.0618,-0.94484 -2.1688,-1.85296 -3.3276,-2.74566 -1.1587,-0.8927 -2.375,-1.75864 -3.6185,-2.6002 -0.5162,-0.34947 -1.1071,-0.64116 -1.6365,-0.98196 -18.7596,41.685 -48.6658,67.50321 -110.1368,78.95234 -10.0671,1.87502 -6.7445,14.14668 0,14.14662 72.5797,0 156.074,-0.34097 218.4553,-19.58348 z"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:8.21086;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3849"
d="m 1430.619,-12.707633 c -45.8854,0 -84.0461,33.091677 -91.8989,76.715697 -4.2316,15.792726 -8.9665,30.004108 -14.7104,42.767336 0.5294,0.3408 1.1203,0.63249 1.6365,0.98196 1.2435,0.84156 2.4598,1.7075 3.6185,2.6002 1.1588,0.8927 2.2658,1.80082 3.3276,2.74566 1.0618,0.9449 2.0659,1.92936 3.0184,2.92756 0.9526,0.99819 1.8601,2.02036 2.6912,3.07302 0.831,1.0526 1.5938,2.12842 2.2911,3.23658 0.6972,1.10823 1.3217,2.25364 1.8729,3.41848 0.5512,1.1649 1.0254,2.35948 1.4183,3.58216 0.3928,1.22268 0.7051,2.46421 0.9273,3.74578 0.2223,1.28156 0.3607,2.58605 0.4001,3.92761 0.039,1.34156 -0.026,2.72489 -0.1819,4.12766 -0.1558,1.40271 -0.1781,2.74787 -0.073,4.05487 0.1054,1.30699 0.3409,2.56755 0.691,3.78214 0.35,1.21459 0.8219,2.38386 1.4001,3.50942 0.5782,1.12551 1.2649,2.21495 2.0547,3.2548 0.7899,1.03986 1.6881,2.04277 2.673,3.00029 0.9849,0.95747 2.055,1.86716 3.2184,2.74566 1.1635,0.87851 2.4204,1.7246 3.7458,2.52747 1.3255,0.80292 2.7295,1.56057 4.2004,2.29116 1.4709,0.73054 3.0005,1.42942 4.6004,2.09106 1.5998,0.66165 3.27,1.29507 4.9822,1.89108 3.4244,1.19207 7.0445,2.25049 10.8191,3.20027 3.7747,0.94979 7.6993,1.79326 11.692,2.52746 3.9926,0.73426 8.0682,1.36379 12.1465,1.90929 4.0782,0.5455 8.1512,0.99837 12.1828,1.38194 4.0316,0.38356 8.0212,0.69713 11.8738,0.94553 0.3981,0.0256 0.6956,0.0122 1.091,0.0361 35.9837,-11.09972 64.9673,-28.45453 80.7161,-55.73206 6.9768,-13.08128 10.9282,-28.03414 10.9282,-43.89477 0,-51.568563 -41.7849,-93.371684 -93.3535,-93.371684 z"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:8.21086;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
transform="matrix(0.01348304,5.4290911e-4,-5.4290911e-4,0.01348304,66.295499,77.254733)"
id="g3861"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:7.41072;stroke-dasharray:none;stroke-opacity:1">
<path
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:14.1112;stroke-dasharray:none;stroke-opacity:1"
d="m 1223.0877,970.60693 c 3.4659,1.1301 69.3268,-132.17231 0.01,1.65348 22.4324,7.9982 57.8129,20.83702 84.2736,35.72149 31.47,17.7026 95.0975,62.919 130.2191,93.4513 159.6062,-258.85749 217.7622,-402.8573 250.8816,-481.45696 1.4672,-3.6726 -10.6381,-224.03518 -7.9288,-242.38296 0.1439,-0.97479 -4.6307,-160.00282 -6.6178,-156.89403 -185.4652,290.15856 -351.8616,515.32983 -450.8377,749.90768 z m -18.5993,36.81547 c -10.343,-2.1435 -12.6893,2.7546 -14.3902,6.9154 -54.017,132.1446 -235.98278,450.7428 -258.39341,511.235 42.95377,6.6425 123.61141,56.0901 148.18501,89.6848 41.8408,-48.8743 238.2435,-354.2773 326.9039,-464.9703 3.3794,-4.2192 7.1429,-9.3124 11.2187,-15.1876 -31.8717,-28.1815 -90.6253,-67.9585 -118.9834,-84.5388 -27.4402,-16.0436 -52.8034,-27.3693 -75.3438,-35.4062 -5.3875,-1.921 -14.1678,-6.1689 -19.1968,-7.7323 z"
id="path3021"
transform="matrix(0.52516433,0,0,0.52516433,961.78994,-828.86591)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 761 B

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48pt"
height="48pt"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="Main">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
</defs>
<g
transform="matrix(0.01348304,5.4290978e-4,-5.4290978e-4,0.01348304,-8.3169871,11.902656)"
id="g3861"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:7.41072;stroke-dasharray:none;stroke-opacity:1">
<path
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:14.1112;stroke-dasharray:none;stroke-opacity:1"
d="m 1223.0877,970.60693 c 3.4659,1.1301 69.3268,-132.17231 0.01,1.65348 22.4324,7.9982 57.8129,20.83702 84.2736,35.72149 31.47,17.7026 95.0975,62.919 130.2191,93.4513 159.6062,-258.85749 217.7622,-402.8573 250.8816,-481.45696 1.4672,-3.6726 -10.6381,-224.03518 -7.9288,-242.38296 0.1439,-0.97479 -4.636,-160.00618 -6.6178,-156.89403 -183.2275,287.73306 -308.4059,492.56152 -450.8377,749.90768 z m -18.5993,36.81547 c -10.343,-2.1435 -12.6893,2.7546 -14.3902,6.9154 -54.017,132.1446 -235.98278,450.7428 -258.39341,511.235 42.95377,6.6425 123.61141,56.0901 148.18501,89.6848 41.8408,-48.8743 238.2435,-354.2773 326.9039,-464.9703 3.3794,-4.2192 7.1429,-9.3124 11.2187,-15.1876 -31.8717,-28.1815 -90.6253,-67.9585 -118.9834,-84.5388 -27.4402,-16.0436 -52.8034,-27.3693 -75.3438,-35.4062 -5.3875,-1.921 -14.1678,-6.1689 -19.1968,-7.7323 z"
id="path3021"
transform="matrix(0.52516433,0,0,0.52516433,961.78994,-828.86591)" />
<g
id="g3795"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:7.41072;stroke-dasharray:none;stroke-opacity:1">
<path
id="path3793"
d="m 1432.3282,180.29088 c -0.3954,-0.0244 -0.6929,-0.0105 -1.091,-0.0361 -3.8526,-0.2484 -7.8422,-0.56196 -11.8738,-0.94553 -4.0316,-0.38357 -8.1046,-0.83644 -12.1828,-1.38194 -4.0783,-0.5455 -8.1539,-1.17502 -12.1465,-1.90928 -3.9927,-0.73421 -7.9173,-1.57768 -11.692,-2.52747 -3.7746,-0.94978 -7.3947,-2.0082 -10.8191,-3.20027 -1.7122,-0.59601 -3.3824,-1.22943 -4.9822,-1.89108 -1.5999,-0.66164 -3.1295,-1.36052 -4.6004,-2.09106 -1.4709,-0.73059 -2.8749,-1.48824 -4.2004,-2.29116 -1.3254,-0.80286 -2.5823,-1.64896 -3.7458,-2.52747 -1.1634,-0.8785 -2.2335,-1.78819 -3.2184,-2.74566 -0.9849,-0.95752 -1.8831,-1.96043 -2.673,-3.00029 -0.7898,-1.03985 -1.4765,-2.12929 -2.0547,-3.25479 -0.5782,-1.12557 -1.0501,-2.29484 -1.4001,-3.50943 -0.3501,-1.21459 -0.5856,-2.47515 -0.691,-3.78214 -0.1054,-1.307 -0.083,-2.65216 0.073,-4.05487 0.1559,-1.40277 0.2212,-2.7861 0.1819,-4.12766 -0.039,-1.34156 -0.1778,-2.64605 -0.4001,-3.92761 -0.2222,-1.28157 -0.5345,-2.5231 -0.9273,-3.74578 -0.3929,-1.22268 -0.8671,-2.41726 -1.4183,-3.58216 -0.5512,-1.16484 -1.1757,-2.31025 -1.8729,-3.41847 -0.6973,-1.10817 -1.4601,-2.18399 -2.2911,-3.23659 -0.8311,-1.05266 -1.7386,-2.07483 -2.6912,-3.07302 -0.9525,-0.9982 -1.9566,-1.98266 -3.0184,-2.92756 -1.0618,-0.94484 -2.1688,-1.85296 -3.3276,-2.74566 -1.1587,-0.8927 -2.375,-1.75864 -3.6185,-2.6002 -0.5162,-0.34947 -1.1071,-0.64116 -1.6365,-0.98196 -18.7596,41.685 -48.6658,67.50321 -110.1368,78.95234 -10.0671,1.87502 -6.7445,14.14668 0,14.14662 72.5797,0 156.074,-0.34097 218.4553,-19.58348 z"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:8.21086;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3849"
d="m 1430.619,-12.707633 c -45.8854,0 -84.0461,33.091677 -91.8989,76.715697 -4.2316,15.792726 -8.9665,30.004108 -14.7104,42.767336 0.5294,0.3408 1.1203,0.63249 1.6365,0.98196 1.2435,0.84156 2.4598,1.7075 3.6185,2.6002 1.1588,0.8927 2.2658,1.80082 3.3276,2.74566 1.0618,0.9449 2.0659,1.92936 3.0184,2.92756 0.9526,0.99819 1.8601,2.02036 2.6912,3.07302 0.831,1.0526 1.5938,2.12842 2.2911,3.23658 0.6972,1.10823 1.3217,2.25364 1.8729,3.41848 0.5512,1.1649 1.0254,2.35948 1.4183,3.58216 0.3928,1.22268 0.7051,2.46421 0.9273,3.74578 0.2223,1.28156 0.3607,2.58605 0.4001,3.92761 0.039,1.34156 -0.026,2.72489 -0.1819,4.12766 -0.1558,1.40271 -0.1781,2.74787 -0.073,4.05487 0.1054,1.30699 0.3409,2.56755 0.691,3.78214 0.35,1.21459 0.8219,2.38386 1.4001,3.50942 0.5782,1.12551 1.2649,2.21495 2.0547,3.2548 0.7899,1.03986 1.6881,2.04277 2.673,3.00029 0.9849,0.95747 2.055,1.86716 3.2184,2.74566 1.1635,0.87851 2.4204,1.7246 3.7458,2.52747 1.3255,0.80292 2.7295,1.56057 4.2004,2.29116 1.4709,0.73054 3.0005,1.42942 4.6004,2.09106 1.5998,0.66165 3.27,1.29507 4.9822,1.89108 3.4244,1.19207 7.0445,2.25049 10.8191,3.20027 3.7747,0.94979 7.6993,1.79326 11.692,2.52746 3.9926,0.73426 8.0682,1.36379 12.1465,1.90929 4.0782,0.5455 8.1512,0.99837 12.1828,1.38194 4.0316,0.38356 8.0212,0.69713 11.8738,0.94553 0.3981,0.0256 0.6956,0.0122 1.091,0.0361 35.9837,-11.09972 64.9673,-28.45453 80.7161,-55.73206 6.9768,-13.08128 10.9282,-28.03414 10.9282,-43.89477 0,-51.568563 -41.7849,-93.371684 -93.3535,-93.371684 z"
style="fill:#4d4d4d;fill-opacity:1;stroke:#808080;stroke-width:8.21086;stroke-dasharray:none;stroke-opacity:1" />
</g>
<rect
style="fill:none;fill-opacity:1;stroke:#920080;stroke-width:24.154;stroke-dasharray:none;stroke-opacity:1"
id="rect4"
width="551.00012"
height="551.00012"
x="759.14246"
y="-686.99188"
transform="rotate(-2.3058334)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 B

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48pt"
height="48pt"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="Main"
gradientTransform="translate(63.499994)">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Blue"
gradientTransform="matrix(0.26458333,0,0,0.26458333,-13.80713,103.48169)">
<stop
style="stop-color:#55c3ff;stop-opacity:1;"
offset="0"
id="stop2484" />
</linearGradient>
<linearGradient
xlink:href="#Blue"
id="linearGradient2809"
gradientUnits="userSpaceOnUse"
x1="7.4083338"
y1="-3.7041662"
x2="11.641667"
y2="-3.7041662" />
</defs>
<g
id="camera-first-person"
style="display:inline"
transform="matrix(1.3333345,0,0,1.333334,-190.85285,-85.019482)">
<path
id="rect2331"
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:url(#linearGradient2809);fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="M 558.40946,259.99589 557.5,270 h 3 l 0.91695,11.00345 A 1.0867995,1.0867995 42.618179 0 0 562.5,282 h 5 a 1.0867995,1.0867995 137.38182 0 0 1.08305,-0.99655 L 569.5,270 h 2.75 l -0.90946,-10.00411 A 1.0950328,1.0950328 42.402786 0 0 570.25,259 H 559.5 a 1.0950328,1.0950328 137.59721 0 0 -1.09054,0.99589 z"
transform="scale(0.26458333)" />
<ellipse
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="ellipse2327"
cx="149.48953"
cy="67.071899"
rx="1.5874941"
ry="1.5875033" />
<circle
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:0.326641px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="circle2329"
cx="150.01872"
cy="67.204193"
r="0.52916664" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 B

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48pt"
height="48pt"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="Main"
gradientTransform="translate(63.499994)">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Blue"
gradientTransform="matrix(0.26458333,0,0,0.26458333,-13.80713,103.48169)">
<stop
style="stop-color:#55c3ff;stop-opacity:1;"
offset="0"
id="stop2484" />
</linearGradient>
</defs>
<g
id="camera-free"
style="display:inline"
transform="matrix(1.3333345,0,0,1.3333335,-169.68618,-85.01945)">
<circle
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="ellipse2315"
cx="133.08542"
cy="72.628128"
r="1.3229166" />
<circle
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="circle2319"
cx="131.36563"
cy="68.130211"
r="1.4552083" />
<ellipse
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:0.237741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="circle2317"
cx="123.01057"
cy="68.208794"
rx="0.48718452"
ry="0.46406204"
transform="matrix(1,0,0.12650781,0.99196561,0,0)" />
<ellipse
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:0.205204px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="circle2321"
cx="132.5358"
cy="72.584305"
rx="0.3821989"
ry="0.440696" />
<circle
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="circle2323"
cx="135.99583"
cy="69.056252"
r="1.984375" />
<ellipse
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:0.326641px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="ellipse2325"
cx="136.92188"
cy="69.171593"
rx="0.65753078"
ry="0.64905536" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48pt"
height="48pt"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="Main"
gradientTransform="translate(63.499994)">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Blue"
gradientTransform="matrix(0.26458333,0,0,0.26458333,-13.80713,103.48169)">
<stop
style="stop-color:#55c3ff;stop-opacity:1;"
offset="0"
id="stop2484" />
</linearGradient>
<linearGradient
xlink:href="#Blue"
id="linearGradient2502"
x1="7.4083338"
y1="-3.7041662"
x2="11.641667"
y2="-3.7041662"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="camera-orbit"
style="display:inline"
transform="matrix(1.3333345,0,0,1.333334,-148.51954,-85.019482)">
<path
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:url(#linearGradient2502);fill-opacity:1;stroke:none;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 114.81181,69.118264 c -3.49828,3.08633 1.51943,2.817376 3.58014,2.334121 2.06071,-0.483255 7.1306,-3.705601 -1.60983,-3.162598 7.34302,-2.156354 7.21991,2.475405 2.60397,3.889003 -4.95879,1.518595 -8.7571,-0.657262 -4.57428,-3.060526 z"
id="path2279" />
<ellipse
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="path2257"
cx="115.8875"
cy="68.659378"
rx="1.5874941"
ry="1.5875033" />
<circle
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
id="path2261"
cx="116.41667"
cy="69.056252"
r="0.52916664" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Red">
<stop
style="stop-color:#ff664d;stop-opacity:1;"
offset="0"
id="stop2533" />
</linearGradient>
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect3119">
<path
id="path3117"
style="opacity:1;fill:#000000;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 10.250537,66.995394 -0.0047,0.0047 c -0.02767,-0.0066 -0.211799,-0.04039 -0.4563028,0.204116 C 9.6572915,67.468749 9.6572915,67.468749 9.5249999,67.49562 9.3927082,67.468749 9.3927082,67.468749 9.2604165,67.204166 9.061979,67.005731 8.8982935,66.990872 8.8289184,66.99591 c -0.023124,0.0016 -0.035657,0.0057 -0.035657,0.0057 0,0 -0.062009,0.20257 0.2025716,0.467154 0.2645833,0.132291 0.2645841,0.132291 0.291455,0.264583 -0.026871,0.132292 -0.026872,0.132292 -0.291455,0.264583 -0.2445017,0.244505 -0.2107512,0.428639 -0.2041218,0.456304 l -0.00465,0.0047 c 0,0 0.00465,2.65e-4 0.00517,5.29e-4 1.736e-4,5.3e-4 0.00103,0.0057 0.00103,0.0057 l 0.00465,-0.0047 c 0.027721,0.0066 0.2118445,0.04033 0.4563028,-0.204123 0.1322919,-0.264584 0.1322919,-0.264584 0.2645833,-0.291455 0.1322919,0.02687 0.1322917,0.02687 0.2645833,0.291455 0.1984378,0.198435 0.3621234,0.213294 0.4314984,0.208256 0.02312,-0.0016 0.03566,-0.0057 0.03566,-0.0057 0,0 0.06201,-0.20257 -0.202572,-0.467156 -0.2645836,-0.132289 -0.2645839,-0.132291 -0.291455,-0.264583 0.026871,-0.132279 0.026873,-0.132292 0.291455,-0.264583 0.244502,-0.244505 0.210752,-0.428639 0.204122,-0.456304 l 0.0047,-0.0047 c 0,0 -0.0046,-7.94e-4 -0.0052,-0.0011 -1.47e-4,-5.29e-4 -10e-4,-0.0052 -10e-4,-0.0052 z" />
</mask>
<linearGradient
id="Main-6">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-3" />
</linearGradient>
<linearGradient
id="Green"
gradientTransform="scale(3.7795276)">
<stop
style="stop-color:#66ff7a;stop-opacity:1;"
offset="0"
id="stop2506" />
</linearGradient>
<linearGradient
xlink:href="#Green"
id="linearGradient4011"
gradientUnits="userSpaceOnUse"
x1="2.1166668"
y1="4.2333336"
x2="6.3500004"
y2="4.2333336"
gradientTransform="translate(21.166665,37.041663)" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient4013"
gradientUnits="userSpaceOnUse"
x1="2.1166666"
y1="4.2333336"
x2="6.3500004"
y2="4.2333336"
gradientTransform="matrix(0.26458333,0,0,0.26458333,21.166665,37.041663)" />
</defs>
<g
id="cell"
transform="matrix(3.7795278,0,0,3.7795278,-87.999996,-147.99999)"
style="display:inline">
<path
id="path887"
style="fill:url(#linearGradient4011);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 24.077082,39.95208 h 2.645833 v 2.645833 c 0,0 -2.645833,0 -2.645833,0 z m -0.264584,-0.79375 v 0.529166 h -0.529166 v 0.264584 h 0.529166 v 2.645833 h -0.529166 v 0.264583 h 0.529166 v 0.529167 h 0.264584 v -0.529167 h 2.645833 v 0.529167 h 0.264583 v -0.529167 h 0.529167 V 42.597913 H 26.987498 V 39.95208 h 0.529167 V 39.687496 H 26.987498 V 39.15833 h -0.264583 v 0.529166 H 24.077082 V 39.15833 Z" />
<path
id="rect890"
style="fill:url(#linearGradient4013);fill-opacity:1;stroke:none;stroke-width:0.264583"
d="m 24.341665,40.216663 v 2.116667 h 2.116667 v -2.116667 z m 0.264583,0.264583 h 1.5875 v 1.5875 h -1.5875 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Red">
<stop
style="stop-color:#ff664d;stop-opacity:1;"
offset="0"
id="stop2533" />
</linearGradient>
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect3119">
<path
id="path3117"
style="opacity:1;fill:#000000;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 10.250537,66.995394 -0.0047,0.0047 c -0.02767,-0.0066 -0.211799,-0.04039 -0.4563028,0.204116 C 9.6572915,67.468749 9.6572915,67.468749 9.5249999,67.49562 9.3927082,67.468749 9.3927082,67.468749 9.2604165,67.204166 9.061979,67.005731 8.8982935,66.990872 8.8289184,66.99591 c -0.023124,0.0016 -0.035657,0.0057 -0.035657,0.0057 0,0 -0.062009,0.20257 0.2025716,0.467154 0.2645833,0.132291 0.2645841,0.132291 0.291455,0.264583 -0.026871,0.132292 -0.026872,0.132292 -0.291455,0.264583 -0.2445017,0.244505 -0.2107512,0.428639 -0.2041218,0.456304 l -0.00465,0.0047 c 0,0 0.00465,2.65e-4 0.00517,5.29e-4 1.736e-4,5.3e-4 0.00103,0.0057 0.00103,0.0057 l 0.00465,-0.0047 c 0.027721,0.0066 0.2118445,0.04033 0.4563028,-0.204123 0.1322919,-0.264584 0.1322919,-0.264584 0.2645833,-0.291455 0.1322919,0.02687 0.1322917,0.02687 0.2645833,0.291455 0.1984378,0.198435 0.3621234,0.213294 0.4314984,0.208256 0.02312,-0.0016 0.03566,-0.0057 0.03566,-0.0057 0,0 0.06201,-0.20257 -0.202572,-0.467156 -0.2645836,-0.132289 -0.2645839,-0.132291 -0.291455,-0.264583 0.026871,-0.132279 0.026873,-0.132292 0.291455,-0.264583 0.244502,-0.244505 0.210752,-0.428639 0.204122,-0.456304 l 0.0047,-0.0047 c 0,0 -0.0046,-7.94e-4 -0.0052,-0.0011 -1.47e-4,-5.29e-4 -10e-4,-0.0052 -10e-4,-0.0052 z" />
</mask>
<linearGradient
id="Main-6">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-3" />
</linearGradient>
<linearGradient
id="Green"
gradientTransform="scale(3.7795276)">
<stop
style="stop-color:#66ff7a;stop-opacity:1;"
offset="0"
id="stop2506" />
</linearGradient>
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect1741">
<path
id="path1739"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#000000;fill-opacity:0.992157;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 20.095414,60.589583 a 0.52916663,0.52916663 0 0 0 -0.516248,0.529166 0.52916663,0.52916663 0 0 0 0.529167,0.529167 0.52916663,0.52916663 0 0 0 0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.529167,-0.529166 0.52916663,0.52916663 0 0 0 -0.01292,0 z m -1.058333,2.116666 A 0.52916663,0.52916663 0 0 0 18.520833,63.235416 0.52916663,0.52916663 0 0 0 19.05,63.764583 0.52916663,0.52916663 0 0 0 19.579166,63.235416 0.52916663,0.52916663 0 0 0 19.05,62.706249 a 0.52916663,0.52916663 0 0 0 -0.01292,0 z m 2.116666,0 a 0.52916663,0.52916663 0 0 0 -0.516247,0.529167 0.52916663,0.52916663 0 0 0 0.529166,0.529167 0.52916663,0.52916663 0 0 0 0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.01292,0 z" />
</mask>
<linearGradient
xlink:href="#Main-6"
id="linearGradient2220"
x1="17.991667"
y1="62.44165"
x2="22.224998"
y2="62.44165"
gradientUnits="userSpaceOnUse" />
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect1741-0">
<path
id="mask-powermask-path-effect1741_box"
style="fill:#ffffff;fill-opacity:1"
d="m 16.991666,59.324974 h 6.233333 v 6.233352 h -6.233333 z" />
<path
id="path1739-0"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#000000;fill-opacity:0.992157;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 20.095414,60.589583 a 0.52916663,0.52916663 0 0 0 -0.516248,0.529166 0.52916663,0.52916663 0 0 0 0.529167,0.529167 0.52916663,0.52916663 0 0 0 0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.529167,-0.529166 0.52916663,0.52916663 0 0 0 -0.01292,0 z m -1.058333,2.116666 A 0.52916663,0.52916663 0 0 0 18.520833,63.235416 0.52916663,0.52916663 0 0 0 19.05,63.764583 0.52916663,0.52916663 0 0 0 19.579166,63.235416 0.52916663,0.52916663 0 0 0 19.05,62.706249 a 0.52916663,0.52916663 0 0 0 -0.01292,0 z m 2.116666,0 a 0.52916663,0.52916663 0 0 0 -0.516247,0.529167 0.52916663,0.52916663 0 0 0 0.529166,0.529167 0.52916663,0.52916663 0 0 0 0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.01292,0 z" />
</mask>
<filter
id="mask-powermask-path-effect1741_inverse"
style="color-interpolation-filters:sRGB"
height="100"
width="100"
x="-50"
y="-50">
<feColorMatrix
id="mask-powermask-path-effect1741_primitive1"
values="1"
type="saturate"
result="fbSourceGraphic" />
<feColorMatrix
id="mask-powermask-path-effect1741_primitive2"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
in="fbSourceGraphic" />
</filter>
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect1741-7">
<path
id="mask-powermask-path-effect1741-7_box"
style="fill:#ffffff;fill-opacity:1"
d="m 16.991666,59.324974 h 6.233333 v 6.233352 h -6.233333 z" />
<path
id="path1"
style="fill:#ffffff;fill-opacity:1"
d="m 16.991666,59.324974 h 6.233333 v 6.233352 h -6.233333 z" />
<path
id="path2"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#000000;fill-opacity:0.992157;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 20.095414,60.589583 a 0.52916663,0.52916663 0 0 0 -0.516248,0.529166 0.52916663,0.52916663 0 0 0 0.529167,0.529167 0.52916663,0.52916663 0 0 0 0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.529167,-0.529166 0.52916663,0.52916663 0 0 0 -0.01292,0 z m -1.058333,2.116666 A 0.52916663,0.52916663 0 0 0 18.520833,63.235416 0.52916663,0.52916663 0 0 0 19.05,63.764583 0.52916663,0.52916663 0 0 0 19.579166,63.235416 0.52916663,0.52916663 0 0 0 19.05,62.706249 a 0.52916663,0.52916663 0 0 0 -0.01292,0 z m 2.116666,0 a 0.52916663,0.52916663 0 0 0 -0.516247,0.529167 0.52916663,0.52916663 0 0 0 0.529166,0.529167 0.52916663,0.52916663 0 0 0 0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.01292,0 z" />
</mask>
<filter
id="mask-powermask-path-effect1741-7_inverse"
style="color-interpolation-filters:sRGB"
height="100"
width="100"
x="-50"
y="-50">
<feColorMatrix
id="mask-powermask-path-effect1741-7_primitive1"
values="1"
type="saturate"
result="fbSourceGraphic" />
<feColorMatrix
id="mask-powermask-path-effect1741-7_primitive2"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
in="fbSourceGraphic" />
</filter>
<linearGradient
xlink:href="#Main-6"
id="linearGradient2"
gradientUnits="userSpaceOnUse"
x1="17.991667"
y1="62.44165"
x2="22.224998"
y2="62.44165" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient3"
gradientUnits="userSpaceOnUse"
x1="17.991667"
y1="62.44165"
x2="22.224998"
y2="62.44165" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient4"
gradientUnits="userSpaceOnUse"
x1="17.991667"
y1="62.44165"
x2="22.224998"
y2="62.44165" />
</defs>
<g
id="class"
transform="matrix(3.7795269,0,0,3.7795061,-67.999981,-147.99907)"
style="display:inline">
<g
id="g1724"
mask="url(#mask-powermask-path-effect1741-7)"
transform="translate(0,-21.166665)"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2220);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">
<path
id="path843"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.108333,60.324994 a 2.1166666,2.1166666 0 0 0 -2.116667,2.116667 2.1166666,2.1166666 0 0 0 2.116667,2.116665 2.1166666,2.1166666 0 0 0 2.116666,-2.116665 2.1166666,2.1166666 0 0 0 -2.116666,-2.116667 z m -0.02479,0.264583 a 1.8520806,1.8520806 0 0 1 0.02479,0 1.8520806,1.8520806 0 0 1 1.852082,1.852084 1.8520806,1.8520806 0 0 1 -1.852082,1.852083 1.8520806,1.8520806 0 0 1 -1.852084,-1.852083 1.8520806,1.8520806 0 0 1 1.827279,-1.852084 z" />
<path
id="path850"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#Main-6);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.108333,60.854161 a 1.5875,1.5875 0 0 0 -1.5875,1.5875 1.5875,1.5875 0 0 0 1.5875,1.587499 1.5875,1.5875 0 0 0 1.587499,-1.587499 1.5875,1.5875 0 0 0 -1.587499,-1.5875 z m 5.29e-4,0.264583 a 1.3235013,1.3229139 0 0 1 1.323435,1.322917 1.3235013,1.3229139 0 0 1 -1.323435,1.322916 1.3235013,1.3229139 0 0 1 -1.323433,-1.322916 1.3235013,1.3229139 0 0 1 1.323433,-1.322917 z" />
<path
id="path860"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient3);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 20.094895,60.325002 a 0.79375,0.79375 0 0 0 -0.780315,0.79375 0.79375,0.79375 0 0 0 0.79375,0.79375 0.79375,0.79375 0 0 0 0.793749,-0.79375 0.79375,0.79375 0 0 0 -0.793749,-0.79375 0.79375,0.79375 0 0 0 -0.01344,0 z m 5.29e-4,0.264584 a 0.52916664,0.52916664 0 0 1 0.01291,0 0.52916664,0.52916664 0 0 1 0.529166,0.529166 0.52916664,0.52916664 0 0 1 -0.529166,0.529167 0.52916664,0.52916664 0 0 1 -0.529167,-0.529167 0.52916664,0.52916664 0 0 1 0.516247,-0.529166 z"
mask="none" />
<path
id="path867"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#Main-6);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 19.036561,62.441668 a 0.79375,0.79375 0 0 0 -0.780315,0.793749 0.79375,0.79375 0 0 0 0.79375,0.79375 0.79375,0.79375 0 0 0 0.79375,-0.79375 0.79375,0.79375 0 0 0 -0.79375,-0.793749 0.79375,0.79375 0 0 0 -0.01344,0 z m 5.29e-4,0.264584 a 0.52916664,0.52916664 0 0 1 0.01291,0 0.52916664,0.52916664 0 0 1 0.529166,0.529165 A 0.52916664,0.52916664 0 0 1 19.05,63.764584 0.52916664,0.52916664 0 0 1 18.520833,63.235417 0.52916664,0.52916664 0 0 1 19.03708,62.706252 Z" />
<path
id="path869"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient4);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 21.153232,62.441668 a 0.79375,0.79375 0 0 0 -0.780314,0.793749 0.79375,0.79375 0 0 0 0.793749,0.79375 0.79375,0.79375 0 0 0 0.79375,-0.79375 0.79375,0.79375 0 0 0 -0.79375,-0.793749 0.79375,0.79375 0 0 0 -0.01344,0 z m 5.3e-4,0.264584 a 0.52916664,0.52916664 0 0 1 0.01291,0 0.52916664,0.52916664 0 0 1 0.529166,0.529165 0.52916664,0.52916664 0 0 1 -0.529166,0.529167 0.52916664,0.52916664 0 0 1 -0.529167,-0.529167 0.52916664,0.52916664 0 0 1 0.516247,-0.529165 z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 B

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="16.000000pt"
height="16.000000pt"
viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet"
id="svg2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="Main">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2082" />
</linearGradient>
<linearGradient
id="Red">
<stop
style="stop-color:#ff664d;stop-opacity:1;"
offset="0"
id="stop2533" />
</linearGradient>
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect3119">
<path
id="path3117"
style="opacity:1;fill:#000000;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 10.250537,66.995394 -0.0047,0.0047 c -0.02767,-0.0066 -0.211799,-0.04039 -0.4563028,0.204116 C 9.6572915,67.468749 9.6572915,67.468749 9.5249999,67.49562 9.3927082,67.468749 9.3927082,67.468749 9.2604165,67.204166 9.061979,67.005731 8.8982935,66.990872 8.8289184,66.99591 c -0.023124,0.0016 -0.035657,0.0057 -0.035657,0.0057 0,0 -0.062009,0.20257 0.2025716,0.467154 0.2645833,0.132291 0.2645841,0.132291 0.291455,0.264583 -0.026871,0.132292 -0.026872,0.132292 -0.291455,0.264583 -0.2445017,0.244505 -0.2107512,0.428639 -0.2041218,0.456304 l -0.00465,0.0047 c 0,0 0.00465,2.65e-4 0.00517,5.29e-4 1.736e-4,5.3e-4 0.00103,0.0057 0.00103,0.0057 l 0.00465,-0.0047 c 0.027721,0.0066 0.2118445,0.04033 0.4563028,-0.204123 0.1322919,-0.264584 0.1322919,-0.264584 0.2645833,-0.291455 0.1322919,0.02687 0.1322917,0.02687 0.2645833,0.291455 0.1984378,0.198435 0.3621234,0.213294 0.4314984,0.208256 0.02312,-0.0016 0.03566,-0.0057 0.03566,-0.0057 0,0 0.06201,-0.20257 -0.202572,-0.467156 -0.2645836,-0.132289 -0.2645839,-0.132291 -0.291455,-0.264583 0.026871,-0.132279 0.026873,-0.132292 0.291455,-0.264583 0.244502,-0.244505 0.210752,-0.428639 0.204122,-0.456304 l 0.0047,-0.0047 c 0,0 -0.0046,-7.94e-4 -0.0052,-0.0011 -1.47e-4,-5.29e-4 -10e-4,-0.0052 -10e-4,-0.0052 z" />
</mask>
<linearGradient
id="Main-6">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop2082-3" />
</linearGradient>
<linearGradient
id="Green"
gradientTransform="scale(3.7795276)">
<stop
style="stop-color:#66ff7a;stop-opacity:1;"
offset="0"
id="stop2506" />
</linearGradient>
<mask
maskUnits="userSpaceOnUse"
id="mask-powermask-path-effect1741">
<path
id="path1739"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#000000;fill-opacity:0.992157;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;stop-color:#000000;stop-opacity:1"
d="m 20.095414,60.589583 a 0.52916663,0.52916663 0 0 0 -0.516248,0.529166 0.52916663,0.52916663 0 0 0 0.529167,0.529167 0.52916663,0.52916663 0 0 0 0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.529167,-0.529166 0.52916663,0.52916663 0 0 0 -0.01292,0 z m -1.058333,2.116666 A 0.52916663,0.52916663 0 0 0 18.520833,63.235416 0.52916663,0.52916663 0 0 0 19.05,63.764583 0.52916663,0.52916663 0 0 0 19.579166,63.235416 0.52916663,0.52916663 0 0 0 19.05,62.706249 a 0.52916663,0.52916663 0 0 0 -0.01292,0 z m 2.116666,0 a 0.52916663,0.52916663 0 0 0 -0.516247,0.529167 0.52916663,0.52916663 0 0 0 0.529166,0.529167 0.52916663,0.52916663 0 0 0 0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.529167,-0.529167 0.52916663,0.52916663 0 0 0 -0.01292,0 z" />
</mask>
<linearGradient
xlink:href="#Main-6"
id="linearGradient2356"
x1="24.077084"
y1="15.345833"
x2="26.722918"
y2="15.345833"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2358"
x1="23.283335"
y1="14.022916"
x2="27.516666"
y2="14.022916"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#Main-6"
id="linearGradient2360"
x1="24.606251"
y1="13.49375"
x2="26.19375"
y2="13.49375"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="clothing"
style="display:inline"
transform="matrix(3.7795278,0,0,3.7795278,-88.000008,-48.000002)">
<path
id="rect923"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2356);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 24.077084,14.022917 v 2.645833 h 2.645833 V 14.022917 H 26.458334 V 15.875 h -2.116667 v -1.852083 z m 0.264583,2.116666 h 2.116667 v 0.264584 h -2.116667 z" />
<path
id="rect939"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2358);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 24.232113,12.964583 -0.948779,0.94878 V 14.2875 l 0.79375,0.79375 0.187068,-0.187069 -0.716235,-0.716235 v -0.155029 l 0.79375,-0.79375 h 2.116667 l 0.79375,0.79375 v 0.155029 l -0.716235,0.716235 0.187068,0.187069 0.79375,-0.79375 v -0.374137 l -0.948779,-0.94878 z" />
<path
id="rect941"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient2360);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 24.606251,13.229167 c 0,0.293158 0.236008,0.529166 0.529166,0.529166 h 0.529167 c 0.293159,0 0.529167,-0.236008 0.529167,-0.529166 -0.264584,0 -0.264584,0 -0.264584,0 0,0.14658 -0.118004,0.264583 -0.264583,0.264583 h -0.529167 c -0.146579,0 -0.264583,-0.118003 -0.264583,-0.264583 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save