From b88595050f3af5ccac06aac331ead4ebdcb9deb9 Mon Sep 17 00:00:00 2001
From: paramat <mat.gregory@virginmedia.com>
Date: Sun, 11 Sep 2016 23:34:43 +0100
Subject: [PATCH] Decorations: Generalise 'spawn by' to be used by all
 decoration types

In lua_api.txt, make clear that 'place on' and 'spawn by' can be lists.
---
 doc/lua_api.txt                 |  16 ++---
 src/mg_decoration.cpp           | 119 ++++++++++++++++----------------
 src/mg_decoration.h             |   9 ++-
 src/script/lua_api/l_mapgen.cpp |  21 +++---
 4 files changed, 80 insertions(+), 85 deletions(-)

diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 74d4b90d5..aa4f129f0 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -3909,7 +3909,7 @@ The Biome API is still in an experimental phase and subject to change.
     {
         deco_type = "simple", -- See "Decoration types"
         place_on = "default:dirt_with_grass",
-    --  ^ Node that decoration can be placed on
+    --  ^ Node (or list of nodes) that the decoration can be placed on
         sidelen = 8,
     --  ^ Size of divisions made in the chunk being generated.
     --  ^ If the chunk size is not evenly divisible by sidelen, sidelen is made equal to the chunk size.
@@ -3928,6 +3928,13 @@ The Biome API is still in an experimental phase and subject to change.
     -- ^ Minimum and maximum `y` positions these decorations can be generated at.
     -- ^ This parameter refers to the `y` position of the decoration base, so
     --   the actual maximum height would be `height_max + size.Y`.
+        spawn_by = "default:water",
+    --  ^ Node (or list of nodes) that the decoration only spawns next to.
+    --  ^ Checks two horizontal planes of neighbouring nodes (including diagonal neighbours),
+    --  ^ one plane at Y = surface and one plane at Y = surface = + 1.
+        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.
         flags = "liquid_surface, force_placement",
     --  ^ Flags for all decoration types.
     --  ^ "liquid_surface": Instead of placement on the highest solid surface
@@ -3945,13 +3952,6 @@ The Biome API is still in an experimental phase and subject to change.
         height_max = 0,
     --      ^ Number of nodes the decoration can be at maximum.
     --  ^ If absent, the parameter 'height' is used as a constant.
-        spawn_by = "default:water",
-    --  ^ Node that the decoration only spawns next to.
-    --  ^ The neighbours checked are the 8 nodes horizontally surrounding the lowest node of the
-    --  ^ decoration, and the 8 nodes horizontally surrounding the ground node below the 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",
diff --git a/src/mg_decoration.cpp b/src/mg_decoration.cpp
index 127915b51..92483abc3 100644
--- a/src/mg_decoration.cpp
+++ b/src/mg_decoration.cpp
@@ -26,12 +26,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/numeric.h"
 
 FlagDesc flagdesc_deco[] = {
-	{"place_center_x", DECO_PLACE_CENTER_X},
-	{"place_center_y", DECO_PLACE_CENTER_Y},
-	{"place_center_z", DECO_PLACE_CENTER_Z},
+	{"place_center_x",  DECO_PLACE_CENTER_X},
+	{"place_center_y",  DECO_PLACE_CENTER_Y},
+	{"place_center_z",  DECO_PLACE_CENTER_Z},
 	{"force_placement", DECO_FORCE_PLACEMENT},
-	{"liquid_surface", DECO_LIQUID_SURFACE},
-	{NULL,             0}
+	{"liquid_surface",  DECO_LIQUID_SURFACE},
+	{NULL,              0}
 };
 
 
@@ -82,6 +82,56 @@ Decoration::~Decoration()
 void Decoration::resolveNodeNames()
 {
 	getIdsFromNrBacklog(&c_place_on);
+	getIdsFromNrBacklog(&c_spawnby);
+}
+
+
+bool Decoration::canPlaceDecoration(MMVManip *vm, v3s16 p)
+{
+	// Check if the decoration can be placed on this node
+	u32 vi = vm->m_area.index(p);
+	if (!CONTAINS(c_place_on, vm->m_data[vi].getContent()))
+		return false;
+
+	// Don't continue if there are no spawnby constraints
+	if (nspawnby == -1)
+		return true;
+
+	int nneighs = 0;
+	static const v3s16 dirs[16] = {
+		v3s16( 0, 0,  1),
+		v3s16( 0, 0, -1),
+		v3s16( 1, 0,  0),
+		v3s16(-1, 0,  0),
+		v3s16( 1, 0,  1),
+		v3s16(-1, 0,  1),
+		v3s16(-1, 0, -1),
+		v3s16( 1, 0, -1),
+
+		v3s16( 0, 1,  1),
+		v3s16( 0, 1, -1),
+		v3s16( 1, 1,  0),
+		v3s16(-1, 1,  0),
+		v3s16( 1, 1,  1),
+		v3s16(-1, 1,  1),
+		v3s16(-1, 1, -1),
+		v3s16( 1, 1, -1)
+	};
+
+	// Check these 16 neighbouring nodes for enough spawnby nodes
+	for (size_t i = 0; i != ARRLEN(dirs); i++) {
+		u32 index = vm->m_area.index(p + dirs[i]);
+		if (!vm->m_area.contains(index))
+			continue;
+
+		if (CONTAINS(c_spawnby, vm->m_data[index].getContent()))
+			nneighs++;
+	}
+
+	if (nneighs < nspawnby)
+		return false;
+
+	return true;
 }
 
 
@@ -236,66 +286,15 @@ void DecoSimple::resolveNodeNames()
 {
 	Decoration::resolveNodeNames();
 	getIdsFromNrBacklog(&c_decos);
-	getIdsFromNrBacklog(&c_spawnby);
 }
 
 
-bool DecoSimple::canPlaceDecoration(MMVManip *vm, v3s16 p)
+size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p)
 {
 	// Don't bother if there aren't any decorations to place
 	if (c_decos.size() == 0)
-		return false;
-
-	u32 vi = vm->m_area.index(p);
-
-	// Check if the decoration can be placed on this node
-	if (!CONTAINS(c_place_on, vm->m_data[vi].getContent()))
-		return false;
-
-	// Don't continue if there are no spawnby constraints
-	if (nspawnby == -1)
-		return true;
-
-	int nneighs = 0;
-	v3s16 dirs[16] = {
-		v3s16( 0, 0,  1),
-		v3s16( 0, 0, -1),
-		v3s16( 1, 0,  0),
-		v3s16(-1, 0,  0),
-		v3s16( 1, 0,  1),
-		v3s16(-1, 0,  1),
-		v3s16(-1, 0, -1),
-		v3s16( 1, 0, -1),
-
-		v3s16( 0, 1,  1),
-		v3s16( 0, 1, -1),
-		v3s16( 1, 1,  0),
-		v3s16(-1, 1,  0),
-		v3s16( 1, 1,  1),
-		v3s16(-1, 1,  1),
-		v3s16(-1, 1, -1),
-		v3s16( 1, 1, -1)
-	};
-
-	// Check a Moore neighborhood if there are enough spawnby nodes
-	for (size_t i = 0; i != ARRLEN(dirs); i++) {
-		u32 index = vm->m_area.index(p + dirs[i]);
-		if (!vm->m_area.contains(index))
-			continue;
-
-		if (CONTAINS(c_spawnby, vm->m_data[index].getContent()))
-			nneighs++;
-	}
-
-	if (nneighs < nspawnby)
-		return false;
-
-	return true;
-}
-
+		return 0;
 
-size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p)
-{
 	if (!canPlaceDecoration(vm, p))
 		return 0;
 
@@ -345,9 +344,7 @@ size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p)
 	if (schematic == NULL)
 		return 0;
 
-	u32 vi = vm->m_area.index(p);
-	content_t c = vm->m_data[vi].getContent();
-	if (!CONTAINS(c_place_on, c))
+	if (!canPlaceDecoration(vm, p))
 		return 0;
 
 	if (flags & DECO_PLACE_CENTER_X)
diff --git a/src/mg_decoration.h b/src/mg_decoration.h
index da98fd482..be0ba44d7 100644
--- a/src/mg_decoration.h
+++ b/src/mg_decoration.h
@@ -68,6 +68,7 @@ class Decoration : public ObjDef, public NodeResolver {
 
 	virtual void resolveNodeNames();
 
+	bool canPlaceDecoration(MMVManip *vm, v3s16 p);
 	size_t placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
 	//size_t placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
 
@@ -82,6 +83,8 @@ class Decoration : public ObjDef, public NodeResolver {
 	s16 y_max;
 	float fill_ratio;
 	NoiseParams np;
+	std::vector<content_t> c_spawnby;
+	s16 nspawnby;
 
 	UNORDERED_SET<u8> biomes;
 	//std::list<CutoffData> cutoffs;
@@ -90,17 +93,13 @@ class Decoration : public ObjDef, public NodeResolver {
 
 class DecoSimple : public Decoration {
 public:
+	virtual void resolveNodeNames();
 	virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p);
-	bool canPlaceDecoration(MMVManip *vm, v3s16 p);
 	virtual int getHeight();
 
-	virtual void resolveNodeNames();
-
 	std::vector<content_t> c_decos;
-	std::vector<content_t> c_spawnby;
 	s16 deco_height;
 	s16 deco_height_max;
-	s16 nspawnby;
 };
 
 class DecoSchematic : public Decoration {
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index 9f28231eb..a176f4f52 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -902,6 +902,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 	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->nspawnby   = getintfield_default(L, index, "num_spawn_by", -1);
 	deco->sidelen    = getintfield_default(L, index, "sidelen", 8);
 	if (deco->sidelen <= 0) {
 		errorstream << "register_decoration: sidelen must be "
@@ -929,6 +930,14 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 		errorstream << "register_decoration: couldn't get all biomes " << std::endl;
 	lua_pop(L, 1);
 
+	//// Get node name(s) to 'spawn by'
+	size_t nnames = getstringlistfield(L, index, "spawn_by", &deco->m_nodenames);
+	deco->m_nnlistsizes.push_back(nnames);
+	if (nnames == 0 && deco->nspawnby != -1) {
+		errorstream << "register_decoration: no spawn_by nodes defined,"
+			" but num_spawn_by specified" << std::endl;
+	}
+
 	//// Handle decoration type-specific parameters
 	bool success = false;
 	switch (decotype) {
@@ -962,12 +971,10 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 
 bool read_deco_simple(lua_State *L, DecoSimple *deco)
 {
-	size_t nnames;
 	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"
@@ -975,7 +982,7 @@ bool read_deco_simple(lua_State *L, DecoSimple *deco)
 		return false;
 	}
 
-	nnames = getstringlistfield(L, index, "decoration", &deco->m_nodenames);
+	size_t nnames = getstringlistfield(L, index, "decoration", &deco->m_nodenames);
 	deco->m_nnlistsizes.push_back(nnames);
 	if (nnames == 0) {
 		errorstream << "register_decoration: no decoration nodes "
@@ -983,14 +990,6 @@ bool read_deco_simple(lua_State *L, DecoSimple *deco)
 		return false;
 	}
 
-	nnames = getstringlistfield(L, index, "spawn_by", &deco->m_nodenames);
-	deco->m_nnlistsizes.push_back(nnames);
-	if (nnames == 0 && deco->nspawnby != -1) {
-		errorstream << "register_decoration: no spawn_by nodes defined,"
-			" but num_spawn_by specified" << std::endl;
-		return false;
-	}
-
 	return true;
 }
 
-- 
GitLab