aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mg_schematic.cpp54
-rw-r--r--src/mg_schematic.h24
-rw-r--r--src/script/common/c_converter.cpp13
-rw-r--r--src/script/common/c_converter.h2
-rw-r--r--src/script/lua_api/l_mapgen.cpp57
-rw-r--r--src/unittest/test_schematic.cpp95
6 files changed, 153 insertions, 92 deletions
diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp
index 33f82a74c..81d849a66 100644
--- a/src/mg_schematic.cpp
+++ b/src/mg_schematic.cpp
@@ -133,8 +133,8 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_pla
s16 y_map = p.Y;
for (s16 y = 0; y != sy; y++) {
- if (slice_probs[y] != MTSCHEM_PROB_ALWAYS &&
- myrand_range(1, 255) > slice_probs[y])
+ if ((slice_probs[y] != MTSCHEM_PROB_ALWAYS) &&
+ (slice_probs[y] <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
continue;
for (s16 z = 0; z != sz; z++) {
@@ -147,17 +147,20 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_pla
if (schemdata[i].getContent() == CONTENT_IGNORE)
continue;
- if (schemdata[i].param1 == MTSCHEM_PROB_NEVER)
+ u8 placement_prob = schemdata[i].param1 & MTSCHEM_PROB_MASK;
+ bool force_place_node = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
+
+ if (placement_prob == MTSCHEM_PROB_NEVER)
continue;
- if (!force_place) {
+ if (!force_place && !force_place_node) {
content_t c = vm->m_data[vi].getContent();
if (c != CONTENT_AIR && c != CONTENT_IGNORE)
continue;
}
- if (schemdata[i].param1 != MTSCHEM_PROB_ALWAYS &&
- myrand_range(1, 255) > schemdata[i].param1)
+ if ((placement_prob != MTSCHEM_PROB_ALWAYS) &&
+ (placement_prob <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
continue;
vm->m_data[vi] = schemdata[i];
@@ -225,6 +228,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
content_t cignore = CONTENT_IGNORE;
bool have_cignore = false;
+ //// Read signature
u32 signature = readU32(ss);
if (signature != MTSCHEM_FILE_SIGNATURE) {
errorstream << "Schematic::deserializeFromMts: invalid schematic "
@@ -232,6 +236,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
return false;
}
+ //// Read version
u16 version = readU16(ss);
if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
errorstream << "Schematic::deserializeFromMts: unsupported schematic "
@@ -239,18 +244,21 @@ bool Schematic::deserializeFromMts(std::istream *is,
return false;
}
+ //// Read size
size = readV3S16(ss);
+ //// Read Y-slice probability values
delete []slice_probs;
slice_probs = new u8[size.Y];
for (int y = 0; y != size.Y; y++)
- slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS;
+ slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD;
+ //// Read node names
u16 nidmapcount = readU16(ss);
for (int i = 0; i != nidmapcount; i++) {
std::string name = deSerializeString(ss);
- // Instances of "ignore" from ver 1 are converted to air (and instances
+ // Instances of "ignore" from v1 are converted to air (and instances
// are fixed to have MTSCHEM_PROB_NEVER later on).
if (name == "ignore") {
name = "air";
@@ -261,6 +269,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
names->push_back(name);
}
+ //// Read node data
size_t nodecount = size.X * size.Y * size.Z;
delete []schemdata;
@@ -269,8 +278,8 @@ bool Schematic::deserializeFromMts(std::istream *is,
MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
nodecount, 2, 2, true);
- // fix any probability values for nodes that were ignore
- if (version == 1) {
+ // Fix probability values for nodes that were ignore; removed in v2
+ if (version < 2) {
for (size_t i = 0; i != nodecount; i++) {
if (schemdata[i].param1 == 0)
schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
@@ -279,6 +288,14 @@ bool Schematic::deserializeFromMts(std::istream *is,
}
}
+ // Fix probability values for probability range truncation introduced in v4
+ if (version < 4) {
+ for (s16 y = 0; y != size.Y; y++)
+ slice_probs[y] >>= 1;
+ for (size_t i = 0; i != nodecount; i++)
+ schemdata[i].param1 >>= 1;
+ }
+
return true;
}
@@ -331,9 +348,11 @@ bool Schematic::serializeToLua(std::ostream *os,
ss << indent << "yslice_prob = {" << std::endl;
for (u16 y = 0; y != size.Y; y++) {
+ u8 probability = slice_probs[y] & MTSCHEM_PROB_MASK;
+
ss << indent << indent << "{"
<< "ypos=" << y
- << ", prob=" << (u16)slice_probs[y]
+ << ", prob=" << (u16)probability * 2
<< "}," << std::endl;
}
@@ -355,11 +374,18 @@ bool Schematic::serializeToLua(std::ostream *os,
}
for (u16 x = 0; x != size.X; x++, i++) {
+ u8 probability = schemdata[i].param1 & MTSCHEM_PROB_MASK;
+ bool force_place = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
+
ss << indent << indent << "{"
<< "name=\"" << names[schemdata[i].getContent()]
- << "\", param1=" << (u16)schemdata[i].param1
- << ", param2=" << (u16)schemdata[i].param2
- << "}," << std::endl;
+ << "\", prob=" << (u16)probability * 2
+ << ", param2=" << (u16)schemdata[i].param2;
+
+ if (force_place)
+ ss << ", force_place=true";
+
+ ss << "}," << std::endl;
}
}
diff --git a/src/mg_schematic.h b/src/mg_schematic.h
index 3d3e328d3..5c732648e 100644
--- a/src/mg_schematic.h
+++ b/src/mg_schematic.h
@@ -36,7 +36,7 @@ class IGameDef;
All values are stored in big-endian byte order.
[u32] signature: 'MTSM'
- [u16] version: 3
+ [u16] version: 4
[u16] size X
[u16] size Y
[u16] size Z
@@ -51,7 +51,9 @@ class IGameDef;
For each node in schematic: (for z, y, x)
[u16] content
For each node in schematic:
- [u8] probability of occurance (param1)
+ [u8] param1
+ bit 0-6: probability
+ bit 7: specific node force placement
For each node in schematic:
[u8] param2
}
@@ -60,17 +62,21 @@ class IGameDef;
1 - Initial version
2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
3 - Added y-slice probabilities; this allows for variable height structures
+ 4 - Compressed range of node occurence prob., added per-node force placement bit
*/
-/////////////////// Schematic flags
-#define SCHEM_CIDS_UPDATED 0x08
-
+//// Schematic constants
#define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM'
-#define MTSCHEM_FILE_VER_HIGHEST_READ 3
-#define MTSCHEM_FILE_VER_HIGHEST_WRITE 3
+#define MTSCHEM_FILE_VER_HIGHEST_READ 4
+#define MTSCHEM_FILE_VER_HIGHEST_WRITE 4
+
+#define MTSCHEM_PROB_MASK 0x7F
+
+#define MTSCHEM_PROB_NEVER 0x00
+#define MTSCHEM_PROB_ALWAYS 0x7F
+#define MTSCHEM_PROB_ALWAYS_OLD 0xFF
-#define MTSCHEM_PROB_NEVER 0x00
-#define MTSCHEM_PROB_ALWAYS 0xFF
+#define MTSCHEM_FORCE_PLACE 0x80
enum SchematicType
{
diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp
index 157db3b7d..fc489ea95 100644
--- a/src/script/common/c_converter.cpp
+++ b/src/script/common/c_converter.cpp
@@ -332,6 +332,19 @@ bool getintfield(lua_State *L, int table,
}
bool getintfield(lua_State *L, int table,
+ const char *fieldname, u8 &result)
+{
+ lua_getfield(L, table, fieldname);
+ bool got = false;
+ if(lua_isnumber(L, -1)){
+ result = lua_tonumber(L, -1);
+ got = true;
+ }
+ lua_pop(L, 1);
+ return got;
+}
+
+bool getintfield(lua_State *L, int table,
const char *fieldname, u16 &result)
{
lua_getfield(L, table, fieldname);
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index 1d5be6971..0847f47c9 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -54,6 +54,8 @@ size_t getstringlistfield(lua_State *L, int table,
bool getintfield(lua_State *L, int table,
const char *fieldname, int &result);
bool getintfield(lua_State *L, int table,
+ const char *fieldname, u8 &result);
+bool getintfield(lua_State *L, int table,
const char *fieldname, u16 &result);
bool getintfield(lua_State *L, int table,
const char *fieldname, u32 &result);
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index 5422447ed..3cb52eab4 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -237,36 +237,32 @@ bool read_schematic_def(lua_State *L, int index,
lua_getfield(L, index, "data");
luaL_checktype(L, -1, LUA_TTABLE);
- int numnodes = size.X * size.Y * size.Z;
+ u32 numnodes = size.X * size.Y * size.Z;
schem->schemdata = new MapNode[numnodes];
- int i = 0;
size_t names_base = names->size();
std::map<std::string, content_t> name_id_map;
- lua_pushnil(L);
- while (lua_next(L, -2)) {
- if (i >= numnodes) {
- i++;
- lua_pop(L, 1);
+ u32 i = 0;
+ for (lua_pushnil(L); lua_next(L, -2); i++, lua_pop(L, 1)) {
+ if (i >= numnodes)
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);
+ //// Read name
+ std::string name;
+ if (!getstringfield(L, -1, "name", name))
+ throw LuaError("Schematic data definition with missing name field");
+ //// Read param1/prob
u8 param1;
- lua_getfield(L, -1, "param1");
- param1 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : MTSCHEM_PROB_ALWAYS;
- lua_pop(L, 1);
+ if (!getintfield(L, -1, "param1", param1) &&
+ !getintfield(L, -1, "prob", param1))
+ param1 = MTSCHEM_PROB_ALWAYS_OLD;
- u8 param2;
- lua_getfield(L, -1, "param2");
- param2 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : 0;
- lua_pop(L, 1);
+ //// Read param2
+ u8 param2 = getintfield_default(L, -1, "param2", 0);
+ //// Find or add new nodename-to-ID mapping
std::map<std::string, content_t>::iterator it = name_id_map.find(name);
content_t name_index;
if (it != name_id_map.end()) {
@@ -277,10 +273,13 @@ bool read_schematic_def(lua_State *L, int index,
names->push_back(name);
}
- schem->schemdata[i] = MapNode(name_index, param1, param2);
+ //// Perform probability/force_place fixup on param1
+ param1 >>= 1;
+ if (getboolfield_default(L, -1, "force_place", false))
+ param1 |= MTSCHEM_FORCE_PLACE;
- i++;
- lua_pop(L, 1);
+ //// Actually set the node in the schematic
+ schem->schemdata[i] = MapNode(name_index, param1, param2);
}
if (i != numnodes) {
@@ -297,13 +296,13 @@ bool read_schematic_def(lua_State *L, int index,
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) {
- schem->slice_probs[i] = getintfield_default(L, -1,
- "prob", MTSCHEM_PROB_ALWAYS);
- }
- lua_pop(L, 1);
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ u16 ypos;
+ if (!getintfield(L, -1, "ypos", ypos) || (ypos >= size.Y) ||
+ !getintfield(L, -1, "prob", schem->slice_probs[ypos]))
+ continue;
+
+ schem->slice_probs[ypos] >>= 1;
}
}
diff --git a/src/unittest/test_schematic.cpp b/src/unittest/test_schematic.cpp
index 24bacf6c5..df47d2bc0 100644
--- a/src/unittest/test_schematic.cpp
+++ b/src/unittest/test_schematic.cpp
@@ -34,8 +34,9 @@ public:
void testLuaTableSerialize(INodeDefManager *ndef);
void testFileSerializeDeserialize(INodeDefManager *ndef);
- static const content_t test_schem_data[7 * 6 * 4];
- static const content_t test_schem_data2[3 * 3 * 3];
+ static const content_t test_schem1_data[7 * 6 * 4];
+ static const content_t test_schem2_data[3 * 3 * 3];
+ static const u8 test_schem2_prob[3 * 3 * 3];
static const char *expected_lua_output;
};
@@ -78,7 +79,7 @@ void TestSchematic::testMtsSerializeDeserialize(INodeDefManager *ndef)
schem.schemdata = new MapNode[volume];
schem.slice_probs = new u8[size.Y];
for (size_t i = 0; i != volume; i++)
- schem.schemdata[i] = MapNode(test_schem_data[i], MTSCHEM_PROB_ALWAYS, 0);
+ schem.schemdata[i] = MapNode(test_schem1_data[i], MTSCHEM_PROB_ALWAYS, 0);
for (s16 y = 0; y != size.Y; y++)
schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;
@@ -115,7 +116,7 @@ void TestSchematic::testLuaTableSerialize(INodeDefManager *ndef)
schem.schemdata = new MapNode[volume];
schem.slice_probs = new u8[size.Y];
for (size_t i = 0; i != volume; i++)
- schem.schemdata[i] = MapNode(test_schem_data2[i], MTSCHEM_PROB_ALWAYS, 0);
+ schem.schemdata[i] = MapNode(test_schem2_data[i], test_schem2_prob[i], 0);
for (s16 y = 0; y != size.Y; y++)
schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;
@@ -160,8 +161,8 @@ void TestSchematic::testFileSerializeDeserialize(INodeDefManager *ndef)
schem1.slice_probs[2] = 240;
for (size_t i = 0; i != volume; i++) {
- content_t c = content_map[test_schem_data2[i]];
- schem1.schemdata[i] = MapNode(c, MTSCHEM_PROB_ALWAYS, 0);
+ content_t c = content_map[test_schem2_data[i]];
+ schem1.schemdata[i] = MapNode(c, test_schem2_prob[i], 0);
}
std::string temp_file = getTestTempFile();
@@ -174,14 +175,14 @@ void TestSchematic::testFileSerializeDeserialize(INodeDefManager *ndef)
UASSERT(schem2.slice_probs[2] == 240);
for (size_t i = 0; i != volume; i++) {
- content_t c = content_map2[test_schem_data2[i]];
- UASSERT(schem2.schemdata[i] == MapNode(c, MTSCHEM_PROB_ALWAYS, 0));
+ content_t c = content_map2[test_schem2_data[i]];
+ UASSERT(schem2.schemdata[i] == MapNode(c, test_schem2_prob[i], 0));
}
}
// Should form a cross-shaped-thing...?
-const content_t TestSchematic::test_schem_data[7 * 6 * 4] = {
+const content_t TestSchematic::test_schem1_data[7 * 6 * 4] = {
3, 3, 1, 1, 1, 3, 3, // Y=0, Z=0
3, 0, 1, 2, 1, 0, 3, // Y=1, Z=0
3, 0, 1, 2, 1, 0, 3, // Y=2, Z=0
@@ -211,7 +212,7 @@ const content_t TestSchematic::test_schem_data[7 * 6 * 4] = {
3, 1, 1, 2, 1, 1, 3, // Y=5, Z=3
};
-const content_t TestSchematic::test_schem_data2[3 * 3 * 3] = {
+const content_t TestSchematic::test_schem2_data[3 * 3 * 3] = {
0, 0, 0,
0, 2, 0,
0, 0, 0,
@@ -225,41 +226,55 @@ const content_t TestSchematic::test_schem_data2[3 * 3 * 3] = {
0, 0, 0,
};
+const u8 TestSchematic::test_schem2_prob[3 * 3 * 3] = {
+ 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0x00,
+ 0x00, 0x00, 0x00,
+
+ 0x00, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0x00,
+
+ 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0x00,
+ 0x00, 0x00, 0x00,
+};
+
const char *TestSchematic::expected_lua_output =
"schematic = {\n"
"\tsize = {x=3, y=3, z=3},\n"
"\tyslice_prob = {\n"
- "\t\t{ypos=0, prob=255},\n"
- "\t\t{ypos=1, prob=255},\n"
- "\t\t{ypos=2, prob=255},\n"
+ "\t\t{ypos=0, prob=254},\n"
+ "\t\t{ypos=1, prob=254},\n"
+ "\t\t{ypos=2, prob=254},\n"
"\t},\n"
"\tdata = {\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"default:glass\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"default:glass\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"default:glass\", param1=255, param2=0},\n"
- "\t\t{name=\"default:lava_source\", param1=255, param2=0},\n"
- "\t\t{name=\"default:glass\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"default:glass\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"default:glass\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
- "\t\t{name=\"air\", param1=255, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"default:lava_source\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
"\t},\n"
"}\n";