#pragma once #include "StarThread.hpp" #include "StarItemRecipe.hpp" #include "StarItem.hpp" #include "StarCasting.hpp" #include "StarLuaRoot.hpp" #include "StarTtlCache.hpp" namespace Star { STAR_CLASS(RecipeDatabase); STAR_CLASS(AugmentItem); STAR_CLASS(ItemDatabase); STAR_EXCEPTION(ItemDatabaseException, ItemException); enum class ItemType { Generic, LiquidItem, MaterialItem, ObjectItem, CurrencyItem, MiningTool, Flashlight, WireTool, BeamMiningTool, HarvestingTool, TillingTool, PaintingBeamTool, HeadArmor, ChestArmor, LegsArmor, BackArmor, Consumable, Blueprint, Codex, InspectionTool, InstrumentItem, GrapplingHook, ThrownItem, UnlockItem, ActiveItem, AugmentItem }; extern EnumMap ItemTypeNames; class ItemDatabase { public: // During item loading, the ItemDatabase takes the ItemDescriptor and // produces a set of things from it: struct ItemConfig { // The relative path in assets to the base config String directory; // A possibly modified / generated config from the base config that is // re-constructed each time an ItemDescriptor is loaded. Become's the // Item's base config. Json config; // The parameters from the ItemDescriptor, also possibly modified during // loading. Since this become's the Item's parameters, it will be // subsequently stored with the Item as the new ItemDescriptor. Json parameters; }; static uint64_t getCountOfItem(List const& bag, ItemDescriptor const& item, bool exactMatch = false); static uint64_t getCountOfItem(HashMap const& bag, ItemDescriptor const& item, bool exactMatch = false); static HashMap normalizeBag(List const& bag); static bool canMakeRecipe(ItemRecipe const& recipe, HashMap const& availableIngredients, StringMap const& availableCurrencies); static HashSet recipesFromSubset(HashMap const& normalizedBag, StringMap const& availableCurrencies, HashSet const& subset); static HashSet recipesFromSubset(HashMap const& normalizedBag, StringMap const& availableCurrencies, HashSet const& subset, StringSet const& allowedTypes); static String guiFilterString(ItemPtr const& item); ItemDatabase(); void cleanup(); // Load an item based on item descriptor. If loadItem is called with a // live ptr, and the ptr matches the descriptor read, then no new item is // constructed. If ItemT is some other type than Item, then loadItem will // clear the item if the new item is not castable to it. Returns whether // itemPtr was changed. No exception will be thrown if there is an error // spawning the new item, it will be logged and the itemPtr will be set to a // default item. template bool loadItem(ItemDescriptor const& descriptor, shared_ptr& itemPtr) const; // Protects against re-instantiating an item in the same was as loadItem template bool diskLoad(Json const& diskStore, shared_ptr& itemPtr) const; ItemPtr diskLoad(Json const& diskStore) const; ItemPtr fromJson(Json const& spec) const; Json diskStore(ItemConstPtr const& itemPtr) const; Json toJson(ItemConstPtr const& itemPtr) const; bool hasItem(String const& itemName) const; ItemType itemType(String const& itemName) const; // Friendly name here can be different than the final friendly name, as it // can be modified by custom config or builder scripts. String itemFriendlyName(String const& itemName) const; StringSet itemTags(String const& itemName) const; // Generate an item config for the given itemName, parameters, level and seed. // Level and seed are used by generation in some item types, and may be stored as part // of the unique item data or may be ignored. ItemConfig itemConfig(String const& itemName, Json parameters, Maybe level = {}, Maybe seed = {}) const; // Generates the config for the given item descriptor and then loads the item // from the appropriate factory. If there is a problem instantiating the // item, will return a default item instead. If item is passed a null // ItemDescriptor, it will return a null pointer. // The returned item pointer will be shared. Either call ->clone() or use item() instead for a copy. ItemPtr itemShared(ItemDescriptor descriptor, Maybe level = {}, Maybe seed = {}) const; // Same as itemShared, but makes a copy instead. Does not cache. ItemPtr item(ItemDescriptor descriptor, Maybe level = {}, Maybe seed = {}, bool ignoreInvalid = false) const; bool hasRecipeToMake(ItemDescriptor const& item) const; bool hasRecipeToMake(ItemDescriptor const& item, StringSet const& allowedTypes) const; HashSet recipesForOutputItem(String itemName) const; HashSet recipesFromBagContents(List const& bag, StringMap const& availableCurrencies) const; HashSet recipesFromBagContents(HashMap const& bag, StringMap const& availableCurrencies) const; HashSet recipesFromBagContents(List const& bag, StringMap const& availableCurrencies, StringSet const& allowedTypes) const; HashSet recipesFromBagContents(HashMap const& bag, StringMap const& availableCurrencies, StringSet const& allowedTypes) const; uint64_t maxCraftableInBag(List const& bag, StringMap const& availableCurrencies, ItemRecipe const& recipe) const; uint64_t maxCraftableInBag(HashMap const& bag, StringMap const& availableCurrencies, ItemRecipe const& recipe) const; ItemRecipe getPreciseRecipeForMaterials(String const& group, List const& bag, StringMap const& availableCurrencies) const; ItemRecipe parseRecipe(Json const& config) const; HashSet const& allRecipes() const; HashSet allRecipes(StringSet const& types) const; ItemPtr applyAugment(ItemPtr const item, AugmentItem* augment) const; bool ageItem(ItemPtr& item, double aging) const; List allItems() const; private: struct ItemData { ItemType type; String name; String friendlyName; StringSet itemTags; StringList agingScripts; Maybe assetsConfig; JsonObject customConfig; String directory; }; static ItemPtr createItem(ItemType type, ItemConfig const& config); ItemPtr tryCreateItem(ItemDescriptor const& descriptor, Maybe level = {}, Maybe seed = {}, bool ignoreInvalid = false) const; ItemData const& itemData(String const& name) const; ItemRecipe makeRecipe(List inputs, ItemDescriptor output, float duration, StringSet groups) const; void addItemSet(ItemType type, String const& extension); void addObjectDropItem(String const& objectPath, Json const& objectConfig); void scanItems(); void addObjectItems(); void scanRecipes(); void addBlueprints(); void addCodexes(); StringMap m_items; HashSet m_recipes; mutable RecursiveMutex m_luaMutex; LuaRootPtr m_luaRoot; typedef tuple, Maybe> ItemCacheEntry; mutable Mutex m_cacheMutex; mutable HashTtlCache m_itemCache; }; template bool ItemDatabase::loadItem(ItemDescriptor const& descriptor, shared_ptr& itemPtr) const { if (descriptor.isNull()) { if (itemPtr) { itemPtr.reset(); return true; } } else { if (!itemPtr || !itemPtr->matches(descriptor, true)) { itemPtr = as(item(descriptor)); return true; } else if (itemPtr->count() != descriptor.count()) { itemPtr->setCount(descriptor.count()); return true; } } return false; } template bool ItemDatabase::diskLoad(Json const& diskStore, shared_ptr& itemPtr) const { try { return loadItem(ItemDescriptor::loadStore(diskStore), itemPtr); } catch (StarException const&) { return false; } } }