diff --git a/builtin/game/item.lua b/builtin/game/item.lua
index d16601d30..c168bf096 100644
--- a/builtin/game/item.lua
+++ b/builtin/game/item.lua
@@ -574,6 +574,7 @@ core.nodedef_default = {
 	diggable = true,
 	climbable = false,
 	buildable_to = false,
+	floodable = false,
 	liquidtype = "none",
 	liquid_alternative_flowing = "",
 	liquid_alternative_source = "",
diff --git a/builtin/game/register.lua b/builtin/game/register.lua
index 992fdf744..ba5f69d67 100644
--- a/builtin/game/register.lua
+++ b/builtin/game/register.lua
@@ -289,6 +289,7 @@ core.register_node(":air", {
 	pointable = false,
 	diggable = false,
 	buildable_to = true,
+	floodable = true,
 	air_equivalent = true,
 	drop = "",
 	groups = {not_in_creative_inventory=1},
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 37d0aaabc..ea5029e61 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -3396,6 +3396,7 @@ Definition tables
         diggable = true, -- If false, can never be dug
         climbable = false, -- If true, can be climbed on (ladder)
         buildable_to = false, -- If true, placed nodes can replace this node
+        floodable = false, -- If true, liquids flow into and replace this node
         liquidtype = "none", -- "none"/"source"/"flowing"
         liquid_alternative_flowing = "", -- Flowing version of source liquid
         liquid_alternative_source = "", -- Source version of flowing liquid
diff --git a/src/map.cpp b/src/map.cpp
index ceebd40a3..ac29cfeee 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -1654,10 +1654,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 	loop_max *= m_transforming_liquid_loop_count_multiplier;
 #endif
 
-	while(m_transforming_liquid.size() != 0)
+	while (m_transforming_liquid.size() != 0)
 	{
 		// This should be done here so that it is done when continue is used
-		if(loopcount >= initial_size || loopcount >= loop_max)
+		if (loopcount >= initial_size || loopcount >= loop_max)
 			break;
 		loopcount++;
 
@@ -1674,21 +1674,24 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 		 */
 		s8 liquid_level = -1;
 		content_t liquid_kind = CONTENT_IGNORE;
-		LiquidType liquid_type = nodemgr->get(n0).liquid_type;
+		content_t floodable_node = CONTENT_AIR;
+		ContentFeatures cf = nodemgr->get(n0);
+		LiquidType liquid_type = cf.liquid_type;
 		switch (liquid_type) {
 			case LIQUID_SOURCE:
 				liquid_level = LIQUID_LEVEL_SOURCE;
-				liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
+				liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing);
 				break;
 			case LIQUID_FLOWING:
 				liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
 				liquid_kind = n0.getContent();
 				break;
 			case LIQUID_NONE:
-				// if this is an air node, it *could* be transformed into a liquid. otherwise,
-				// continue with the next node.
-				if (n0.getContent() != CONTENT_AIR)
+				// if this node is 'floodable', it *could* be transformed
+				// into a liquid, otherwise, continue with the next node.
+				if (!cf.floodable)
 					continue;
+				floodable_node = n0.getContent();
 				liquid_kind = CONTENT_AIR;
 				break;
 		}
@@ -1718,9 +1721,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 			}
 			v3s16 npos = p0 + dirs[i];
 			NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
+			ContentFeatures cfnb = nodemgr->get(nb.n);
 			switch (nodemgr->get(nb.n.getContent()).liquid_type) {
 				case LIQUID_NONE:
-					if (nb.n.getContent() == CONTENT_AIR) {
+					if (cfnb.floodable) {
 						airs[num_airs++] = nb;
 						// if the current node is a water source the neighbor
 						// should be enqueded for transformation regardless of whether the
@@ -1738,8 +1742,8 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 				case LIQUID_SOURCE:
 					// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
 					if (liquid_kind == CONTENT_AIR)
-						liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
-					if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
+						liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
+					if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
 						neutrals[num_neutrals++] = nb;
 					} else {
 						// Do not count bottom source, it will screw things up
@@ -1750,8 +1754,8 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 				case LIQUID_FLOWING:
 					// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
 					if (liquid_kind == CONTENT_AIR)
-						liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
-					if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
+						liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
+					if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
 						neutrals[num_neutrals++] = nb;
 					} else {
 						flows[num_flows++] = nb;
@@ -1770,8 +1774,8 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 		s8 max_node_level = -1;
 
 		u8 range = nodemgr->get(liquid_kind).liquid_range;
-		if (range > LIQUID_LEVEL_MAX+1)
-			range = LIQUID_LEVEL_MAX+1;
+		if (range > LIQUID_LEVEL_MAX + 1)
+			range = LIQUID_LEVEL_MAX + 1;
 
 		if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
 			// liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
@@ -1780,10 +1784,11 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 			new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
 		} else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
 			// liquid_kind is set properly, see above
-			new_node_content = liquid_kind;
 			max_node_level = new_node_level = LIQUID_LEVEL_MAX;
-			if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
-				new_node_content = CONTENT_AIR;
+			if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
+				new_node_content = liquid_kind;
+			else
+				new_node_content = floodable_node;
 		} else {
 			// no surrounding sources, so get the maximum level that can flow into this node
 			for (u16 i = 0; i < num_flows; i++) {
@@ -1794,16 +1799,16 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 							max_node_level = LIQUID_LEVEL_MAX;
 							if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
 								max_node_level = nb_liquid_level + WATER_DROP_BOOST;
-						} else if (nb_liquid_level > max_node_level)
+						} else if (nb_liquid_level > max_node_level) {
 							max_node_level = nb_liquid_level;
+						}
 						break;
 					case NEIGHBOR_LOWER:
 						break;
 					case NEIGHBOR_SAME_LEVEL:
 						if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
-							nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
+								nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
 							max_node_level = nb_liquid_level - 1;
-						}
 						break;
 				}
 			}
@@ -1821,23 +1826,25 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 					new_node_level = liquid_level + 1;
 				if (new_node_level != max_node_level)
 					must_reflow.push_back(p0);
-			} else
+			} else {
 				new_node_level = max_node_level;
+			}
 
-			if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
+			if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
 				new_node_content = liquid_kind;
 			else
-				new_node_content = CONTENT_AIR;
+				new_node_content = floodable_node;
 
 		}
 
 		/*
 			check if anything has changed. if not, just continue with the next node.
 		 */
-		if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
-										 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
-										 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
-										 == flowing_down)))
+		if (new_node_content == n0.getContent() &&
+				(nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
+				((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
+				((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
+				== flowing_down)))
 			continue;
 
 
@@ -1857,11 +1864,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 
 		// Find out whether there is a suspect for this action
 		std::string suspect;
-		if(m_gamedef->rollback()) {
+		if (m_gamedef->rollback())
 			suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
-		}
 
-		if(m_gamedef->rollback() && !suspect.empty()){
+		if (m_gamedef->rollback() && !suspect.empty()) {
 			// Blame suspect
 			RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
 			// Get old node for rollback
@@ -1880,10 +1886,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 
 		v3s16 blockpos = getNodeBlockPos(p0);
 		MapBlock *block = getBlockNoCreateNoEx(blockpos);
-		if(block != NULL) {
+		if (block != NULL) {
 			modified_blocks[blockpos] =  block;
 			// If new or old node emits light, MapBlock requires lighting update
-			if(nodemgr->get(n0).light_source != 0 ||
+			if (nodemgr->get(n0).light_source != 0 ||
 					nodemgr->get(n00).light_source != 0)
 				lighting_modified_blocks[block->getPos()] = block;
 		}
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index b5ccc3b94..2ebe1c131 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -233,6 +233,7 @@ void ContentFeatures::reset()
 	diggable = true;
 	climbable = false;
 	buildable_to = false;
+	floodable = false;
 	rightclickable = true;
 	leveled = 0;
 	liquid_type = LIQUID_NONE;
@@ -318,6 +319,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
 	// the protocol version
 	os<<serializeString(mesh);
 	collision_box.serialize(os, protocol_version);
+	writeU8(os, floodable);
 }
 
 void ContentFeatures::deSerialize(std::istream &is)
@@ -388,6 +390,7 @@ void ContentFeatures::deSerialize(std::istream &is)
 		// otherwise changes the protocol version
 	mesh = deSerializeString(is);
 	collision_box.deSerialize(is);
+	floodable = readU8(is);
 	}catch(SerializationError &e) {};
 }
 
@@ -520,6 +523,7 @@ void CNodeDefManager::clear()
 		f.pointable           = false;
 		f.diggable            = false;
 		f.buildable_to        = true;
+		f.floodable           = true;
 		f.is_ground_content   = true;
 		// Insert directly into containers
 		content_t c = CONTENT_AIR;
diff --git a/src/nodedef.h b/src/nodedef.h
index 7a84b0316..8f4942c04 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -229,6 +229,8 @@ struct ContentFeatures
 	bool climbable;
 	// Player can build on these
 	bool buildable_to;
+	// Liquids flow into and replace node
+	bool floodable;
 	// Player cannot build to these (placement prediction disabled)
 	bool rightclickable;
 	// Flowing liquid or snow, value = default level
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index 36a5c2ddd..62de2c968 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -481,6 +481,8 @@ ContentFeatures read_content_features(lua_State *L, int index)
 	getboolfield(L, index, "climbable", f.climbable);
 	// Player can build on these
 	getboolfield(L, index, "buildable_to", f.buildable_to);
+	// Liquids flow into and replace node
+	getboolfield(L, index, "floodable", f.floodable);
 	// Whether the node is non-liquid, source liquid or flowing liquid
 	f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype",
 			ScriptApiNode::es_LiquidType, LIQUID_NONE);