From 7d37913ea4f83807c508cec81b39744d69f481f8 Mon Sep 17 00:00:00 2001
From: Perttu Ahola <celeron55@gmail.com>
Date: Sat, 3 Dec 2011 18:18:59 +0200
Subject: [PATCH] Add ABM required neighbor check

---
 data/mods/default/init.lua | 11 +++++++++++
 src/environment.cpp        | 36 ++++++++++++++++++++++++++++++++++++
 src/environment.h          |  8 +++++++-
 src/scriptapi.cpp          | 32 ++++++++++++++++++++++++++++++--
 4 files changed, 84 insertions(+), 3 deletions(-)

diff --git a/data/mods/default/init.lua b/data/mods/default/init.lua
index 2e07b5412..7383996b1 100644
--- a/data/mods/default/init.lua
+++ b/data/mods/default/init.lua
@@ -73,6 +73,7 @@
 -- minetest.register_node(name, node definition)
 -- minetest.register_craftitem(name, craftitem definition)
 -- minetest.register_craft(recipe)
+-- minetest.register_abm(abm definition)
 -- minetest.register_globalstep(func(dtime))
 -- minetest.register_on_placenode(func(pos, newnode, placer))
 -- minetest.register_on_dignode(func(pos, oldnode, digger))
@@ -285,6 +286,16 @@
 --     }
 -- }
 --
+-- ABM (ActiveBlockModifier) definition:
+-- {
+--     nodenames = {"lava_source"},
+--     neighbors = {"water_source", "water_flowing"}, -- (any of these)
+--      ^ If left out or empty, any neighbor will do
+--      ^ This might get removed in the future
+--     interval = 1.0, -- (operation interval)
+--     chance = 1, -- (chance of trigger is 1.0/this)
+--     action = func(pos, node, active_object_count, active_object_count_wider),
+-- }
 
 -- print("minetest dump: "..dump(minetest))
 
diff --git a/src/environment.cpp b/src/environment.cpp
index 67ea05cf6..aa2b45f8f 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -565,6 +565,7 @@ struct ActiveABM
 {
 	ActiveBlockModifier *abm;
 	int chance;
+	std::set<content_t> required_neighbors;
 };
 
 class ABMHandler
@@ -602,6 +603,18 @@ class ABMHandler
 			aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
 			if(aabm.chance == 0)
 				aabm.chance = 1;
+			// Trigger neighbors
+			std::set<std::string> required_neighbors_s
+					= abm->getRequiredNeighbors();
+			for(std::set<std::string>::iterator
+					i = required_neighbors_s.begin();
+					i != required_neighbors_s.end(); i++){
+				content_t c = ndef->getId(*i);
+				if(c == CONTENT_IGNORE)
+					continue;
+				aabm.required_neighbors.insert(c);
+			}
+			// Trigger contents
 			std::set<std::string> contents_s = abm->getTriggerContents();
 			for(std::set<std::string>::iterator
 					i = contents_s.begin(); i != contents_s.end(); i++){
@@ -646,6 +659,29 @@ class ABMHandler
 				if(myrand() % i->chance != 0)
 					continue;
 
+				// Check neighbors
+				if(!i->required_neighbors.empty())
+				{
+					v3s16 p1;
+					for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
+					for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
+					for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
+					{
+						if(p1 == p)
+							continue;
+						MapNode n = map->getNodeNoEx(p1);
+						content_t c = n.getContent();
+						std::set<content_t>::const_iterator k;
+						k = i->required_neighbors.find(c);
+						if(k != i->required_neighbors.end()){
+							goto neighbor_found;
+						}
+					}
+					// No required neighbor found
+					continue;
+				}
+neighbor_found:
+
 				// Find out how many objects the block contains
 				u32 active_object_count = block->m_static_objects.m_active.size();
 				// Find out how many objects this and all the neighbors contain
diff --git a/src/environment.h b/src/environment.h
index 7759d43af..e14a9c485 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -108,9 +108,15 @@ class ActiveBlockModifier
 	ActiveBlockModifier(){};
 	virtual ~ActiveBlockModifier(){};
 	
+	// Set of contents to trigger on
 	virtual std::set<std::string> getTriggerContents()=0;
+	// Set of required neighbors (trigger doesn't happen if none are found)
+	// Empty = do not check neighbors
+	virtual std::set<std::string> getRequiredNeighbors()
+	{ return std::set<std::string>(); }
+	// Trigger interval in seconds
 	virtual float getTriggerInterval() = 0;
-	// chance of (1 / return value), 0 is disallowed
+	// Random chance of (1 / return value), 0 is disallowed
 	virtual u32 getTriggerChance() = 0;
 	// This is called usually at interval for 1/chance of the nodes
 	virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp
index c9f132f4a..3fe69e709 100644
--- a/src/scriptapi.cpp
+++ b/src/scriptapi.cpp
@@ -709,15 +709,18 @@ class LuaABM : public ActiveBlockModifier
 	int m_id;
 
 	std::set<std::string> m_trigger_contents;
+	std::set<std::string> m_required_neighbors;
 	float m_trigger_interval;
 	u32 m_trigger_chance;
 public:
 	LuaABM(lua_State *L, int id,
 			const std::set<std::string> &trigger_contents,
+			const std::set<std::string> &required_neighbors,
 			float trigger_interval, u32 trigger_chance):
 		m_lua(L),
 		m_id(id),
 		m_trigger_contents(trigger_contents),
+		m_required_neighbors(required_neighbors),
 		m_trigger_interval(trigger_interval),
 		m_trigger_chance(trigger_chance)
 	{
@@ -726,6 +729,10 @@ class LuaABM : public ActiveBlockModifier
 	{
 		return m_trigger_contents;
 	}
+	virtual std::set<std::string> getRequiredNeighbors()
+	{
+		return m_required_neighbors;
+	}
 	virtual float getTriggerInterval()
 	{
 		return m_trigger_interval;
@@ -2575,7 +2582,9 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
 	lua_pushlightuserdata(L, env);
 	lua_setfield(L, LUA_REGISTRYINDEX, "minetest_env");
 
-	/* Add ActiveBlockModifiers to environment */
+	/*
+		Add ActiveBlockModifiers to environment
+	*/
 
 	// Get minetest.registered_abms
 	lua_getglobal(L, "minetest");
@@ -2603,6 +2612,25 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
 					// removes value, keeps key for next iteration
 					lua_pop(L, 1);
 				}
+			} else if(lua_isstring(L, -1)){
+				trigger_contents.insert(lua_tostring(L, -1));
+			}
+			lua_pop(L, 1);
+
+			std::set<std::string> required_neighbors;
+			lua_getfield(L, current_abm, "neighbors");
+			if(lua_istable(L, -1)){
+				int table = lua_gettop(L);
+				lua_pushnil(L);
+				while(lua_next(L, table) != 0){
+					// key at index -2 and value at index -1
+					luaL_checktype(L, -1, LUA_TSTRING);
+					required_neighbors.insert(lua_tostring(L, -1));
+					// removes value, keeps key for next iteration
+					lua_pop(L, 1);
+				}
+			} else if(lua_isstring(L, -1)){
+				required_neighbors.insert(lua_tostring(L, -1));
 			}
 			lua_pop(L, 1);
 
@@ -2613,7 +2641,7 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
 			getintfield(L, current_abm, "chance", trigger_chance);
 
 			LuaABM *abm = new LuaABM(L, id, trigger_contents,
-					trigger_interval, trigger_chance);
+					required_neighbors, trigger_interval, trigger_chance);
 			
 			env->addActiveBlockModifier(abm);
 
-- 
GitLab