diff --git a/data/lava.png b/data/lava.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb02ada1318a66f6af28e2095b73d79b103080ed
Binary files /dev/null and b/data/lava.png differ
diff --git a/src/constants.h b/src/constants.h
index 21ab2a64c3b0097536b14e2aa2343ddf12b0b69b..1af5f1f1b2a9e55d4cbe5d1806e11cc6e75fd85f 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -30,9 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #define DEBUGFILE "debug.txt"
 
-#define WATER_ALPHA 160
-//#define WATER_ALPHA 190
-
 // Define for simulating the quirks of sending through internet.
 // Causes the socket class to deliberately drop random packets.
 // This disables unit testing of socket and connection.
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp
index ed2cd766a032acdca7c640b7b459a162168498b3..ab5cc1b54975f6d180d36a5d145505506a99e507 100644
--- a/src/content_mapblock.cpp
+++ b/src/content_mapblock.cpp
@@ -133,23 +133,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 	//bool smooth_lighting = g_settings.getBool("smooth_lighting");
 	bool invisible_stone = g_settings.getBool("invisible_stone");
 	
-	float node_water_level = 1.0;
+	float node_liquid_level = 1.0;
 	if(new_style_water)
-		node_water_level = 0.85;
+		node_liquid_level = 0.85;
 	
 	v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
 
-	// Flowing water material
-	video::SMaterial material_water1;
-	material_water1.setFlag(video::EMF_LIGHTING, false);
-	material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
-	material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
-	material_water1.setFlag(video::EMF_FOG_ENABLE, true);
-	material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
-	AtlasPointer pa_water1 = g_texturesource->getTexture(
-			g_texturesource->getTextureId("water.png"));
-	material_water1.setTexture(0, pa_water1.atlas);
-
 	// New-style leaves material
 	video::SMaterial material_leaves1;
 	material_leaves1.setFlag(video::EMF_LIGHTING, false);
@@ -337,30 +326,40 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 			collector.append(material, vertices, 4, indices, 6);
 		}
 		/*
-			Add flowing water to mesh
+			Add flowing liquid to mesh
 		*/
-		else if(n.getContent() == CONTENT_WATER)
+		else if(content_features(n).liquid_type == LIQUID_FLOWING)
 		{
-			bool top_is_water = false;
+			assert(content_features(n).special_material);
+			video::SMaterial &liquid_material =
+					*content_features(n).special_material;
+			assert(content_features(n).special_atlas);
+			AtlasPointer &pa_liquid1 =
+					*content_features(n).special_atlas;
+
+			bool top_is_same_liquid = false;
 			MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
-			if(ntop.getContent() == CONTENT_WATER || ntop.getContent() == CONTENT_WATERSOURCE)
-				top_is_water = true;
+			content_t c_flowing = content_features(n).liquid_alternative_flowing;
+			content_t c_source = content_features(n).liquid_alternative_source;
+			if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
+				top_is_same_liquid = true;
 			
 			u8 l = 0;
 			// Use the light of the node on top if possible
 			if(content_features(ntop).param_type == CPT_LIGHT)
 				l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
-			// Otherwise use the light of this node (the water)
+			// Otherwise use the light of this node (the liquid)
 			else
 				l = decode_light(n.getLightBlend(data->m_daynight_ratio));
-			video::SColor c = MapBlock_LightColor(WATER_ALPHA, l);
+			video::SColor c = MapBlock_LightColor(
+					content_features(n).vertex_alpha, l);
 			
-			// Neighbor water levels (key = relative position)
+			// Neighbor liquid levels (key = relative position)
 			// Includes current node
 			core::map<v3s16, f32> neighbor_levels;
 			core::map<v3s16, content_t> neighbor_contents;
 			core::map<v3s16, u8> neighbor_flags;
-			const u8 neighborflag_top_is_water = 0x01;
+			const u8 neighborflag_top_is_same_liquid = 0x01;
 			v3s16 neighbor_dirs[9] = {
 				v3s16(0,0,0),
 				v3s16(0,0,1),
@@ -384,19 +383,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 				{
 					content = n2.getContent();
 
-					if(n2.getContent() == CONTENT_WATERSOURCE)
-						level = (-0.5+node_water_level) * BS;
-					else if(n2.getContent() == CONTENT_WATER)
+					if(n2.getContent() == c_source)
+						level = (-0.5+node_liquid_level) * BS;
+					else if(n2.getContent() == c_flowing)
 						level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
-								* node_water_level) * BS;
+								* node_liquid_level) * BS;
 
 					// Check node above neighbor.
 					// NOTE: This doesn't get executed if neighbor
 					//       doesn't exist
 					p2.Y += 1;
 					n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
-					if(n2.getContent() == CONTENT_WATERSOURCE || n2.getContent() == CONTENT_WATER)
-						flags |= neighborflag_top_is_water;
+					if(n2.getContent() == c_source ||
+							n2.getContent() == c_flowing)
+						flags |= neighborflag_top_is_same_liquid;
 				}
 				
 				neighbor_levels.insert(neighbor_dirs[i], level);
@@ -404,10 +404,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 				neighbor_flags.insert(neighbor_dirs[i], flags);
 			}
 
-			//float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
-			//float water_level = neighbor_levels[v3s16(0,0,0)];
+			//float liquid_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
+			//float liquid_level = neighbor_levels[v3s16(0,0,0)];
 
-			// Corner heights (average between four waters)
+			// Corner heights (average between four liquids)
 			f32 corner_levels[4];
 			
 			v3s16 halfdirs[4] = {
@@ -426,13 +426,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 					v3s16 neighbordir = cornerdir - halfdirs[j];
 					u8 content = neighbor_contents[neighbordir];
 					// Special case for source nodes
-					if(content == CONTENT_WATERSOURCE)
+					if(content == c_source)
 					{
-						cornerlevel = (-0.5+node_water_level)*BS;
+						cornerlevel = (-0.5+node_liquid_level)*BS;
 						valid_count = 1;
 						break;
 					}
-					else if(content == CONTENT_WATER)
+					else if(content == c_flowing)
 					{
 						cornerlevel += neighbor_levels[neighbordir];
 						valid_count++;
@@ -469,24 +469,24 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 				v3s16 dir = side_dirs[i];
 
 				/*
-					If our topside is water and neighbor's topside
-					is water, don't draw side face
+					If our topside is liquid and neighbor's topside
+					is liquid, don't draw side face
 				*/
-				if(top_is_water &&
-						neighbor_flags[dir] & neighborflag_top_is_water)
+				if(top_is_same_liquid &&
+						neighbor_flags[dir] & neighborflag_top_is_same_liquid)
 					continue;
 
 				u8 neighbor_content = neighbor_contents[dir];
 				
-				// Don't draw face if neighbor is not air or water
+				// Don't draw face if neighbor is not air or liquid
 				if(neighbor_content != CONTENT_AIR
-						&& neighbor_content != CONTENT_WATER)
+						&& neighbor_content != c_source)
 					continue;
 				
-				bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
+				bool neighbor_is_liquid = (neighbor_content == c_source);
 				
-				// Don't draw any faces if neighbor is water and top is water
-				if(neighbor_is_water == true && top_is_water == false)
+				// Don't draw any faces if neighbor is liquid and top is liquid
+				if(neighbor_is_liquid == true && top_is_same_liquid == false)
 					continue;
 				
 				video::S3DVertex vertices[4] =
@@ -496,20 +496,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
-							pa_water1.x0(), pa_water1.y1()),
+							pa_liquid1.x0(), pa_liquid1.y1()),
 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
-							pa_water1.x1(), pa_water1.y1()),
+							pa_liquid1.x1(), pa_liquid1.y1()),
 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
-							pa_water1.x1(), pa_water1.y0()),
+							pa_liquid1.x1(), pa_liquid1.y0()),
 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
-							pa_water1.x0(), pa_water1.y0()),
+							pa_liquid1.x0(), pa_liquid1.y0()),
 				};
 				
 				/*
-					If our topside is water, set upper border of face
+					If our topside is liquid, set upper border of face
 					at upper border of node
 				*/
-				if(top_is_water)
+				if(top_is_same_liquid)
 				{
 					vertices[2].Pos.Y = 0.5*BS;
 					vertices[3].Pos.Y = 0.5*BS;
@@ -524,16 +524,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 				}
 				
 				/*
-					If neighbor is water, lower border of face is corner
-					water levels
+					If neighbor is liquid, lower border of face is corner
+					liquid levels
 				*/
-				if(neighbor_is_water)
+				if(neighbor_is_liquid)
 				{
 					vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
 					vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
 				}
 				/*
-					If neighbor is not water, lower border of face is
+					If neighbor is not liquid, lower border of face is
 					lower border of node
 				*/
 				else
@@ -558,14 +558,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 
 				u16 indices[] = {0,1,2,2,3,0};
 				// Add to mesh collector
-				collector.append(material_water1, vertices, 4, indices, 6);
+				collector.append(liquid_material, vertices, 4, indices, 6);
 			}
 			
 			/*
 				Generate top side, if appropriate
 			*/
 			
-			if(top_is_water == false)
+			if(top_is_same_liquid == false)
 			{
 				video::S3DVertex vertices[4] =
 				{
@@ -574,13 +574,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
-							pa_water1.x0(), pa_water1.y1()),
+							pa_liquid1.x0(), pa_liquid1.y1()),
 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
-							pa_water1.x1(), pa_water1.y1()),
+							pa_liquid1.x1(), pa_liquid1.y1()),
 					video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
-							pa_water1.x1(), pa_water1.y0()),
+							pa_liquid1.x1(), pa_liquid1.y0()),
 					video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
-							pa_water1.x0(), pa_water1.y0()),
+							pa_liquid1.x0(), pa_liquid1.y0()),
 				};
 				
 				// This fixes a strange bug
@@ -588,7 +588,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 
 				for(s32 i=0; i<4; i++)
 				{
-					//vertices[i].Pos.Y += water_level;
+					//vertices[i].Pos.Y += liquid_level;
 					//vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
 					s32 j = corner_resolve[i];
 					vertices[i].Pos.Y += corner_levels[j];
@@ -597,29 +597,33 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 
 				u16 indices[] = {0,1,2,2,3,0};
 				// Add to mesh collector
-				collector.append(material_water1, vertices, 4, indices, 6);
+				collector.append(liquid_material, vertices, 4, indices, 6);
 			}
 		}
 		/*
 			Add water sources to mesh if using new style
 		*/
-		else if(n.getContent() == CONTENT_WATERSOURCE && new_style_water)
+		else if(content_features(n).liquid_type == LIQUID_SOURCE
+				&& new_style_water)
 		{
-			//bool top_is_water = false;
+			assert(content_features(n).special_material);
+			video::SMaterial &liquid_material =
+					*content_features(n).special_material;
+			assert(content_features(n).special_atlas);
+			AtlasPointer &pa_liquid1 =
+					*content_features(n).special_atlas;
+
 			bool top_is_air = false;
 			MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
-			/*if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
-				top_is_water = true;*/
 			if(n.getContent() == CONTENT_AIR)
 				top_is_air = true;
 			
-			/*if(top_is_water == true)
-				continue;*/
 			if(top_is_air == false)
 				continue;
 
 			u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
-			video::SColor c = MapBlock_LightColor(WATER_ALPHA, l);
+			video::SColor c = MapBlock_LightColor(
+					content_features(n).vertex_alpha, l);
 			
 			video::S3DVertex vertices[4] =
 			{
@@ -628,24 +632,24 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 				video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
 				video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
 				video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
-						pa_water1.x0(), pa_water1.y1()),
+						pa_liquid1.x0(), pa_liquid1.y1()),
 				video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
-						pa_water1.x1(), pa_water1.y1()),
+						pa_liquid1.x1(), pa_liquid1.y1()),
 				video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
-						pa_water1.x1(), pa_water1.y0()),
+						pa_liquid1.x1(), pa_liquid1.y0()),
 				video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
-						pa_water1.x0(), pa_water1.y0()),
+						pa_liquid1.x0(), pa_liquid1.y0()),
 			};
 
 			for(s32 i=0; i<4; i++)
 			{
-				vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
+				vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS;
 				vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
 			}
 
 			u16 indices[] = {0,1,2,2,3,0};
 			// Add to mesh collector
-			collector.append(material_water1, vertices, 4, indices, 6);
+			collector.append(liquid_material, vertices, 4, indices, 6);
 		}
 		/*
 			Add leaves if using new style
diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp
index b164033db6e8669a3d2f0f2c2152e2af2695def5..3c135346785720c76c39dd20e3bdbb56af633179 100644
--- a/src/content_mapnode.cpp
+++ b/src/content_mapnode.cpp
@@ -372,6 +372,21 @@ void content_mapnode_init()
 	f->liquid_type = LIQUID_FLOWING;
 	f->liquid_alternative_flowing = CONTENT_WATER;
 	f->liquid_alternative_source = CONTENT_WATERSOURCE;
+	f->vertex_alpha = 160;
+	if(f->special_material == NULL && g_texturesource)
+	{
+		// Flowing water material
+		f->special_material = new video::SMaterial;
+		f->special_material->setFlag(video::EMF_LIGHTING, false);
+		f->special_material->setFlag(video::EMF_BACK_FACE_CULLING, false);
+		f->special_material->setFlag(video::EMF_BILINEAR_FILTER, false);
+		f->special_material->setFlag(video::EMF_FOG_ENABLE, true);
+		f->special_material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+		AtlasPointer *pa_water1 = new AtlasPointer(g_texturesource->getTexture(
+				g_texturesource->getTextureId("water.png")));
+		f->special_material->setTexture(0, pa_water1->atlas);
+		f->special_atlas = pa_water1;
+	}
 	
 	i = CONTENT_WATERSOURCE;
 	f = &content_features(i);
@@ -389,7 +404,7 @@ void content_mapnode_init()
 		if(g_texturesource)
 			t.texture = g_texturesource->getTexture("water.png");
 		
-		t.alpha = WATER_ALPHA;
+		t.alpha = 160;
 		t.material_type = MATERIAL_ALPHA_VERTEX;
 		t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
 		f->setAllTiles(t);
@@ -404,6 +419,98 @@ void content_mapnode_init()
 	f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
 	f->liquid_alternative_flowing = CONTENT_WATER;
 	f->liquid_alternative_source = CONTENT_WATERSOURCE;
+	f->vertex_alpha = 160;
+	if(f->special_material == NULL && g_texturesource)
+	{
+		// Flowing water material
+		f->special_material = new video::SMaterial;
+		f->special_material->setFlag(video::EMF_LIGHTING, false);
+		f->special_material->setFlag(video::EMF_BACK_FACE_CULLING, false);
+		f->special_material->setFlag(video::EMF_BILINEAR_FILTER, false);
+		f->special_material->setFlag(video::EMF_FOG_ENABLE, true);
+		f->special_material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+		AtlasPointer *pa_water1 = new AtlasPointer(g_texturesource->getTexture(
+				g_texturesource->getTextureId("water.png")));
+		f->special_material->setTexture(0, pa_water1->atlas);
+		f->special_atlas = pa_water1;
+	}
+	
+	i = CONTENT_LAVA;
+	f = &content_features(i);
+	f->setInventoryTextureCube("lava.png", "lava.png", "lava.png");
+	f->param_type = CPT_LIGHT;
+	f->light_propagates = false;
+	f->light_source = LIGHT_MAX-1;
+	f->solidness = 0; // Drawn separately, makes no faces
+	f->walkable = false;
+	f->pointable = false;
+	f->diggable = false;
+	f->buildable_to = true;
+	f->liquid_type = LIQUID_FLOWING;
+	f->liquid_alternative_flowing = CONTENT_LAVA;
+	f->liquid_alternative_source = CONTENT_LAVASOURCE;
+	if(f->special_material == NULL && g_texturesource)
+	{
+		// Flowing lava material
+		f->special_material = new video::SMaterial;
+		f->special_material->setFlag(video::EMF_LIGHTING, false);
+		f->special_material->setFlag(video::EMF_BACK_FACE_CULLING, false);
+		f->special_material->setFlag(video::EMF_BILINEAR_FILTER, false);
+		f->special_material->setFlag(video::EMF_FOG_ENABLE, true);
+		f->special_material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+		AtlasPointer *pa_lava1 = new AtlasPointer(
+			g_texturesource->getTexture(
+				g_texturesource->getTextureId("lava.png")));
+		f->special_material->setTexture(0, pa_lava1->atlas);
+		f->special_atlas = pa_lava1;
+	}
+	
+	i = CONTENT_LAVASOURCE;
+	f = &content_features(i);
+	f->setInventoryTextureCube("lava.png", "lava.png", "lava.png");
+	if(new_style_water)
+	{
+		f->solidness = 0; // drawn separately, makes no faces
+	}
+	else // old style
+	{
+		f->solidness = 2;
+
+		TileSpec t;
+		if(g_texturesource)
+			t.texture = g_texturesource->getTexture("lava.png");
+		
+		//t.alpha = 255;
+		//t.material_type = MATERIAL_ALPHA_VERTEX;
+		//t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
+		f->setAllTiles(t);
+	}
+	f->param_type = CPT_LIGHT;
+	f->light_propagates = false;
+	f->light_source = LIGHT_MAX-1;
+	f->walkable = false;
+	f->pointable = false;
+	f->diggable = false;
+	f->buildable_to = true;
+	f->liquid_type = LIQUID_SOURCE;
+	f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
+	f->liquid_alternative_flowing = CONTENT_LAVA;
+	f->liquid_alternative_source = CONTENT_LAVASOURCE;
+	if(f->special_material == NULL && g_texturesource)
+	{
+		// Flowing lava material
+		f->special_material = new video::SMaterial;
+		f->special_material->setFlag(video::EMF_LIGHTING, false);
+		f->special_material->setFlag(video::EMF_BACK_FACE_CULLING, false);
+		f->special_material->setFlag(video::EMF_BILINEAR_FILTER, false);
+		f->special_material->setFlag(video::EMF_FOG_ENABLE, true);
+		f->special_material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+		AtlasPointer *pa_lava1 = new AtlasPointer(
+			g_texturesource->getTexture(
+				g_texturesource->getTextureId("lava.png")));
+		f->special_material->setTexture(0, pa_lava1->atlas);
+		f->special_atlas = pa_lava1;
+	}
 	
 	i = CONTENT_TORCH;
 	f = &content_features(i);
diff --git a/src/content_mapnode.h b/src/content_mapnode.h
index 9643db7463f9f79cf85351f3be0deba6a3f1f548..1f6292ba478db84db98fe0500a86a79c4b184b4f 100644
--- a/src/content_mapnode.h
+++ b/src/content_mapnode.h
@@ -37,16 +37,24 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
 // 0x000...0x07f (0...127): param2 is fully usable
 // 126 and 127 are reserved.
 // Use these sparingly, only when the extra space in param2 might be needed.
+// Add a space when there is unused space between numbers.
 #define CONTENT_STONE 0
+
 #define CONTENT_WATER 2
 #define CONTENT_TORCH 3
+
 #define CONTENT_WATERSOURCE 9
+
 #define CONTENT_SIGN_WALL 14
 #define CONTENT_CHEST 15
 #define CONTENT_FURNACE 16
+
 #define CONTENT_FENCE 21
+
 #define CONTENT_RAIL 30
 #define CONTENT_LADDER 31
+#define CONTENT_LAVA 32
+#define CONTENT_LAVASOURCE 33
 
 // 0x800...0xfff (2048...4095): higher 4 bytes of param2 are not usable
 #define CONTENT_GRASS 0x800 //1
diff --git a/src/environment.cpp b/src/environment.cpp
index 2349852f28d7c61aa1715e74ab0dc7d3265d5f49..05efe9eea75e1970b61928b4b1641d3d4c46f718 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -1632,6 +1632,28 @@ void ClientEnvironment::step(float dtime)
 		}
 	}
 	
+	/*
+		A quick draft of lava damage
+	*/
+	if(m_lava_hurt_interval.step(dtime, 1.0))
+	{
+		v3f pf = lplayer->getPosition();
+		v3s16 p1 = floatToInt(pf + v3f(0, BS*0.0, 0), BS);
+		MapNode n1 = m_map->getNodeNoEx(p1);
+		v3s16 p2 = floatToInt(pf + v3f(0, BS*1.5, 0), BS);
+		MapNode n2 = m_map->getNodeNoEx(p2);
+		if(n1.getContent() == CONTENT_LAVA ||
+				n1.getContent() == CONTENT_LAVASOURCE ||
+				n2.getContent() == CONTENT_LAVA ||
+				n2.getContent() == CONTENT_LAVASOURCE)
+		{
+			ClientEnvEvent event;
+			event.type = CEE_PLAYER_DAMAGE;
+			event.player_damage.amount = 4*2; // 4 hearts
+			m_client_event_queue.push_back(event);
+		}
+	}
+	
 	/*
 		Stuff that can be done in an arbitarily large dtime
 	*/
@@ -1917,6 +1939,13 @@ void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos)
 		core::rect<s32> rect(0,0, ss.X, ss.Y);
 		driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect);
 	}
+	else if(content_features(n).solidness == 2 &&
+			g_settings.getBool("free_move") == false)
+	{
+		v2u32 ss = driver->getScreenSize();
+		core::rect<s32> rect(0,0, ss.X, ss.Y);
+		driver->draw2DRectangle(video::SColor(255, 0, 0, 0), rect);
+	}
 }
 
 #endif // #ifndef SERVER
diff --git a/src/environment.h b/src/environment.h
index b6767858ad5c49100a188e501e7c7bea05cc68a5..d9248d2adbb7d131b9edffe335be7911c60d5446 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -416,6 +416,7 @@ class ClientEnvironment : public Environment
 	core::map<u16, ClientActiveObject*> m_active_objects;
 	Queue<ClientEnvEvent> m_client_event_queue;
 	IntervalLimiter m_active_object_light_update_interval;
+	IntervalLimiter m_lava_hurt_interval;
 };
 
 #endif
diff --git a/src/game.cpp b/src/game.cpp
index a3a5f424a376c53b598d2ae6ecd251deaca0a884..c740ed252c2c949ba6abbffd57923dba8b0af215 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -2315,6 +2315,13 @@ void the_game(
 		// 0-1ms
 		guienv->drawAll();
 
+		/*
+			Environment post fx
+		*/
+		{
+			client.getEnv()->drawPostFx(driver, camera_position);
+		}
+		
 		/*
 			Draw hotbar
 		*/
@@ -2337,13 +2344,6 @@ void the_game(
 					NULL);
 		}
 
-		/*
-			Environment post fx
-		*/
-		{
-			client.getEnv()->drawPostFx(driver, camera_position);
-		}
-		
 		/*
 			End scene
 		*/
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index 9effa8ac66203eed0b98bc1008bb4efb816080dc..4285378a09e55c2708f6ac01d2dfe8888613f361 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -1817,6 +1817,16 @@ void make_block(BlockMakeData *data)
 						if(noisebuf_ground_wetness.get(x,y,z) < -0.6)
 							vmanip.m_data[i] = MapNode(CONTENT_GRAVEL);
 					}
+					else if(noisebuf_ground_crumbleness.get(x,y,z) <
+							-2.3 + MYMIN(0.1 * sqrt(MYMAX(0, -y)), 1.5))
+					{
+						vmanip.m_data[i] = MapNode(CONTENT_LAVASOURCE);
+						for(s16 x1=-1; x1<=1; x1++)
+						for(s16 y1=-1; y1<=1; y1++)
+						for(s16 z1=-1; z1<=1; z1++)
+							data->transforming_liquid.push_back(
+									v3s16(p2d.X+x1, y+y1, p2d.Y+z1));
+					}
 				}
 
 				data->vmanip->m_area.add_y(em, i, -1);
diff --git a/src/mapnode.cpp b/src/mapnode.cpp
index 9a8a73295b35e2bfdb87c5bc39e868c903b0c46c..956abe5c714b86d36f828f31beb20ae030f42c7e 100644
--- a/src/mapnode.cpp
+++ b/src/mapnode.cpp
@@ -30,8 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 ContentFeatures::~ContentFeatures()
 {
-	if(initial_metadata)
-		delete initial_metadata;
+	delete initial_metadata;
+	delete special_material;
+	delete special_atlas;
 }
 
 void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha)
diff --git a/src/mapnode.h b/src/mapnode.h
index 389fa1c9c7f73da1587d2e6aa34709c9d4ace4af..7ac050ef08055145cf60760eaf40c66aa1f5220a 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -128,8 +128,8 @@ struct ContentFeatures
 	bool pointable;
 	// Player can dig these
 	bool diggable;
-        // Player can climb these
-        bool climbable;
+	// Player can climb these
+	bool climbable;
 	// Player can build on these
 	bool buildable_to;
 	// Whether the node has no liquid, source liquid or flowing liquid
@@ -153,6 +153,11 @@ struct ContentFeatures
 	content_t liquid_alternative_flowing;
 	// If the content is liquid, this is the source version of the liquid.
 	content_t liquid_alternative_source;
+	// Used currently for flowing liquids
+	u8 vertex_alpha;
+	// Special irrlicht material, used sometimes
+	video::SMaterial *special_material;
+	AtlasPointer *special_atlas;
 	
 	// Amount of light the node emits
 	u8 light_source;
@@ -181,6 +186,10 @@ struct ContentFeatures
 		dug_item = "";
 		initial_metadata = NULL;
 		liquid_alternative_flowing = CONTENT_IGNORE;
+		liquid_alternative_source = CONTENT_IGNORE;
+		vertex_alpha = 255;
+		special_material = NULL;
+		special_atlas = NULL;
 		light_source = 0;
 		digging_properties.clear();
 	}