diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDModularBuildComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDModularBuildComponentTable.cpp index 5e8c84820..89195b04d 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDModularBuildComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDModularBuildComponentTable.cpp @@ -1,5 +1,7 @@ #include "CDModularBuildComponentTable.h" +#include + #include #include @@ -55,3 +57,8 @@ std::optional CDModularBuildComponentTable::FindByNumbe std::optional CDModularBuildComponentTable::GetByNumberOfParts(const uint32_t numberOfParts) const { return FindByNumberOfParts(GetEntries(), numberOfParts); } + +bool CDModularBuildComponentTable::IsCreatedLot(LOT lot) const { + const auto& entries = GetEntries(); + return std::any_of(entries.begin(), entries.end(), [lot](const CDModularBuildComponent& build) { return build.createdLOT == lot; }); +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDModularBuildComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDModularBuildComponentTable.h index 947b5f4ff..46fc1e21b 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDModularBuildComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDModularBuildComponentTable.h @@ -21,6 +21,9 @@ public: // The first build made of this many parts std::optional GetByNumberOfParts(uint32_t numberOfParts) const; + // Whether a LOT is the item some build becomes (a car or rocket) + bool IsCreatedLot(LOT lot) const; + // Fills numberOfParts and rootPartExampleLOT from a ModularBuildComponent.xml static void ParseXml(std::string_view xml, CDModularBuildComponent& build); static std::optional FindByNumberOfParts(const std::vector& builds, uint32_t numberOfParts); diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index a60613194..d677de14d 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -19,6 +19,7 @@ #include "GameMessages.h" #include "InventoryMessages.h" #include "Database.h" +#include "CDModularBuildComponentTable.h" #include "SkillMessages.h" #include "MovementMessages.h" #include "SkillComponent.h" @@ -610,6 +611,27 @@ bool InventoryComponent::HasSpaceForLoot(const Loot::Return& loot) { return true; } +namespace { + /** + * Cars and rockets built before builds were recorded have no subkey, so the client has no blueprint id to ask for + * their icon with. Give such an item what a new build gets (ModularBuildFinish): a persistent id as its subkey and a + * ugc_modular_build row with its modules. The next save keeps the subkey. Main thread (character load). + */ + void AssignModularBuildId(Item& item, Entity* owner) { + if (item.GetSubKey() != LWOOBJID_EMPTY || !CDClientManager::GetTable()->IsCreatedLot(item.GetLot())) return; + const auto& config = item.GetConfig(); + const auto modules = config.find(u"assemblyPartLOTs"); + if (modules == config.values.end() || !modules->second) return; + const auto modulesText = modules->second->GetValueAsString(); + if (modulesText.empty()) return; + const auto id = ObjectIDManager::GetPersistentID(); + auto* character = owner ? owner->GetCharacter() : nullptr; + Database::Get()->InsertUgcBuild(modulesText, id, character ? std::optional(character->GetID()) : std::nullopt); + item.SetSubKey(id); + LOG("Gave the %i build %llu (made before builds were recorded) the build id %llu", item.GetLot(), item.GetId(), id); + } +} + void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) { LoadPetXml(document); @@ -712,6 +734,7 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) { auto* item = new Item(id, lot, inventory, slot, count, bound, {}, parent, subKey); item->LoadConfigXml(*itemElement); + AssignModularBuildId(*item, m_Parent); if (equipped) { const auto info = Inventory::FindItemComponent(lot);