1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-21 08:53:52 +00:00

Merge branch openmw:master into mwdialogue-bindings

This commit is contained in:
trav 2024-04-20 12:27:47 +00:00
commit 807d78a0d0
394 changed files with 21501 additions and 2099 deletions

View file

@ -225,6 +225,7 @@
Feature #7875: Disable MyGUI windows snapping Feature #7875: Disable MyGUI windows snapping
Feature #7914: Do not allow to move GUI windows out of screen 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 #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 #5896: Do not use deprecated MyGUI properties
Task #6085: Replace boost::filesystem with std::filesystem Task #6085: Replace boost::filesystem with std::filesystem
Task #6149: Dehardcode Lua API_REVISION Task #6149: Dehardcode Lua API_REVISION

View file

@ -347,6 +347,16 @@ add_qt_style_dlls() {
QT_STYLES[$CONFIG]="${QT_STYLES[$CONFIG]} $@" 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 if [ -z $PLATFORM ]; then
PLATFORM="$(uname -m)" PLATFORM="$(uname -m)"
fi fi
@ -913,12 +923,13 @@ printf "Qt ${QT_VER}... "
DLLSUFFIX="" DLLSUFFIX=""
fi fi
if [ "${QT_VER:0:1}" -eq "6" ]; then 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 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 fi
add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll" add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll"
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${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 done
echo Done. echo Done.
} }
@ -1112,6 +1123,13 @@ fi
echo " $(basename $DLL)" echo " $(basename $DLL)"
cp "$DLL" "${DLL_PREFIX}styles" cp "$DLL" "${DLL_PREFIX}styles"
done 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 echo
done done
#fi #fi

View file

@ -36,7 +36,7 @@ declare -rA GROUPED_DEPS=(
libsdl2-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libopenal-dev libsdl2-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libopenal-dev
libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-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 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. # These dependencies can alternatively be built and linked statically.

View file

@ -234,7 +234,7 @@ else()
endif(APPLE) endif(APPLE)
if (WIN32) 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() endif()
if(MSVC) if(MSVC)
@ -249,9 +249,9 @@ find_package(LZ4 REQUIRED)
if (USE_QT) if (USE_QT)
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5) find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5)
if (QT_VERSION_MAJOR VERSION_EQUAL 5) 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() 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() endif()
message(STATUS "Using Qt${QT_VERSION}") message(STATUS "Using Qt${QT_VERSION}")
endif() endif()
@ -710,9 +710,8 @@ if (WIN32)
if (USE_DEBUG_CONSOLE AND BUILD_OPENMW) if (USE_DEBUG_CONSOLE AND BUILD_OPENMW)
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") 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 LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_CONSOLE>)
elseif (BUILD_OPENMW) 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_DEBUG "/SUBSYSTEM:WINDOWS")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS") set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
endif() 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_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("${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) 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 () endif ()
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime) install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime)

View file

@ -1,5 +1,6 @@
#include "labels.hpp" #include "labels.hpp"
#include <components/esm3/dialoguecondition.hpp>
#include <components/esm3/loadalch.hpp> #include <components/esm3/loadalch.hpp>
#include <components/esm3/loadbody.hpp> #include <components/esm3/loadbody.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
@ -572,13 +573,14 @@ std::string_view enchantTypeLabel(int idx)
std::string_view ruleFunction(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[] = { static constexpr std::string_view ruleFunctions[] = {
"Reaction Low", "Lowest Faction Reaction",
"Reaction High", "Highest Faction Reaction",
"Rank Requirement", "Rank Requirement",
"NPC? Reputation", "NPC Reputation",
"Health Percent", "Health Percent",
"Player Reputation", "Player Reputation",
"NPC Level", "NPC Level",
@ -648,6 +650,7 @@ std::string_view ruleFunction(int idx)
"Flee", "Flee",
"Should Attack", "Should Attack",
"Werewolf", "Werewolf",
"Werewolf Kills",
}; };
return ruleFunctions[idx]; return ruleFunctions[idx];
} }

View file

@ -57,112 +57,82 @@ namespace
std::cout << " Cell Name: " << p.mCellName << std::endl; 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) switch (ss.mFunction)
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)
{ {
case '1': case ESM::DialogueCondition::Function_Global:
type_str = "Function"; type_str = "Global";
func_str = std::string(ruleFunction(func)); func_str = ss.mVariable;
break; break;
case '2': case ESM::DialogueCondition::Function_Local:
if (indicator == 's') type_str = "Local";
type_str = "Global short"; func_str = ss.mVariable;
else if (indicator == 'l')
type_str = "Global long";
else if (indicator == 'f')
type_str = "Global float";
break; break;
case '3': case ESM::DialogueCondition::Function_Journal:
if (indicator == 's') type_str = "Journal";
type_str = "Local short"; func_str = ss.mVariable;
else if (indicator == 'l')
type_str = "Local long";
else if (indicator == 'f')
type_str = "Local float";
break; break;
case '4': case ESM::DialogueCondition::Function_Item:
if (indicator == 'J') type_str = "Item count";
type_str = "Journal"; func_str = ss.mVariable;
break; break;
case '5': case ESM::DialogueCondition::Function_Dead:
if (indicator == 'I') type_str = "Dead";
type_str = "Item type"; func_str = ss.mVariable;
break; break;
case '6': case ESM::DialogueCondition::Function_NotId:
if (indicator == 'D') type_str = "Not ID";
type_str = "NPC Dead"; func_str = ss.mVariable;
break; break;
case '7': case ESM::DialogueCondition::Function_NotFaction:
if (indicator == 'X') type_str = "Not Faction";
type_str = "Not ID"; func_str = ss.mVariable;
break; break;
case '8': case ESM::DialogueCondition::Function_NotClass:
if (indicator == 'F') type_str = "Not Class";
type_str = "Not Faction"; func_str = ss.mVariable;
break; break;
case '9': case ESM::DialogueCondition::Function_NotRace:
if (indicator == 'C') type_str = "Not Race";
type_str = "Not Class"; func_str = ss.mVariable;
break; break;
case 'A': case ESM::DialogueCondition::Function_NotCell:
if (indicator == 'R') type_str = "Not Cell";
type_str = "Not Race"; func_str = ss.mVariable;
break; break;
case 'B': case ESM::DialogueCondition::Function_NotLocal:
if (indicator == 'L') type_str = "Not Local";
type_str = "Not Cell"; func_str = ss.mVariable;
break;
case 'C':
if (indicator == 's')
type_str = "Not Local";
break; break;
default: default:
type_str = "Function";
func_str = ruleFunction(ss.mFunction);
break; break;
} }
// Append the variable name to the function string if any. std::string_view oper_str = "??";
if (type != '1') switch (ss.mComparison)
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)
{ {
case '0': case ESM::DialogueCondition::Comp_Eq:
oper_str = "=="; oper_str = "==";
break; break;
case '1': case ESM::DialogueCondition::Comp_Ne:
oper_str = "!="; oper_str = "!=";
break; break;
case '2': case ESM::DialogueCondition::Comp_Gt:
oper_str = "> "; oper_str = "> ";
break; break;
case '3': case ESM::DialogueCondition::Comp_Ge:
oper_str = ">="; oper_str = ">=";
break; break;
case '4': case ESM::DialogueCondition::Comp_Ls:
oper_str = "< "; oper_str = "< ";
break; break;
case '5': case ESM::DialogueCondition::Comp_Le:
oper_str = "<="; oper_str = "<=";
break; break;
default: default:
@ -170,7 +140,7 @@ namespace
} }
std::ostringstream stream; std::ostringstream stream;
stream << ss.mValue; std::visit([&](auto value) { stream << value; }, ss.mValue);
std::string result std::string result
= Misc::StringUtils::format("%-12s %-32s %2s %s", type_str, func_str, oper_str, stream.str()); = Misc::StringUtils::format("%-12s %-32s %2s %s", type_str, func_str, oper_str, stream.str());
@ -842,7 +812,7 @@ namespace EsmTool
<< std::endl; << std::endl;
std::cout << " Type: " << dialogTypeLabel(mData.mData.mType) << 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; std::cout << " Select Rule: " << ruleString(rule) << std::endl;
if (!mData.mResultScript.empty()) if (!mData.mResultScript.empty())

View file

@ -250,9 +250,9 @@ target_link_libraries(openmw-cs-lib
) )
if (QT_VERSION_MAJOR VERSION_EQUAL 6) 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() 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() endif()
if (WIN32) if (WIN32)

View file

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

View file

@ -171,10 +171,9 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
// Check info conditions // Check info conditions
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator it = topicInfo.mSelects.begin(); for (const auto& select : topicInfo.mSelects)
it != topicInfo.mSelects.end(); ++it)
{ {
verifySelectStruct((*it), id, messages); verifySelectStruct(select, id, messages);
} }
} }
@ -308,49 +307,15 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(
} }
bool CSMTools::TopicInfoCheckStage::verifySelectStruct( 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); 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); messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error);
return false; 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()) else if (infoCondition.conditionIsAlwaysTrue())
{ {
messages.add( messages.add(
@ -365,48 +330,48 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(
} }
// Id checks // Id checks
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global if (select.mFunction == ESM::DialogueCondition::Function_Global
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mGlobals, id, messages)) && !verifyId(ESM::RefId::stringRefId(select.mVariable), mGlobals, id, messages))
{ {
return false; return false;
} }
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal else if (select.mFunction == ESM::DialogueCondition::Function_Journal
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mJournals, id, messages)) && !verifyId(ESM::RefId::stringRefId(select.mVariable), mJournals, id, messages))
{ {
return false; return false;
} }
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item else if (select.mFunction == ESM::DialogueCondition::Function_Item
&& !verifyItem(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages)) && !verifyItem(ESM::RefId::stringRefId(select.mVariable), id, messages))
{ {
return false; return false;
} }
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead else if (select.mFunction == ESM::DialogueCondition::Function_Dead
&& !verifyActor(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages)) && !verifyActor(ESM::RefId::stringRefId(select.mVariable), id, messages))
{ {
return false; return false;
} }
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId else if (select.mFunction == ESM::DialogueCondition::Function_NotId
&& !verifyActor(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages)) && !verifyActor(ESM::RefId::stringRefId(select.mVariable), id, messages))
{ {
return false; return false;
} }
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction else if (select.mFunction == ESM::DialogueCondition::Function_NotFaction
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mFactions, id, messages)) && !verifyId(ESM::RefId::stringRefId(select.mVariable), mFactions, id, messages))
{ {
return false; return false;
} }
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass else if (select.mFunction == ESM::DialogueCondition::Function_NotClass
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mClasses, id, messages)) && !verifyId(ESM::RefId::stringRefId(select.mVariable), mClasses, id, messages))
{ {
return false; return false;
} }
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace else if (select.mFunction == ESM::DialogueCondition::Function_NotRace
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mRaces, id, messages)) && !verifyId(ESM::RefId::stringRefId(select.mVariable), mRaces, id, messages))
{ {
return false; return false;
} }
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell else if (select.mFunction == ESM::DialogueCondition::Function_NotCell
&& !verifyCell(infoCondition.getVariableName(), id, messages)) && !verifyCell(select.mVariable, id, messages))
{ {
return false; return false;
} }

View file

@ -84,7 +84,7 @@ namespace CSMTools
const ESM::RefId& name, int rank, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); 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 verifyItem(const ESM::RefId& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifySelectStruct( 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); bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
template <typename T> template <typename T>

File diff suppressed because it is too large Load diff

View file

@ -7,133 +7,13 @@
#include <components/esm3/loadinfo.hpp> #include <components/esm3/loadinfo.hpp>
namespace ESM #include <QVariant>
{
class Variant;
}
namespace CSMWorld 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 class ConstInfoSelectWrapper
{ {
public: 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 enum ComparisonType
{ {
Comparison_Boolean, Comparison_Boolean,
@ -143,25 +23,13 @@ namespace CSMWorld
Comparison_None 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* FunctionEnumStrings[];
static const char* RelationEnumStrings[]; static const char* RelationEnumStrings[];
static const char* ComparisonEnumStrings[];
static std::string convertToString(FunctionName name); ConstInfoSelectWrapper(const ESM::DialogueCondition& select);
static std::string convertToString(RelationType type);
static std::string convertToString(ComparisonType type);
ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select); ESM::DialogueCondition::Function getFunctionName() const;
ESM::DialogueCondition::Comparison getRelationType() const;
FunctionName getFunctionName() const;
RelationType getRelationType() const;
ComparisonType getComparisonType() const; ComparisonType getComparisonType() const;
bool hasVariable() const; bool hasVariable() const;
@ -169,17 +37,12 @@ namespace CSMWorld
bool conditionIsAlwaysTrue() const; bool conditionIsAlwaysTrue() const;
bool conditionIsNeverTrue() const; bool conditionIsNeverTrue() const;
bool variantTypeIsValid() const;
const ESM::Variant& getVariant() const; QVariant getValue() const;
std::string toString() const; std::string toString() const;
protected: protected:
void readRule();
void readFunctionName();
void readRelationType();
void readVariableName();
void updateHasVariable(); void updateHasVariable();
void updateComparisonType(); void updateComparisonType();
@ -207,38 +70,29 @@ namespace CSMWorld
template <typename Type1, typename Type2> template <typename Type1, typename Type2>
bool conditionIsNeverTrue(std::pair<Type1, Type1> conditionRange, std::pair<Type2, Type2> validRange) const; bool conditionIsNeverTrue(std::pair<Type1, Type1> conditionRange, std::pair<Type2, Type2> validRange) const;
FunctionName mFunctionName;
RelationType mRelationType;
ComparisonType mComparisonType; ComparisonType mComparisonType;
bool mHasVariable; bool mHasVariable;
std::string mVariableName;
private: 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 class InfoSelectWrapper : public ConstInfoSelectWrapper
{ {
public: public:
InfoSelectWrapper(ESM::DialInfo::SelectStruct& select); InfoSelectWrapper(ESM::DialogueCondition& select);
// Wrapped SelectStruct will not be modified until update() is called // Wrapped SelectStruct will not be modified until update() is called
void setFunctionName(FunctionName name); void setFunctionName(ESM::DialogueCondition::Function name);
void setRelationType(RelationType type); void setRelationType(ESM::DialogueCondition::Comparison type);
void setVariableName(const std::string& name); void setVariableName(const std::string& name);
void setValue(int value);
// Modified wrapped SelectStruct void setValue(float value);
void update();
// This sets properties based on the function name to its defaults and updates the wrapped object
void setDefaults();
ESM::Variant& getVariant();
private: private:
ESM::DialInfo::SelectStruct& mSelect; ESM::DialogueCondition& mSelect;
void writeRule(); void writeRule();
}; };

View file

@ -538,13 +538,11 @@ namespace CSMWorld
{ {
Info info = record.get(); Info info = record.get();
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects; auto& conditions = info.mSelects;
// default row // default row
ESM::DialInfo::SelectStruct condStruct; ESM::DialogueCondition condStruct;
condStruct.mSelectRule = "01000"; condStruct.mIndex = conditions.size();
condStruct.mValue = ESM::Variant();
condStruct.mValue.setType(ESM::VT_Int);
conditions.insert(conditions.begin() + position, condStruct); conditions.insert(conditions.begin() + position, condStruct);
@ -555,7 +553,7 @@ namespace CSMWorld
{ {
Info info = record.get(); 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())) if (rowToRemove < 0 || rowToRemove >= static_cast<int>(conditions.size()))
throw std::runtime_error("index out of range"); throw std::runtime_error("index out of range");
@ -569,8 +567,8 @@ namespace CSMWorld
{ {
Info info = record.get(); Info info = record.get();
info.mSelects = static_cast<const NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct>>&>(nestedTable) info.mSelects
.mNestedTable; = static_cast<const NestedTableWrapper<std::vector<ESM::DialogueCondition>>&>(nestedTable).mNestedTable;
record.setModified(info); record.setModified(info);
} }
@ -578,14 +576,14 @@ namespace CSMWorld
NestedTableWrapperBase* InfoConditionAdapter::table(const Record<Info>& record) const NestedTableWrapperBase* InfoConditionAdapter::table(const Record<Info>& record) const
{ {
// deleted by dtor of NestedTableStoring // 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 QVariant InfoConditionAdapter::getData(const Record<Info>& record, int subRowIndex, int subColIndex) const
{ {
Info info = record.get(); 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())) if (subRowIndex < 0 || subRowIndex >= static_cast<int>(conditions.size()))
throw std::runtime_error("index out of range"); throw std::runtime_error("index out of range");
@ -607,23 +605,11 @@ namespace CSMWorld
} }
case 2: case 2:
{ {
return infoSelectWrapper.getRelationType(); return infoSelectWrapper.getRelationType() - ESM::DialogueCondition::Comp_Eq;
} }
case 3: case 3:
{ {
switch (infoSelectWrapper.getVariant().getType()) return infoSelectWrapper.getValue();
{
case ESM::VT_Int:
{
return infoSelectWrapper.getVariant().getInteger();
}
case ESM::VT_Float:
{
return infoSelectWrapper.getVariant().getFloat();
}
default:
return QVariant();
}
} }
default: default:
throw std::runtime_error("Info condition subcolumn index out of range"); throw std::runtime_error("Info condition subcolumn index out of range");
@ -635,7 +621,7 @@ namespace CSMWorld
{ {
Info info = record.get(); 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())) if (subRowIndex < 0 || subRowIndex >= static_cast<int>(conditions.size()))
throw std::runtime_error("index out of range"); throw std::runtime_error("index out of range");
@ -647,27 +633,18 @@ namespace CSMWorld
{ {
case 0: // Function case 0: // Function
{ {
infoSelectWrapper.setFunctionName(static_cast<ConstInfoSelectWrapper::FunctionName>(value.toInt())); infoSelectWrapper.setFunctionName(static_cast<ESM::DialogueCondition::Function>(value.toInt()));
if (infoSelectWrapper.getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric
&& infoSelectWrapper.getVariant().getType() != ESM::VT_Int)
{
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
}
infoSelectWrapper.update();
break; break;
} }
case 1: // Variable case 1: // Variable
{ {
infoSelectWrapper.setVariableName(value.toString().toUtf8().constData()); infoSelectWrapper.setVariableName(value.toString().toUtf8().constData());
infoSelectWrapper.update();
break; break;
} }
case 2: // Relation case 2: // Relation
{ {
infoSelectWrapper.setRelationType(static_cast<ConstInfoSelectWrapper::RelationType>(value.toInt())); infoSelectWrapper.setRelationType(
infoSelectWrapper.update(); static_cast<ESM::DialogueCondition::Comparison>(value.toInt() + ESM::DialogueCondition::Comp_Eq));
break; break;
} }
case 3: // Value case 3: // Value
@ -679,13 +656,11 @@ namespace CSMWorld
// QVariant seems to have issues converting 0 // QVariant seems to have issues converting 0
if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0) if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0)
{ {
infoSelectWrapper.getVariant().setType(ESM::VT_Int); infoSelectWrapper.setValue(value.toInt());
infoSelectWrapper.getVariant().setInteger(value.toInt());
} }
else if (value.toFloat(&conversionResult) && conversionResult) else if (value.toFloat(&conversionResult) && conversionResult)
{ {
infoSelectWrapper.getVariant().setType(ESM::VT_Float); infoSelectWrapper.setValue(value.toFloat());
infoSelectWrapper.getVariant().setFloat(value.toFloat());
} }
break; break;
} }
@ -694,8 +669,7 @@ namespace CSMWorld
{ {
if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0) if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0)
{ {
infoSelectWrapper.getVariant().setType(ESM::VT_Int); infoSelectWrapper.setValue(value.toInt());
infoSelectWrapper.getVariant().setInteger(value.toInt());
} }
break; break;
} }

View file

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

View file

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

View file

@ -71,30 +71,30 @@ void CSVDoc::View::setupFileMenu()
{ {
QMenu* file = menuBar()->addMenu(tr("File")); 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); 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); 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); 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); connect(save, &QAction::triggered, this, &View::save);
mSave = save; mSave = save;
file->addSeparator(); 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); connect(verify, &QAction::triggered, this, &View::verify);
mVerify = 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); connect(merge, &QAction::triggered, this, &View::merge);
mMerge = 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); connect(loadErrors, &QAction::triggered, this, &View::loadErrorLog);
QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata"); QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata");
@ -102,10 +102,10 @@ void CSVDoc::View::setupFileMenu()
file->addSeparator(); 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); 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(exit, &QAction::triggered, this, &View::exit);
connect(this, &View::exitApplicationRequest, &mViewManager, &ViewManager::exitApplication); connect(this, &View::exitApplicationRequest, &mViewManager, &ViewManager::exitApplication);
@ -140,17 +140,16 @@ void CSVDoc::View::setupEditMenu()
mUndo = mDocument->getUndoStack().createUndoAction(this, tr("Undo")); mUndo = mDocument->getUndoStack().createUndoAction(this, tr("Undo"));
setupShortcut("document-edit-undo", mUndo); setupShortcut("document-edit-undo", mUndo);
connect(mUndo, &QAction::changed, this, &View::undoActionChanged); 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); edit->addAction(mUndo);
mRedo = mDocument->getUndoStack().createRedoAction(this, tr("Redo")); mRedo = mDocument->getUndoStack().createRedoAction(this, tr("Redo"));
connect(mRedo, &QAction::changed, this, &View::redoActionChanged); connect(mRedo, &QAction::changed, this, &View::redoActionChanged);
setupShortcut("document-edit-redo", mRedo); setupShortcut("document-edit-redo", mRedo);
mRedo->setIcon(QIcon(QString::fromStdString(":./menu-redo.png"))); mRedo->setIcon(QIcon(QString::fromStdString(":menu-redo")));
edit->addAction(mRedo); edit->addAction(mRedo);
QAction* userSettings QAction* userSettings = createMenuEntry("Preferences", ":menu-preferences", edit, "document-edit-preferences");
= createMenuEntry("Preferences", ":./menu-preferences.png", edit, "document-edit-preferences");
connect(userSettings, &QAction::triggered, this, &View::editSettingsRequest); connect(userSettings, &QAction::triggered, this, &View::editSettingsRequest);
QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search"); 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")); 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); 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); connect(mShowStatusBar, &QAction::toggled, this, &View::toggleShowStatusBar);
mShowStatusBar->setCheckable(true); mShowStatusBar->setCheckable(true);
mShowStatusBar->setChecked(CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); mShowStatusBar->setChecked(CSMPrefs::get()["Windows"]["show-statusbar"].isTrue());
@ -289,7 +288,7 @@ void CSVDoc::View::setupAssetsMenu()
{ {
QMenu* assets = menuBar()->addMenu(tr("Assets")); 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); connect(reload, &QAction::triggered, &mDocument->getData(), &CSMWorld::Data::assetsChanged);
assets->addSeparator(); assets->addSeparator();
@ -341,9 +340,9 @@ void CSVDoc::View::setupDebugMenu()
QAction* runDebug = debug->addMenu(mGlobalDebugProfileMenu); QAction* runDebug = debug->addMenu(mGlobalDebugProfileMenu);
runDebug->setText(tr("Run OpenMW")); runDebug->setText(tr("Run OpenMW"));
setupShortcut("document-debug-run", runDebug); 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); connect(stopDebug, &QAction::triggered, this, &View::stop);
mStopDebug = stopDebug; mStopDebug = stopDebug;
@ -355,16 +354,16 @@ void CSVDoc::View::setupHelpMenu()
{ {
QMenu* help = menuBar()->addMenu(tr("Help")); 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); 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); 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); 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); connect(aboutQt, &QAction::triggered, this, &View::infoAboutQt);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -46,41 +46,41 @@ QWidget* CSVWorld::IdCompletionDelegate::createEditor(QWidget* parent, const QSt
switch (conditionFunction) switch (conditionFunction)
{ {
case CSMWorld::ConstInfoSelectWrapper::Function_Global: case ESM::DialogueCondition::Function_Global:
{ {
return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_GlobalVariable); 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); 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); return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Referenceable);
} }
case CSMWorld::ConstInfoSelectWrapper::Function_Dead: case ESM::DialogueCondition::Function_Dead:
case CSMWorld::ConstInfoSelectWrapper::Function_NotId: case ESM::DialogueCondition::Function_NotId:
{ {
return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Referenceable); 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); 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); 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); 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); return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_Cell);
} }
case CSMWorld::ConstInfoSelectWrapper::Function_Local: case ESM::DialogueCondition::Function_Local:
case CSMWorld::ConstInfoSelectWrapper::Function_NotLocal: case ESM::DialogueCondition::Function_NotLocal:
{ {
return new CSVWidget::DropLineEdit(display, parent); return new CSVWidget::DropLineEdit(display, parent);
} }

View file

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

View file

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

View file

@ -149,39 +149,36 @@ namespace CSMWorld
Params{ UniversalId(UniversalId::Type_None), UniversalId::Type_None, UniversalId::Class_None, Params{ UniversalId(UniversalId::Type_None), UniversalId::Type_None, UniversalId::Class_None,
UniversalId::ArgumentType_None, "-", "-", ":placeholder" }, UniversalId::ArgumentType_None, "-", "-", ":placeholder" },
Params{ UniversalId(UniversalId::Type_RegionMap), UniversalId::Type_RegionMap, UniversalId::Class_NonRecord, 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, 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, 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, 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, Params{ UniversalId(UniversalId::Type_Activator, "a"), UniversalId::Type_Activator,
UniversalId::Class_RefRecord, UniversalId::ArgumentType_Id, "Activator", "Activator: a", UniversalId::Class_RefRecord, UniversalId::ArgumentType_Id, "Activator", "Activator: a", ":activator" },
":./activator.png" },
Params{ UniversalId(UniversalId::Type_Gmst, "b"), UniversalId::Type_Gmst, UniversalId::Class_Record, 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, 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, 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, Params{ UniversalId(UniversalId::Type_Reference, "e"), UniversalId::Type_Reference,
UniversalId::Class_SubRecord, UniversalId::ArgumentType_Id, "Instance", "Instance: e", UniversalId::Class_SubRecord, UniversalId::ArgumentType_Id, "Instance", "Instance: e", ":instance" },
":./instance.png" },
Params{ UniversalId(UniversalId::Type_Search, 42), UniversalId::Type_Search, UniversalId::Class_Transient, 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, 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, Params{ UniversalId(UniversalId::Type_Reference, ESM::RefId::stringRefId("g")), UniversalId::Type_Reference,
UniversalId::Class_SubRecord, UniversalId::ArgumentType_RefId, "Instance", "Instance: g", UniversalId::Class_SubRecord, UniversalId::ArgumentType_RefId, "Instance", "Instance: g", ":instance" },
":./instance.png" },
Params{ UniversalId(UniversalId::Type_Reference, ESM::RefId::index(ESM::REC_SKIL, 42)), Params{ UniversalId(UniversalId::Type_Reference, ESM::RefId::index(ESM::REC_SKIL, 42)),
UniversalId::Type_Reference, UniversalId::Class_SubRecord, UniversalId::ArgumentType_RefId, "Instance", 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)); INSTANTIATE_TEST_SUITE_P(ValidParams, CSMWorldUniversalIdValidPerTypeTest, ValuesIn(validParams));

View file

@ -30,16 +30,16 @@ namespace
{ {
bool matchesStaticFilters(const MWDialogue::SelectWrapper& select, const MWWorld::Ptr& actor) bool matchesStaticFilters(const MWDialogue::SelectWrapper& select, const MWWorld::Ptr& actor)
{ {
const ESM::RefId selectId = ESM::RefId::stringRefId(select.getName()); const ESM::RefId selectId = select.getId();
if (select.getFunction() == MWDialogue::SelectWrapper::Function_NotId) if (select.getFunction() == ESM::DialogueCondition::Function_NotId)
return actor.getCellRef().getRefId() != selectId; return actor.getCellRef().getRefId() != selectId;
if (actor.getClass().isNpc()) if (actor.getClass().isNpc())
{ {
if (select.getFunction() == MWDialogue::SelectWrapper::Function_NotFaction) if (select.getFunction() == ESM::DialogueCondition::Function_NotFaction)
return actor.getClass().getPrimaryFaction(actor) != selectId; 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; 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 actor.get<ESM::NPC>()->mBase->mRace != selectId;
} }
return true; return true;
@ -47,7 +47,7 @@ namespace
bool matchesStaticFilters(const ESM::DialInfo& info, const MWWorld::Ptr& actor) 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; MWDialogue::SelectWrapper wrapper = select;
if (wrapper.getType() == MWDialogue::SelectWrapper::Type_Boolean) if (wrapper.getType() == MWDialogue::SelectWrapper::Type_Boolean)
@ -62,7 +62,7 @@ namespace
} }
else if (wrapper.getType() == MWDialogue::SelectWrapper::Type_Numeric) 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); const ESM::RefId& scriptName = actor.getClass().getScript(actor);
if (scriptName.empty()) 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 bool MWDialogue::Filter::testSelectStructs(const ESM::DialInfo& info) const
{ {
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter(info.mSelects.begin()); for (const auto& select : info.mSelects)
iter != info.mSelects.end(); ++iter) if (!testSelectStruct(select))
if (!testSelectStruct(*iter))
return false; return false;
return true; 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. // If the actor is a creature, we pass all conditions only applicable to NPCs.
return true; 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. // If not currently in a choice, we reject all conditions that test against choices.
return false; return false;
if (select.getFunction() == SelectWrapper::Function_Weather if (select.getFunction() == ESM::DialogueCondition::Function_Weather
&& !(MWBase::Environment::get().getWorld()->isCellExterior() && !(MWBase::Environment::get().getWorld()->isCellExterior()
|| MWBase::Environment::get().getWorld()->isCellQuasiExterior())) || MWBase::Environment::get().getWorld()->isCellQuasiExterior()))
// Reject weather conditions in interior cells // Reject weather conditions in interior cells
@ -305,29 +304,31 @@ bool MWDialogue::Filter::testSelectStructNumeric(const SelectWrapper& select) co
{ {
switch (select.getFunction()) switch (select.getFunction())
{ {
case SelectWrapper::Function_Global: case ESM::DialogueCondition::Function_Global:
// internally all globals are float :( // internally all globals are float :(
return select.selectCompare(MWBase::Environment::get().getWorld()->getGlobalFloat(select.getName())); return select.selectCompare(MWBase::Environment::get().getWorld()->getGlobalFloat(select.getName()));
case SelectWrapper::Function_Local: case ESM::DialogueCondition::Function_Local:
{ {
return testFunctionLocal(select); return testFunctionLocal(select);
} }
case SelectWrapper::Function_NotLocal: case ESM::DialogueCondition::Function_NotLocal:
{ {
return !testFunctionLocal(select); return !testFunctionLocal(select);
} }
case SelectWrapper::Function_PcHealthPercent: case ESM::DialogueCondition::Function_PcHealthPercent:
{ {
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
return select.selectCompare( return select.selectCompare(
static_cast<int>(player.getClass().getCreatureStats(player).getHealth().getRatio() * 100)); 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(); MWWorld::Ptr player = MWMechanics::getPlayer();
@ -336,7 +337,7 @@ bool MWDialogue::Filter::testSelectStructNumeric(const SelectWrapper& select) co
return select.selectCompare(value); return select.selectCompare(value);
} }
case SelectWrapper::Function_HealthPercent: case ESM::DialogueCondition::Function_Health_Percent:
{ {
return select.selectCompare( return select.selectCompare(
static_cast<int>(mActor.getClass().getCreatureStats(mActor).getHealth().getRatio() * 100)); 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()) 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); 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( return MWBase::Environment::get().getMechanicsManager()->countDeaths(select.getId());
ESM::RefId::stringRefId(select.getName()));
case SelectWrapper::Function_Choice: case ESM::DialogueCondition::Function_Choice:
return mChoice; 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(); int argument = select.getArgument();
if (argument < 0 || argument > 3) if (argument < 0 || argument > 3)
@ -387,32 +390,65 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
.getAiSetting(static_cast<MWMechanics::AiSetting>(argument)) .getAiSetting(static_cast<MWMechanics::AiSetting>(argument))
.getModified(false); .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()); ESM::RefId attribute = ESM::Attribute::indexToRefId(select.getArgument());
return player.getClass().getCreatureStats(player).getAttribute(attribute).getModified(); 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()); ESM::RefId skill = ESM::Skill::indexToRefId(select.getArgument());
return static_cast<int>(player.getClass().getNpcStats(player).getSkill(skill).getModified()); 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(); int hits = mActor.getClass().getCreatureStats(mActor).getFriendlyHits();
return hits > 4 ? 4 : hits; return hits > 4 ? 4 : hits;
} }
case SelectWrapper::Function_PcLevel: case ESM::DialogueCondition::Function_PcLevel:
return player.getClass().getCreatureStats(player).getLevel(); return player.getClass().getCreatureStats(player).getLevel();
case SelectWrapper::Function_PcGender: case ESM::DialogueCondition::Function_PcGender:
return player.get<ESM::NPC>()->mBase->isMale() ? 0 : 1; 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); const MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
@ -429,11 +465,11 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
return value; return value;
} }
case SelectWrapper::Function_PcCrimeLevel: case ESM::DialogueCondition::Function_PcCrimeLevel:
return player.getClass().getNpcStats(player).getBounty(); return player.getClass().getNpcStats(player).getBounty();
case SelectWrapper::Function_RankRequirement: case ESM::DialogueCondition::Function_RankRequirement:
{ {
const ESM::RefId& faction = mActor.getClass().getPrimaryFaction(mActor); const ESM::RefId& faction = mActor.getClass().getPrimaryFaction(mActor);
if (faction.empty()) if (faction.empty())
@ -455,23 +491,23 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
return result; return result;
} }
case SelectWrapper::Function_Level: case ESM::DialogueCondition::Function_Level:
return mActor.getClass().getCreatureStats(mActor).getLevel(); return mActor.getClass().getCreatureStats(mActor).getLevel();
case SelectWrapper::Function_PCReputation: case ESM::DialogueCondition::Function_PcReputation:
return player.getClass().getNpcStats(player).getReputation(); return player.getClass().getNpcStats(player).getReputation();
case SelectWrapper::Function_Weather: case ESM::DialogueCondition::Function_Weather:
return MWBase::Environment::get().getWorld()->getCurrentWeather(); return MWBase::Environment::get().getWorld()->getCurrentWeather();
case SelectWrapper::Function_Reputation: case ESM::DialogueCondition::Function_Reputation:
return mActor.getClass().getNpcStats(mActor).getReputation(); return mActor.getClass().getNpcStats(mActor).getReputation();
case SelectWrapper::Function_FactionRankDiff: case ESM::DialogueCondition::Function_FactionRankDifference:
{ {
const ESM::RefId& faction = mActor.getClass().getPrimaryFaction(mActor); const ESM::RefId& faction = mActor.getClass().getPrimaryFaction(mActor);
@ -483,14 +519,14 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
return rank - npcRank; return rank - npcRank;
} }
case SelectWrapper::Function_WerewolfKills: case ESM::DialogueCondition::Function_PcWerewolfKills:
return player.getClass().getNpcStats(player).getWerewolfKills(); return player.getClass().getNpcStats(player).getWerewolfKills();
case SelectWrapper::Function_RankLow: case ESM::DialogueCondition::Function_FacReactionLowest:
case SelectWrapper::Function_RankHigh: 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); const ESM::RefId& factionId = mActor.getClass().getPrimaryFaction(mActor);
@ -513,7 +549,7 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
return value; return value;
} }
case SelectWrapper::Function_CreatureTargetted: case ESM::DialogueCondition::Function_CreatureTarget:
{ {
MWWorld::Ptr target; MWWorld::Ptr target;
@ -540,53 +576,49 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con
switch (select.getFunction()) switch (select.getFunction())
{ {
case SelectWrapper::Function_False: case ESM::DialogueCondition::Function_NotId:
return false; return mActor.getCellRef().getRefId() != select.getId();
case SelectWrapper::Function_NotId: case ESM::DialogueCondition::Function_NotFaction:
return !(mActor.getCellRef().getRefId() == ESM::RefId::stringRefId(select.getName())); return mActor.getClass().getPrimaryFaction(mActor) != select.getId();
case SelectWrapper::Function_NotFaction: case ESM::DialogueCondition::Function_NotClass:
return !(mActor.getClass().getPrimaryFaction(mActor) == ESM::RefId::stringRefId(select.getName())); return mActor.get<ESM::NPC>()->mBase->mClass != select.getId();
case SelectWrapper::Function_NotClass: case ESM::DialogueCondition::Function_NotRace:
return !(mActor.get<ESM::NPC>()->mBase->mClass == ESM::RefId::stringRefId(select.getName())); return mActor.get<ESM::NPC>()->mBase->mRace != select.getId();
case SelectWrapper::Function_NotRace: case ESM::DialogueCondition::Function_NotCell:
return !(mActor.get<ESM::NPC>()->mBase->mRace == ESM::RefId::stringRefId(select.getName()));
case SelectWrapper::Function_NotCell:
{ {
std::string_view actorCell = MWBase::Environment::get().getWorld()->getCellName(mActor.getCell()); 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) return (player.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female)
== (mActor.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; 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)); 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(); return player.getClass().getCreatureStats(player).hasCommonDisease();
case SelectWrapper::Function_PcBlightDisease: case ESM::DialogueCondition::Function_PcBlightDisease:
return player.getClass().getCreatureStats(player).hasBlightDisease(); return player.getClass().getCreatureStats(player).hasBlightDisease();
case SelectWrapper::Function_PcCorprus: case ESM::DialogueCondition::Function_PcCorprus:
return player.getClass() return player.getClass()
.getCreatureStats(player) .getCreatureStats(player)
@ -595,7 +627,7 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con
.getMagnitude() .getMagnitude()
!= 0; != 0;
case SelectWrapper::Function_PcExpelled: case ESM::DialogueCondition::Function_PcExpelled:
{ {
const ESM::RefId& faction = mActor.getClass().getPrimaryFaction(mActor); 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); return player.getClass().getNpcStats(player).getExpelled(faction);
} }
case SelectWrapper::Function_PcVampire: case ESM::DialogueCondition::Function_PcVampire:
return player.getClass() return player.getClass()
.getCreatureStats(player) .getCreatureStats(player)
@ -614,27 +646,27 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con
.getMagnitude() .getMagnitude()
> 0; > 0;
case SelectWrapper::Function_TalkedToPc: case ESM::DialogueCondition::Function_TalkedToPc:
return mTalkedToPlayer; return mTalkedToPlayer;
case SelectWrapper::Function_Alarmed: case ESM::DialogueCondition::Function_Alarmed:
return mActor.getClass().getCreatureStats(mActor).isAlarmed(); return mActor.getClass().getCreatureStats(mActor).isAlarmed();
case SelectWrapper::Function_Detected: case ESM::DialogueCondition::Function_Detected:
return MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, mActor); return MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, mActor);
case SelectWrapper::Function_Attacked: case ESM::DialogueCondition::Function_Attacked:
return mActor.getClass().getCreatureStats(mActor).getAttacked(); return mActor.getClass().getCreatureStats(mActor).getAttacked();
case SelectWrapper::Function_ShouldAttack: case ESM::DialogueCondition::Function_ShouldAttack:
return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor, MWMechanics::getPlayer()); return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor, MWMechanics::getPlayer());
case SelectWrapper::Function_Werewolf: case ESM::DialogueCondition::Function_Werewolf:
return mActor.getClass().getNpcStats(mActor).isWerewolf(); return mActor.getClass().getNpcStats(mActor).isWerewolf();

View file

@ -10,431 +10,264 @@
namespace namespace
{ {
template <typename T1, typename T2> 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) switch (comp)
{ {
case '0': case ESM::DialogueCondition::Comp_Eq:
return value1 == value2; return value1 == value2;
case '1': case ESM::DialogueCondition::Comp_Ne:
return value1 != value2; return value1 != value2;
case '2': case ESM::DialogueCondition::Comp_Gt:
return value1 > value2; return value1 > value2;
case '3': case ESM::DialogueCondition::Comp_Ge:
return value1 >= value2; return value1 >= value2;
case '4': case ESM::DialogueCondition::Comp_Ls:
return value1 < value2; return value1 < value2;
case '5': case ESM::DialogueCondition::Comp_Le:
return value1 <= value2; 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> template <typename T>
bool selectCompareImp(const ESM::DialInfo::SelectStruct& select, T value1) bool selectCompareImp(const ESM::DialogueCondition& select, T value1)
{ {
if (select.mValue.getType() == ESM::VT_Int) return std::visit(
{ [&](auto value) { return selectCompareImp(select.mComparison, value1, value); }, select.mValue);
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 MWDialogue::SelectWrapper::SelectWrapper(const ESM::DialogueCondition& select)
{
const int index = Misc::StringUtils::toNumeric<int>(mSelect.mSelectRule.substr(2, 2), 0);
switch (index)
{
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 Function_False;
}
MWDialogue::SelectWrapper::SelectWrapper(const ESM::DialInfo::SelectStruct& select)
: mSelect(select) : mSelect(select)
{ {
} }
MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::getFunction() const ESM::DialogueCondition::Function MWDialogue::SelectWrapper::getFunction() const
{ {
char type = mSelect.mSelectRule[1]; return mSelect.mFunction;
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;
} }
int MWDialogue::SelectWrapper::getArgument() const int MWDialogue::SelectWrapper::getArgument() const
{ {
if (mSelect.mSelectRule[1] != '1') switch (mSelect.mFunction)
return 0;
int index = 0;
std::istringstream(mSelect.mSelectRule.substr(2, 2)) >> index;
switch (index)
{ {
// AI settings // AI settings
case 67: case ESM::DialogueCondition::Function_Fight:
return 1; return 1;
case 68: case ESM::DialogueCondition::Function_Hello:
return 0; return 0;
case 69: case ESM::DialogueCondition::Function_Alarm:
return 3; return 3;
case 70: case ESM::DialogueCondition::Function_Flee:
return 2; return 2;
// attributes // attributes
case 10: case ESM::DialogueCondition::Function_PcStrength:
return 0; return 0;
case 51: case ESM::DialogueCondition::Function_PcIntelligence:
return 1; return 1;
case 52: case ESM::DialogueCondition::Function_PcWillpower:
return 2; return 2;
case 53: case ESM::DialogueCondition::Function_PcAgility:
return 3; return 3;
case 54: case ESM::DialogueCondition::Function_PcSpeed:
return 4; return 4;
case 55: case ESM::DialogueCondition::Function_PcEndurance:
return 5; return 5;
case 56: case ESM::DialogueCondition::Function_PcPersonality:
return 6; return 6;
case 57: case ESM::DialogueCondition::Function_PcLuck:
return 7; return 7;
// skills // skills
case 11: case ESM::DialogueCondition::Function_PcBlock:
return 0; return 0;
case 12: case ESM::DialogueCondition::Function_PcArmorer:
return 1; return 1;
case 13: case ESM::DialogueCondition::Function_PcMediumArmor:
return 2; return 2;
case 14: case ESM::DialogueCondition::Function_PcHeavyArmor:
return 3; return 3;
case 15: case ESM::DialogueCondition::Function_PcBluntWeapon:
return 4; return 4;
case 16: case ESM::DialogueCondition::Function_PcLongBlade:
return 5; return 5;
case 17: case ESM::DialogueCondition::Function_PcAxe:
return 6; return 6;
case 18: case ESM::DialogueCondition::Function_PcSpear:
return 7; return 7;
case 19: case ESM::DialogueCondition::Function_PcAthletics:
return 8; return 8;
case 20: case ESM::DialogueCondition::Function_PcEnchant:
return 9; return 9;
case 21: case ESM::DialogueCondition::Function_PcDestruction:
return 10; return 10;
case 22: case ESM::DialogueCondition::Function_PcAlteration:
return 11; return 11;
case 23: case ESM::DialogueCondition::Function_PcIllusion:
return 12; return 12;
case 24: case ESM::DialogueCondition::Function_PcConjuration:
return 13; return 13;
case 25: case ESM::DialogueCondition::Function_PcMysticism:
return 14; return 14;
case 26: case ESM::DialogueCondition::Function_PcRestoration:
return 15; return 15;
case 27: case ESM::DialogueCondition::Function_PcAlchemy:
return 16; return 16;
case 28: case ESM::DialogueCondition::Function_PcUnarmored:
return 17; return 17;
case 29: case ESM::DialogueCondition::Function_PcSecurity:
return 18; return 18;
case 30: case ESM::DialogueCondition::Function_PcSneak:
return 19; return 19;
case 31: case ESM::DialogueCondition::Function_PcAcrobatics:
return 20; return 20;
case 32: case ESM::DialogueCondition::Function_PcLightArmor:
return 21; return 21;
case 33: case ESM::DialogueCondition::Function_PcShortBlade:
return 22; return 22;
case 34: case ESM::DialogueCondition::Function_PcMarksman:
return 23; return 23;
case 35: case ESM::DialogueCondition::Function_PcMerchantile:
return 24; return 24;
case 36: case ESM::DialogueCondition::Function_PcSpeechcraft:
return 25; return 25;
case 37: case ESM::DialogueCondition::Function_PcHandToHand:
return 26; return 26;
// dynamic stats // dynamic stats
case 8: case ESM::DialogueCondition::Function_PcMagicka:
return 1; return 1;
case 9: case ESM::DialogueCondition::Function_PcFatigue:
return 2; return 2;
case 64: case ESM::DialogueCondition::Function_PcHealth:
return 0;
default:
return 0; return 0;
} }
return 0;
} }
MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const
{ {
static const Function integerFunctions[] = { switch (mSelect.mFunction)
Function_Journal, {
Function_Item, case ESM::DialogueCondition::Function_Journal:
Function_Dead, case ESM::DialogueCondition::Function_Item:
Function_Choice, case ESM::DialogueCondition::Function_Dead:
Function_AiSetting, case ESM::DialogueCondition::Function_Choice:
Function_PcAttribute, case ESM::DialogueCondition::Function_Fight:
Function_PcSkill, case ESM::DialogueCondition::Function_Hello:
Function_FriendlyHit, case ESM::DialogueCondition::Function_Alarm:
Function_PcLevel, case ESM::DialogueCondition::Function_Flee:
Function_PcGender, case ESM::DialogueCondition::Function_PcStrength:
Function_PcClothingModifier, case ESM::DialogueCondition::Function_PcIntelligence:
Function_PcCrimeLevel, case ESM::DialogueCondition::Function_PcWillpower:
Function_RankRequirement, case ESM::DialogueCondition::Function_PcAgility:
Function_Level, case ESM::DialogueCondition::Function_PcSpeed:
Function_PCReputation, case ESM::DialogueCondition::Function_PcEndurance:
Function_Weather, case ESM::DialogueCondition::Function_PcPersonality:
Function_Reputation, case ESM::DialogueCondition::Function_PcLuck:
Function_FactionRankDiff, case ESM::DialogueCondition::Function_PcBlock:
Function_WerewolfKills, case ESM::DialogueCondition::Function_PcArmorer:
Function_RankLow, case ESM::DialogueCondition::Function_PcMediumArmor:
Function_RankHigh, case ESM::DialogueCondition::Function_PcHeavyArmor:
Function_CreatureTargetted, case ESM::DialogueCondition::Function_PcBluntWeapon:
// end marker case ESM::DialogueCondition::Function_PcLongBlade:
Function_None, case ESM::DialogueCondition::Function_PcAxe:
}; case ESM::DialogueCondition::Function_PcSpear:
case ESM::DialogueCondition::Function_PcAthletics:
static const Function numericFunctions[] = { case ESM::DialogueCondition::Function_PcEnchant:
Function_Global, case ESM::DialogueCondition::Function_PcDestruction:
Function_Local, case ESM::DialogueCondition::Function_PcAlteration:
Function_NotLocal, case ESM::DialogueCondition::Function_PcIllusion:
Function_PcDynamicStat, case ESM::DialogueCondition::Function_PcConjuration:
Function_PcHealthPercent, case ESM::DialogueCondition::Function_PcMysticism:
Function_HealthPercent, case ESM::DialogueCondition::Function_PcRestoration:
// end marker case ESM::DialogueCondition::Function_PcAlchemy:
Function_None, case ESM::DialogueCondition::Function_PcUnarmored:
}; case ESM::DialogueCondition::Function_PcSecurity:
case ESM::DialogueCondition::Function_PcSneak:
static const Function booleanFunctions[] = { case ESM::DialogueCondition::Function_PcAcrobatics:
Function_False, case ESM::DialogueCondition::Function_PcLightArmor:
Function_SameGender, case ESM::DialogueCondition::Function_PcShortBlade:
Function_SameRace, case ESM::DialogueCondition::Function_PcMarksman:
Function_SameFaction, case ESM::DialogueCondition::Function_PcMerchantile:
Function_PcCommonDisease, case ESM::DialogueCondition::Function_PcSpeechcraft:
Function_PcBlightDisease, case ESM::DialogueCondition::Function_PcHandToHand:
Function_PcCorprus, case ESM::DialogueCondition::Function_FriendHit:
Function_PcExpelled, case ESM::DialogueCondition::Function_PcLevel:
Function_PcVampire, case ESM::DialogueCondition::Function_PcGender:
Function_TalkedToPc, case ESM::DialogueCondition::Function_PcClothingModifier:
Function_Alarmed, case ESM::DialogueCondition::Function_PcCrimeLevel:
Function_Detected, case ESM::DialogueCondition::Function_RankRequirement:
Function_Attacked, case ESM::DialogueCondition::Function_Level:
Function_ShouldAttack, case ESM::DialogueCondition::Function_PcReputation:
Function_Werewolf, case ESM::DialogueCondition::Function_Weather:
// end marker case ESM::DialogueCondition::Function_Reputation:
Function_None, case ESM::DialogueCondition::Function_FactionRankDifference:
}; case ESM::DialogueCondition::Function_PcWerewolfKills:
case ESM::DialogueCondition::Function_FacReactionLowest:
static const Function invertedBooleanFunctions[] = { case ESM::DialogueCondition::Function_FacReactionHighest:
Function_NotId, case ESM::DialogueCondition::Function_CreatureTarget:
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)
return Type_Integer; return Type_Integer;
case ESM::DialogueCondition::Function_Global:
for (int i = 0; numericFunctions[i] != Function_None; ++i) case ESM::DialogueCondition::Function_Local:
if (numericFunctions[i] == function) 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; return Type_Numeric;
case ESM::DialogueCondition::Function_SameSex:
for (int i = 0; booleanFunctions[i] != Function_None; ++i) case ESM::DialogueCondition::Function_SameRace:
if (booleanFunctions[i] == function) 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; return Type_Boolean;
case ESM::DialogueCondition::Function_NotId:
for (int i = 0; invertedBooleanFunctions[i] != Function_None; ++i) case ESM::DialogueCondition::Function_NotFaction:
if (invertedBooleanFunctions[i] == function) case ESM::DialogueCondition::Function_NotClass:
case ESM::DialogueCondition::Function_NotRace:
case ESM::DialogueCondition::Function_NotCell:
return Type_Inverted; return Type_Inverted;
default:
return Type_None; return Type_None;
};
} }
bool MWDialogue::SelectWrapper::isNpcOnly() const bool MWDialogue::SelectWrapper::isNpcOnly() const
{ {
static const Function functions[] = { switch (mSelect.mFunction)
Function_NotFaction, {
Function_NotClass, case ESM::DialogueCondition::Function_NotFaction:
Function_NotRace, case ESM::DialogueCondition::Function_NotClass:
Function_SameGender, case ESM::DialogueCondition::Function_NotRace:
Function_SameRace, case ESM::DialogueCondition::Function_SameSex:
Function_SameFaction, case ESM::DialogueCondition::Function_SameRace:
Function_RankRequirement, case ESM::DialogueCondition::Function_SameFaction:
Function_Reputation, case ESM::DialogueCondition::Function_RankRequirement:
Function_FactionRankDiff, case ESM::DialogueCondition::Function_Reputation:
Function_Werewolf, case ESM::DialogueCondition::Function_FactionRankDifference:
Function_WerewolfKills, case ESM::DialogueCondition::Function_Werewolf:
Function_RankLow, case ESM::DialogueCondition::Function_PcWerewolfKills:
Function_RankHigh, case ESM::DialogueCondition::Function_FacReactionLowest:
// end marker case ESM::DialogueCondition::Function_FacReactionHighest:
Function_None,
};
Function function = getFunction();
for (int i = 0; functions[i] != Function_None; ++i)
if (functions[i] == function)
return true; return true;
default:
return false; return false;
}
} }
bool MWDialogue::SelectWrapper::selectCompare(int value) const bool MWDialogue::SelectWrapper::selectCompare(int value) const
@ -454,5 +287,15 @@ bool MWDialogue::SelectWrapper::selectCompare(bool value) const
std::string MWDialogue::SelectWrapper::getName() 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);
} }

View file

@ -7,62 +7,9 @@ namespace MWDialogue
{ {
class SelectWrapper class SelectWrapper
{ {
const ESM::DialInfo::SelectStruct& mSelect; const ESM::DialogueCondition& mSelect;
public: 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 enum Type
{ {
Type_None, Type_None,
@ -72,13 +19,10 @@ namespace MWDialogue
Type_Inverted Type_Inverted
}; };
private:
Function decodeFunction() const;
public: public:
SelectWrapper(const ESM::DialInfo::SelectStruct& select); SelectWrapper(const ESM::DialogueCondition& select);
Function getFunction() const; ESM::DialogueCondition::Function getFunction() const;
int getArgument() const; int getArgument() const;
@ -95,6 +39,10 @@ namespace MWDialogue
std::string getName() const; std::string getName() const;
///< Return case-smashed name. ///< Return case-smashed name.
std::string_view getCellName() const;
ESM::RefId getId() const;
}; };
} }

View file

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

View file

@ -32,14 +32,43 @@ extern "C"
namespace MWSound 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 class FFmpeg_Decoder final : public Sound_Decoder
{ {
AVFormatContext* mFormatCtx; AVIOContextPtr mIoCtx;
AVCodecContext* mCodecCtx; AVFormatContextPtr mFormatCtx;
AVCodecContextPtr mCodecCtx;
AVStream** mStream; AVStream** mStream;
AVPacket mPacket; AVPacket mPacket;
AVFrame* mFrame; AVFramePtr mFrame;
std::size_t mFrameSize; std::size_t mFrameSize;
std::size_t mFramePos; std::size_t mFramePos;

View file

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

View file

@ -591,6 +591,12 @@ namespace MWWorld
// Must be cleared before mRendering is destroyed // Must be cleared before mRendering is destroyed
if (mProjectileManager) if (mProjectileManager)
mProjectileManager->clear(); 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) void World::setRandomSeed(uint32_t seed)

View file

@ -267,7 +267,6 @@ namespace
updater.wait(WaitConditionType::allJobsDone, &mListener); updater.wait(WaitConditionType::allJobsDone, &mListener);
updater.stop(); updater.stop();
const std::set<TilePosition> present{ const std::set<TilePosition> present{
TilePosition(-2, 0),
TilePosition(-1, -1), TilePosition(-1, -1),
TilePosition(-1, 0), TilePosition(-1, 0),
TilePosition(-1, 1), TilePosition(-1, 1),
@ -278,6 +277,7 @@ namespace
TilePosition(0, 2), TilePosition(0, 2),
TilePosition(1, -1), TilePosition(1, -1),
TilePosition(1, 0), TilePosition(1, 0),
TilePosition(1, 1),
}; };
for (int x = -5; x <= 5; ++x) for (int x = -5; x <= 5; ++x)
for (int y = -5; y <= 5; ++y) for (int y = -5; y <= 5; ++y)
@ -336,4 +336,273 @@ namespace
EXPECT_EQ(tile->mTileId, 2); EXPECT_EQ(tile->mTileId, 2);
EXPECT_EQ(tile->mVersion, navMeshFormatVersion); 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);
}
} }

View file

@ -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 struct AddHeightfieldSurface
{ {
static constexpr std::size_t sSize = 65; static constexpr std::size_t sSize = 65;

View file

@ -6,6 +6,7 @@
#include <components/esm3/esmwriter.hpp> #include <components/esm3/esmwriter.hpp>
#include <components/esm3/loadcont.hpp> #include <components/esm3/loadcont.hpp>
#include <components/esm3/loaddial.hpp> #include <components/esm3/loaddial.hpp>
#include <components/esm3/loadinfo.hpp>
#include <components/esm3/loadregn.hpp> #include <components/esm3/loadregn.hpp>
#include <components/esm3/loadscpt.hpp> #include <components/esm3/loadscpt.hpp>
#include <components/esm3/loadweap.hpp> #include <components/esm3/loadweap.hpp>
@ -603,6 +604,83 @@ namespace ESM
EXPECT_EQ(result.mIcon, record.mIcon); 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())); INSTANTIATE_TEST_SUITE_P(FormatVersions, Esm3SaveLoadRecordTest, ValuesIn(getFormats()));
} }
} }

View file

@ -181,7 +181,7 @@ add_component_dir (esm3
inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats
weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
aisequence magiceffects custommarkerstate stolenitems transport animationstate controlsstate mappings readerscache aisequence magiceffects custommarkerstate stolenitems transport animationstate controlsstate mappings readerscache
infoorder timestamp formatversion landrecorddata selectiongroup infoorder timestamp formatversion landrecorddata selectiongroup dialoguecondition
) )
add_component_dir (esmterrain add_component_dir (esmterrain

View file

@ -61,6 +61,11 @@ namespace Debug
bool outRedirected = isRedirected(STD_OUTPUT_HANDLE); bool outRedirected = isRedirected(STD_OUTPUT_HANDLE);
bool errRedirected = isRedirected(STD_ERROR_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)) if (AttachConsole(ATTACH_PARENT_PROCESS))
{ {
fflush(stdout); fflush(stdout);
@ -73,16 +78,19 @@ namespace Debug
{ {
_wfreopen(L"CON", L"r", stdin); _wfreopen(L"CON", L"r", stdin);
freopen("CON", "r", stdin); freopen("CON", "r", stdin);
std::cin.clear();
} }
if (!outRedirected) if (!outRedirected)
{ {
_wfreopen(L"CON", L"w", stdout); _wfreopen(L"CON", L"w", stdout);
freopen("CON", "w", stdout); freopen("CON", "w", stdout);
std::cout.clear();
} }
if (!errRedirected) if (!errRedirected)
{ {
_wfreopen(L"CON", L"w", stderr); _wfreopen(L"CON", L"w", stderr);
freopen("CON", "w", stderr); freopen("CON", "w", stderr);
std::cerr.clear();
} }
return true; return true;
@ -256,9 +264,17 @@ namespace Debug
private: private:
static bool useColoredOutput() static bool useColoredOutput()
{ {
// Note: cmd.exe in Win10 should support ANSI colors, but in its own way.
#if defined(_WIN32) #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 #else
char* term = getenv("TERM"); char* term = getenv("TERM");
bool useColor = term && !getenv("NO_COLOR") && isatty(fileno(stderr)); bool useColor = term && !getenv("NO_COLOR") && isatty(fileno(stderr));

View file

@ -16,8 +16,9 @@
#include <osg/io_utils> #include <osg/io_utils>
#include <boost/geometry.hpp>
#include <algorithm> #include <algorithm>
#include <numeric>
#include <optional> #include <optional>
#include <set> #include <set>
#include <tuple> #include <tuple>
@ -49,40 +50,6 @@ namespace DetourNavigator
return false; 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 auto getAgentAndTile(const Job& job) noexcept
{ {
return std::make_tuple(job.mAgentBounds, job.mChangedTile); return std::make_tuple(job.mAgentBounds, job.mChangedTile);
@ -97,16 +64,6 @@ namespace DetourNavigator
settings.mRecast, settings.mWriteToNavMeshDb); 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() std::size_t getNextJobId()
{ {
static std::atomic_size_t nextJobId{ 1 }; static std::atomic_size_t nextJobId{ 1 };
@ -134,7 +91,7 @@ namespace DetourNavigator
} }
Job::Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem, 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) std::chrono::steady_clock::time_point processTime)
: mId(getNextJobId()) : mId(getNextJobId())
, mAgentBounds(agentBounds) , mAgentBounds(agentBounds)
@ -143,11 +100,148 @@ namespace DetourNavigator
, mChangedTile(changedTile) , mChangedTile(changedTile)
, mProcessTime(processTime) , mProcessTime(processTime)
, mChangeType(changeType) , 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, AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db) OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings) : mSettings(settings)
@ -180,48 +274,47 @@ namespace DetourNavigator
if (!playerTileChanged && changedTiles.empty()) if (!playerTileChanged && changedTiles.empty())
return; return;
const int maxTiles
= std::min(mSettings.get().mMaxTilesNumber, navMeshCacheItem->lockConst()->getImpl().getParams()->maxTiles);
std::unique_lock lock(mMutex); std::unique_lock lock(mMutex);
if (playerTileChanged) 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) for (const auto& [changedTile, changeType] : changedTiles)
{ {
if (mPushed.emplace(agentBounds, changedTile).second) if (mPushed.emplace(agentBounds, changedTile).second)
{ {
const auto processTime = changeType == ChangeType::update const auto processTime = [&, changedTile = changedTile, changeType = changeType] {
? mLastUpdates[std::tie(agentBounds, changedTile)] + mSettings.get().mMinUpdateInterval if (changeType != ChangeType::update)
: std::chrono::steady_clock::time_point(); 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, const JobIt it = mJobs.emplace(
changeType, getManhattanDistance(changedTile, playerTile), processTime); mJobs.end(), agentBounds, navMeshCacheItem, worldspace, changedTile, changeType, processTime);
Log(Debug::Debug) << "Post job " << it->mId << " for agent=(" << it->mAgentBounds << ")" Log(Debug::Debug) << "Post job " << it->mId << " for agent=(" << it->mAgentBounds << ")"
<< " changedTile=(" << it->mChangedTile << ") " << " changedTile=(" << it->mChangedTile << ")"
<< " changeType=" << it->mChangeType; << " changeType=" << it->mChangeType;
if (playerTileChanged) mWaiting.push(it);
mWaiting.push_back(it);
else
insertPrioritizedJob(it, mWaiting);
} }
} }
if (playerTileChanged)
std::sort(mWaiting.begin(), mWaiting.end(), LessByJobPriority{});
Log(Debug::Debug) << "Posted " << mJobs.size() << " navigator jobs"; Log(Debug::Debug) << "Posted " << mJobs.size() << " navigator jobs";
if (!mWaiting.empty()) if (mWaiting.hasJob())
mHasJob.notify_all(); mHasJob.notify_all();
lock.unlock(); lock.unlock();
if (playerTileChanged && mDbWorker != nullptr) if (playerTileChanged && mDbWorker != nullptr)
mDbWorker->updateJobs(playerTile, maxTiles); mDbWorker->update(playerTile);
} }
void AsyncNavMeshUpdater::wait(WaitConditionType waitConditionType, Loading::Listener* listener) void AsyncNavMeshUpdater::wait(WaitConditionType waitConditionType, Loading::Listener* listener)
@ -313,7 +406,7 @@ namespace DetourNavigator
{ {
const std::lock_guard<std::mutex> lock(mMutex); const std::lock_guard<std::mutex> lock(mMutex);
result.mJobs = mJobs.size(); result.mJobs = mJobs.size();
result.mWaiting = mWaiting.size(); result.mWaiting = mWaiting.getStats();
result.mPushed = mPushed.size(); result.mPushed = mPushed.size();
} }
result.mProcessing = mProcessingTiles.lockConst()->size(); result.mProcessing = mProcessingTiles.lockConst()->size();
@ -335,7 +428,8 @@ namespace DetourNavigator
if (JobIt job = getNextJob(); job != mJobs.end()) if (JobIt job = getNextJob(); job != mJobs.end())
{ {
const JobStatus status = processJob(*job); 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) switch (status)
{ {
case JobStatus::Done: case JobStatus::Done:
@ -346,7 +440,8 @@ namespace DetourNavigator
removeJob(job); removeJob(job);
break; break;
case JobStatus::Fail: case JobStatus::Fail:
repost(job); unlockTile(job->mId, job->mAgentBounds, job->mChangedTile);
removeJob(job);
break; break;
case JobStatus::MemoryCacheMiss: case JobStatus::MemoryCacheMiss:
{ {
@ -368,7 +463,9 @@ namespace DetourNavigator
JobStatus AsyncNavMeshUpdater::processJob(Job& job) 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(); const auto navMeshCacheItem = job.mNavMeshCacheItem.lock();
@ -376,12 +473,11 @@ namespace DetourNavigator
return JobStatus::Done; return JobStatus::Done;
const auto playerTile = *mPlayerTile.lockConst(); 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"; Log(Debug::Debug) << "Ignore add tile by job " << job.mId << ": too far from player";
job.mChangeType = ChangeType::remove;
navMeshCacheItem->lock()->removeTile(job.mChangedTile); navMeshCacheItem->lock()->removeTile(job.mChangedTile);
return JobStatus::Done; return JobStatus::Done;
} }
@ -549,9 +645,8 @@ namespace DetourNavigator
bool shouldStop = false; bool shouldStop = false;
const auto hasJob = [&] { const auto hasJob = [&] {
shouldStop = mShouldStop; shouldStop = mShouldStop.load();
return shouldStop return shouldStop || mWaiting.hasJob();
|| (!mWaiting.empty() && mWaiting.front()->mProcessTime <= std::chrono::steady_clock::now());
}; };
if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob)) if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob))
@ -564,9 +659,15 @@ namespace DetourNavigator
if (shouldStop) if (shouldStop)
return mJobs.end(); return mJobs.end();
const JobIt job = mWaiting.front(); const TilePosition playerTile = *mPlayerTile.lockConst();
mWaiting.pop_front(); JobIt job = mJobs.end();
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(); 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)) if (!lockTile(job->mId, job->mAgentBounds, job->mChangedTile))
{ {
Log(Debug::Debug) << "Failed to lock tile by job " << job->mId << " try=" << job->mTryNumber; Log(Debug::Debug) << "Failed to lock tile by job " << job->mId;
++job->mTryNumber; job->mProcessTime = std::chrono::steady_clock::now() + mSettings.get().mMinUpdateInterval;
insertPrioritizedJob(job, mWaiting); mWaiting.push(job);
return mJobs.end(); return mJobs.end();
} }
@ -613,26 +714,6 @@ namespace DetourNavigator
writeToFile(shared->lockConst()->getImpl(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); 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( bool AsyncNavMeshUpdater::lockTile(
std::size_t jobId, const AgentBounds& agentBounds, const TilePosition& changedTile) 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(); Log(Debug::Debug) << "Enqueueing job " << job->mId << " by thread=" << std::this_thread::get_id();
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
insertPrioritizedJob(job, mWaiting); mWaiting.push(job);
mHasJob.notify_all(); mHasJob.notify_all();
} }
@ -691,40 +772,47 @@ namespace DetourNavigator
void DbJobQueue::push(JobIt job) void DbJobQueue::push(JobIt job)
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
insertPrioritizedDbJob(job, mJobs);
if (isWritingDbJob(*job)) if (isWritingDbJob(*job))
++mWritingJobs; mWriting.push_back(job);
else else
++mReadingJobs; mReading.push(job);
mHasJob.notify_all(); mHasJob.notify_all();
} }
std::optional<JobIt> DbJobQueue::pop() std::optional<JobIt> DbJobQueue::pop()
{ {
std::unique_lock lock(mMutex); 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; return std::nullopt;
const JobIt job = mJobs.front();
mJobs.pop_front(); if (const std::optional<JobIt> job = mReading.pop(mPlayerTile))
if (isWritingDbJob(*job)) return job;
--mWritingJobs;
else if (mWriting.empty())
--mReadingJobs; return std::nullopt;
const JobIt job = mWriting.front();
mWriting.pop_front();
return job; return job;
} }
void DbJobQueue::update(TilePosition playerTile, int maxTiles) void DbJobQueue::update(TilePosition playerTile)
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
updateJobs(mJobs, playerTile, maxTiles); mPlayerTile = playerTile;
std::sort(mJobs.begin(), mJobs.end(), LessByJobDbPriority{});
} }
void DbJobQueue::stop() void DbJobQueue::stop()
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
mJobs.clear(); mReading.clear();
mWriting.clear();
mShouldStop = true; mShouldStop = true;
mHasJob.notify_all(); mHasJob.notify_all();
} }
@ -732,7 +820,10 @@ namespace DetourNavigator
DbJobQueueStats DbJobQueue::getStats() const DbJobQueueStats DbJobQueue::getStats() const
{ {
const std::lock_guard lock(mMutex); 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, DbWorker::DbWorker(AsyncNavMeshUpdater& updater, std::unique_ptr<NavMeshDb>&& db, TileVersion version,
@ -761,8 +852,10 @@ namespace DetourNavigator
DbWorkerStats DbWorker::getStats() const DbWorkerStats DbWorker::getStats() const
{ {
return DbWorkerStats{ .mJobs = mQueue.getStats(), return DbWorkerStats{
.mGetTileCount = mGetTileCount.load(std::memory_order_relaxed) }; .mJobs = mQueue.getStats(),
.mGetTileCount = mGetTileCount.load(std::memory_order_relaxed),
};
} }
void DbWorker::stop() void DbWorker::stop()

View file

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

View file

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

View file

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

View file

@ -480,15 +480,6 @@ namespace DetourNavigator
return true; 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( std::pair<float, float> getBoundsByZ(
const RecastMesh& recastMesh, float agentHalfExtentsZ, const RecastSettings& settings) const RecastMesh& recastMesh, float agentHalfExtentsZ, const RecastSettings& settings)
{ {
@ -528,10 +519,7 @@ namespace DetourNavigator
return { minZ, maxZ }; return { minZ, maxZ };
} }
} }
} // namespace DetourNavigator
namespace DetourNavigator
{
std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh,
std::string_view worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds, std::string_view worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds,
const RecastSettings& settings) const RecastSettings& settings)
@ -621,22 +609,12 @@ namespace DetourNavigator
void initEmptyNavMesh(const Settings& settings, dtNavMesh& navMesh) 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; dtNavMeshParams params;
std::fill_n(params.orig, 3, 0.0f); std::fill_n(params.orig, 3, 0.0f);
params.tileWidth = settings.mRecast.mTileSize * settings.mRecast.mCellSize; params.tileWidth = settings.mRecast.mTileSize * settings.mRecast.mCellSize;
params.tileHeight = settings.mRecast.mTileSize * settings.mRecast.mCellSize; params.tileHeight = settings.mRecast.mTileSize * settings.mRecast.mCellSize;
params.maxTiles = 1 << tilesBits; params.maxTiles = settings.mMaxTilesNumber;
params.maxPolys = 1 << polysBits; params.maxPolys = settings.mDetour.mMaxPolys;
const auto status = navMesh.init(&params); const auto status = navMesh.init(&params);

View file

@ -131,6 +131,15 @@ namespace DetourNavigator
function(position, tile.mVersion, *meshTile); 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: private:
struct Tile struct Tile
{ {

View file

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

View file

@ -3,41 +3,81 @@
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
#include <components/settings/values.hpp> #include <components/settings/values.hpp>
#include <algorithm>
#include <stdexcept>
#include <string>
namespace DetourNavigator namespace DetourNavigator
{ {
RecastSettings makeRecastSettingsFromSettingsManager() namespace
{ {
RecastSettings result; struct NavMeshLimits
{
int mMaxTiles;
int mMaxPolys;
};
result.mBorderSize = ::Settings::navigator().mBorderSize; template <class T>
result.mCellHeight = ::Settings::navigator().mCellHeight; unsigned long getMinValuableBitsNumber(const T value)
result.mCellSize = ::Settings::navigator().mCellSize; {
result.mDetailSampleDist = ::Settings::navigator().mDetailSampleDist; unsigned long power = 0;
result.mDetailSampleMaxError = ::Settings::navigator().mDetailSampleMaxError; while (power < sizeof(T) * 8 && (static_cast<T>(1) << power) < value)
result.mMaxClimb = Constants::sStepSizeUp; ++power;
result.mMaxSimplificationError = ::Settings::navigator().mMaxSimplificationError; return power;
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; 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);
DetourSettings makeDetourSettingsFromSettingsManager() if (polysBits >= polysAndTilesBits)
{ throw std::invalid_argument("Too many polygons per tile: " + std::to_string(settings.mMaxPolys));
DetourSettings result;
result.mMaxNavMeshQueryNodes = ::Settings::navigator().mMaxNavMeshQueryNodes; const unsigned long tilesBits = polysAndTilesBits - polysBits;
result.mMaxPolys = ::Settings::navigator().mMaxPolygonsPerTile;
result.mMaxPolygonPathSize = ::Settings::navigator().mMaxPolygonPathSize;
result.mMaxSmoothPathSize = ::Settings::navigator().mMaxSmoothPathSize;
return result; 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() Settings makeSettingsFromSettingsManager()
@ -46,7 +86,12 @@ namespace DetourNavigator
result.mRecast = makeRecastSettingsFromSettingsManager(); result.mRecast = makeRecastSettingsFromSettingsManager();
result.mDetour = makeDetourSettingsFromSettingsManager(); 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.mWaitUntilMinDistanceToPlayer = ::Settings::navigator().mWaitUntilMinDistanceToPlayer;
result.mAsyncNavMeshUpdaterThreads = ::Settings::navigator().mAsyncNavMeshUpdaterThreads; result.mAsyncNavMeshUpdaterThreads = ::Settings::navigator().mAsyncNavMeshUpdaterThreads;
result.mMaxNavMeshTilesCacheSize = ::Settings::navigator().mMaxNavMeshTilesCacheSize; result.mMaxNavMeshTilesCacheSize = ::Settings::navigator().mMaxNavMeshTilesCacheSize;

View file

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

View file

@ -9,16 +9,18 @@ namespace DetourNavigator
void reportStats(const AsyncNavMeshUpdaterStats& stats, unsigned int frameNumber, osg::Stats& out) 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 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 Pushed", static_cast<double>(stats.mPushed));
out.setAttribute(frameNumber, "NavMesh Processing", static_cast<double>(stats.mProcessing)); out.setAttribute(frameNumber, "NavMesh Processing", static_cast<double>(stats.mProcessing));
if (stats.mDb.has_value()) if (stats.mDb.has_value())
{ {
out.setAttribute(
frameNumber, "NavMesh DbJobs Write", static_cast<double>(stats.mDb->mJobs.mWritingJobs));
out.setAttribute( out.setAttribute(
frameNumber, "NavMesh DbJobs Read", static_cast<double>(stats.mDb->mJobs.mReadingJobs)); 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 Get", static_cast<double>(stats.mDb->mGetTileCount));
out.setAttribute(frameNumber, "NavMesh DbCache Hit", static_cast<double>(stats.mDbGetTileHits)); out.setAttribute(frameNumber, "NavMesh DbCache Hit", static_cast<double>(stats.mDbGetTileHits));

View file

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

View file

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

View file

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

View file

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

View file

@ -66,10 +66,9 @@ namespace ESM
break; break;
case fourCC("SCVR"): case fourCC("SCVR"):
{ {
SelectStruct ss; auto filter = DialogueCondition::load(esm, mId);
ss.mSelectRule = esm.getHString(); if (filter)
ss.mValue.read(esm, Variant::Format_Info); mSelects.emplace_back(std::move(*filter));
mSelects.push_back(ss);
break; break;
} }
case fourCC("BNAM"): case fourCC("BNAM"):
@ -120,11 +119,8 @@ namespace ESM
esm.writeHNOCString("SNAM", mSound); esm.writeHNOCString("SNAM", mSound);
esm.writeHNOString("NAME", mResponse); esm.writeHNOString("NAME", mResponse);
for (std::vector<SelectStruct>::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) for (const auto& rule : mSelects)
{ rule.save(esm);
esm.writeHNString("SCVR", it->mSelectRule);
it->mValue.write(esm, Variant::Format_Info);
}
esm.writeHNOString("BNAM", mResultScript); esm.writeHNOString("BNAM", mResultScript);

View file

@ -4,8 +4,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "components/esm/defs.hpp" #include <components/esm/defs.hpp>
#include "components/esm/refid.hpp" #include <components/esm/refid.hpp>
#include "dialoguecondition.hpp"
#include "variant.hpp" #include "variant.hpp"
namespace ESM namespace ESM
@ -47,13 +49,6 @@ namespace ESM
}; // 12 bytes }; // 12 bytes
DATAstruct mData; 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) // Journal quest indices (introduced with the quest system in Tribunal)
enum QuestStatus enum QuestStatus
{ {
@ -65,7 +60,7 @@ namespace ESM
// Rules for when to include this item in the final list of options // Rules for when to include this item in the final list of options
// visible to the player. // visible to the player.
std::vector<SelectStruct> mSelects; std::vector<DialogueCondition> mSelects;
// Id of this, previous and next INFO items // Id of this, previous and next INFO items
RefId mId, mPrev, mNext; RefId mId, mPrev, mNext;

View file

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

View file

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

View file

@ -286,4 +286,125 @@ namespace SceneUtil
mOperationQueue->add(operation); 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);
}
} }

View file

@ -112,6 +112,10 @@ namespace SceneUtil
protected: protected:
osg::ref_ptr<osg::OperationQueue> mOperationQueue; 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 #endif

View file

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

View file

@ -26,6 +26,7 @@
#include <components/sceneutil/morphgeometry.hpp> #include <components/sceneutil/morphgeometry.hpp>
#include <components/sceneutil/riggeometry.hpp> #include <components/sceneutil/riggeometry.hpp>
#include <components/sceneutil/riggeometryosgaextension.hpp> #include <components/sceneutil/riggeometryosgaextension.hpp>
#include <components/sceneutil/util.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/stereo/stereomanager.hpp> #include <components/stereo/stereomanager.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
@ -184,6 +185,7 @@ namespace Shader
, mAdditiveBlending(false) , mAdditiveBlending(false)
, mDiffuseHeight(false) , mDiffuseHeight(false)
, mNormalHeight(false) , mNormalHeight(false)
, mReconstructNormalZ(false)
, mTexStageRequiringTangents(-1) , mTexStageRequiringTangents(-1)
, mSoftParticles(false) , mSoftParticles(false)
, mNode(nullptr) , mNode(nullptr)
@ -429,6 +431,7 @@ namespace Shader
normalMapTex->setFilter(osg::Texture::MAG_FILTER, diffuseMap->getFilter(osg::Texture::MAG_FILTER)); normalMapTex->setFilter(osg::Texture::MAG_FILTER, diffuseMap->getFilter(osg::Texture::MAG_FILTER));
normalMapTex->setMaxAnisotropy(diffuseMap->getMaxAnisotropy()); normalMapTex->setMaxAnisotropy(diffuseMap->getMaxAnisotropy());
normalMapTex->setName("normalMap"); normalMapTex->setName("normalMap");
normalMap = normalMapTex;
int unit = texAttributes.size(); int unit = texAttributes.size();
if (!writableStateSet) if (!writableStateSet)
@ -440,6 +443,21 @@ namespace Shader
mRequirements.back().mNormalHeight = normalHeight; 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)) if (mAutoUseSpecularMaps && diffuseMap != nullptr && specularMap == nullptr && diffuseMap->getImage(0))
{ {
std::string specularMapFileName = diffuseMap->getImage(0)->getFileName(); std::string specularMapFileName = diffuseMap->getImage(0)->getFileName();
@ -629,6 +647,7 @@ namespace Shader
defineMap["diffuseParallax"] = reqs.mDiffuseHeight ? "1" : "0"; defineMap["diffuseParallax"] = reqs.mDiffuseHeight ? "1" : "0";
defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0"; defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0";
defineMap["reconstructNormalZ"] = reqs.mReconstructNormalZ ? "1" : "0";
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode)); writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
addedState->addUniform("colorMode"); addedState->addUniform("colorMode");

View file

@ -110,6 +110,7 @@ namespace Shader
bool mDiffuseHeight; // true if diffuse map has height info in alpha channel 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 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 // -1 == no tangents required
int mTexStageRequiringTangents; int mTexStageRequiringTangents;

View file

@ -10,6 +10,7 @@
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/sceneutil/depth.hpp> #include <components/sceneutil/depth.hpp>
#include <components/sceneutil/util.hpp>
#include <components/shader/shadermanager.hpp> #include <components/shader/shadermanager.hpp>
#include <components/stereo/stereomanager.hpp> #include <components/stereo/stereomanager.hpp>
@ -271,18 +272,37 @@ namespace Terrain
stateset->addUniform(UniformCollection::value().mBlendMap); stateset->addUniform(UniformCollection::value().mBlendMap);
} }
bool parallax = it->mNormalMap && it->mParallax;
bool reconstructNormalZ = false;
if (it->mNormalMap) if (it->mNormalMap)
{ {
stateset->setTextureAttributeAndModes(2, it->mNormalMap); stateset->setTextureAttributeAndModes(2, it->mNormalMap);
stateset->addUniform(UniformCollection::value().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; Shader::ShaderManager::DefineMap defineMap;
defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0"; defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0";
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0"; defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
defineMap["specularMap"] = it->mSpecular ? "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["writeNormals"] = (it == layers.end() - 1) ? "1" : "0";
defineMap["reconstructNormalZ"] = reconstructNormalZ ? "1" : "0";
Stereo::shaderStereoDefines(defineMap); Stereo::shaderStereoDefines(defineMap);
stateset->setAttributeAndModes(shaderManager.getProgram("terrain", defineMap)); stateset->setAttributeAndModes(shaderManager.getProgram("terrain", defineMap));

View file

@ -245,6 +245,16 @@ Absent pieces usually mean a bug in recast mesh tiles building.
Allows to do in-game debug. Allows to do in-game debug.
Potentially decreases performance. 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 Expert settings
*************** ***************

View file

@ -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. 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 Specular Mapping
################ ################

View file

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

126
files/opencs/activator.svg Normal file
View file

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

View file

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

58
files/opencs/armor.svg Normal file
View file

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

View file

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

View file

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

View file

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

166
files/opencs/book.svg Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

83
files/opencs/cell.svg Normal file
View file

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

186
files/opencs/class.svg Normal file
View file

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

101
files/opencs/clothing.svg Normal file
View file

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