diff --git a/src/map.cpp b/src/map.cpp
index e1769b8eff897718ace3b053b00cdf9c20a3bd08..ab4acd4d55e1d36d2301128d7b98f59f0e8cad23 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -1540,17 +1540,6 @@ void Map::PrintInfo(std::ostream &out)
 
 #define WATER_DROP_BOOST 4
 
-enum NeighborType {
-	NEIGHBOR_UPPER,
-	NEIGHBOR_SAME_LEVEL,
-	NEIGHBOR_LOWER
-};
-struct NodeNeighbor {
-	MapNode n;
-	NeighborType t;
-	v3s16 p;
-};
-
 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 {
 	DSTACK(__FUNCTION_NAME);
@@ -1570,220 +1559,240 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 		v3s16 p0 = m_transforming_liquid.pop_front();
 
 		MapNode n0 = getNodeNoEx(p0);
-				
-		/*
-			Collect information about current node
-		 */
-		s8 liquid_level = -1;
-		u8 liquid_kind = CONTENT_IGNORE;
-		LiquidType liquid_type = content_features(n0.d).liquid_type;
-		switch (liquid_type) {
-			case LIQUID_SOURCE:
-				liquid_level = 8;
-				liquid_kind = content_features(n0.d).liquid_alternative_flowing;
-				break;
-			case LIQUID_FLOWING:
-				liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
-				liquid_kind = n0.d;
-				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.d != CONTENT_AIR)
-					continue;
-				liquid_kind = CONTENT_AIR;
-				break;
-		}
-		
+
+		// Don't deal with non-liquids
+		if(content_liquid(n0.d) == false)
+			continue;
+
+		bool is_source = !content_flowing_liquid(n0.d);
+
+		u8 liquid_level = 8;
+		if(is_source == false)
+			liquid_level = n0.param2 & 0x0f;
+
+		// Turn possible source into non-source
+		u8 nonsource_c = make_liquid_flowing(n0.d);
+
 		/*
-			Collect information about the environment
-		 */
-		v3s16 dirs[6] = {
-			v3s16( 0, 1, 0), // top
-			v3s16( 0,-1, 0), // bottom
-			v3s16( 1, 0, 0), // right
-			v3s16(-1, 0, 0), // left
-			v3s16( 0, 0, 1), // back
-			v3s16( 0, 0,-1), // front
-		};
-		NodeNeighbor sources[6]; // surrounding sources
-		int num_sources = 0;
-		NodeNeighbor flows[6]; // surrounding flowing liquid nodes
-		int num_flows = 0;
-		NodeNeighbor airs[6]; // surrounding air
-		int num_airs = 0;
-		NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
-		int num_neutrals = 0;
-		bool flowing_down = false;
-		for (u16 i = 0; i < 6; i++) {
-			NeighborType nt = NEIGHBOR_SAME_LEVEL;
-			switch (i) {
-				case 0:
-					nt = NEIGHBOR_UPPER;
-					break;
-				case 1:
-					nt = NEIGHBOR_LOWER;
-					break;
-			}
-			v3s16 npos = p0 + dirs[i];
-			NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
-			switch (content_features(nb.n.d).liquid_type) {
-				case LIQUID_NONE:
-					if (nb.n.d == CONTENT_AIR) {
-						airs[num_airs++] = nb;
-						// if the current node happens to be a flowing node, it will start to flow down here.
-						if (nb.t == NEIGHBOR_LOWER)
-							flowing_down = true;
-					} else {
-						neutrals[num_neutrals++] = nb;
+			If not source, check that some node flows into this one
+			and what is the level of liquid in this one
+		*/
+		if(is_source == false)
+		{
+			s8 new_liquid_level_max = -1;
+
+			v3s16 dirs_from[5] = {
+				v3s16(0,1,0), // top
+				v3s16(0,0,1), // back
+				v3s16(1,0,0), // right
+				v3s16(0,0,-1), // front
+				v3s16(-1,0,0), // left
+			};
+			for(u16 i=0; i<5; i++)
+			{
+				bool from_top = (i==0);
+
+				v3s16 p2 = p0 + dirs_from[i];
+				MapNode n2 = getNodeNoEx(p2);
+
+				if(content_liquid(n2.d))
+				{
+					u8 n2_nonsource_c = make_liquid_flowing(n2.d);
+					// Check that the liquids are the same type
+					if(n2_nonsource_c != nonsource_c)
+					{
+						dstream<<"WARNING: Not handling: different liquids"
+								" collide"<<std::endl;
+						continue;
 					}
-					break;
-				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 = content_features(nb.n.d).liquid_alternative_flowing;
-					if (content_features(nb.n.d).liquid_alternative_flowing !=liquid_kind) {
-						neutrals[num_neutrals++] = nb;
-					} else {
-						sources[num_sources++] = nb;
+					bool n2_is_source = !content_flowing_liquid(n2.d);
+					s8 n2_liquid_level = 8;
+					if(n2_is_source == false)
+						n2_liquid_level = n2.param2 & 0x07;
+
+					s8 new_liquid_level = -1;
+					if(from_top)
+					{
+						//new_liquid_level = 7;
+						if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
+							new_liquid_level = 7;
+						else
+							new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
 					}
-					break;
-				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 = content_features(nb.n.d).liquid_alternative_flowing;
-					if (content_features(nb.n.d).liquid_alternative_flowing != liquid_kind) {
-						neutrals[num_neutrals++] = nb;
-					} else {
-						flows[num_flows++] = nb;
-						if (nb.t == NEIGHBOR_LOWER)
-							flowing_down = true;
+					else if(n2_liquid_level > 0)
+					{
+						new_liquid_level = n2_liquid_level - 1;
 					}
-					break;
-			}
-		}
-		
-		/*
-			decide on the type (and possibly level) of the current node
-		 */
-		u8 new_node_content;
-		s8 new_node_level = -1;
-		if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
-			// liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
-			// or the flowing alternative of the first of the surrounding sources (if it's air), so
-			// it's perfectly safe to use liquid_kind here to determine the new node content.
-			new_node_content = content_features(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;
-			new_node_level = 7;
-		} else {
-			// no surrounding sources, so get the maximum level that can flow into this node
-			for (u16 i = 0; i < num_flows; i++) {
-				u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
-				switch (flows[i].t) {
-					case NEIGHBOR_UPPER:
-						if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
-							new_node_level = 7;
-							if (nb_liquid_level + WATER_DROP_BOOST < 7)
-								new_node_level = nb_liquid_level + WATER_DROP_BOOST;
-						}
-						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 > new_node_level) {
-							new_node_level = nb_liquid_level - 1;
-						}
-						break;
+
+					if(new_liquid_level > new_liquid_level_max)
+						new_liquid_level_max = new_liquid_level;
 				}
-			}
-			// don't flow as far in open terrain - if there isn't at least one adjacent solid block,
-			// substract another unit from the resulting water level.
-			if (!flowing_down && new_node_level >= 1) {
-				bool at_wall = false;
-				for (u16 i = 0; i < num_neutrals; i++) {
-					if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) {
-						at_wall = true;
-						break;
+			} //for
+
+			/*
+				If liquid level should be something else, update it and
+				add all the neighboring water nodes to the transform queue.
+			*/
+			if(new_liquid_level_max != liquid_level)
+			{
+				if(new_liquid_level_max == -1)
+				{
+					// Remove water alltoghether
+					n0.d = CONTENT_AIR;
+					n0.param2 = 0;
+					setNode(p0, n0);
+				}
+				else
+				{
+					n0.param2 = new_liquid_level_max;
+					setNode(p0, n0);
+				}
+
+				// Block has been modified
+				{
+					v3s16 blockpos = getNodeBlockPos(p0);
+					MapBlock *block = getBlockNoCreateNoEx(blockpos);
+					if(block != NULL)
+						modified_blocks.insert(blockpos, block);
+				}
+
+				/*
+					Add neighboring non-source liquid nodes to transform queue.
+				*/
+				v3s16 dirs[6] = {
+					v3s16(0,0,1), // back
+					v3s16(0,1,0), // top
+					v3s16(1,0,0), // right
+					v3s16(0,0,-1), // front
+					v3s16(0,-1,0), // bottom
+					v3s16(-1,0,0), // left
+				};
+				for(u16 i=0; i<6; i++)
+				{
+					v3s16 p2 = p0 + dirs[i];
+
+					MapNode n2 = getNodeNoEx(p2);
+					if(content_flowing_liquid(n2.d))
+					{
+						m_transforming_liquid.push_back(p2);
 					}
 				}
-				if (!at_wall)
-					new_node_level -= 1;
 			}
-			
-			if (new_node_level >= 0)
-				new_node_content = liquid_kind;
-			else
-				new_node_content = CONTENT_AIR;
 		}
-		
-		/*
-			check if anything has changed. if not, just continue with the next node.
-		 */
-		if (new_node_content == n0.d && (content_features(n0.d).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)))
+
+		// Get a new one from queue if the node has turned into non-water
+		if(content_liquid(n0.d) == false)
 			continue;
-		
-		
-		/*
-			update the current node
-		 */
-		bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
-		n0.d = new_node_content;
-		if (content_features(n0.d).liquid_type == LIQUID_FLOWING) {
-			// set level to last 3 bits, flowing down bit to 4th bit
-			n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
-		} else {
-			n0.param2 = 0;
-		}
-		setNode(p0, n0);
-		v3s16 blockpos = getNodeBlockPos(p0);
-		MapBlock *block = getBlockNoCreateNoEx(blockpos);
-		if(block != NULL)
-			modified_blocks.insert(blockpos, block);
-		
+
 		/*
-			enqueue neighbors for update if neccessary
-		 */
-		switch (content_features(n0.d).liquid_type) {
-			case LIQUID_SOURCE:
-				// make sure source flows into all neighboring nodes
-				for (u16 i = 0; i < num_flows; i++)
-					if (flows[i].t != NEIGHBOR_UPPER)
-						m_transforming_liquid.push_back(flows[i].p);
-				for (u16 i = 0; i < num_airs; i++)
-					if (airs[i].t != NEIGHBOR_UPPER)
-						m_transforming_liquid.push_back(airs[i].p);
-				break;
-			case LIQUID_NONE:
-				// this flow has turned to air; neighboring flows might need to do the same
-				for (u16 i = 0; i < num_flows; i++)
-					m_transforming_liquid.push_back(flows[i].p);
-				break;
-			case LIQUID_FLOWING:
-				for (u16 i = 0; i < num_flows; i++) {
-					u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
-					// liquid_level is still the ORIGINAL level of this node.
-					if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
-						flow_down_enabled))
-						m_transforming_liquid.push_back(flows[i].p);
+			Flow water from this node
+		*/
+		v3s16 dirs_to[5] = {
+			v3s16(0,-1,0), // bottom
+			v3s16(0,0,1), // back
+			v3s16(1,0,0), // right
+			v3s16(0,0,-1), // front
+			v3s16(-1,0,0), // left
+		};
+		for(u16 i=0; i<5; i++)
+		{
+			bool to_bottom = (i == 0);
+
+			// If liquid is at lowest possible height, it's not going
+			// anywhere except down
+			if(liquid_level == 0 && to_bottom == false)
+				continue;
+
+			u8 liquid_next_level = 0;
+			// If going to bottom
+			if(to_bottom)
+			{
+				//liquid_next_level = 7;
+				if(liquid_level >= 7 - WATER_DROP_BOOST)
+					liquid_next_level = 7;
+				else
+					liquid_next_level = liquid_level + WATER_DROP_BOOST;
+			}
+			else
+				liquid_next_level = liquid_level - 1;
+
+			bool n2_changed = false;
+			bool flowed = false;
+
+			v3s16 p2 = p0 + dirs_to[i];
+
+			MapNode n2 = getNodeNoEx(p2);
+			//dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
+
+			if(content_liquid(n2.d))
+			{
+				u8 n2_nonsource_c = make_liquid_flowing(n2.d);
+				// Check that the liquids are the same type
+				if(n2_nonsource_c != nonsource_c)
+				{
+					dstream<<"WARNING: Not handling: different liquids"
+							" collide"<<std::endl;
+					continue;
+				}
+				bool n2_is_source = !content_flowing_liquid(n2.d);
+				u8 n2_liquid_level = 8;
+				if(n2_is_source == false)
+					n2_liquid_level = n2.param2 & 0x07;
+
+				if(to_bottom)
+				{
+					flowed = true;
 				}
-				for (u16 i = 0; i < num_airs; i++) {
-					if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
-						m_transforming_liquid.push_back(airs[i].p);
+
+				if(n2_is_source)
+				{
+					// Just flow into the source, nothing changes.
+					// n2_changed is not set because destination didn't change
+					flowed = true;
+				}
+				else
+				{
+					if(liquid_next_level > liquid_level)
+					{
+						n2.param2 = liquid_next_level;
+						setNode(p2, n2);
+
+						n2_changed = true;
+						flowed = true;
+					}
 				}
+			}
+			else if(n2.d == CONTENT_AIR)
+			{
+				n2.d = nonsource_c;
+				n2.param2 = liquid_next_level;
+				setNode(p2, n2);
+
+				n2_changed = true;
+				flowed = true;
+			}
+
+			//dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
+
+			if(n2_changed)
+			{
+				m_transforming_liquid.push_back(p2);
+
+				v3s16 blockpos = getNodeBlockPos(p2);
+				MapBlock *block = getBlockNoCreateNoEx(blockpos);
+				if(block != NULL)
+					modified_blocks.insert(blockpos, block);
+			}
+
+			// If n2_changed to bottom, don't flow anywhere else
+			if(to_bottom && flowed && !is_source)
 				break;
 		}
-		
+
 		loopcount++;
 		//if(loopcount >= 100000)
-		if(loopcount >= initial_size * 10) {
+		if(loopcount >= initial_size * 1)
 			break;
-		}
 	}
 	//dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
 }