aboutsummaryrefslogtreecommitdiff
path: root/games/minimal/mods
Commit message (Collapse)AuthorAge
* Add support for different drowning damage and allow drowning in other nodetypesBlockMen2013-08-06
|
* Add drowningPilzAdam2013-06-19
|
* Compress texturesDavid Gumberg2013-06-18
|
* Move scriptapi to separate folder (by sapier)sapier2013-05-25
| | | | | | | | | | | On the lua side, notably minetest.env:<function>(<args>) should now be replaced by minetest.<function>(<args>). The old way is and will stay supported for a long time. Also: Update and clean up lua_api.txt (by celeron55) Move EnvRef to lua and remove add_rat and add_firefly (by kahrl) Add separate src/util/CMakeLists.txt, other minor fixes (by kahrl)
* Use the group "soil" for nodes that saplings grow onShadowNinja2013-05-20
|
* 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
| | | | Nodes in this group will be dropped as items if the node under them or the node in the wallmounted direction is not walkable.
* 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 ↵Perttu Ahola2012-07-25
| | | | not_in_creative_inventory instead
* 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 ↵Perttu Ahola2012-07-22
| | | | inventory form
* 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 ↵Perttu Ahola2012-06-05
| | | | after_destruct for quick skipping when a node does not have them
* 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 ↵darkrose2012-06-03
| | | | empty in minimal game
* 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
|
48' href='#n748'>748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
/*
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_mapgen.h"
#include "lua_api/l_internal.h"
#include "lua_api/l_vmanip.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "util/serialize.h"
#include "server.h"
#include "environment.h"
#include "emerge.h"
#include "mg_biome.h"
#include "mg_ore.h"
#include "mg_decoration.h"
#include "mg_schematic.h"
#include "mapgen_v5.h"
#include "mapgen_v7.h"
#include "settings.h"
#include "main.h"
#include "log.h"


struct EnumString ModApiMapgen::es_BiomeTerrainType[] =
{
	{BIOME_TYPE_NORMAL, "normal"},
	{BIOME_TYPE_LIQUID, "liquid"},
	{BIOME_TYPE_NETHER, "nether"},
	{BIOME_TYPE_AETHER, "aether"},
	{BIOME_TYPE_FLAT,   "flat"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_DecorationType[] =
{
	{DECO_SIMPLE,    "simple"},
	{DECO_SCHEMATIC, "schematic"},
	{DECO_LSYSTEM,   "lsystem"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_MapgenObject[] =
{
	{MGOBJ_VMANIP,    "voxelmanip"},
	{MGOBJ_HEIGHTMAP, "heightmap"},
	{MGOBJ_BIOMEMAP,  "biomemap"},
	{MGOBJ_HEATMAP,   "heatmap"},
	{MGOBJ_HUMIDMAP,  "humiditymap"},
	{MGOBJ_GENNOTIFY, "gennotify"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_OreType[] =
{
	{ORE_TYPE_SCATTER, "scatter"},
	{ORE_TYPE_SHEET,   "sheet"},
	{ORE_TYPE_BLOB,    "blob"},
	{ORE_TYPE_VEIN,    "vein"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_Rotation[] =
{
	{ROTATE_0,    "0"},
	{ROTATE_90,   "90"},
	{ROTATE_180,  "180"},
	{ROTATE_270,  "270"},
	{ROTATE_RAND, "random"},
	{0, NULL},
};


///////////////////////////////////////////////////////////////////////////////


bool read_schematic(lua_State *L, int index, Schematic *schem,
	INodeDefManager *ndef, std::map<std::string, std::string> &replace_names)
{
	//// Get schematic size
	lua_getfield(L, index, "size");
	v3s16 size = read_v3s16(L, -1);
	lua_pop(L, 1);

	//// Get schematic data
	lua_getfield(L, index, "data");
	luaL_checktype(L, -1, LUA_TTABLE);

	int numnodes = size.X * size.Y * size.Z;
	MapNode *schemdata = new MapNode[numnodes];
	int i = 0;

	lua_pushnil(L);
	while (lua_next(L, -2)) {
		if (i >= numnodes) {
			i++;
			lua_pop(L, 1);
			continue;
		}

		// same as readnode, except param1 default is MTSCHEM_PROB_CONST
		lua_getfield(L, -1, "name");
		std::string name = luaL_checkstring(L, -1);
		lua_pop(L, 1);

		u8 param1;
		lua_getfield(L, -1, "param1");
		param1 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : MTSCHEM_PROB_ALWAYS;
		lua_pop(L, 1);

		u8 param2;
		lua_getfield(L, -1, "param2");
		param2 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : 0;
		lua_pop(L, 1);

		std::map<std::string, std::string>::iterator it;
		it = replace_names.find(name);
		if (it != replace_names.end())
			name = it->second;

		schemdata[i] = MapNode(ndef, name, param1, param2);

		i++;
		lua_pop(L, 1);
	}

	if (i != numnodes) {
		errorstream << "read_schematic: incorrect number of "
			"nodes provided in raw schematic data (got " << i <<
			", expected " << numnodes << ")." << std::endl;
		delete schemdata;
		return false;
	}

	//// Get Y-slice probability values (if present)
	u8 *slice_probs = new u8[size.Y];
	for (i = 0; i != size.Y; i++)
		slice_probs[i] = MTSCHEM_PROB_ALWAYS;

	lua_getfield(L, index, "yslice_prob");
	if (lua_istable(L, -1)) {
		lua_pushnil(L);
		while (lua_next(L, -2)) {
			if (getintfield(L, -1, "ypos", i) && i >= 0 && i < size.Y) {
				slice_probs[i] = getintfield_default(L, -1,
					"prob", MTSCHEM_PROB_ALWAYS);
			}
			lua_pop(L, 1);
		}
	}

	// Here, we read the nodes directly from the INodeDefManager - there is no
	// need for pending node resolutions so we'll mark this schematic as updated
	schem->flags       = SCHEM_CIDS_UPDATED;

	schem->size        = size;
	schem->schemdata   = schemdata;
	schem->slice_probs = slice_probs;
	return true;
}


bool get_schematic(lua_State *L, int index, Schematic *schem,
	INodeDefManager *ndef, std::map<std::string, std::string> &replace_names)
{
	if (index < 0)
		index = lua_gettop(L) + 1 + index;

	if (lua_istable(L, index)) {
		return read_schematic(L, index, schem, ndef, replace_names);
	} else if (lua_isstring(L, index)) {
		const char *filename = lua_tostring(L, index);
		return schem->loadSchematicFromFile(filename, ndef, replace_names);
	} else {
		return false;
	}
}


void read_schematic_replacements(lua_State *L,
	std::map<std::string, std::string> &replace_names, int index)
{
	lua_pushnil(L);
	while (lua_next(L, index)) {
		std::string replace_from;
		std::string replace_to;

		if (lua_istable(L, -1)) { // Old {{"x", "y"}, ...} format
			lua_rawgeti(L, -1, 1);
			replace_from = lua_tostring(L, -1);
			lua_pop(L, 1);

			lua_rawgeti(L, -1, 2);
			replace_to = lua_tostring(L, -1);
			lua_pop(L, 1);
		} else { // New {x = "y", ...} format
			replace_from = lua_tostring(L, -2);
			replace_to = lua_tostring(L, -1);
		}

		replace_names[replace_from] = replace_to;
		lua_pop(L, 1);
	}
}


// get_mapgen_object(objectname)
// returns the requested object used during map generation
int ModApiMapgen::l_get_mapgen_object(lua_State *L)
{
	const char *mgobjstr = lua_tostring(L, 1);

	int mgobjint;
	if (!string_to_enum(es_MapgenObject, mgobjint, mgobjstr ? mgobjstr : ""))
		return 0;

	enum MapgenObject mgobj = (MapgenObject)mgobjint;

	EmergeManager *emerge = getServer(L)->getEmergeManager();
	Mapgen *mg = emerge->getCurrentMapgen();
	if (!mg)
		return 0;

	size_t maplen = mg->csize.X * mg->csize.Z;

	switch (mgobj) {
		case MGOBJ_VMANIP: {
			MMVManip *vm = mg->vm;

			// VoxelManip object
			LuaVoxelManip *o = new LuaVoxelManip(vm, true);
			*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
			luaL_getmetatable(L, "VoxelManip");
			lua_setmetatable(L, -2);

			// emerged min pos
			push_v3s16(L, vm->m_area.MinEdge);

			// emerged max pos
			push_v3s16(L, vm->m_area.MaxEdge);

			return 3;
		}
		case MGOBJ_HEIGHTMAP: {
			if (!mg->heightmap)
				return 0;

			lua_newtable(L);
			for (size_t i = 0; i != maplen; i++) {
				lua_pushinteger(L, mg->heightmap[i]);
				lua_rawseti(L, -2, i + 1);
			}

			return 1;
		}
		case MGOBJ_BIOMEMAP: {
			if (!mg->biomemap)
				return 0;

			lua_newtable(L);
			for (size_t i = 0; i != maplen; i++) {
				lua_pushinteger(L, mg->biomemap[i]);
				lua_rawseti(L, -2, i + 1);
			}

			return 1;
		}
		case MGOBJ_HEATMAP: { // Mapgen V7 specific objects
		case MGOBJ_HUMIDMAP:
			if (strcmp(emerge->params.mg_name.c_str(), "v7"))
				return 0;

			MapgenV7 *mgv7 = (MapgenV7 *)mg;

			float *arr = (mgobj == MGOBJ_HEATMAP) ?
				mgv7->noise_heat->result : mgv7->noise_humidity->result;
			if (!arr)
				return 0;

			lua_newtable(L);
			for (size_t i = 0; i != maplen; i++) {
				lua_pushnumber(L, arr[i]);
				lua_rawseti(L, -2, i + 1);
			}

			return 1;
		}
		case MGOBJ_GENNOTIFY: {
			std::map<std::string, std::vector<v3s16> >event_map;
			std::map<std::string, std::vector<v3s16> >::iterator it;

			mg->gennotify.getEvents(event_map);

			lua_newtable(L);
			for (it = event_map.begin(); it != event_map.end(); ++it) {
				lua_newtable(L);

				for (size_t j = 0; j != it->second.size(); j++) {
					push_v3s16(L, it->second[j]);
					lua_rawseti(L, -2, j + 1);
				}

				lua_setfield(L, -2, it->first.c_str());
			}

			return 1;
		}
	}

	return 0;
}

int ModApiMapgen::l_get_mapgen_params(lua_State *L)
{
	MapgenParams *params = &getServer(L)->getEmergeManager()->params;

	lua_newtable(L);

	lua_pushstring(L, params->mg_name.c_str());
	lua_setfield(L, -2, "mgname");

	lua_pushinteger(L, params->seed);
	lua_setfield(L, -2, "seed");

	lua_pushinteger(L, params->water_level);
	lua_setfield(L, -2, "water_level");

	lua_pushinteger(L, params->chunksize);
	lua_setfield(L, -2, "chunksize");

	std::string flagstr = writeFlagString(params->flags, flagdesc_mapgen, (u32)-1);
	lua_pushstring(L, flagstr.c_str());
	lua_setfield(L, -2, "flags");

	return 1;
}

// set_mapgen_params(params)
// set mapgen parameters
int ModApiMapgen::l_set_mapgen_params(lua_State *L)
{
	if (!lua_istable(L, 1))
		return 0;

	MapgenParams *params = &getServer(L)->getEmergeManager()->params;
	u32 flags = 0, flagmask = 0;

	lua_getfield(L, 1, "mgname");
	if (lua_isstring(L, -1)) {
		params->mg_name = lua_tostring(L, -1);
		delete params->sparams;
		params->sparams = NULL;
	}

	lua_getfield(L, 1, "seed");
	if (lua_isnumber(L, -1))
		params->seed = lua_tointeger(L, -1);

	lua_getfield(L, 1, "water_level");
	if (lua_isnumber(L, -1))
		params->water_level = lua_tointeger(L, -1);

	warn_if_field_exists(L, 1, "flagmask",
		"Deprecated: flags field now includes unset flags.");
	lua_getfield(L, 1, "flagmask");
	if (lua_isstring(L, -1))
		params->flags &= ~readFlagString(lua_tostring(L, -1), flagdesc_mapgen, NULL);

	if (getflagsfield(L, 1, "flags", flagdesc_mapgen, &flags, &flagmask)) {
		params->flags &= ~flagmask;
		params->flags |= flags;
	}

	return 0;
}

// set_noiseparams(name, noiseparams, set_default)
// set global config values for noise parameters
int ModApiMapgen::l_set_noiseparams(lua_State *L)
{
	const char *name = luaL_checkstring(L, 1);

	NoiseParams np;
	if (!read_noiseparams(L, 2, &np))
		return 0;

	bool set_default = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : true;

	g_settings->setNoiseParams(name, np, set_default);

	return 0;
}

// set_gen_notify(flags, {deco_id_table})
int ModApiMapgen::l_set_gen_notify(lua_State *L)
{
	u32 flags = 0, flagmask = 0;
	EmergeManager *emerge = getServer(L)->getEmergeManager();

	if (read_flags(L, 1, flagdesc_gennotify, &flags, &flagmask)) {
		emerge->gen_notify_on &= ~flagmask;
		emerge->gen_notify_on |= flags;
	}

	if (lua_istable(L, 2)) {
		lua_pushnil(L);
		while (lua_next(L, 2)) {
			if (lua_isnumber(L, -1))
				emerge->gen_notify_on_deco_ids.insert(lua_tonumber(L, -1));
			lua_pop(L, 1);
		}
	}

	return 0;
}

// register_biome({lots of stuff})
int ModApiMapgen::l_register_biome(lua_State *L)
{
	int index = 1;
	luaL_checktype(L, index, LUA_TTABLE);

	INodeDefManager *ndef = getServer(L)->getNodeDefManager();
	BiomeManager *bmgr    = getServer(L)->getEmergeManager()->biomemgr;

	enum BiomeType biometype = (BiomeType)getenumfield(L, index, "type",
		es_BiomeTerrainType, BIOME_TYPE_NORMAL);
	Biome *b = bmgr->create(biometype);

	b->name            = getstringfield_default(L, index, "name", "");
	b->depth_top       = getintfield_default(L, index, "depth_top",          1);
	b->depth_filler    = getintfield_default(L, index, "depth_filler",       2);
	b->depth_water_top = getintfield_default(L, index, "depth_water_top",    0);
	b->y_min           = getintfield_default(L, index, "y_min",         -31000);
	b->y_max           = getintfield_default(L, index, "y_max",          31000);
	b->heat_point      = getfloatfield_default(L, index, "heat_point",     0.f);
	b->humidity_point  = getfloatfield_default(L, index, "humidity_point", 0.f);
	b->flags           = 0; //reserved

	u32 id = bmgr->add(b);
	if (id == (u32)-1) {
		delete b;
		return 0;
	}

	NodeResolveInfo *nri = new NodeResolveInfo(b);
	std::vector<std::string> &nnames = nri->nodenames;
	nnames.push_back(getstringfield_default(L, index, "node_top",          ""));
	nnames.push_back(getstringfield_default(L, index, "node_filler",       ""));
	nnames.push_back(getstringfield_default(L, index, "node_stone",        ""));
	nnames.push_back(getstringfield_default(L, index, "node_water_top",    ""));
	nnames.push_back(getstringfield_default(L, index, "node_water",        ""));
	nnames.push_back(getstringfield_default(L, index, "node_dust",         ""));
	ndef->pendNodeResolve(nri);

	verbosestream << "register_biome: " << b->name << std::endl;

	lua_pushinteger(L, id);
	return 1;
}

int ModApiMapgen::l_clear_registered_biomes(lua_State *L)
{
	BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr;
	bmgr->clear();
	return 0;
}

int ModApiMapgen::l_clear_registered_decorations(lua_State *L)
{
	DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr;
	dmgr->clear();
	return 0;
}

int ModApiMapgen::l_clear_registered_ores(lua_State *L)
{
	OreManager *omgr = getServer(L)->getEmergeManager()->oremgr;
	omgr->clear();
	return 0;
}

// register_decoration({lots of stuff})
int ModApiMapgen::l_register_decoration(lua_State *L)
{
	int index = 1;
	luaL_checktype(L, index, LUA_TTABLE);

	INodeDefManager *ndef      = getServer(L)->getNodeDefManager();
	DecorationManager *decomgr = getServer(L)->getEmergeManager()->decomgr;
	BiomeManager *biomemgr     = getServer(L)->getEmergeManager()->biomemgr;

	enum DecorationType decotype = (DecorationType)getenumfield(L, index,
				"deco_type", es_DecorationType, -1);

	Decoration *deco = decomgr->create(decotype);
	if (!deco) {
		errorstream << "register_decoration: decoration placement type "
			<< decotype << " not implemented";
		return 0;
	}

	deco->name       = getstringfield_default(L, index, "name", "");
	deco->fill_ratio = getfloatfield_default(L, index, "fill_ratio", 0.02);
	deco->y_min      = getintfield_default(L, index, "y_min", -31000);
	deco->y_max      = getintfield_default(L, index, "y_max", 31000);
	deco->sidelen    = getintfield_default(L, index, "sidelen", 8);
	if (deco->sidelen <= 0) {
		errorstream << "register_decoration: sidelen must be "
			"greater than 0" << std::endl;
		delete deco;
		return 0;
	}

	NodeResolveInfo *nri = new NodeResolveInfo(deco);

	//// Get node name(s) to place decoration on
	std::vector<const char *> place_on_names;
	getstringlistfield(L, index, "place_on", place_on_names);
	nri->nodelistinfo.push_back(NodeListInfo(place_on_names.size()));
	for (size_t i = 0; i != place_on_names.size(); i++)
		nri->nodenames.push_back(place_on_names[i]);

	getflagsfield(L, index, "flags", flagdesc_deco, &deco->flags, NULL);

	//// Get NoiseParams to define how decoration is placed
	lua_getfield(L, index, "noise_params");
	if (read_noiseparams(L, -1, &deco->np))
		deco->flags |= DECO_USE_NOISE;
	lua_pop(L, 1);

	//// Get biomes associated with this decoration (if any)
	std::vector<const char *> biome_list;
	getstringlistfield(L, index, "biomes", biome_list);
	for (size_t i = 0; i != biome_list.size(); i++) {
		Biome *b = (Biome *)biomemgr->getByName(biome_list[i]);
		if (!b)
			continue;

		deco->biomes.insert(b->id);
	}

	//// Handle decoration type-specific parameters
	bool success = false;
	switch (decotype) {
		case DECO_SIMPLE:
			success = regDecoSimple(L, nri, (DecoSimple *)deco);
			break;
		case DECO_SCHEMATIC:
			success = regDecoSchematic(L, ndef, (DecoSchematic *)deco);
			break;
		case DECO_LSYSTEM:
			break;
	}

	ndef->pendNodeResolve(nri);

	if (!success) {
		delete deco;
		return 0;
	}

	u32 id = decomgr->add(deco);
	if (id == (u32)-1) {
		delete deco;
		return 0;
	}

	verbosestream << "register_decoration: " << deco->name << std::endl;

	lua_pushinteger(L, id);
	return 1;
}

bool ModApiMapgen::regDecoSimple(lua_State *L,
		NodeResolveInfo *nri, DecoSimple *deco)
{
	int index = 1;

	deco->deco_height     = getintfield_default(L, index, "height", 1);
	deco->deco_height_max = getintfield_default(L, index, "height_max", 0);
	deco->nspawnby        = getintfield_default(L, index, "num_spawn_by", -1);

	if (deco->deco_height <= 0) {
		errorstream << "register_decoration: simple decoration height"
			" must be greater than 0" << std::endl;
		return false;
	}

	std::vector<const char *> deco_names;
	getstringlistfield(L, index, "decoration", deco_names);
	if (deco_names.size() == 0) {
		errorstream << "register_decoration: no decoration nodes "
			"defined" << std::endl;
		return false;
	}
	nri->nodelistinfo.push_back(NodeListInfo(deco_names.size()));
	for (size_t i = 0; i != deco_names.size(); i++)
		nri->nodenames.push_back(deco_names[i]);

	std::vector<const char *> spawnby_names;
	getstringlistfield(L, index, "spawn_by", spawnby_names);
	if (deco->nspawnby != -1 && spawnby_names.size() == 0) {
		errorstream << "register_decoration: no spawn_by nodes defined,"
			" but num_spawn_by specified" << std::endl;
		return false;
	}
	nri->nodelistinfo.push_back(NodeListInfo(spawnby_names.size()));
	for (size_t i = 0; i != spawnby_names.size(); i++)
		nri->nodenames.push_back(spawnby_names[i]);

	return true;
}

bool ModApiMapgen::regDecoSchematic(lua_State *L, INodeDefManager *ndef,
	DecoSchematic *deco)
{
	int index = 1;

	deco->rotation = (Rotation)getenumfield(L, index, "rotation",
		es_Rotation, ROTATE_0);

	std::map<std::string, std::string> replace_names;
	lua_getfield(L, index, "replacements");
	if (lua_istable(L, -1))
		read_schematic_replacements(L, replace_names, lua_gettop(L));
	lua_pop(L, 1);

	// TODO(hmmmm): get a ref from registered schematics
	Schematic *schem = new Schematic;
	lua_getfield(L, index, "schematic");
	if (!get_schematic(L, -1, schem, ndef, replace_names)) {
		lua_pop(L, 1);
		delete schem;
		return false;
	}
	lua_pop(L, 1);

	deco->schematic = schem;

	return true;
}

// register_ore({lots of stuff})
int ModApiMapgen::l_register_ore(lua_State *L)
{
	int index = 1;
	luaL_checktype(L, index, LUA_TTABLE);

	INodeDefManager *ndef = getServer(L)->getNodeDefManager();
	OreManager *oremgr    = getServer(L)->getEmergeManager()->oremgr;

	enum OreType oretype = (OreType)getenumfield(L, index,
				"ore_type", es_OreType, ORE_TYPE_SCATTER);
	Ore *ore = oremgr->create(oretype);
	if (!ore) {
		errorstream << "register_ore: ore_type " << oretype << " not implemented";
		return 0;
	}

	ore->name           = getstringfield_default(L, index, "name", "");
	ore->ore_param2     = (u8)getintfield_default(L, index, "ore_param2", 0);
	ore->clust_scarcity = getintfield_default(L, index, "clust_scarcity", 1);
	ore->clust_num_ores = getintfield_default(L, index, "clust_num_ores", 1);
	ore->clust_size     = getintfield_default(L, index, "clust_size", 0);
	ore->nthresh        = getfloatfield_default(L, index, "noise_threshhold", 0);
	ore->noise          = NULL;
	ore->flags          = 0;

	warn_if_field_exists(L, index, "height_min",
		"Deprecated: new name is \"y_min\".");
	warn_if_field_exists(L, index, "height_max",
		"Deprecated: new name is \"y_max\".");

	int ymin, ymax;
	if (!getintfield(L, index, "y_min", ymin) &&
		!getintfield(L, index, "height_min", ymin))
		ymin = -31000;
	if (!getintfield(L, index, "y_max", ymax) &&
		!getintfield(L, index, "height_max", ymax))
		ymax = 31000;
	ore->y_min = ymin;
	ore->y_max = ymax;

	if (ore->clust_scarcity <= 0 || ore->clust_num_ores <= 0) {
		errorstream << "register_ore: clust_scarcity and clust_num_ores"
			"must be greater than 0" << std::endl;
		delete ore;
		return 0;
	}

	getflagsfield(L, index, "flags", flagdesc_ore, &ore->flags, NULL);

	lua_getfield(L, index, "noise_params");
	if (read_noiseparams(L, -1, &ore->np)) {
		ore->flags |= OREFLAG_USE_NOISE;
	} else if (ore->NEEDS_NOISE) {
		errorstream << "register_ore: specified ore type requires valid "
			"noise parameters" << std::endl;
		delete ore;
		return 0;
	}
	lua_pop(L, 1);

	if (oretype == ORE_TYPE_VEIN) {
		OreVein *orevein = (OreVein *)ore;
		orevein->random_factor = getfloatfield_default(L, index,
			"random_factor", 1.f);
	}

	u32 id = oremgr->add(ore);
	if (id == (u32)-1) {
		delete ore;
		return 0;
	}

	NodeResolveInfo *nri = new NodeResolveInfo(ore);
	nri->nodenames.push_back(getstringfield_default(L, index, "ore", ""));

	std::vector<const char *> wherein_names;
	getstringlistfield(L, index, "wherein", wherein_names);
	nri->nodelistinfo.push_back(NodeListInfo(wherein_names.size()));
	for (size_t i = 0; i != wherein_names.size(); i++)
		nri->nodenames.push_back(wherein_names[i]);

	ndef->pendNodeResolve(nri);