Decoration: Add Schematic decoration type

This commit is contained in:
kwolekr 2013-06-22 00:29:44 -04:00
parent b1a74df26d
commit c1b829077a
8 changed files with 589 additions and 22 deletions

@ -421,6 +421,32 @@ The default value is simple, and is currently the only type supported.
for example. Can also generate a decoration of random height between a specified lower and for example. Can also generate a decoration of random height between a specified lower and
upper bound. This type of decoration is intended for placement of grass, flowers, cacti, upper bound. This type of decoration is intended for placement of grass, flowers, cacti,
papyrus, and so on. papyrus, and so on.
- schematic
Copies a box of MapNodes from a specified schematic file (or raw description). Can specify a
probability of a node randomly appearing when placed. This decoration type is intended to be used
for multi-node sized discrete structures, such as trees, cave spikes, rocks, and so on.
Schematic specifier
--------------------
A schematic specifier identifies a schematic by either a filename to a Minetest Schematic file (.mts)
or through raw data supplied through Lua, in the form of a table. This table must specify two fields:
- The 'size' field is a 3d vector containing the dimensions of the provided schematic.
- The 'data' field is a flat table of MapNodes making up the schematic, in the order of [z [y [x]]].
In the bulk MapNode data, param1, instead of the typical light values, instead represents the
probability of that node appearing in the structure. It is an integer with a value from 0-255; 0 means
that node will always appear. If the probability value p is non-zero, then there is a
(p / 256 * 100)% chance that node will appear when the schematic is placed on the map.
Important note: Node aliases cannot be used for a raw schematic provided when registering as a decoration.
Schematic attributes
-----------------
Currently supported flags: place_center_x, place_center_y, place_center_z
- place_center_x
Placement of this decoration is centered along the X axis.
- place_center_y
Placement of this decoration is centered along the Y axis.
- place_center_z
Placement of this decoration is centered along the Z axis.
HUD element types HUD element types
------------------- -------------------
@ -1018,6 +1044,7 @@ minetest.setting_get(name) -> string or nil
minetest.setting_getbool(name) -> boolean value or nil minetest.setting_getbool(name) -> boolean value or nil
minetest.setting_get_pos(name) -> position or nil minetest.setting_get_pos(name) -> position or nil
minetest.setting_save() -> nil, save all settings to config file minetest.setting_save() -> nil, save all settings to config file
minetest.add_to_creative_inventory(itemstring)
Authentication: Authentication:
minetest.notify_authentication_modified(name) minetest.notify_authentication_modified(name)
@ -1248,6 +1275,21 @@ minetest.delete_particlespawner(id, player)
^ If playername is specified, only deletes on the player's client, ^ If playername is specified, only deletes on the player's client,
^ otherwise on all clients ^ otherwise on all clients
Schematics:
minetest.create_schematic(p1, p2, probability_list, filename)
^ Create a schematic from the volume of map specified by the box formed by p1 and p2.
^ Apply the specified probability values to the specified nodes in probability_list.
^ probability_list is an array of tables containing two fields, pos and prob.
^ pos is the 3d vector specifying the absolute coordinates of the node being modified,
^ and prob is the integer value from 0 to 255 of the probability (see: Schematic specifier).
^ If there are two or more entries with the same pos value, the last occuring in the array is used.
^ If pos is not inside the box formed by p1 and p2, it is ignored.
^ If probability_list is nil, no probabilities are applied.
^ Saves schematic in the Minetest Schematic format to filename.
minetest.place_schematic(pos, schematic)
^ Place the schematic specified by schematic (see: Schematic specifier) at pos.
Random: Random:
minetest.get_connected_players() -> list of ObjectRefs minetest.get_connected_players() -> list of ObjectRefs
minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer
@ -1292,6 +1334,15 @@ minetest.object_refs
minetest.luaentities minetest.luaentities
^ List of lua entities, indexed by active object id ^ List of lua entities, indexed by active object id
Deprecated but defined for backwards compatibility:
minetest.digprop_constanttime(time)
minetest.digprop_stonelike(toughness)
minetest.digprop_dirtlike(toughness)
minetest.digprop_gravellike(toughness)
minetest.digprop_woodlike(toughness)
minetest.digprop_leaveslike(toughness)
minetest.digprop_glasslike(toughness)
Class reference Class reference
---------------- ----------------
NodeMetaRef: Node metadata - reference extra data and functionality stored NodeMetaRef: Node metadata - reference extra data and functionality stored
@ -1700,7 +1751,6 @@ Node definition (register_node)
liquid_alternative_source = "", -- Source version of flowing liquid liquid_alternative_source = "", -- Source version of flowing liquid
liquid_viscosity = 0, -- Higher viscosity = slower flow (max. 7) liquid_viscosity = 0, -- Higher viscosity = slower flow (max. 7)
liquid_renewable = true, -- Can new liquid source be created by placing liquid_renewable = true, -- Can new liquid source be created by placing
drowning = true, -- Player will drown in these
two or more sources nearly? two or more sources nearly?
light_source = 0, -- Amount of light emitted by node light_source = 0, -- Amount of light emitted by node
damage_per_second = 0, -- If player is inside node, this damage is caused damage_per_second = 0, -- If player is inside node, this damage is caused
@ -1894,6 +1944,16 @@ Decoration definition (register_decoration)
num_spawn_by = 1, num_spawn_by = 1,
^ Number of spawn_by nodes that must be surrounding the decoration position to occur. ^ Number of spawn_by nodes that must be surrounding the decoration position to occur.
^ If absent or -1, decorations occur next to any nodes. ^ If absent or -1, decorations occur next to any nodes.
----- Schematic-type parameters
schematic = "foobar.mts",
^ If schematic is a string, it is the filepath relative to the current working directory of the
^ specified Minetest schematic file.
^ - OR -, could instead be a table containing two fields, size and data:
schematic = {size = {x=4, y=6, z=4}, data = { {"cobble", 0, 0}, {"dirt_with_grass", 0, 0}, ...}},
^ See 'Schematic specifier' for details.
flags = "place_center_x, place_center_z",
^ Flags for schematic decorations. See 'Schematic attributes'.
} }
Chatcommand definition (register_chatcommand) Chatcommand definition (register_chatcommand)

@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "treegen.h" #include "treegen.h"
#include "mapgen_v6.h" #include "mapgen_v6.h"
#include "mapgen_v7.h" #include "mapgen_v7.h"
#include "util/serialize.h"
FlagDesc flagdesc_mapgen[] = { FlagDesc flagdesc_mapgen[] = {
{"trees", MG_TREES}, {"trees", MG_TREES},
@ -53,6 +54,12 @@ FlagDesc flagdesc_ore[] = {
{NULL, 0} {NULL, 0}
}; };
FlagDesc flagdesc_deco_schematic[] = {
{"place_center_x", DECO_PLACE_CENTER_X},
{"place_center_y", DECO_PLACE_CENTER_Y},
{"place_center_z", DECO_PLACE_CENTER_Z},
{NULL, 0}
};
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -198,12 +205,15 @@ void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed,
} }
///////////////////////////////////////////////////////////////////////////////
Decoration *createDecoration(DecorationType type) { Decoration *createDecoration(DecorationType type) {
switch (type) { switch (type) {
case DECO_SIMPLE: case DECO_SIMPLE:
return new DecoSimple; return new DecoSimple;
//case DECO_SCHEMATIC: case DECO_SCHEMATIC:
// return new DecoSchematic; return new DecoSchematic;
//case DECO_LSYSTEM: //case DECO_LSYSTEM:
// return new DecoLSystem; // return new DecoLSystem;
default: default:
@ -212,6 +222,14 @@ Decoration *createDecoration(DecorationType type) {
} }
Decoration::Decoration() {
mapseed = 0;
np = NULL;
fill_ratio = 0;
sidelen = 1;
}
Decoration::~Decoration() { Decoration::~Decoration() {
delete np; delete np;
} }
@ -352,6 +370,9 @@ void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
#endif #endif
///////////////////////////////////////////////////////////////////////////////
void DecoSimple::resolveNodeNames(INodeDefManager *ndef) { void DecoSimple::resolveNodeNames(INodeDefManager *ndef) {
Decoration::resolveNodeNames(ndef); Decoration::resolveNodeNames(ndef);
@ -359,7 +380,7 @@ void DecoSimple::resolveNodeNames(INodeDefManager *ndef) {
c_deco = ndef->getId(deco_name); c_deco = ndef->getId(deco_name);
if (c_deco == CONTENT_IGNORE) { if (c_deco == CONTENT_IGNORE) {
errorstream << "DecoSimple::resolveNodeNames: decoration node '" errorstream << "DecoSimple::resolveNodeNames: decoration node '"
<< deco_name << "' not defined"; << deco_name << "' not defined" << std::endl;
c_deco = CONTENT_AIR; c_deco = CONTENT_AIR;
} }
} }
@ -367,7 +388,7 @@ void DecoSimple::resolveNodeNames(INodeDefManager *ndef) {
c_spawnby = ndef->getId(spawnby_name); c_spawnby = ndef->getId(spawnby_name);
if (c_spawnby == CONTENT_IGNORE) { if (c_spawnby == CONTENT_IGNORE) {
errorstream << "DecoSimple::resolveNodeNames: spawnby node '" errorstream << "DecoSimple::resolveNodeNames: spawnby node '"
<< deco_name << "' not defined"; << deco_name << "' not defined" << std::endl;
nspawnby = -1; nspawnby = -1;
c_spawnby = CONTENT_AIR; c_spawnby = CONTENT_AIR;
} }
@ -380,7 +401,7 @@ void DecoSimple::resolveNodeNames(INodeDefManager *ndef) {
content_t c = ndef->getId(decolist_names[i]); content_t c = ndef->getId(decolist_names[i]);
if (c == CONTENT_IGNORE) { if (c == CONTENT_IGNORE) {
errorstream << "DecoSimple::resolveNodeNames: decolist node '" errorstream << "DecoSimple::resolveNodeNames: decolist node '"
<< decolist_names[i] << "' not defined"; << decolist_names[i] << "' not defined" << std::endl;
c = CONTENT_AIR; c = CONTENT_AIR;
} }
c_decolist.push_back(c); c_decolist.push_back(c);
@ -388,7 +409,8 @@ void DecoSimple::resolveNodeNames(INodeDefManager *ndef) {
} }
void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, s16 start_y, v3s16 p) { void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y,
s16 start_y, v3s16 p) {
ManualMapVoxelManipulator *vm = mg->vm; ManualMapVoxelManipulator *vm = mg->vm;
u32 vi = vm->m_area.index(p); u32 vi = vm->m_area.index(p);
@ -454,6 +476,291 @@ std::string DecoSimple::getName() {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
DecoSchematic::DecoSchematic() {
node_names = NULL;
schematic = NULL;
flags = 0;
size = v3s16(0, 0, 0);
}
DecoSchematic::~DecoSchematic() {
delete node_names;
delete []schematic;
}
void DecoSchematic::resolveNodeNames(INodeDefManager *ndef) {
Decoration::resolveNodeNames(ndef);
if (filename.empty())
return;
if (!node_names) {
errorstream << "DecoSchematic::resolveNodeNames: node name list was "
"not created" << std::endl;
return;
}
for (size_t i = 0; i != node_names->size(); i++) {
content_t c = ndef->getId(node_names->at(i));
if (c == CONTENT_IGNORE) {
errorstream << "DecoSchematic::resolveNodeNames: node '"
<< node_names->at(i) << "' not defined" << std::endl;
c = CONTENT_AIR;
}
c_nodes.push_back(c);
}
for (int i = 0; i != size.X * size.Y * size.Z; i++)
schematic[i].setContent(c_nodes[schematic[i].getContent()]);
delete node_names;
node_names = NULL;
}
void DecoSchematic::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y,
s16 start_y, v3s16 p) {
ManualMapVoxelManipulator *vm = mg->vm;
if (flags & DECO_PLACE_CENTER_X)
p.X -= (size.X + 1) / 2;
if (flags & DECO_PLACE_CENTER_Y)
p.Y -= (size.Y + 1) / 2;
if (flags & DECO_PLACE_CENTER_Z)
p.Z -= (size.Z + 1) / 2;
u32 vi = vm->m_area.index(p);
if (vm->m_data[vi].getContent() != c_place_on &&
c_place_on != CONTENT_IGNORE)
return;
u32 i = 0;
for (s16 z = 0; z != size.Z; z++)
for (s16 y = 0; y != size.Y; y++) {
vi = vm->m_area.index(p.X, p.Y + y, p.Z + z);
for (s16 x = 0; x != size.X; x++, i++, vi++) {
if (!vm->m_area.contains(vi))
continue;
content_t c = vm->m_data[vi].getContent();
if (c != CONTENT_AIR && c != CONTENT_IGNORE)
continue;
if (schematic[i].param1 && myrand_range(1, 256) > schematic[i].param1)
continue;
vm->m_data[vi] = schematic[i];
vm->m_data[vi].param1 = 0;
}
}
}
int DecoSchematic::getHeight() {
return size.Y;
}
std::string DecoSchematic::getName() {
return filename;
}
void DecoSchematic::placeStructure(Map *map, v3s16 p) {
assert(schematic != NULL);
ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map);
if (flags & DECO_PLACE_CENTER_X)
p.X -= (size.X + 1) / 2;
if (flags & DECO_PLACE_CENTER_Y)
p.Y -= (size.Y + 1) / 2;
if (flags & DECO_PLACE_CENTER_Z)
p.Z -= (size.Z + 1) / 2;
v3s16 bp1 = getNodeBlockPos(p);
v3s16 bp2 = getNodeBlockPos(p + size - v3s16(1,1,1));
vm->initialEmerge(bp1, bp2);
u32 i = 0;
for (s16 z = 0; z != size.Z; z++)
for (s16 y = 0; y != size.Y; y++) {
u32 vi = vm->m_area.index(p.X, p.Y + y, p.Z + z);
for (s16 x = 0; x != size.X; x++, i++, vi++) {
if (!vm->m_area.contains(vi))
continue;
if (schematic[i].param1 && myrand_range(1, 256) > schematic[i].param1)
continue;
vm->m_data[vi] = schematic[i];
vm->m_data[vi].param1 = 0;
}
}
std::map<v3s16, MapBlock *> lighting_modified_blocks;
std::map<v3s16, MapBlock *> modified_blocks;
vm->blitBackAll(&modified_blocks);
// TODO: Optimize this by using Mapgen::calcLighting() instead
lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
map->updateLighting(lighting_modified_blocks, modified_blocks);
MapEditEvent event;
event.type = MEET_OTHER;
for (std::map<v3s16, MapBlock *>::iterator
it = modified_blocks.begin();
it != modified_blocks.end(); ++it)
event.modified_blocks.insert(it->first);
map->dispatchEvent(&event);
}
bool DecoSchematic::loadSchematicFile() {
std::ifstream is(filename.c_str(), std::ios_base::binary);
u32 signature = readU32(is);
if (signature != 'MTSM') {
errorstream << "loadSchematicFile: invalid schematic "
"file" << std::endl;
return false;
}
u16 version = readU16(is);
if (version != 1) {
errorstream << "loadSchematicFile: unsupported schematic "
"file version" << std::endl;
return false;
}
size = readV3S16(is);
int nodecount = size.X * size.Y * size.Z;
u16 nidmapcount = readU16(is);
node_names = new std::vector<std::string>;
for (int i = 0; i != nidmapcount; i++) {
std::string name = deSerializeString(is);
node_names->push_back(name);
}
delete schematic;
schematic = new MapNode[nodecount];
MapNode::deSerializeBulk(is, SER_FMT_VER_HIGHEST, schematic,
nodecount, 2, 2, true);
return true;
}
/*
Minetest Schematic File Format
All values are stored in big-endian byte order.
[u32] signature: 'MTSM'
[u16] version: 1
[u16] size X
[u16] size Y
[u16] size Z
[Name-ID table] Name ID Mapping Table
[u16] name-id count
For each name-id mapping:
[u16] name length
[u8[]] name
ZLib deflated {
For each node in schematic: (for z, y, x)
[u16] content
For each node in schematic:
[u8] probability of occurance (param1)
For each node in schematic:
[u8] param2
}
*/
void DecoSchematic::saveSchematicFile(INodeDefManager *ndef) {
std::ofstream os(filename.c_str(), std::ios_base::binary);
writeU32(os, 'MTSM'); // signature
writeU16(os, 1); // version
writeV3S16(os, size); // schematic size
std::vector<content_t> usednodes;
int nodecount = size.X * size.Y * size.Z;
build_nnlist_and_update_ids(schematic, nodecount, &usednodes);
u16 numids = usednodes.size();
writeU16(os, numids); // name count
for (int i = 0; i != numids; i++)
os << serializeString(ndef->get(usednodes[i]).name); // node names
// compressed bulk node data
MapNode::serializeBulk(os, SER_FMT_VER_HIGHEST, schematic,
nodecount, 2, 2, true);
}
void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount,
std::vector<content_t> *usednodes) {
std::map<content_t, content_t> nodeidmap;
content_t numids = 0;
for (u32 i = 0; i != nodecount; i++) {
content_t id;
content_t c = nodes[i].getContent();
std::map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
if (it == nodeidmap.end()) {
id = numids;
numids++;
usednodes->push_back(c);
nodeidmap.insert(std::make_pair(c, id));
} else {
id = it->second;
}
nodes[i].setContent(id);
}
}
bool DecoSchematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2) {
ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map);
v3s16 bp1 = getNodeBlockPos(p1);
v3s16 bp2 = getNodeBlockPos(p2);
vm->initialEmerge(bp1, bp2);
size = p2 - p1 + 1;
schematic = new MapNode[size.X * size.Y * size.Z];
u32 i = 0;
for (s16 z = p1.Z; z <= p2.Z; z++)
for (s16 y = p1.Y; y <= p2.Y; y++) {
u32 vi = vm->m_area.index(p1.X, y, z);
for (s16 x = p1.X; x <= p2.X; x++, i++, vi++) {
schematic[i] = vm->m_data[vi];
schematic[i].param1 = 0;
}
}
delete vm;
return true;
}
void DecoSchematic::applyProbabilities(std::vector<std::pair<v3s16, u8> > *plist, v3s16 p0) {
for (size_t i = 0; i != plist->size(); i++) {
v3s16 p = (*plist)[i].first - p0;
int index = p.Z * (size.Y * size.X) + p.Y * size.X + p.X;
if (index < size.Z * size.Y * size.X)
schematic[index].param1 = (*plist)[i].second;
}
}
///////////////////////////////////////////////////////////////////////////////
Mapgen::Mapgen() { Mapgen::Mapgen() {
seed = 0; seed = 0;
water_level = 0; water_level = 0;

@ -45,8 +45,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// nodes isn't the specified node // nodes isn't the specified node
#define OREFLAG_NODEISNT 0x04 // not yet implemented #define OREFLAG_NODEISNT 0x04 // not yet implemented
/////////////////// Decoration flags
#define DECO_PLACE_CENTER_X 1
#define DECO_PLACE_CENTER_Y 2
#define DECO_PLACE_CENTER_Z 4
extern FlagDesc flagdesc_mapgen[]; extern FlagDesc flagdesc_mapgen[];
extern FlagDesc flagdesc_ore[]; extern FlagDesc flagdesc_ore[];
extern FlagDesc flagdesc_deco_schematic[];
class BiomeDefManager; class BiomeDefManager;
class Biome; class Biome;
@ -57,6 +63,7 @@ class VoxelManipulator;
class INodeDefManager; class INodeDefManager;
struct BlockMakeData; struct BlockMakeData;
class VoxelArea; class VoxelArea;
class Map;
struct MapgenParams { struct MapgenParams {
std::string mg_name; std::string mg_name;
@ -207,6 +214,7 @@ public:
//std::list<CutoffData> cutoffs; //std::list<CutoffData> cutoffs;
//JMutex cutoff_mutex; //JMutex cutoff_mutex;
Decoration();
virtual ~Decoration(); virtual ~Decoration();
virtual void resolveNodeNames(INodeDefManager *ndef); virtual void resolveNodeNames(INodeDefManager *ndef);
@ -241,12 +249,36 @@ public:
virtual std::string getName(); virtual std::string getName();
}; };
/*
class DecoSchematic : public Decoration { class DecoSchematic : public Decoration {
public: public:
virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); std::string filename;
std::vector<std::string> *node_names;
std::vector<content_t> c_nodes;
u32 flags;
v3s16 size;
MapNode *schematic;
DecoSchematic();
~DecoSchematic();
void resolveNodeNames(INodeDefManager *ndef);
virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y,
s16 start_y, v3s16 p);
virtual int getHeight();
virtual std::string getName();
bool loadSchematicFile();
void saveSchematicFile(INodeDefManager *ndef);
bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
void placeStructure(Map *map, v3s16 p);
void applyProbabilities(std::vector<std::pair<v3s16, u8> > *plist, v3s16 p0);
}; };
*/
void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount,
std::vector<content_t> *usednodes);
/* /*
class DecoLSystem : public Decoration { class DecoLSystem : public Decoration {

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h" #include "log.h"
#include "tool.h" #include "tool.h"
#include "server.h" #include "server.h"
#include "mapgen.h"
struct EnumString es_TileAnimationType[] = struct EnumString es_TileAnimationType[] =
{ {
@ -922,3 +923,51 @@ NoiseParams *read_noiseparams(lua_State *L, int index)
return np; return np;
} }
/******************************************************************************/
bool read_schematic(lua_State *L, int index, DecoSchematic *dschem, Server *server) {
if (index < 0)
index = lua_gettop(L) + 1 + index;
INodeDefManager *ndef = server->getNodeDefManager();
if (lua_istable(L, index)) {
lua_getfield(L, index, "size");
v3s16 size = read_v3s16(L, -1);
lua_pop(L, 1);
int numnodes = size.X * size.Y * size.Z;
MapNode *schemdata = new MapNode[numnodes];
int i = 0;
lua_getfield(L, index, "data");
luaL_checktype(L, -1, LUA_TTABLE);
lua_pushnil(L);
while (lua_next(L, -2)) {
if (i < numnodes)
schemdata[i] = readnode(L, -1, ndef);
i++;
lua_pop(L, 1);
}
dschem->size = size;
dschem->schematic = schemdata;
if (i != numnodes) {
errorstream << "read_schematic: incorrect number of "
"nodes provided in raw schematic data (got " << i <<
", expected " << numnodes << ")." << std::endl;
return false;
}
} else if (lua_isstring(L, index)) {
dschem->filename = std::string(lua_tostring(L, index));
} else {
errorstream << "read_schematic: missing schematic "
"filename or raw schematic data" << std::endl;
return false;
}
return true;
}

@ -57,6 +57,7 @@ struct DigParams;
struct HitParams; struct HitParams;
struct EnumString; struct EnumString;
struct NoiseParams; struct NoiseParams;
class DecoSchematic;
ContentFeatures read_content_features (lua_State *L, int index); ContentFeatures read_content_features (lua_State *L, int index);
@ -139,6 +140,10 @@ bool string_to_enum (const EnumString *spec,
NoiseParams* read_noiseparams (lua_State *L, int index); NoiseParams* read_noiseparams (lua_State *L, int index);
bool read_schematic (lua_State *L, int index,
DecoSchematic *dschem,
Server *server);
void luaentity_get (lua_State *L,u16 id); void luaentity_get (lua_State *L,u16 id);
extern struct EnumString es_TileAnimationType[]; extern struct EnumString es_TileAnimationType[];

@ -101,6 +101,8 @@ bool ModApiBasic::Initialize(lua_State* L,int top) {
retval &= API_FCT(register_ore); retval &= API_FCT(register_ore);
retval &= API_FCT(register_decoration); retval &= API_FCT(register_decoration);
retval &= API_FCT(create_schematic);
retval &= API_FCT(place_schematic);
return retval; return retval;
} }
@ -701,8 +703,14 @@ int ModApiBasic::l_register_decoration(lua_State *L)
deco->c_place_on = CONTENT_IGNORE; deco->c_place_on = CONTENT_IGNORE;
deco->place_on_name = getstringfield_default(L, index, "place_on", "ignore"); deco->place_on_name = getstringfield_default(L, index, "place_on", "ignore");
deco->sidelen = getintfield_default(L, index, "sidelen", 8);
deco->fill_ratio = getfloatfield_default(L, index, "fill_ratio", 0.02); deco->fill_ratio = getfloatfield_default(L, index, "fill_ratio", 0.02);
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;
}
lua_getfield(L, index, "noise_params"); lua_getfield(L, index, "noise_params");
deco->np = read_noiseparams(L, -1); deco->np = read_noiseparams(L, -1);
@ -743,7 +751,7 @@ int ModApiBasic::l_register_decoration(lua_State *L)
lua_pop(L, 1); lua_pop(L, 1);
} }
} else if (lua_isstring(L, -1)) { } else if (lua_isstring(L, -1)) {
dsimple->deco_name = std::string(lua_tostring(L, -1)); dsimple->deco_name = std::string(lua_tostring(L, -1));
} else { } else {
dsimple->deco_name = std::string("air"); dsimple->deco_name = std::string("air");
} }
@ -758,8 +766,22 @@ int ModApiBasic::l_register_decoration(lua_State *L)
break; } break; }
case DECO_SCHEMATIC: { case DECO_SCHEMATIC: {
//DecoSchematic *decoschematic = (DecoSchematic *)deco; DecoSchematic *dschem = (DecoSchematic *)deco;
dschem->flags = getflagsfield(L, index, "flags", flagdesc_deco_schematic);
lua_getfield(L, index, "schematic");
if (!read_schematic(L, -1, dschem, getServer(L))) {
delete dschem;
return 0;
}
lua_pop(L, -1);
if (!dschem->filename.empty() && !dschem->loadSchematicFile()) {
errorstream << "register_decoration: failed to load schematic file '"
<< dschem->filename << "'" << std::endl;
delete dschem;
return 0;
}
break; } break; }
case DECO_LSYSTEM: { case DECO_LSYSTEM: {
//DecoLSystem *decolsystem = (DecoLSystem *)deco; //DecoLSystem *decolsystem = (DecoLSystem *)deco;
@ -767,13 +789,6 @@ int ModApiBasic::l_register_decoration(lua_State *L)
break; } break; }
} }
if (deco->sidelen <= 0) {
errorstream << "register_decoration: sidelen must be "
"greater than 0" << std::endl;
delete deco;
return 0;
}
emerge->decorations.push_back(deco); emerge->decorations.push_back(deco);
verbosestream << "register_decoration: decoration '" << deco->getName() verbosestream << "register_decoration: decoration '" << deco->getName()
@ -781,5 +796,82 @@ int ModApiBasic::l_register_decoration(lua_State *L)
return 0; return 0;
} }
// create_schematic(p1, p2, probability_list, filename)
int ModApiBasic::l_create_schematic(lua_State *L)
{
DecoSchematic dschem;
Map *map = &(getEnv(L)->getMap());
INodeDefManager *ndef = getServer(L)->getNodeDefManager();
v3s16 p1 = read_v3s16(L, 1);
v3s16 p2 = read_v3s16(L, 2);
sortBoxVerticies(p1, p2);
std::vector<std::pair<v3s16, u8> > probability_list;
if (lua_istable(L, 3)) {
lua_pushnil(L);
while (lua_next(L, 3)) {
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "pos");
v3s16 pos = read_v3s16(L, -1);
lua_pop(L, 1);
int prob = getintfield_default(L, -1, "prob", 0);
if (prob < 0 || prob >= UCHAR_MAX) {
errorstream << "create_schematic: probability value of "
<< prob << " at " << PP(pos) << " out of range" << std::endl;
} else {
probability_list.push_back(std::make_pair(pos, (u8)prob));
}
}
lua_pop(L, 1);
}
}
dschem.filename = std::string(lua_tostring(L, 4));
if (!dschem.getSchematicFromMap(map, p1, p2)) {
errorstream << "create_schematic: failed to get schematic "
"from map" << std::endl;
return 0;
}
dschem.applyProbabilities(&probability_list, p1);
dschem.saveSchematicFile(ndef);
actionstream << "create_schematic: saved schematic file '" << dschem.filename << "'." << std::endl;
return 1;
}
// place_schematic(p, schematic)
int ModApiBasic::l_place_schematic(lua_State *L)
{
DecoSchematic dschem;
Map *map = &(getEnv(L)->getMap());
INodeDefManager *ndef = getServer(L)->getNodeDefManager();
v3s16 p = read_v3s16(L, 1);
if (!read_schematic(L, 2, &dschem, getServer(L)))
return 0;
if (!dschem.filename.empty()) {
if (!dschem.loadSchematicFile()) {
errorstream << "place_schematic: failed to load schematic file '"
<< dschem.filename << "'" << std::endl;
return 0;
}
dschem.resolveNodeNames(ndef);
}
dschem.placeStructure(map, p);
return 1;
}
ModApiBasic modapibasic_prototype; ModApiBasic modapibasic_prototype;

@ -133,6 +133,12 @@ private:
// register_decoration(deco) // register_decoration(deco)
static int l_register_decoration(lua_State *L); static int l_register_decoration(lua_State *L);
// create_schematic(p1, p2, filename)
static int l_create_schematic(lua_State *L);
// place_schematic(p, filename)
static int l_place_schematic(lua_State *L);
static struct EnumString es_OreType[]; static struct EnumString es_OreType[];
static struct EnumString es_DecorationType[]; static struct EnumString es_DecorationType[];

@ -140,6 +140,22 @@ inline v3s16 arealim(v3s16 p, s16 d)
return p; return p;
} }
// The naive swap performs better than the xor version
#define SWAP(t, x, y) do { \
t temp = x; \
x = y; \
y = temp; \
} while (0)
inline void sortBoxVerticies(v3s16 &p1, v3s16 &p2) {
if (p1.X > p2.X)
SWAP(s16, p1.X, p2.X);
if (p1.Y > p2.Y)
SWAP(s16, p1.Y, p2.Y);
if (p1.Z > p2.Z)
SWAP(s16, p1.Z, p2.Z);
}
/* /*
See test.cpp for example cases. See test.cpp for example cases.