]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/craftdef.cpp
Don't include client/game.h on server build
[dragonfireclient.git] / src / craftdef.cpp
index 077d7e0443b397eebf26d4015671eb28d7b6bcd5..c05a0cfb75c57984121425015e35c0ffdf7d0563 100644 (file)
@@ -37,6 +37,15 @@ inline bool isGroupRecipeStr(const std::string &rec_name)
        return str_starts_with(rec_name, std::string("group:"));
 }
 
+static bool hasGroupItem(const std::vector<std::string> &recipe)
+{
+       for (const auto &item : recipe) {
+               if (isGroupRecipeStr(item))
+                       return true;
+       }
+       return false;
+}
+
 inline u64 getHashForString(const std::string &recipe_str)
 {
        /*errorstream << "Hashing craft string  \"" << recipe_str << '"';*/
@@ -278,6 +287,15 @@ std::string craftDumpMatrix(const std::vector<ItemStack> &items,
        CraftInput
 */
 
+bool CraftInput::empty() const
+{
+       for (const auto &item : items) {
+               if (!item.empty())
+                       return false;
+       }
+       return true;
+}
+
 std::string CraftInput::dump() const
 {
        std::ostringstream os(std::ios::binary);
@@ -320,6 +338,19 @@ std::string CraftReplacements::dump() const
        CraftDefinitionShaped
 */
 
+CraftDefinitionShaped::CraftDefinitionShaped(
+               const std::string &output_,
+               unsigned int width_,
+               const std::vector<std::string> &recipe_,
+               const CraftReplacements &replacements_):
+       output(output_), width(width_), recipe(recipe_), replacements(replacements_)
+{
+       if (hasGroupItem(recipe))
+               priority = PRIORITY_SHAPED_AND_GROUPS;
+       else
+               priority = PRIORITY_SHAPED;
+}
+
 std::string CraftDefinitionShaped::getName() const
 {
        return "shaped";
@@ -407,22 +438,6 @@ void CraftDefinitionShaped::decrementInput(CraftInput &input, std::vector<ItemSt
        craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
 }
 
-CraftHashType CraftDefinitionShaped::getHashType() const
-{
-       assert(hash_inited); // Pre-condition
-       bool has_group = false;
-       for (const auto &recipe_name : recipe_names) {
-               if (isGroupRecipeStr(recipe_name)) {
-                       has_group = true;
-                       break;
-               }
-       }
-       if (has_group)
-               return CRAFT_HASH_TYPE_COUNT;
-
-       return CRAFT_HASH_TYPE_ITEM_NAMES;
-}
-
 u64 CraftDefinitionShaped::getHash(CraftHashType type) const
 {
        assert(hash_inited); // Pre-condition
@@ -440,6 +455,11 @@ void CraftDefinitionShaped::initHash(IGameDef *gamedef)
                return;
        hash_inited = true;
        recipe_names = craftGetItemNames(recipe, gamedef);
+
+       if (hasGroupItem(recipe_names))
+               hash_type = CRAFT_HASH_TYPE_COUNT;
+       else
+               hash_type = CRAFT_HASH_TYPE_ITEM_NAMES;
 }
 
 std::string CraftDefinitionShaped::dump() const
@@ -455,6 +475,18 @@ std::string CraftDefinitionShaped::dump() const
        CraftDefinitionShapeless
 */
 
+CraftDefinitionShapeless::CraftDefinitionShapeless(
+               const std::string &output_,
+               const std::vector<std::string> &recipe_,
+               const CraftReplacements &replacements_):
+       output(output_), recipe(recipe_), replacements(replacements_)
+{
+       if (hasGroupItem(recipe))
+               priority = PRIORITY_SHAPELESS_AND_GROUPS;
+       else
+               priority = PRIORITY_SHAPELESS;
+}
+
 std::string CraftDefinitionShapeless::getName() const
 {
        return "shapeless";
@@ -527,22 +559,6 @@ void CraftDefinitionShapeless::decrementInput(CraftInput &input, std::vector<Ite
        craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
 }
 
-CraftHashType CraftDefinitionShapeless::getHashType() const
-{
-       assert(hash_inited); // Pre-condition
-       bool has_group = false;
-       for (const auto &recipe_name : recipe_names) {
-               if (isGroupRecipeStr(recipe_name)) {
-                       has_group = true;
-                       break;
-               }
-       }
-       if (has_group)
-               return CRAFT_HASH_TYPE_COUNT;
-
-       return CRAFT_HASH_TYPE_ITEM_NAMES;
-}
-
 u64 CraftDefinitionShapeless::getHash(CraftHashType type) const
 {
        assert(hash_inited); // Pre-condition
@@ -558,6 +574,11 @@ void CraftDefinitionShapeless::initHash(IGameDef *gamedef)
        hash_inited = true;
        recipe_names = craftGetItemNames(recipe, gamedef);
        std::sort(recipe_names.begin(), recipe_names.end());
+
+       if (hasGroupItem(recipe_names))
+               hash_type = CRAFT_HASH_TYPE_COUNT;
+       else
+               hash_type = CRAFT_HASH_TYPE_ITEM_NAMES;
 }
 
 std::string CraftDefinitionShapeless::dump() const
@@ -573,6 +594,12 @@ std::string CraftDefinitionShapeless::dump() const
        CraftDefinitionToolRepair
 */
 
+CraftDefinitionToolRepair::CraftDefinitionToolRepair(float additional_wear_):
+       additional_wear(additional_wear_)
+{
+       priority = PRIORITY_TOOLREPAIR;
+}
+
 static ItemStack craftToolRepair(
                const ItemStack &item1,
                const ItemStack &item2,
@@ -667,6 +694,19 @@ std::string CraftDefinitionToolRepair::dump() const
        CraftDefinitionCooking
 */
 
+CraftDefinitionCooking::CraftDefinitionCooking(
+               const std::string &output_,
+               const std::string &recipe_,
+               float cooktime_,
+               const CraftReplacements &replacements_):
+       output(output_), recipe(recipe_), cooktime(cooktime_), replacements(replacements_)
+{
+       if (isGroupRecipeStr(recipe))
+               priority = PRIORITY_SHAPELESS_AND_GROUPS;
+       else
+               priority = PRIORITY_SHAPELESS;
+}
+
 std::string CraftDefinitionCooking::getName() const
 {
        return "cooking";
@@ -694,7 +734,8 @@ bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) c
        }
 
        // Check the single input item
-       return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
+       std::string rec_name = craftGetItemName(recipe, gamedef);
+       return inputItemMatchesRecipe(input_filtered[0], rec_name, gamedef->idef());
 }
 
 CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
@@ -715,14 +756,6 @@ void CraftDefinitionCooking::decrementInput(CraftInput &input, std::vector<ItemS
        craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
 }
 
-CraftHashType CraftDefinitionCooking::getHashType() const
-{
-       if (isGroupRecipeStr(recipe_name))
-               return CRAFT_HASH_TYPE_COUNT;
-
-       return CRAFT_HASH_TYPE_ITEM_NAMES;
-}
-
 u64 CraftDefinitionCooking::getHash(CraftHashType type) const
 {
        if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
@@ -744,6 +777,11 @@ void CraftDefinitionCooking::initHash(IGameDef *gamedef)
                return;
        hash_inited = true;
        recipe_name = craftGetItemName(recipe, gamedef);
+
+       if (isGroupRecipeStr(recipe_name))
+               hash_type = CRAFT_HASH_TYPE_COUNT;
+       else
+               hash_type = CRAFT_HASH_TYPE_ITEM_NAMES;
 }
 
 std::string CraftDefinitionCooking::dump() const
@@ -760,6 +798,18 @@ std::string CraftDefinitionCooking::dump() const
        CraftDefinitionFuel
 */
 
+CraftDefinitionFuel::CraftDefinitionFuel(
+               const std::string &recipe_,
+               float burntime_,
+               const CraftReplacements &replacements_):
+       recipe(recipe_), burntime(burntime_), replacements(replacements_)
+{
+       if (isGroupRecipeStr(recipe_name))
+               priority = PRIORITY_SHAPELESS_AND_GROUPS;
+       else
+               priority = PRIORITY_SHAPELESS;
+}
+
 std::string CraftDefinitionFuel::getName() const
 {
        return "fuel";
@@ -787,7 +837,8 @@ bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) cons
        }
 
        // Check the single input item
-       return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
+       std::string rec_name = craftGetItemName(recipe, gamedef);
+       return inputItemMatchesRecipe(input_filtered[0], rec_name, gamedef->idef());
 }
 
 CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
@@ -808,14 +859,6 @@ void CraftDefinitionFuel::decrementInput(CraftInput &input, std::vector<ItemStac
        craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
 }
 
-CraftHashType CraftDefinitionFuel::getHashType() const
-{
-       if (isGroupRecipeStr(recipe_name))
-               return CRAFT_HASH_TYPE_COUNT;
-
-       return CRAFT_HASH_TYPE_ITEM_NAMES;
-}
-
 u64 CraftDefinitionFuel::getHash(CraftHashType type) const
 {
        if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
@@ -837,7 +880,13 @@ void CraftDefinitionFuel::initHash(IGameDef *gamedef)
                return;
        hash_inited = true;
        recipe_name = craftGetItemName(recipe, gamedef);
+
+       if (isGroupRecipeStr(recipe_name))
+               hash_type = CRAFT_HASH_TYPE_COUNT;
+       else
+               hash_type = CRAFT_HASH_TYPE_ITEM_NAMES;
 }
+
 std::string CraftDefinitionFuel::dump() const
 {
        std::ostringstream os(std::ios::binary);
@@ -868,25 +917,18 @@ class CCraftDefManager: public IWritableCraftDefManager
                        std::vector<ItemStack> &output_replacement, bool decrementInput,
                        IGameDef *gamedef) const
        {
-               output.item = "";
-               output.time = 0;
-
-               // If all input items are empty, abort.
-               bool all_empty = true;
-               for (const auto &item : input.items) {
-                       if (!item.empty()) {
-                               all_empty = false;
-                               break;
-                       }
-               }
-               if (all_empty)
+               if (input.empty())
                        return false;
 
                std::vector<std::string> input_names;
                input_names = craftGetItemNames(input.items, gamedef);
                std::sort(input_names.begin(), input_names.end());
 
-               // Try hash types with increasing collision rate, and return if found.
+               // Try hash types with increasing collision rate
+               // while remembering the latest, highest priority recipe.
+               CraftDefinition::RecipePriority priority_best =
+                       CraftDefinition::PRIORITY_NO_RECIPE;
+               CraftDefinition *def_best = nullptr;
                for (int type = 0; type <= craft_hash_type_max; type++) {
                        u64 hash = getHashForGrid((CraftHashType) type, input_names);
 
@@ -909,7 +951,9 @@ class CCraftDefManager: public IWritableCraftDefManager
                                /*errorstream << "Checking " << input.dump() << std::endl
                                        << " against " << def->dump() << std::endl;*/
 
-                               if (def->check(input, gamedef)) {
+                               CraftDefinition::RecipePriority priority = def->getPriority();
+                               if (priority > priority_best
+                                               && def->check(input, gamedef)) {
                                        // Check if the crafted node/item exists
                                        CraftOutput out = def->getOutput(input, gamedef);
                                        ItemStack is;
@@ -920,17 +964,17 @@ class CCraftDefManager: public IWritableCraftDefManager
                                                continue;
                                        }
 
-                                       // Get output, then decrement input (if requested)
                                        output = out;
-                    
-                                       if (decrementInput)
-                                               def->decrementInput(input, output_replacement, gamedef);
-                                       /*errorstream << "Check RETURNS TRUE" << std::endl;*/
-                                       return true;
+                                       priority_best = priority;
+                                       def_best = def;
                                }
                        }
                }
-               return false;
+               if (priority_best == CraftDefinition::PRIORITY_NO_RECIPE)
+                       return false;
+               if (decrementInput)
+                       def_best->decrementInput(input, output_replacement, gamedef);
+               return true;
        }
 
        virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
@@ -958,84 +1002,48 @@ class CCraftDefManager: public IWritableCraftDefManager
                return recipes;
        }
 
-       virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef)
+       virtual bool clearCraftsByOutput(const CraftOutput &output, IGameDef *gamedef)
        {
-               auto vec_iter = m_output_craft_definitions.find(output.item);
+               auto to_clear = m_output_craft_definitions.find(output.item);
 
-               if (vec_iter == m_output_craft_definitions.end())
+               if (to_clear == m_output_craft_definitions.end())
                        return false;
 
-               std::vector<CraftDefinition*> &vec = vec_iter->second;
-               for (auto def : vec) {
+               for (auto def : to_clear->second) {
                        // Recipes are not yet hashed at this point
-                       std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
-                       std::vector<CraftDefinition*> new_vec_by_input;
-                       /* We will preallocate necessary memory addresses, so we don't need to reallocate them later.
-                               This would save us some performance. */
-                       new_vec_by_input.reserve(unhashed_inputs_vec.size());
-                       for (auto &i2 : unhashed_inputs_vec) {
-                               if (def != i2) {
-                                       new_vec_by_input.push_back(i2);
-                               }
-                       }
-                       m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);
+                       std::vector<CraftDefinition *> &defs = m_craft_defs[(int)CRAFT_HASH_TYPE_UNHASHED][0];
+                       defs.erase(std::remove(defs.begin(), defs.end(), def), defs.end());
+                       delete def;
                }
-               m_output_craft_definitions.erase(output.item);
+               m_output_craft_definitions.erase(to_clear);
                return true;
        }
 
-       virtual bool clearCraftRecipesByInput(CraftMethod craft_method, unsigned int craft_grid_width,
-               const std::vector<std::string> &recipe, IGameDef *gamedef)
+       virtual bool clearCraftsByInput(const CraftInput &input, IGameDef *gamedef)
        {
-               bool all_empty = true;
-               for (const auto &i : recipe) {
-                       if (!i.empty()) {
-                               all_empty = false;
-                               break;
-                       }
-               }
-               if (all_empty)
+               if (input.empty())
                        return false;
 
-               CraftInput input(craft_method, craft_grid_width, craftGetItems(recipe, gamedef));
                // Recipes are not yet hashed at this point
-               std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
-               std::vector<CraftDefinition*> new_vec_by_input;
+               std::vector<CraftDefinition *> &defs = m_craft_defs[(int)CRAFT_HASH_TYPE_UNHASHED][0];
+               std::vector<CraftDefinition *> new_defs;
                bool got_hit = false;
-               for (std::vector<CraftDefinition*>::size_type
-                               i = unhashed_inputs_vec.size(); i > 0; i--) {
-                       CraftDefinition *def = unhashed_inputs_vec[i - 1];
-                       /* If the input doesn't match the recipe definition, this recipe definition later
-                               will be added back in source map. */
+               for (auto def : defs) {
                        if (!def->check(input, gamedef)) {
-                               new_vec_by_input.push_back(def);
+                               new_defs.push_back(def);
                                continue;
                        }
-                       CraftOutput output = def->getOutput(input, gamedef);
                        got_hit = true;
-                       auto vec_iter = m_output_craft_definitions.find(output.item);
-                       if (vec_iter == m_output_craft_definitions.end())
+                       std::string output = def->getOutput(input, gamedef).item;
+                       delete def;
+                       auto it = m_output_craft_definitions.find(craftGetItemName(output, gamedef));
+                       if (it == m_output_craft_definitions.end())
                                continue;
-                       std::vector<CraftDefinition*> &vec = vec_iter->second;
-                       std::vector<CraftDefinition*> new_vec_by_output;
-                       /* We will preallocate necessary memory addresses, so we don't need
-                               to reallocate them later. This would save us some performance. */
-                       new_vec_by_output.reserve(vec.size());
-                       for (auto &vec_i : vec) {
-                               /* If pointers from map by input and output are not same,
-                                       we will add 'CraftDefinition*' to a new vector. */
-                               if (def != vec_i) {
-                                       /* Adding dereferenced iterator value (which are
-                                               'CraftDefinition' reference) to a new vector. */
-                                       new_vec_by_output.push_back(vec_i);
-                               }
-                       }
-                       // Swaps assigned to current key value with new vector for output map.
-                       m_output_craft_definitions[output.item].swap(new_vec_by_output);
+                       std::vector<CraftDefinition *> &outdefs = it->second;
+                       outdefs.erase(std::remove(outdefs.begin(), outdefs.end(), def), outdefs.end());
                }
                if (got_hit)
-                       // Swaps value with new vector for input map.
-                       m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);
+                       defs.swap(new_defs);
 
                return got_hit;
        }
@@ -1060,8 +1068,8 @@ class CCraftDefManager: public IWritableCraftDefManager
        }
        virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef)
        {
-               verbosestream << "registerCraft: registering craft definition: "
-                               << def->dump() << std::endl;
+               TRACESTREAM(<< "registerCraft: registering craft definition: "
+                               << def->dump() << std::endl);
                m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def);
 
                CraftInput input;
@@ -1099,9 +1107,10 @@ class CCraftDefManager: public IWritableCraftDefManager
                unhashed.clear();
        }
 private:
-       //TODO: change both maps to unordered_map when c++11 can be used
-       std::vector<std::map<u64, std::vector<CraftDefinition*> > > m_craft_defs;
-       std::map<std::string, std::vector<CraftDefinition*> > m_output_craft_definitions;
+       std::vector<std::unordered_map<u64, std::vector<CraftDefinition*> > >
+               m_craft_defs;
+       std::unordered_map<std::string, std::vector<CraftDefinition*> >
+               m_output_craft_definitions;
 };
 
 IWritableCraftDefManager* createCraftDefManager()