From c1b829077a3518f3a129eee11887b2358a53f20b Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 22 Jun 2013 00:29:44 -0400 Subject: [PATCH] Decoration: Add Schematic decoration type --- doc/lua_api.txt | 62 ++++++- src/mapgen.cpp | 319 +++++++++++++++++++++++++++++++- src/mapgen.h | 38 +++- src/script/common/c_content.cpp | 49 +++++ src/script/common/c_content.h | 5 + src/script/lua_api/luaapi.cpp | 116 ++++++++++-- src/script/lua_api/luaapi.h | 6 + src/util/numeric.h | 16 ++ 8 files changed, 589 insertions(+), 22 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index a19174548..0027b1c86 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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 upper bound. This type of decoration is intended for placement of grass, flowers, cacti, 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 ------------------- @@ -1018,6 +1044,7 @@ minetest.setting_get(name) -> string or nil minetest.setting_getbool(name) -> boolean value or nil minetest.setting_get_pos(name) -> position or nil minetest.setting_save() -> nil, save all settings to config file +minetest.add_to_creative_inventory(itemstring) Authentication: 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, ^ 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: minetest.get_connected_players() -> list of ObjectRefs minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer @@ -1292,6 +1334,15 @@ minetest.object_refs minetest.luaentities ^ 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 ---------------- 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_viscosity = 0, -- Higher viscosity = slower flow (max. 7) liquid_renewable = true, -- Can new liquid source be created by placing - drowning = true, -- Player will drown in these two or more sources nearly? light_source = 0, -- Amount of light emitted by node 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, ^ Number of spawn_by nodes that must be surrounding the decoration position to occur. ^ 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) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 72757f4fa..a79789b06 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "treegen.h" #include "mapgen_v6.h" #include "mapgen_v7.h" +#include "util/serialize.h" FlagDesc flagdesc_mapgen[] = { {"trees", MG_TREES}, @@ -53,6 +54,12 @@ FlagDesc flagdesc_ore[] = { {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) { switch (type) { case DECO_SIMPLE: return new DecoSimple; - //case DECO_SCHEMATIC: - // return new DecoSchematic; + case DECO_SCHEMATIC: + return new DecoSchematic; //case DECO_LSYSTEM: // return new DecoLSystem; default: @@ -212,6 +222,14 @@ Decoration *createDecoration(DecorationType type) { } +Decoration::Decoration() { + mapseed = 0; + np = NULL; + fill_ratio = 0; + sidelen = 1; +} + + Decoration::~Decoration() { delete np; } @@ -352,6 +370,9 @@ void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) #endif +/////////////////////////////////////////////////////////////////////////////// + + void DecoSimple::resolveNodeNames(INodeDefManager *ndef) { Decoration::resolveNodeNames(ndef); @@ -359,7 +380,7 @@ void DecoSimple::resolveNodeNames(INodeDefManager *ndef) { c_deco = ndef->getId(deco_name); if (c_deco == CONTENT_IGNORE) { errorstream << "DecoSimple::resolveNodeNames: decoration node '" - << deco_name << "' not defined"; + << deco_name << "' not defined" << std::endl; c_deco = CONTENT_AIR; } } @@ -367,7 +388,7 @@ void DecoSimple::resolveNodeNames(INodeDefManager *ndef) { c_spawnby = ndef->getId(spawnby_name); if (c_spawnby == CONTENT_IGNORE) { errorstream << "DecoSimple::resolveNodeNames: spawnby node '" - << deco_name << "' not defined"; + << deco_name << "' not defined" << std::endl; nspawnby = -1; c_spawnby = CONTENT_AIR; } @@ -380,7 +401,7 @@ void DecoSimple::resolveNodeNames(INodeDefManager *ndef) { content_t c = ndef->getId(decolist_names[i]); if (c == CONTENT_IGNORE) { errorstream << "DecoSimple::resolveNodeNames: decolist node '" - << decolist_names[i] << "' not defined"; + << decolist_names[i] << "' not defined" << std::endl; c = CONTENT_AIR; } 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; 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 lighting_modified_blocks; + std::map 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::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; + 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 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 *usednodes) { + std::map nodeidmap; + content_t numids = 0; + + for (u32 i = 0; i != nodecount; i++) { + content_t id; + content_t c = nodes[i].getContent(); + + std::map::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 > *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() { seed = 0; water_level = 0; diff --git a/src/mapgen.h b/src/mapgen.h index f3d90a14e..aa71b70c3 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -45,8 +45,14 @@ with this program; if not, write to the Free Software Foundation, Inc., // nodes isn't the specified node #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_ore[]; +extern FlagDesc flagdesc_deco_schematic[]; class BiomeDefManager; class Biome; @@ -57,6 +63,7 @@ class VoxelManipulator; class INodeDefManager; struct BlockMakeData; class VoxelArea; +class Map; struct MapgenParams { std::string mg_name; @@ -207,6 +214,7 @@ public: //std::list cutoffs; //JMutex cutoff_mutex; + Decoration(); virtual ~Decoration(); virtual void resolveNodeNames(INodeDefManager *ndef); @@ -241,12 +249,36 @@ public: virtual std::string getName(); }; -/* class DecoSchematic : public Decoration { public: - virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); + std::string filename; + + std::vector *node_names; + std::vector 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 > *plist, v3s16 p0); }; -*/ + +void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount, + std::vector *usednodes); /* class DecoLSystem : public Decoration { diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 64c76ef7c..4ea296523 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "tool.h" #include "server.h" +#include "mapgen.h" struct EnumString es_TileAnimationType[] = { @@ -922,3 +923,51 @@ NoiseParams *read_noiseparams(lua_State *L, int index) 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; +} diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 58be7118c..fc00ce089 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -57,6 +57,7 @@ struct DigParams; struct HitParams; struct EnumString; struct NoiseParams; +class DecoSchematic; 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); +bool read_schematic (lua_State *L, int index, + DecoSchematic *dschem, + Server *server); + void luaentity_get (lua_State *L,u16 id); extern struct EnumString es_TileAnimationType[]; diff --git a/src/script/lua_api/luaapi.cpp b/src/script/lua_api/luaapi.cpp index e19c90952..dea4ccf33 100644 --- a/src/script/lua_api/luaapi.cpp +++ b/src/script/lua_api/luaapi.cpp @@ -101,6 +101,8 @@ bool ModApiBasic::Initialize(lua_State* L,int top) { retval &= API_FCT(register_ore); retval &= API_FCT(register_decoration); + retval &= API_FCT(create_schematic); + retval &= API_FCT(place_schematic); return retval; } @@ -680,7 +682,7 @@ int ModApiBasic::l_register_decoration(lua_State *L) { int index = 1; luaL_checktype(L, index, LUA_TTABLE); - + EmergeManager *emerge = getServer(L)->getEmergeManager(); BiomeDefManager *bdef = emerge->biomedef; @@ -701,8 +703,14 @@ int ModApiBasic::l_register_decoration(lua_State *L) deco->c_place_on = CONTENT_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->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"); deco->np = read_noiseparams(L, -1); @@ -743,7 +751,7 @@ int ModApiBasic::l_register_decoration(lua_State *L) lua_pop(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 { dsimple->deco_name = std::string("air"); } @@ -758,22 +766,29 @@ int ModApiBasic::l_register_decoration(lua_State *L) break; } 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; } case DECO_LSYSTEM: { //DecoLSystem *decolsystem = (DecoLSystem *)deco; 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); verbosestream << "register_decoration: decoration '" << deco->getName() @@ -781,5 +796,82 @@ int ModApiBasic::l_register_decoration(lua_State *L) 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 > 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; diff --git a/src/script/lua_api/luaapi.h b/src/script/lua_api/luaapi.h index d03c14117..ba76117d4 100644 --- a/src/script/lua_api/luaapi.h +++ b/src/script/lua_api/luaapi.h @@ -132,6 +132,12 @@ private: // register_decoration(deco) 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_DecorationType[]; diff --git a/src/util/numeric.h b/src/util/numeric.h index e66af2376..3e82997bd 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -140,6 +140,22 @@ inline v3s16 arealim(v3s16 p, s16 d) 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. -- 2.25.1