Adding minetest.clear_craft
authorFoghrye4 <foghrye4@gmail.com>
Mon, 25 Apr 2016 15:50:10 +0000 (18:50 +0300)
committerest31 <MTest31@outlook.com>
Tue, 5 Jul 2016 19:40:13 +0000 (21:40 +0200)
Modifications by est31: grammar fixes in doc + error messages and
a little style fix, no functional change.

doc/lua_api.txt
src/craftdef.cpp
src/craftdef.h
src/script/lua_api/l_craft.cpp
src/script/lua_api/l_craft.h

index 29756606850c332f028bd53581aa07842b9c1233..a59a9c0f2550e91870dfe47ef1009baefb5dde17 100644 (file)
@@ -1847,6 +1847,14 @@ Call these functions only at load time!
 * `minetest.register_craftitem(name, item definition)`
 * `minetest.register_alias(name, convert_to)`
 * `minetest.register_craft(recipe)`
+    * Check recipe table syntax for different types below.
+* `minetest.clear_craft(recipe)`
+    * Will erase existing craft based either on output item or on input recipe.
+    * Specify either output or input only. If you specify both, input will be ignored. For input use the same recipe table
+      syntax as for `minetest.register_craft(recipe)`. For output specify only the item, without a quantity.
+    * If no erase candidate could be found, Lua exception will be thrown.
+    * Warning! The type field ("shaped","cooking" or any other) will be ignored if the recipe
+      contains output. Erasing is then done independently from the crafting method.
 * `minetest.register_ore(ore definition)`
 * `minetest.register_decoration(decoration definition)`
 * `minetest.override_item(name, redefinition)`
index d3f1edaf9ae9707455c04ecbad8387a1d53984e7..45d3018a7b6bef1692bb1d5aad7b6334c9b64c08 100644 (file)
@@ -960,6 +960,96 @@ public:
 
                return recipes;
        }
+
+       virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef)
+       {
+               std::map<std::string, std::vector<CraftDefinition*> >::iterator vec_iter =
+                       m_output_craft_definitions.find(output.item);
+
+               if (vec_iter == m_output_craft_definitions.end())
+                       return false;
+
+               std::vector<CraftDefinition*> &vec = vec_iter->second;
+               for (std::vector<CraftDefinition*>::iterator i = vec.begin();
+                               i != vec.end(); ++i) {
+                       CraftDefinition *def = *i;
+                       // 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 (std::vector<CraftDefinition*>::iterator i2 = unhashed_inputs_vec.begin();
+                                       i2 != unhashed_inputs_vec.end(); ++i2) {
+                               if (def != *i2) {
+                                       new_vec_by_input.push_back(*i2);
+                               }
+                       }
+                       m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);
+               }
+               m_output_craft_definitions.erase(output.item);
+               return true;
+       }
+
+       virtual bool clearCraftRecipesByInput(CraftMethod craft_method, unsigned int craft_grid_width,
+               const std::vector<std::string> &recipe, IGameDef *gamedef)
+       {
+               bool all_empty = true;
+               for (std::vector<std::string>::size_type i = 0;
+                               i < recipe.size(); i++) {
+                       if (!recipe[i].empty()) {
+                               all_empty = false;
+                               break;
+                       }
+               }
+               if (all_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;
+               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. */
+                       if (!def->check(input, gamedef)) {
+                               new_vec_by_input.push_back(def);
+                               continue;
+                       }
+                       CraftOutput output = def->getOutput(input, gamedef);
+                       got_hit = true;
+                       std::map<std::string, std::vector<CraftDefinition*> >::iterator
+                               vec_iter = m_output_craft_definitions.find(output.item);
+                       if (vec_iter == 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 (std::vector<CraftDefinition*>::iterator i = vec.begin();
+                                       i != vec.end(); ++i) {
+                               /* If pointers from map by input and output are not same,
+                                       we will add 'CraftDefinition*' to a new vector. */
+                               if (def != *i) {
+                                       /* Adding dereferenced iterator value (which are
+                                               'CraftDefinition' reference) to a new vector. */
+                                       new_vec_by_output.push_back(*i);
+                               }
+                       }
+                       // Swaps assigned to current key value with new vector for output map.
+                       m_output_craft_definitions[output.item].swap(new_vec_by_output);
+               }
+               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);
+
+               return got_hit;
+       }
+
        virtual std::string dump() const
        {
                std::ostringstream os(std::ios::binary);
index cebb2d7ae2d82771b1d174f8704c757ed7f44763..695ee0c2c2fa1f7aebac0e516b1a4b32c3abacf3 100644 (file)
@@ -426,6 +426,10 @@ public:
        virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
                        IGameDef *gamedef, unsigned limit=0) const=0;
 
+       virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef) = 0;
+       virtual bool clearCraftRecipesByInput(CraftMethod craft_method,
+                       unsigned int craft_grid_width, const std::vector<std::string> &recipe, IGameDef *gamedef) = 0;
+
        // Print crafting recipes for debugging
        virtual std::string dump() const=0;
 
index 391a0133d9e1172731dd5a7823499d4816e56f45..d135c689f86a8f0ffdd759e29d8fd7c8effa5452 100644 (file)
@@ -34,7 +34,6 @@ struct EnumString ModApiCraft::es_CraftMethod[] =
        {0, NULL},
 };
 
-
 // helper for register_craft
 bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
                int &width, std::vector<std::string> &recipe)
@@ -281,6 +280,80 @@ int ModApiCraft::l_register_craft(lua_State *L)
        return 0; /* number of results */
 }
 
+// clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}])
+int ModApiCraft::l_clear_craft(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+       luaL_checktype(L, 1, LUA_TTABLE);
+       int table = 1;
+
+       // Get the writable craft definition manager from the server
+       IWritableCraftDefManager *craftdef =
+                       getServer(L)->getWritableCraftDefManager();
+
+       std::string output = getstringfield_default(L, table, "output", "");
+       std::string type = getstringfield_default(L, table, "type", "shaped");
+       CraftOutput c_output(output, 0);
+       if (output != "") {
+               if (craftdef->clearCraftRecipesByOutput(c_output, getServer(L)))
+                       return 0;
+               else
+                       throw LuaError("No craft recipe known for output"
+                                       " (output=\"" + output + "\")");
+       }
+       std::vector<std::string> recipe;
+       int width = 0;
+       CraftMethod method = CRAFT_METHOD_NORMAL;
+       /*
+               CraftDefinitionShaped
+       */
+       if (type == "shaped") {
+               lua_getfield(L, table, "recipe");
+               if (lua_isnil(L, -1))
+                       throw LuaError("Either output or recipe has to be defined");
+               if (!readCraftRecipeShaped(L, -1, width, recipe))
+                       throw LuaError("Invalid crafting recipe");
+       }
+       /*
+               CraftDefinitionShapeless
+       */
+       else if (type == "shapeless") {
+               lua_getfield(L, table, "recipe");
+               if (lua_isnil(L, -1))
+                       throw LuaError("Either output or recipe has to be defined");
+               if (!readCraftRecipeShapeless(L, -1, recipe))
+                       throw LuaError("Invalid crafting recipe");
+       }
+       /*
+               CraftDefinitionCooking
+       */
+       else if (type == "cooking") {
+               method = CRAFT_METHOD_COOKING;
+               std::string rec = getstringfield_default(L, table, "recipe", "");
+               if (rec == "")
+                       throw LuaError("Crafting definition (cooking)"
+                                       " is missing a recipe");
+               recipe.push_back(rec);
+       }
+       /*
+               CraftDefinitionFuel
+       */
+       else if (type == "fuel") {
+               method = CRAFT_METHOD_FUEL;
+               std::string rec = getstringfield_default(L, table, "recipe", "");
+               if (rec == "")
+                       throw LuaError("Crafting definition (fuel)"
+                                       " is missing a recipe");
+               recipe.push_back(rec);
+       } else {
+               throw LuaError("Unknown crafting definition type: \"" + type + "\"");
+       }
+       if (!craftdef->clearCraftRecipesByInput(method, width, recipe, getServer(L)))
+               throw LuaError("No crafting specified for input");
+       lua_pop(L, 1);
+       return 0;
+}
+
 // get_craft_result(input)
 int ModApiCraft::l_get_craft_result(lua_State *L)
 {
@@ -431,4 +504,5 @@ void ModApiCraft::Initialize(lua_State *L, int top)
        API_FCT(get_craft_recipe);
        API_FCT(get_craft_result);
        API_FCT(register_craft);
+       API_FCT(clear_craft);
 }
index 548608776ef6aebd253ea7ab70ef5adc486256f2..eb2bce7065fbe385e3e868baa0fe3a45e5f799ff 100644 (file)
@@ -33,6 +33,7 @@ private:
        static int l_get_craft_recipe(lua_State *L);
        static int l_get_all_craft_recipes(lua_State *L);
        static int l_get_craft_result(lua_State *L);
+       static int l_clear_craft(lua_State *L);
 
        static bool readCraftReplacements(lua_State *L, int index,
                        CraftReplacements &replacements);