diff --git a/source/core/StarColor.cpp b/source/core/StarColor.cpp index 0503f19..02b359a 100644 --- a/source/core/StarColor.cpp +++ b/source/core/StarColor.cpp @@ -221,7 +221,7 @@ Color::Color(const String& name) { if (i != NamedColors.end()) *this = i->second; else - throw ColorException(strf("Named color %s not found", name)); + throw ColorException(strf("Named color %s not found", name), false); } } @@ -303,30 +303,7 @@ uint32_t Color::toUint32() const { } Color Color::fromHex(String const& s) { - uint8_t cbytes[4]; - - if (s.utf8Size() == 3) { - nibbleDecode(s.utf8Ptr(), 3, (char*)cbytes, 4); - cbytes[0] = (cbytes[0] << 4) | cbytes[0]; - cbytes[1] = (cbytes[1] << 4) | cbytes[1]; - cbytes[2] = (cbytes[2] << 4) | cbytes[2]; - cbytes[3] = 255; - } else if (s.utf8Size() == 4) { - nibbleDecode(s.utf8Ptr(), 4, (char*)cbytes, 4); - cbytes[0] = (cbytes[0] << 4) | cbytes[0]; - cbytes[1] = (cbytes[1] << 4) | cbytes[1]; - cbytes[2] = (cbytes[2] << 4) | cbytes[2]; - cbytes[3] = (cbytes[3] << 4) | cbytes[3]; - } else if (s.utf8Size() == 6) { - hexDecode(s.utf8Ptr(), 6, (char*)cbytes, 4); - cbytes[3] = 255; - } else if (s.utf8Size() == 8) { - hexDecode(s.utf8Ptr(), 8, (char*)cbytes, 4); - } else { - throw ColorException(strf("Improper size for hex string '%s' in Color::fromHex", s)); - } - - return Color::rgba(cbytes[0], cbytes[1], cbytes[2], cbytes[3]); + return Color::rgba(hexToVec4B(s)); } Vec4B Color::toRgba() const { @@ -624,4 +601,31 @@ Vec4B Color::hueShiftVec4B(Vec4B color, float hue) { } } +Vec4B Color::hexToVec4B(String const& s) { + Array cbytes; + + if (s.utf8Size() == 3) { + nibbleDecode(s.utf8Ptr(), 3, (char*)cbytes.data(), 4); + cbytes[0] = (cbytes[0] << 4) | cbytes[0]; + cbytes[1] = (cbytes[1] << 4) | cbytes[1]; + cbytes[2] = (cbytes[2] << 4) | cbytes[2]; + cbytes[3] = 255; + } else if (s.utf8Size() == 4) { + nibbleDecode(s.utf8Ptr(), 4, (char*)cbytes.data(), 4); + cbytes[0] = (cbytes[0] << 4) | cbytes[0]; + cbytes[1] = (cbytes[1] << 4) | cbytes[1]; + cbytes[2] = (cbytes[2] << 4) | cbytes[2]; + cbytes[3] = (cbytes[3] << 4) | cbytes[3]; + } else if (s.utf8Size() == 6) { + hexDecode(s.utf8Ptr(), 6, (char*)cbytes.data(), 4); + cbytes[3] = 255; + } else if (s.utf8Size() == 8) { + hexDecode(s.utf8Ptr(), 8, (char*)cbytes.data(), 4); + } else { + throw ColorException(strf("Improper size for hex string '%s' in Color::hexToVec4B", s), false); + } + + return Vec4B(move(cbytes)); +} + } diff --git a/source/core/StarColor.hpp b/source/core/StarColor.hpp index e2fe2b5..68bcc10 100644 --- a/source/core/StarColor.hpp +++ b/source/core/StarColor.hpp @@ -67,7 +67,7 @@ public: static Color temperature(float temp); static Vec4B hueShiftVec4B(Vec4B color, float hue); - + static Vec4B Color::hexToVec4B(String const& s); // Black Color(); diff --git a/source/core/StarDirectives.cpp b/source/core/StarDirectives.cpp index 1245eae..5dd82cf 100644 --- a/source/core/StarDirectives.cpp +++ b/source/core/StarDirectives.cpp @@ -229,6 +229,10 @@ inline size_t DirectivesGroup::hash() const { return hasher.digest(); } +const List& DirectivesGroup::list() const { + return m_directives; +} + bool operator==(DirectivesGroup const& a, DirectivesGroup const& b) { return a.compare(b); } @@ -237,6 +241,21 @@ bool operator!=(DirectivesGroup const& a, DirectivesGroup const& b) { return !a.compare(b); } +DataStream& operator>>(DataStream& ds, DirectivesGroup& directivesGroup) { + String string; + ds.read(string); + + directivesGroup = move(DirectivesGroup(move(string))); + + return ds; +} + +DataStream& operator<<(DataStream& ds, DirectivesGroup const& directivesGroup) { + ds.write(directivesGroup.toString()); + + return ds; +} + size_t hash::operator()(DirectivesGroup const& s) const { return s.hash(); } diff --git a/source/core/StarDirectives.hpp b/source/core/StarDirectives.hpp index 440b33a..dafa2a0 100644 --- a/source/core/StarDirectives.hpp +++ b/source/core/StarDirectives.hpp @@ -73,9 +73,13 @@ public: void applyExistingImage(Image& image) const; inline size_t hash() const; + const List& list() const; friend bool operator==(DirectivesGroup const& a, DirectivesGroup const& b); friend bool operator!=(DirectivesGroup const& a, DirectivesGroup const& b); + + friend DataStream& operator>>(DataStream& ds, DirectivesGroup& directives); + friend DataStream& operator<<(DataStream& ds, DirectivesGroup const& directives); private: void buildString(String& string, const DirectivesGroup& directives) const; diff --git a/source/core/StarException.hpp b/source/core/StarException.hpp index b427511..7e6fe3e 100644 --- a/source/core/StarException.hpp +++ b/source/core/StarException.hpp @@ -13,7 +13,7 @@ public: StarException() noexcept; virtual ~StarException() noexcept; - explicit StarException(std::string message) noexcept; + explicit StarException(std::string message, bool genStackTrace = true) noexcept; explicit StarException(std::exception const& cause) noexcept; StarException(std::string message, std::exception const& cause) noexcept; @@ -26,7 +26,7 @@ public: friend OutputProxy outputException(std::exception const& e, bool fullStacktrace); protected: - StarException(char const* type, std::string message) noexcept; + StarException(char const* type, std::string message, bool genStackTrace = true) noexcept; StarException(char const* type, std::string message, std::exception const& cause) noexcept; private: @@ -68,22 +68,22 @@ void fatalException(std::exception const& e, bool showStackTrace); {} #endif -#define STAR_EXCEPTION(ClassName, BaseName) \ - class ClassName : public BaseName { \ - public: \ - template \ - static ClassName format(char const* fmt, Args const&... args) { \ - return ClassName(strf(fmt, args...)); \ - } \ - ClassName() : BaseName(#ClassName, std::string()) {} \ - explicit ClassName(std::string message) : BaseName(#ClassName, move(message)) {} \ - explicit ClassName(std::exception const& cause) : BaseName(#ClassName, std::string(), cause) {} \ - ClassName(std::string message, std::exception const& cause) : BaseName(#ClassName, move(message), cause) {} \ - \ - protected: \ - ClassName(char const* type, std::string message) : BaseName(type, move(message)) {} \ - ClassName(char const* type, std::string message, std::exception const& cause) \ - : BaseName(type, move(message), cause) {} \ +#define STAR_EXCEPTION(ClassName, BaseName) \ + class ClassName : public BaseName { \ + public: \ + template \ + static ClassName format(char const* fmt, Args const&... args) { \ + return ClassName(strf(fmt, args...)); \ + } \ + ClassName() : BaseName(#ClassName, std::string()) {} \ + explicit ClassName(std::string message, bool genStackTrace = true) : BaseName(#ClassName, move(message), genStackTrace) {} \ + explicit ClassName(std::exception const& cause) : BaseName(#ClassName, std::string(), cause) {} \ + ClassName(std::string message, std::exception const& cause) : BaseName(#ClassName, move(message), cause) {} \ + \ + protected: \ + ClassName(char const* type, std::string message, bool genStackTrace = true) : BaseName(type, move(message), genStackTrace) {} \ + ClassName(char const* type, std::string message, std::exception const& cause) \ + : BaseName(type, move(message), cause) {} \ } STAR_EXCEPTION(OutOfRangeException, StarException); diff --git a/source/core/StarException_unix.cpp b/source/core/StarException_unix.cpp index db23c7c..578c0b4 100644 --- a/source/core/StarException_unix.cpp +++ b/source/core/StarException_unix.cpp @@ -38,8 +38,8 @@ StarException::StarException() noexcept StarException::~StarException() noexcept {} -StarException::StarException(std::string message) noexcept - : StarException("StarException", move(message)) {} +StarException::StarException(std::string message, bool genStackTrace) noexcept + : StarException("StarException", move(message), genStackTrace) {} StarException::StarException(std::exception const& cause) noexcept : StarException("StarException", std::string(), cause) {} @@ -56,19 +56,19 @@ const char* StarException::what() const throw() { return m_whatBuffer.c_str(); } -StarException::StarException(char const* type, std::string message) noexcept { - auto printException = [](std::ostream& os, bool fullStacktrace, char const* type, std::string message, StackCapture stack) { +StarException::StarException(char const* type, std::string message, bool genStackTrace) noexcept { + auto printException = [](std::ostream& os, bool fullStacktrace, char const* type, std::string message, Maybe stack) { os << "(" << type << ")"; if (!message.empty()) os << " " << message; - if (fullStacktrace) { + if (fullStacktrace && stack) { os << std::endl; - os << outputStack(stack); + os << outputStack(*stack); } }; - m_printException = bind(printException, _1, _2, type, move(message), captureStack()); + m_printException = bind(printException, _1, _2, type, move(message), genStackTrace ? captureStack() : Maybe()); } StarException::StarException(char const* type, std::string message, std::exception const& cause) noexcept diff --git a/source/core/StarException_windows.cpp b/source/core/StarException_windows.cpp index 8892eb2..ca81332 100644 --- a/source/core/StarException_windows.cpp +++ b/source/core/StarException_windows.cpp @@ -145,7 +145,7 @@ StarException::StarException() noexcept : StarException(std::string("StarExcepti StarException::~StarException() noexcept {} -StarException::StarException(std::string message) noexcept : StarException("StarException", move(message)) {} +StarException::StarException(std::string message, bool genStackTrace) noexcept : StarException("StarException", move(message), genStackTrace) {} StarException::StarException(std::exception const& cause) noexcept : StarException("StarException", std::string(), cause) {} @@ -162,20 +162,20 @@ const char* StarException::what() const throw() { return m_whatBuffer.c_str(); } -StarException::StarException(char const* type, std::string message) noexcept { +StarException::StarException(char const* type, std::string message, bool genStackTrace) noexcept { auto printException = []( - std::ostream& os, bool fullStacktrace, char const* type, std::string message, StackCapture stack) { + std::ostream& os, bool fullStacktrace, char const* type, std::string message, Maybe stack) { os << "(" << type << ")"; if (!message.empty()) os << " " << message; - if (fullStacktrace) { + if (fullStacktrace && stack) { os << std::endl; - os << outputStack(stack); + os << outputStack(*stack); } }; - m_printException = bind(printException, _1, _2, type, move(message), captureStack()); + m_printException = bind(printException, _1, _2, type, move(message), genStackTrace ? captureStack() : Maybe()); } StarException::StarException(char const* type, std::string message, std::exception const& cause) noexcept diff --git a/source/core/StarImageProcessing.cpp b/source/core/StarImageProcessing.cpp index c2f980e..56ac130 100644 --- a/source/core/StarImageProcessing.cpp +++ b/source/core/StarImageProcessing.cpp @@ -173,7 +173,7 @@ ImageOperation imageOperationFromString(String const& string) { } else if (type == "replace") { ColorReplaceImageOperation operation; for (size_t i = 0; i < (bits.size() - 1) / 2; ++i) - operation.colorReplaceMap[Color::fromHex(bits[i * 2 + 1]).toRgba()] = Color::fromHex(bits[i * 2 + 2]).toRgba(); + operation.colorReplaceMap[Color::hexToVec4B(bits[i * 2 + 1])] = Color::hexToVec4B(bits[i * 2 + 2]); return operation; @@ -259,7 +259,7 @@ ImageOperation imageOperationFromString(String const& string) { return FlipImageOperation{FlipImageOperation::FlipXY}; } else { - throw ImageOperationException(strf("Could not recognize ImageOperation type %s", type)); + throw ImageOperationException(strf("Could not recognize ImageOperation type %s", type), false); } } catch (OutOfRangeException const& e) { throw ImageOperationException("Error reading ImageOperation", e); diff --git a/source/game/StarArmorWearer.cpp b/source/game/StarArmorWearer.cpp index bb091e2..fe61bad 100644 --- a/source/game/StarArmorWearer.cpp +++ b/source/game/StarArmorWearer.cpp @@ -16,7 +16,7 @@ namespace Star { -ArmorWearer::ArmorWearer() : m_lastNude(true), m_needsHumanoidSync(true) { +ArmorWearer::ArmorWearer() : m_lastNude(true) { addNetElement(&m_headItemDataNetState); addNetElement(&m_chestItemDataNetState); addNetElement(&m_legsItemDataNetState); @@ -25,26 +25,34 @@ ArmorWearer::ArmorWearer() : m_lastNude(true), m_needsHumanoidSync(true) { addNetElement(&m_chestCosmeticItemDataNetState); addNetElement(&m_legsCosmeticItemDataNetState); addNetElement(&m_backCosmeticItemDataNetState); + + m_headNeedsSync = m_chestNeedsSync = m_legsNeedsSync = m_backNeedsSync = true; } void ArmorWearer::setupHumanoidClothingDrawables(Humanoid& humanoid, bool forceNude) { - if (m_lastNude != forceNude) + bool nudeChanged = m_lastNude != forceNude; + if (nudeChanged) m_lastNude = forceNude; - else if (!m_needsHumanoidSync) - return; - m_needsHumanoidSync = false; + bool headNeedsSync = nudeChanged || m_headNeedsSync; + bool chestNeedsSync = nudeChanged || m_chestNeedsSync; + bool legsNeedsSync = nudeChanged || m_legsNeedsSync; + bool backNeedsSync = nudeChanged || m_backNeedsSync; bool bodyHidden = false; if (m_headCosmeticItem && !forceNude) { - humanoid.setHeadArmorFrameset(m_headCosmeticItem->frameset(humanoid.identity().gender)); - humanoid.setHeadArmorDirectives(m_headCosmeticItem->directives()); - humanoid.setHelmetMaskDirectives(m_headCosmeticItem->maskDirectives()); + if (headNeedsSync) { + humanoid.setHeadArmorFrameset(m_headCosmeticItem->frameset(humanoid.identity().gender)); + humanoid.setHeadArmorDirectives(m_headCosmeticItem->directives()); + humanoid.setHelmetMaskDirectives(m_headCosmeticItem->maskDirectives()); + } bodyHidden = bodyHidden || m_headCosmeticItem->hideBody(); } else if (m_headItem && !forceNude) { - humanoid.setHeadArmorFrameset(m_headItem->frameset(humanoid.identity().gender)); - humanoid.setHeadArmorDirectives(m_headItem->directives()); - humanoid.setHelmetMaskDirectives(m_headItem->maskDirectives()); + if (headNeedsSync) { + humanoid.setHeadArmorFrameset(m_headItem->frameset(humanoid.identity().gender)); + humanoid.setHeadArmorDirectives(m_headItem->directives()); + humanoid.setHelmetMaskDirectives(m_headItem->maskDirectives()); + } bodyHidden = bodyHidden || m_headItem->hideBody(); } else { humanoid.setHeadArmorFrameset(""); @@ -52,16 +60,20 @@ void ArmorWearer::setupHumanoidClothingDrawables(Humanoid& humanoid, bool forceN } if (m_chestCosmeticItem && !forceNude) { - humanoid.setBackSleeveFrameset(m_chestCosmeticItem->backSleeveFrameset(humanoid.identity().gender)); - humanoid.setFrontSleeveFrameset(m_chestCosmeticItem->frontSleeveFrameset(humanoid.identity().gender)); - humanoid.setChestArmorFrameset(m_chestCosmeticItem->bodyFrameset(humanoid.identity().gender)); - humanoid.setChestArmorDirectives(m_chestCosmeticItem->directives()); + if (chestNeedsSync) { + humanoid.setBackSleeveFrameset(m_chestCosmeticItem->backSleeveFrameset(humanoid.identity().gender)); + humanoid.setFrontSleeveFrameset(m_chestCosmeticItem->frontSleeveFrameset(humanoid.identity().gender)); + humanoid.setChestArmorFrameset(m_chestCosmeticItem->bodyFrameset(humanoid.identity().gender)); + humanoid.setChestArmorDirectives(m_chestCosmeticItem->directives()); + } bodyHidden = bodyHidden || m_chestCosmeticItem->hideBody(); } else if (m_chestItem && !forceNude) { - humanoid.setBackSleeveFrameset(m_chestItem->backSleeveFrameset(humanoid.identity().gender)); - humanoid.setFrontSleeveFrameset(m_chestItem->frontSleeveFrameset(humanoid.identity().gender)); - humanoid.setChestArmorFrameset(m_chestItem->bodyFrameset(humanoid.identity().gender)); - humanoid.setChestArmorDirectives(m_chestItem->directives()); + if (chestNeedsSync) { + humanoid.setBackSleeveFrameset(m_chestItem->backSleeveFrameset(humanoid.identity().gender)); + humanoid.setFrontSleeveFrameset(m_chestItem->frontSleeveFrameset(humanoid.identity().gender)); + humanoid.setChestArmorFrameset(m_chestItem->bodyFrameset(humanoid.identity().gender)); + humanoid.setChestArmorDirectives(m_chestItem->directives()); + } bodyHidden = bodyHidden || m_chestItem->hideBody(); } else { humanoid.setBackSleeveFrameset(""); @@ -70,29 +82,39 @@ void ArmorWearer::setupHumanoidClothingDrawables(Humanoid& humanoid, bool forceN } if (m_legsCosmeticItem && !forceNude) { - humanoid.setLegsArmorFrameset(m_legsCosmeticItem->frameset(humanoid.identity().gender)); - humanoid.setLegsArmorDirectives(m_legsCosmeticItem->directives()); + if (legsNeedsSync) { + humanoid.setLegsArmorFrameset(m_legsCosmeticItem->frameset(humanoid.identity().gender)); + humanoid.setLegsArmorDirectives(m_legsCosmeticItem->directives()); + } bodyHidden = bodyHidden || m_legsCosmeticItem->hideBody(); } else if (m_legsItem && !forceNude) { - humanoid.setLegsArmorFrameset(m_legsItem->frameset(humanoid.identity().gender)); - humanoid.setLegsArmorDirectives(m_legsItem->directives()); + if (legsNeedsSync) { + humanoid.setLegsArmorFrameset(m_legsItem->frameset(humanoid.identity().gender)); + humanoid.setLegsArmorDirectives(m_legsItem->directives()); + } bodyHidden = bodyHidden || m_legsItem->hideBody(); } else { humanoid.setLegsArmorFrameset(""); } if (m_backCosmeticItem && !forceNude) { - humanoid.setBackArmorFrameset(m_backCosmeticItem->frameset(humanoid.identity().gender)); - humanoid.setBackArmorDirectives(m_backCosmeticItem->directives()); + if (backNeedsSync) { + humanoid.setBackArmorFrameset(m_backCosmeticItem->frameset(humanoid.identity().gender)); + humanoid.setBackArmorDirectives(m_backCosmeticItem->directives()); + } bodyHidden = bodyHidden || m_backCosmeticItem->hideBody(); } else if (m_backItem && !forceNude) { - humanoid.setBackArmorFrameset(m_backItem->frameset(humanoid.identity().gender)); - humanoid.setBackArmorDirectives(m_backItem->directives()); + if (backNeedsSync) { + humanoid.setBackArmorFrameset(m_backItem->frameset(humanoid.identity().gender)); + humanoid.setBackArmorDirectives(m_backItem->directives()); + } bodyHidden = bodyHidden || m_backItem->hideBody(); } else { humanoid.setBackArmorFrameset(""); } + m_headNeedsSync = m_chestNeedsSync = m_legsNeedsSync = m_backNeedsSync = false; + humanoid.setBodyHidden(bodyHidden); } @@ -171,56 +193,56 @@ void ArmorWearer::setHeadItem(HeadArmorPtr headItem) { if (Item::itemsEqual(m_headItem, headItem)) return; m_headItem = headItem; - m_needsHumanoidSync = true; + m_headNeedsSync |= !m_headCosmeticItem; } void ArmorWearer::setHeadCosmeticItem(HeadArmorPtr headCosmeticItem) { if (Item::itemsEqual(m_headCosmeticItem, headCosmeticItem)) return; m_headCosmeticItem = headCosmeticItem; - m_needsHumanoidSync = true; -} - -void ArmorWearer::setChestCosmeticItem(ChestArmorPtr chestCosmeticItem) { - if (Item::itemsEqual(m_chestCosmeticItem, chestCosmeticItem)) - return; - m_chestCosmeticItem = chestCosmeticItem; - m_needsHumanoidSync = true; + m_headNeedsSync = true; } void ArmorWearer::setChestItem(ChestArmorPtr chestItem) { if (Item::itemsEqual(m_chestItem, chestItem)) return; m_chestItem = chestItem; - m_needsHumanoidSync = true; + m_chestNeedsSync |= !m_chestCosmeticItem; +} + +void ArmorWearer::setChestCosmeticItem(ChestArmorPtr chestCosmeticItem) { + if (Item::itemsEqual(m_chestCosmeticItem, chestCosmeticItem)) + return; + m_chestCosmeticItem = chestCosmeticItem; + m_chestNeedsSync = true; } void ArmorWearer::setLegsItem(LegsArmorPtr legsItem) { if (Item::itemsEqual(m_legsItem, legsItem)) return; m_legsItem = legsItem; - m_needsHumanoidSync = true; + m_legsNeedsSync |= !m_legsCosmeticItem; } void ArmorWearer::setLegsCosmeticItem(LegsArmorPtr legsCosmeticItem) { if (Item::itemsEqual(m_legsCosmeticItem, legsCosmeticItem)) return; m_legsCosmeticItem = legsCosmeticItem; - m_needsHumanoidSync = true; + m_legsNeedsSync = true; } void ArmorWearer::setBackItem(BackArmorPtr backItem) { if (Item::itemsEqual(m_backItem, backItem)) return; m_backItem = backItem; - m_needsHumanoidSync = true; + m_backNeedsSync |= !m_backCosmeticItem; } void ArmorWearer::setBackCosmeticItem(BackArmorPtr backCosmeticItem) { if (Item::itemsEqual(m_backCosmeticItem, backCosmeticItem)) return; m_backCosmeticItem = backCosmeticItem; - m_needsHumanoidSync = true; + m_backNeedsSync = true; } HeadArmorPtr ArmorWearer::headItem() const { @@ -306,26 +328,23 @@ ItemDescriptor ArmorWearer::backCosmeticItemDescriptor() const { void ArmorWearer::netElementsNeedLoad(bool) { auto itemDatabase = Root::singleton().itemDatabase(); - bool changed = false; - if (m_headItemDataNetState.pullUpdated()) - changed |= itemDatabase->loadItem(m_headItemDataNetState.get(), m_headItem); - if (m_chestItemDataNetState.pullUpdated()) - changed |= itemDatabase->loadItem(m_chestItemDataNetState.get(), m_chestItem); - if (m_legsItemDataNetState.pullUpdated()) - changed |= itemDatabase->loadItem(m_legsItemDataNetState.get(), m_legsItem); - if (m_backItemDataNetState.pullUpdated()) - changed |= itemDatabase->loadItem(m_backItemDataNetState.get(), m_backItem); - if (m_headCosmeticItemDataNetState.pullUpdated()) - changed |= itemDatabase->loadItem(m_headCosmeticItemDataNetState.get(), m_headCosmeticItem); + m_headNeedsSync |= itemDatabase->loadItem(m_headCosmeticItemDataNetState.get(), m_headCosmeticItem); if (m_chestCosmeticItemDataNetState.pullUpdated()) - changed |= itemDatabase->loadItem(m_chestCosmeticItemDataNetState.get(), m_chestCosmeticItem); + m_chestNeedsSync |= itemDatabase->loadItem(m_chestCosmeticItemDataNetState.get(), m_chestCosmeticItem); if (m_legsCosmeticItemDataNetState.pullUpdated()) - changed |= itemDatabase->loadItem(m_legsCosmeticItemDataNetState.get(), m_legsCosmeticItem); + m_legsNeedsSync |= itemDatabase->loadItem(m_legsCosmeticItemDataNetState.get(), m_legsCosmeticItem); if (m_backCosmeticItemDataNetState.pullUpdated()) - changed |= itemDatabase->loadItem(m_backCosmeticItemDataNetState.get(), m_backCosmeticItem); + m_backNeedsSync |= itemDatabase->loadItem(m_backCosmeticItemDataNetState.get(), m_backCosmeticItem); - m_needsHumanoidSync = changed; + if (m_headItemDataNetState.pullUpdated()) + m_headNeedsSync |= !m_headCosmeticItem && itemDatabase->loadItem(m_headItemDataNetState.get(), m_headItem); + if (m_chestItemDataNetState.pullUpdated()) + m_chestNeedsSync |= !m_chestCosmeticItem && itemDatabase->loadItem(m_chestItemDataNetState.get(), m_chestItem); + if (m_legsItemDataNetState.pullUpdated()) + m_legsNeedsSync |= !m_legsCosmeticItem && itemDatabase->loadItem(m_legsItemDataNetState.get(), m_legsItem); + if (m_backItemDataNetState.pullUpdated()) + m_backNeedsSync |= !m_backCosmeticItem && itemDatabase->loadItem(m_backItemDataNetState.get(), m_backItem); } void ArmorWearer::netElementsNeedStore() { diff --git a/source/game/StarArmorWearer.hpp b/source/game/StarArmorWearer.hpp index e4ddf01..3b02ab2 100644 --- a/source/game/StarArmorWearer.hpp +++ b/source/game/StarArmorWearer.hpp @@ -35,8 +35,8 @@ public: void setHeadItem(HeadArmorPtr headItem); void setHeadCosmeticItem(HeadArmorPtr headCosmeticItem); - void setChestCosmeticItem(ChestArmorPtr chestCosmeticItem); void setChestItem(ChestArmorPtr chestItem); + void setChestCosmeticItem(ChestArmorPtr chestCosmeticItem); void setLegsItem(LegsArmorPtr legsItem); void setLegsCosmeticItem(LegsArmorPtr legsCosmeticItem); void setBackItem(BackArmorPtr backItem); @@ -84,7 +84,10 @@ private: NetElementData m_backCosmeticItemDataNetState; bool m_lastNude; - bool m_needsHumanoidSync; + bool m_headNeedsSync; + bool m_chestNeedsSync; + bool m_legsNeedsSync; + bool m_backNeedsSync; }; } diff --git a/source/game/StarDrawable.cpp b/source/game/StarDrawable.cpp index aeb8cb6..90b36c5 100644 --- a/source/game/StarDrawable.cpp +++ b/source/game/StarDrawable.cpp @@ -29,6 +29,28 @@ Drawable::ImagePart& Drawable::ImagePart::addDirectives(Directives const& direct return *this; } +Drawable::ImagePart& Drawable::ImagePart::addDirectivesGroup(DirectivesGroup const& directivesGroup, bool keepImageCenterPosition) { + if (directivesGroup.empty()) + return *this; + + if (keepImageCenterPosition) { + auto imageMetadata = Root::singleton().imageMetadataDatabase(); + Vec2F imageSize = Vec2F(imageMetadata->imageSize(image)); + for (Directives const& directives : directivesGroup.list()) + image.directives += directives; + Vec2F newImageSize = Vec2F(imageMetadata->imageSize(image)); + + // If we are trying to maintain the image center, PRE translate the image by + // the change in size / 2 + transformation *= Mat3F::translation((imageSize - newImageSize) / 2); + } else { + for (Directives const& directives : directivesGroup.list()) + image.directives += directives; + } + + return *this; +} + Drawable::ImagePart& Drawable::ImagePart::removeDirectives(bool keepImageCenterPosition) { if (keepImageCenterPosition) { auto imageMetadata = Root::singleton().imageMetadataDatabase(); diff --git a/source/game/StarDrawable.hpp b/source/game/StarDrawable.hpp index cf1c8c8..f707eec 100644 --- a/source/game/StarDrawable.hpp +++ b/source/game/StarDrawable.hpp @@ -30,6 +30,7 @@ struct Drawable { // transformed center of the image the same if the directives change the // image size. ImagePart& addDirectives(Directives const& directives, bool keepImageCenterPosition = false); + ImagePart& addDirectivesGroup(DirectivesGroup const& directivesGroup, bool keepImageCenterPosition = false); // Remove directives from this ImagePart, while optionally keeping the // transformed center of the image the same if the directives change the diff --git a/source/game/StarMonster.cpp b/source/game/StarMonster.cpp index a4ce597..2b994d9 100644 --- a/source/game/StarMonster.cpp +++ b/source/game/StarMonster.cpp @@ -487,7 +487,7 @@ void Monster::update(uint64_t) { void Monster::render(RenderCallback* renderCallback) { for (auto& drawable : m_networkedAnimator.drawables(position())) { if (drawable.isImage()) - drawable.imagePart().addDirectives(m_statusController->parentDirectives(), true); + drawable.imagePart().addDirectivesGroup(m_statusController->parentDirectives(), true); renderCallback->addDrawable(move(drawable), m_monsterVariant.renderLayer); } diff --git a/source/game/StarNetworkedAnimator.cpp b/source/game/StarNetworkedAnimator.cpp index aadbda6..db3524b 100644 --- a/source/game/StarNetworkedAnimator.cpp +++ b/source/game/StarNetworkedAnimator.cpp @@ -385,7 +385,7 @@ void NetworkedAnimator::setPartTag(String const& partType, String tagName, Strin m_partTags[partType].set(move(tagName), move(tagValue)); } -void NetworkedAnimator::setProcessingDirectives(String const& directives) { +void NetworkedAnimator::setProcessingDirectives(Directives const& directives) { m_processingDirectives.set(directives); } @@ -562,8 +562,7 @@ List NetworkedAnimator::drawables(Vec2F const& position) const { } List> NetworkedAnimator::drawablesWithZLevel(Vec2F const& position) const { - String baseProcessingDirectives = "?"; - baseProcessingDirectives.append(m_processingDirectives.get()); + List baseProcessingDirectives = { m_processingDirectives.get() }; for (auto& pair : m_effects) { auto const& effectState = pair.second; @@ -571,11 +570,9 @@ List> NetworkedAnimator::drawablesWithZLevel(Vec2F const& auto const& effect = m_effects.get(pair.first); if (effect.type == "flash") { if (effectState.timer > effect.time / 2) { - baseProcessingDirectives.append("?"); baseProcessingDirectives.append(effect.directives); } } else if (effect.type == "directive") { - baseProcessingDirectives.append("?"); baseProcessingDirectives.append(effect.directives); } else { throw NetworkedAnimatorException(strf("No such NetworkedAnimator effect type '%s'", effect.type)); @@ -595,10 +592,9 @@ List> NetworkedAnimator::drawablesWithZLevel(Vec2F const& maybeZLevel = activePart.properties.value("flippedZLevel").optFloat().orMaybe(maybeZLevel); float zLevel = maybeZLevel.value(0.0f); - String processingDirectives = baseProcessingDirectives; + size_t originalDirectivesSize = baseProcessingDirectives.size(); if (auto directives = activePart.properties.value("processingDirectives").optString()) { - processingDirectives.append("?"); - processingDirectives.append(*directives); + baseProcessingDirectives.append(*directives); } Maybe frame; @@ -606,8 +602,7 @@ List> NetworkedAnimator::drawablesWithZLevel(Vec2F const& frame = activePart.activeState->frame; if (auto directives = activePart.activeState->properties.value("processingDirectives").optString()) { - processingDirectives.append("?"); - processingDirectives.append(*directives); + baseProcessingDirectives.append(*directives); } } @@ -629,9 +624,12 @@ List> NetworkedAnimator::drawablesWithZLevel(Vec2F const& }); if (!image.empty() && image[0] != ':' && image[0] != '?') { - image = AssetPath::relativeTo(m_relativePath, image) + processingDirectives; + image = AssetPath::relativeTo(m_relativePath, image); auto drawable = Drawable::makeImage(move(image), 1.0f / TilePixels, centered, Vec2F()); + auto& imagePart = drawable.imagePart(); + for (Directives const& directives : baseProcessingDirectives) + imagePart.addDirectives(directives); drawable.transform(partTransformation(partName)); drawable.transform(globalTransformation()); drawable.fullbright = fullbright; @@ -639,6 +637,8 @@ List> NetworkedAnimator::drawablesWithZLevel(Vec2F const& drawables.append({move(drawable), zLevel}); } + + baseProcessingDirectives.resize(originalDirectivesSize); }); sort(drawables, [](auto const& a, auto const& b) { return a.second < b.second; }); diff --git a/source/game/StarNetworkedAnimator.hpp b/source/game/StarNetworkedAnimator.hpp index 17fdcad..dc6d299 100644 --- a/source/game/StarNetworkedAnimator.hpp +++ b/source/game/StarNetworkedAnimator.hpp @@ -117,7 +117,7 @@ public: void setGlobalTag(String tagName, String tagValue); void setPartTag(String const& partType, String tagName, String tagValue); - void setProcessingDirectives(String const& directives); + void setProcessingDirectives(Directives const& directives); void setZoom(float zoom); bool flipped() const; float flippedRelativeCenterLine() const; @@ -287,7 +287,7 @@ private: struct Effect { String type; float time; - String directives; + Directives directives; NetElementBool enabled; float timer; @@ -314,7 +314,7 @@ private: OrderedHashMap m_sounds; OrderedHashMap m_effects; - NetElementString m_processingDirectives; + NetElementData m_processingDirectives; NetElementFloat m_zoom; NetElementBool m_flipped; diff --git a/source/game/StarNpc.cpp b/source/game/StarNpc.cpp index 62b2c06..b7a07ae 100644 --- a/source/game/StarNpc.cpp +++ b/source/game/StarNpc.cpp @@ -454,7 +454,7 @@ void Npc::render(RenderCallback* renderCallback) { for (auto& drawable : m_humanoid.render()) { drawable.translate(position()); if (drawable.isImage()) - drawable.imagePart().addDirectives(m_statusController->parentDirectives(), true); + drawable.imagePart().addDirectivesGroup(m_statusController->parentDirectives(), true); renderCallback->addDrawable(move(drawable), renderLayer); } diff --git a/source/game/StarPlayer.cpp b/source/game/StarPlayer.cpp index 0f7484a..bb47704 100644 --- a/source/game/StarPlayer.cpp +++ b/source/game/StarPlayer.cpp @@ -353,11 +353,11 @@ List Player::drawables() const { for (auto& drawable : m_humanoid->render()) { drawable.translate(position() + m_techController->parentOffset()); if (drawable.isImage()) { - drawable.imagePart().addDirectives(m_techController->parentDirectives(), true); - drawable.imagePart().addDirectives(m_statusController->parentDirectives(), true); + drawable.imagePart().addDirectivesGroup(m_techController->parentDirectives(), true); + drawable.imagePart().addDirectivesGroup(m_statusController->parentDirectives(), true); if (auto anchor = as(m_movementController->entityAnchor())) { - if (auto directives = anchor->directives) + if (auto& directives = anchor->directives) drawable.imagePart().addDirectives(*directives, true); } } diff --git a/source/game/StarStatusController.cpp b/source/game/StarStatusController.cpp index 3b794f0..8e26cdc 100644 --- a/source/game/StarStatusController.cpp +++ b/source/game/StarStatusController.cpp @@ -337,11 +337,11 @@ bool StatusController::uniqueStatusEffectActive(String const& effectName) const return false; } -String StatusController::primaryDirectives() const { +const Directives& StatusController::primaryDirectives() const { return m_primaryDirectives; } -void StatusController::setPrimaryDirectives(String const& directives) { +void StatusController::setPrimaryDirectives(Directives const& directives) { m_primaryDirectives = directives; } @@ -509,11 +509,11 @@ void StatusController::tickMaster() { removeUniqueEffect(key); } - String parentDirectives = m_primaryDirectives; - for (auto const& pair : m_uniqueEffects) { - parentDirectives.append("?"); + DirectivesGroup parentDirectives; + parentDirectives.append(m_primaryDirectives); + for (auto const& pair : m_uniqueEffects) parentDirectives.append(pair.second.parentDirectives); - } + m_parentDirectives.set(move(parentDirectives)); updateAnimators(); @@ -524,7 +524,7 @@ void StatusController::tickSlave() { updateAnimators(); } -String StatusController::parentDirectives() const { +const DirectivesGroup& StatusController::parentDirectives() const { return m_parentDirectives.get(); } diff --git a/source/game/StarStatusController.hpp b/source/game/StarStatusController.hpp index 3789bff..4e9839f 100644 --- a/source/game/StarStatusController.hpp +++ b/source/game/StarStatusController.hpp @@ -78,8 +78,8 @@ public: bool uniqueStatusEffectActive(String const& effectName) const; - String primaryDirectives() const; - void setPrimaryDirectives(String const& directives); + const Directives& primaryDirectives() const; + void setPrimaryDirectives(Directives const& directives); // damage request and notification methods should only be called on the master controller. List applyDamageRequest(DamageRequest const& damageRequest); @@ -118,7 +118,7 @@ public: void tickMaster(); void tickSlave(); - String parentDirectives() const; + const DirectivesGroup& parentDirectives() const; List drawables() const; List lightSources() const; List overheadBars(); @@ -180,7 +180,7 @@ private: struct UniqueEffectInstance { UniqueStatusEffectConfig effectConfig; - String parentDirectives; + Directives parentDirectives; HashSet modifierGroups; StatScript script; UniqueEffectMetadataGroup::ElementId metadataId; @@ -205,7 +205,7 @@ private: NetElementGroup m_netGroup; StatCollection m_statCollection; NetElementData m_statusProperties; - NetElementString m_parentDirectives; + NetElementData m_parentDirectives; UniqueEffectMetadataGroup m_uniqueEffectMetadata; EffectAnimatorGroup m_effectAnimators; @@ -226,7 +226,7 @@ private: Maybe m_primaryAnimationConfig; StatScript m_primaryScript; - String m_primaryDirectives; + Directives m_primaryDirectives; EffectAnimatorGroup::ElementId m_primaryAnimatorId; List m_pendingSelfDamageNotifications; diff --git a/source/game/StarTechController.cpp b/source/game/StarTechController.cpp index 1734c61..7ceb54e 100644 --- a/source/game/StarTechController.cpp +++ b/source/game/StarTechController.cpp @@ -251,7 +251,7 @@ Maybe TechController::parentState() const { return m_parentState.get(); } -String TechController::parentDirectives() const { +DirectivesGroup const& TechController::parentDirectives() const { return m_parentDirectives.get(); } @@ -501,10 +501,11 @@ LuaCallbacks TechController::makeTechCallbacks(TechModule& techModule) { callbacks.registerCallback("setParentDirectives", [this, &techModule](Maybe const& directives) { techModule.parentDirectives = directives.value(); - String newParentDirectives; + + DirectivesGroup newParentDirectives; for (auto& module : m_techModules) - newParentDirectives += module.parentDirectives; - m_parentDirectives.set(newParentDirectives); + newParentDirectives.append(module.parentDirectives); + m_parentDirectives.set(move(newParentDirectives)); }); callbacks.registerCallback("setParentHidden", [this](bool hidden) { diff --git a/source/game/StarTechController.hpp b/source/game/StarTechController.hpp index badc4ec..584cf5d 100644 --- a/source/game/StarTechController.hpp +++ b/source/game/StarTechController.hpp @@ -6,6 +6,7 @@ #include "StarLuaComponents.hpp" #include "StarLuaActorMovementComponent.hpp" #include "StarTechDatabase.hpp" +#include "StarDirectives.hpp" namespace Star { @@ -67,7 +68,7 @@ public: void tickSlave(); Maybe parentState() const; - String parentDirectives() const; + DirectivesGroup const& parentDirectives() const; Vec2F parentOffset() const; bool toolUsageSuppressed() const; @@ -120,7 +121,7 @@ private: scriptComponent; bool visible; bool toolUsageSuppressed; - String parentDirectives; + Directives parentDirectives; TechAnimatorGroup::ElementId animatorId; }; @@ -159,7 +160,7 @@ private: Vec2F m_aimPosition; NetElementData> m_parentState; - NetElementString m_parentDirectives; + NetElementData m_parentDirectives; NetElementFloat m_xParentOffset; NetElementFloat m_yParentOffset; NetElementBool m_parentHidden; diff --git a/source/game/interfaces/StarLoungingEntities.hpp b/source/game/interfaces/StarLoungingEntities.hpp index bec553c..282fe2c 100644 --- a/source/game/interfaces/StarLoungingEntities.hpp +++ b/source/game/interfaces/StarLoungingEntities.hpp @@ -28,7 +28,7 @@ struct LoungeAnchor : EntityAnchor { StringSet effectEmitters; Maybe emote; Maybe dance; - Maybe directives; + Maybe directives; JsonObject armorCosmeticOverrides; Maybe cursorOverride; bool cameraFocus;