aboutsummaryrefslogtreecommitdiff
path: root/games/minimal/mods
Commit message (Expand)AuthorAge
* Add Mapgen V7, reorganize biomeskwolekr2013-04-07
* unkn own block -> unkn own nodekhonkhortisan2013-04-05
* Add different place sound for nodesPilzAdam2013-03-29
* Use minetest.register_ore() in minimalPilzAdam2013-03-24
* Mapgen indev: float islands, larger far biomesproller2013-03-24
* Liquid fine tuningproller2013-03-14
* new adjustable finite liquidproller2013-02-24
* Readded and optimized mapgen V6kwolekr2013-01-21
* Add initial Lua biomedef support, fixed biome selectionkwolekr2013-01-21
* Add the group attached_nodePilzAdam2012-12-01
* Swap out pixel-perfect nyan cat by request of Chris TorresPerttu Ahola2012-11-09
* Add functions to the default mod of minimal game to support old codePilzAdam2012-11-01
* Move falling to builtinPilzAdam2012-10-31
* Fix crash when furnace is full (minimal game)Perttu Ahola2012-08-12
* Deprecate minetest.add_to_creative_inventory and use group not_in_creative_in...Perttu Ahola2012-07-25
* Add notice in the minimal gamePerttu Ahola2012-07-25
* Improve inventory callbacks a bitPerttu Ahola2012-07-25
* Detached inventory callbacks and reworked node metadata callbacksPerttu Ahola2012-07-25
* Detached inventoriesPerttu Ahola2012-07-24
* Add node timer test in minimal/experimentalPerttu Ahola2012-07-24
* Move /give, /giveme, /spawnentity and /pulverize to builtin/chatcommands.luaPerttu Ahola2012-07-23
* Formspec button_exit[] and image_button_exit[]Perttu Ahola2012-07-22
* Add /test1 command to minimal for testing a more complicated player inventory...Perttu Ahola2012-07-22
* Implement formspecdarkrose2012-07-22
* Actually fix facedir-rotated nodes placed using minetest.env:place_node()Perttu Ahola2012-07-21
* Make lava buckets work as fuel in minimal gamedarkrose2012-07-21
* Allow defining player's inventory form in LuaPerttu Ahola2012-07-19
* Custom boxy nodes (stairs, slabs) and collision changesKahrl2012-06-17
* Revert back proper crack texturePerttu Ahola2012-06-16
* Allow node cracking animations of any lengthPerttu Ahola2012-06-16
* Update field names to non-deprecated ones in node definition prototypePerttu Ahola2012-06-16
* Use new field names and reorder fields a bit in minimal gamePerttu Ahola2012-06-16
* Node texture animationPerttu Ahola2012-06-16
* Add experimental_tester_tool_1.png to minimal game (was accidentally left out)Perttu Ahola2012-06-08
* Allow groups in crafting recipesPerttu Ahola2012-06-06
* Add after_destruct and cache the existence of on_construct, on_destruct and a...Perttu Ahola2012-06-05
* place_node, dig_node and punch_node; an in-game tester tool; remove old codePerttu Ahola2012-06-05
* Add InvRef:is_empty(listname) and make chests/furnaces not diggable if not em...darkrose2012-06-03
* fix locked chest to not destroy denied items (minimal game)darkrose2012-06-03
* Add fire visualization to minimal furnace menuPerttu Ahola2012-06-03
* Use proper furnace cook timePerttu Ahola2012-06-03
* Lua implementation of furnace with visible active statedarkrose2012-06-03
* Implement locked chest; add after_place_node and after_dig_node node callbacksPerttu Ahola2012-06-03
* minetest.get_craft_resultPerttu Ahola2012-06-03
* Implement sign using form field protocolPerttu Ahola2012-06-03
* Properly create metadata inventories in minimalPerttu Ahola2012-06-03
* Random node metadata thingsPerttu Ahola2012-06-03
* Attempt to begin to implement chests and furnace in Lua (with problems)Perttu Ahola2012-06-03
* Add missing mapgen.lua to games/minimalPerttu Ahola2012-04-06
* experimental:soundblock dig_immediate=3Perttu Ahola2012-04-06
id='n451' href='#n451'>451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


#include "lua_api/l_craft.h"
#include "lua_api/l_internal.h"
#include "lua_api/l_item.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "server.h"
#include "craftdef.h"

struct EnumString ModApiCraft::es_CraftMethod[] =
{
	{CRAFT_METHOD_NORMAL, "normal"},
	{CRAFT_METHOD_COOKING, "cooking"},
	{CRAFT_METHOD_FUEL, "fuel"},
	{0, NULL},
};

// helper for register_craft
bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
		int &width, std::vector<std::string> &recipe)
{
	if(index < 0)
		index = lua_gettop(L) + 1 + index;

	if(!lua_istable(L, index))
		return false;

	lua_pushnil(L);
	int rowcount = 0;
	while(lua_next(L, index) != 0){
		int colcount = 0;
		// key at index -2 and value at index -1
		if(!lua_istable(L, -1))
			return false;
		int table2 = lua_gettop(L);
		lua_pushnil(L);
		while(lua_next(L, table2) != 0){
			// key at index -2 and value at index -1
			if(!lua_isstring(L, -1))
				return false;
			recipe.push_back(lua_tostring(L, -1));
			// removes value, keeps key for next iteration
			lua_pop(L, 1);
			colcount++;
		}
		if(rowcount == 0){
			width = colcount;
		} else {
			if(colcount != width)
				return false;
		}
		// removes value, keeps key for next iteration
		lua_pop(L, 1);
		rowcount++;
	}
	return width != 0;
}

// helper for register_craft
bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index,
		std::vector<std::string> &recipe)
{
	if(index < 0)
		index = lua_gettop(L) + 1 + index;

	if(!lua_istable(L, index))
		return false;

	lua_pushnil(L);
	while(lua_next(L, index) != 0){
		// key at index -2 and value at index -1
		if(!lua_isstring(L, -1))
			return false;
		recipe.push_back(lua_tostring(L, -1));
		// removes value, keeps key for next iteration
		lua_pop(L, 1);
	}
	return true;
}

// helper for register_craft
bool ModApiCraft::readCraftReplacements(lua_State *L, int index,
		CraftReplacements &replacements)
{
	if(index < 0)
		index = lua_gettop(L) + 1 + index;

	if(!lua_istable(L, index))
		return false;

	lua_pushnil(L);
	while(lua_next(L, index) != 0){
		// key at index -2 and value at index -1
		if(!lua_istable(L, -1))
			return false;
		lua_rawgeti(L, -1, 1);
		if(!lua_isstring(L, -1))
			return false;
		std::string replace_from = lua_tostring(L, -1);
		lua_pop(L, 1);
		lua_rawgeti(L, -1, 2);
		if(!lua_isstring(L, -1))
			return false;
		std::string replace_to = lua_tostring(L, -1);
		lua_pop(L, 1);
		replacements.pairs.push_back(
				std::make_pair(replace_from, replace_to));
		// removes value, keeps key for next iteration
		lua_pop(L, 1);
	}
	return true;
}
// register_craft({output=item, recipe={{item00,item10},{item01,item11}})
int ModApiCraft::l_register_craft(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	//infostream<<"register_craft"<<std::endl;
	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 type = getstringfield_default(L, table, "type", "shaped");

	/*
		CraftDefinitionShaped
	*/
	if(type == "shaped"){
		std::string output = getstringfield_default(L, table, "output", "");
		if(output == "")
			throw LuaError("Crafting definition is missing an output");

		int width = 0;
		std::vector<std::string> recipe;
		lua_getfield(L, table, "recipe");
		if(lua_isnil(L, -1))
			throw LuaError("Crafting definition is missing a recipe"
					" (output=\"" + output + "\")");
		if(!readCraftRecipeShaped(L, -1, width, recipe))
			throw LuaError("Invalid crafting recipe"
					" (output=\"" + output + "\")");

		CraftReplacements replacements;
		lua_getfield(L, table, "replacements");
		if(!lua_isnil(L, -1))
		{
			if(!readCraftReplacements(L, -1, replacements))
				throw LuaError("Invalid replacements"
						" (output=\"" + output + "\")");
		}

		CraftDefinition *def = new CraftDefinitionShaped(
				output, width, recipe, replacements);
		craftdef->registerCraft(def, getServer(L));
	}
	/*
		CraftDefinitionShapeless
	*/
	else if(type == "shapeless"){
		std::string output = getstringfield_default(L, table, "output", "");
		if(output == "")
			throw LuaError("Crafting definition (shapeless)"
					" is missing an output");

		std::vector<std::string> recipe;
		lua_getfield(L, table, "recipe");
		if(lua_isnil(L, -1))
			throw LuaError("Crafting definition (shapeless)"
					" is missing a recipe"
					" (output=\"" + output + "\")");
		if(!readCraftRecipeShapeless(L, -1, recipe))
			throw LuaError("Invalid crafting recipe"
					" (output=\"" + output + "\")");

		CraftReplacements replacements;
		lua_getfield(L, table, "replacements");
		if(!lua_isnil(L, -1))
		{
			if(!readCraftReplacements(L, -1, replacements))
				throw LuaError("Invalid replacements"
						" (output=\"" + output + "\")");
		}

		CraftDefinition *def = new CraftDefinitionShapeless(
				output, recipe, replacements);
		craftdef->registerCraft(def, getServer(L));
	}
	/*
		CraftDefinitionToolRepair
	*/
	else if(type == "toolrepair"){
		float additional_wear = getfloatfield_default(L, table,
				"additional_wear", 0.0);

		CraftDefinition *def = new CraftDefinitionToolRepair(
				additional_wear);
		craftdef->registerCraft(def, getServer(L));
	}
	/*
		CraftDefinitionCooking
	*/
	else if(type == "cooking"){
		std::string output = getstringfield_default(L, table, "output", "");
		if(output == "")
			throw LuaError("Crafting definition (cooking)"
					" is missing an output");

		std::string recipe = getstringfield_default(L, table, "recipe", "");
		if(recipe == "")
			throw LuaError("Crafting definition (cooking)"
					" is missing a recipe"
					" (output=\"" + output + "\")");

		float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);

		CraftReplacements replacements;
		lua_getfield(L, table, "replacements");
		if(!lua_isnil(L, -1))
		{
			if(!readCraftReplacements(L, -1, replacements))
				throw LuaError("Invalid replacements"
						" (cooking output=\"" + output + "\")");
		}

		CraftDefinition *def = new CraftDefinitionCooking(
				output, recipe, cooktime, replacements);
		craftdef->registerCraft(def, getServer(L));
	}
	/*
		CraftDefinitionFuel
	*/
	else if(type == "fuel"){
		std::string recipe = getstringfield_default(L, table, "recipe", "");
		if(recipe == "")
			throw LuaError("Crafting definition (fuel)"
					" is missing a recipe");

		float burntime = getfloatfield_default(L, table, "burntime", 1.0);

		CraftReplacements replacements;
		lua_getfield(L, table, "replacements");
		if(!lua_isnil(L, -1))
		{
			if(!readCraftReplacements(L, -1, replacements))
				throw LuaError("Invalid replacements"
						" (fuel recipe=\"" + recipe + "\")");
		}

		CraftDefinition *def = new CraftDefinitionFuel(
				recipe, burntime, replacements);
		craftdef->registerCraft(def, getServer(L));
	}
	else
	{
		throw LuaError("Unknown crafting definition type: \"" + type + "\"");
	}

	lua_pop(L, 1);
	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)
{
	NO_MAP_LOCK_REQUIRED;

	int input_i = 1;
	std::string method_s = getstringfield_default(L, input_i, "method", "normal");
	enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
				es_CraftMethod, CRAFT_METHOD_NORMAL);
	int width = 1;
	lua_getfield(L, input_i, "width");
	if(lua_isnumber(L, -1))
		width = luaL_checkinteger(L, -1);
	lua_pop(L, 1);
	lua_getfield(L, input_i, "items");
	std::vector<ItemStack> items = read_items(L, -1,getServer(L));
	lua_pop(L, 1); // items

	IGameDef *gdef = getServer(L);
	ICraftDefManager *cdef = gdef->cdef();
	CraftInput input(method, width, items);
	CraftOutput output;
	std::vector<ItemStack> output_replacements;
	bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
	lua_newtable(L); // output table
	if (got) {
		ItemStack item;
		item.deSerialize(output.item, gdef->idef());
		LuaItemStack::create(L, item);
		lua_setfield(L, -2, "item");
		setintfield(L, -1, "time", output.time);
		push_items(L, output_replacements);
		lua_setfield(L, -2, "replacements");
	} else {
		LuaItemStack::create(L, ItemStack());
		lua_setfield(L, -2, "item");
		setintfield(L, -1, "time", 0);
		lua_newtable(L);
		lua_setfield(L, -2, "replacements");
	}
	lua_newtable(L); // decremented input table
	lua_pushstring(L, method_s.c_str());
	lua_setfield(L, -2, "method");
	lua_pushinteger(L, width);
	lua_setfield(L, -2, "width");
	push_items(L, input.items);
	lua_setfield(L, -2, "items");
	return 2;
}


static void push_craft_recipe(lua_State *L, IGameDef *gdef,
		const CraftDefinition *recipe,
		const CraftOutput &tmpout)
{
	CraftInput input = recipe->getInput(tmpout, gdef);
	CraftOutput output = recipe->getOutput(input, gdef);

	lua_newtable(L); // items
	std::vector<ItemStack>::const_iterator iter = input.items.begin();
	for (u16 j = 1; iter != input.items.end(); iter++, j++) {
		if (iter->empty())
			continue;
		lua_pushstring(L, iter->name.c_str());
		lua_rawseti(L, -2, j);
	}
	lua_setfield(L, -2, "items");
	setintfield(L, -1, "width", input.width);

	std::string method_s;
	switch (input.method) {
	case CRAFT_METHOD_NORMAL:
		method_s = "normal";
		break;
	case CRAFT_METHOD_COOKING:
		method_s = "cooking";
		break;
	case CRAFT_METHOD_FUEL:
		method_s = "fuel";
		break;
	default:
		method_s = "unknown";
	}
	lua_pushstring(L, method_s.c_str());
	lua_setfield(L, -2, "method");

	// Deprecated, only for compatibility's sake
	lua_pushstring(L, method_s.c_str());
	lua_setfield(L, -2, "type");

	lua_pushstring(L, output.item.c_str());
	lua_setfield(L, -2, "output");
}

static void push_craft_recipes(lua_State *L, IGameDef *gdef,
		const std::vector<CraftDefinition*> &recipes,
		const CraftOutput &output)
{
	lua_createtable(L, recipes.size(), 0);

	if (recipes.empty()) {
		lua_pushnil(L);
		return;
	}

	std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
	for (unsigned i = 0; it != recipes.end(); ++it) {
		lua_newtable(L);
		push_craft_recipe(L, gdef, *it, output);
		lua_rawseti(L, -2, ++i);
	}
}


// get_craft_recipe(result item)