]> git.lizzy.rs Git - minetest.git/commitdiff
merged the content type extension and delta
authorPerttu Ahola <celeron55@gmail.com>
Sat, 23 Jul 2011 16:04:37 +0000 (19:04 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 23 Jul 2011 16:04:37 +0000 (19:04 +0300)
1  2 
src/content_craft.cpp
src/content_inventory.cpp
src/content_mapblock.cpp
src/content_mapnode.cpp
src/content_mapnode.h
src/environment.cpp
src/game.cpp
src/main.cpp
src/map.cpp
src/mapgen.cpp
src/mapnode.h

diff --combined src/content_craft.cpp
index e4573a211b2c41e07ccd8dd2e357ab6ca581241a,069e68300b9bdf412c082a84b7aaa2fdc250c19e..b5a1dc7761795deeee13cabf022ef920f15859f9
@@@ -21,7 -21,6 +21,7 @@@ with this program; if not, write to th
  #include "inventory.h"
  #include "content_mapnode.h"
  #include "player.h"
 +#include "mapnode.h" // For content_t
  
  /*
        items: actually *items[9]
@@@ -262,6 -261,24 +262,24 @@@ InventoryItem *craft_get_result(Invento
                }
        }
  
+       // Rail
+       {
+               ItemSpec specs[9];
+               specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+               specs[1] = ItemSpec(ITEM_CRAFT, "Stick");
+               specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+               specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+               specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
+               specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+               specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+               specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
+               specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+               if(checkItemCombination(items, specs))
+               {
+                       return new MaterialItem(CONTENT_RAIL, 15);
+               }
+       }
        // Chest
        {
                ItemSpec specs[9];
                }
        }
  
+       // Sandstone
+       {
+               ItemSpec specs[9];
+               specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
+               specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
+               specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
+               specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
+               if(checkItemCombination(items, specs))
+               {
+                       return new MaterialItem(CONTENT_SANDSTONE, 1);
+               }
+       }
+       // Clay
+       {
+               ItemSpec specs[9];
+               specs[3] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
+               specs[4] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
+               specs[6] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
+               specs[7] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
+               if(checkItemCombination(items, specs))
+               {
+                       return new MaterialItem(CONTENT_CLAY, 1);
+               }
+       }
+       // Brick
+       {
+               ItemSpec specs[9];
+               specs[3] = ItemSpec(ITEM_CRAFT, "clay_brick");
+               specs[4] = ItemSpec(ITEM_CRAFT, "clay_brick");
+               specs[6] = ItemSpec(ITEM_CRAFT, "clay_brick");
+               specs[7] = ItemSpec(ITEM_CRAFT, "clay_brick");
+               if(checkItemCombination(items, specs))
+               {
+                       return new MaterialItem(CONTENT_BRICK, 1);
+               }
+       }
+       // Paper
+       {
+               ItemSpec specs[9];
+               specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+               specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+               specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+               if(checkItemCombination(items, specs))
+               {
+                       return new CraftItem("paper", 1);
+               }
+       }
+       // Book
+       {
+               ItemSpec specs[9];
+               specs[1] = ItemSpec(ITEM_CRAFT, "paper");
+               specs[4] = ItemSpec(ITEM_CRAFT, "paper");
+               specs[7] = ItemSpec(ITEM_CRAFT, "paper");
+               if(checkItemCombination(items, specs))
+               {
+                       return new CraftItem("book", 1);
+               }
+       }
+       // Book shelf
+       {
+               ItemSpec specs[9];
+               specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+               specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+               specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+               specs[3] = ItemSpec(ITEM_CRAFT, "book");
+               specs[4] = ItemSpec(ITEM_CRAFT, "book");
+               specs[5] = ItemSpec(ITEM_CRAFT, "book");
+               specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+               specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+               specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+               if(checkItemCombination(items, specs))
+               {
+                       return new MaterialItem(CONTENT_BOOKSHELF, 1);
+               }
+       }
        return NULL;
  }
  
@@@ -348,16 -446,23 +447,23 @@@ void craft_set_creative_inventory(Playe
        */
        
        // CONTENT_IGNORE-terminated list
 -      u8 material_items[] = {
 +      content_t material_items[] = {
                CONTENT_TORCH,
                CONTENT_COBBLE,
                CONTENT_MUD,
                CONTENT_STONE,
                CONTENT_SAND,
+               CONTENT_SANDSTONE,
+               CONTENT_CLAY,
+               CONTENT_BRICK,
                CONTENT_TREE,
                CONTENT_LEAVES,
+               CONTENT_CACTUS,
+               CONTENT_PAPYRUS,
+               CONTENT_BOOKSHELF,
                CONTENT_GLASS,
                CONTENT_FENCE,
+               CONTENT_RAIL,
                CONTENT_MESE,
                CONTENT_WATERSOURCE,
                CONTENT_CLOUD,
                CONTENT_IGNORE
        };
        
 -      u8 *mip = material_items;
 +      content_t *mip = material_items;
        for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
        {
                if(*mip == CONTENT_IGNORE)
index 78fa6f3d1f4bee835abff93076144d3c15f74da0,1068defb5e8772a3289704fcef077aba8b5dfd03..3222506067169eb5af7be5805162fdc34dde07f8
@@@ -23,7 -23,7 +23,7 @@@ with this program; if not, write to th
  //#include "serverobject.h"
  #include "content_sao.h"
  
 -bool item_material_is_cookable(u8 content)
 +bool item_material_is_cookable(content_t content)
  {
        if(content == CONTENT_TREE)
                return true;
@@@ -34,7 -34,7 +34,7 @@@
        return false;
  }
  
 -InventoryItem* item_material_create_cook_result(u8 content)
 +InventoryItem* item_material_create_cook_result(content_t content)
  {
        if(content == CONTENT_TREE)
                return new CraftItem("lump_of_coal", 1);
@@@ -49,14 -49,24 +49,24 @@@ std::string item_craft_get_image_name(c
  {
        if(subname == "Stick")
                return "stick.png";
+       else if(subname == "paper")
+               return "paper.png";
+       else if(subname == "book")
+               return "book.png";
        else if(subname == "lump_of_coal")
                return "lump_of_coal.png";
        else if(subname == "lump_of_iron")
                return "lump_of_iron.png";
+       else if(subname == "lump_of_clay")
+               return "lump_of_clay.png";
        else if(subname == "steel_ingot")
                return "steel_ingot.png";
+       else if(subname == "clay_brick")
+               return "clay_brick.png";
        else if(subname == "rat")
                return "rat.png";
+       else if(subname == "firefly")
+               return "firefly.png";
        else
                return "cloud.png"; // just something
  }
@@@ -69,13 -79,18 +79,18 @@@ ServerActiveObject* item_craft_create_o
                ServerActiveObject *obj = new RatSAO(env, id, pos);
                return obj;
        }
+       else if(subname == "firefly")
+       {
+               ServerActiveObject *obj = new FireflySAO(env, id, pos);
+               return obj;
+       }
  
        return NULL;
  }
  
  s16 item_craft_get_drop_count(const std::string &subname)
  {
-       if(subname == "rat")
+       if(subname == "rat" || subname == "firefly")
                return 1;
  
        return -1;
@@@ -83,7 -98,7 +98,7 @@@
  
  bool item_craft_is_cookable(const std::string &subname)
  {
-       if(subname == "lump_of_iron")
+       if(subname == "lump_of_iron" || subname == "lump_of_clay")
                return true;
                
        return false;
@@@ -93,6 -108,8 +108,8 @@@ InventoryItem* item_craft_create_cook_r
  {
        if(subname == "lump_of_iron")
                return new CraftItem("steel_ingot", 1);
+       else if(subname == "lump_of_clay")
+               return new CraftItem("clay_brick", 1);
  
        return NULL;
  }
diff --combined src/content_mapblock.cpp
index 38bc9620cd748c6b7209adaf35d884c8527326f3,d8bf71dc032ad3d77da461e9f19d763aeffe6c11..4a9fa5e98cdfaf8c4c54f184e56854c9125b6244
@@@ -188,6 -188,16 +188,16 @@@ void mapblock_mesh_generate_special(Mes
        material_general.setFlag(video::EMF_FOG_ENABLE, true);
        material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
  
+       // Papyrus material
+       video::SMaterial material_papyrus;
+       material_papyrus.setFlag(video::EMF_LIGHTING, false);
+       material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false);
+       material_papyrus.setFlag(video::EMF_FOG_ENABLE, true);
+       material_papyrus.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+       AtlasPointer pa_papyrus = g_texturesource->getTexture(
+                       g_texturesource->getTextureId("papyrus.png"));
+       material_papyrus.setTexture(0, pa_papyrus.atlas);
        for(s16 z=0; z<MAP_BLOCKSIZE; z++)
        for(s16 y=0; y<MAP_BLOCKSIZE; y++)
        for(s16 x=0; x<MAP_BLOCKSIZE; x++)
                /*
                        Add torches to mesh
                */
 -              if(n.d == CONTENT_TORCH)
 +              if(n.getContent() == CONTENT_TORCH)
                {
                        video::SColor c(255,255,255,255);
  
                                video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
                        };
  
 -                      v3s16 dir = unpackDir(n.dir);
 +                      v3s16 dir = unpackDir(n.param2);
  
                        for(s32 i=0; i<4; i++)
                        {
                /*
                        Signs on walls
                */
 -              else if(n.d == CONTENT_SIGN_WALL)
 +              else if(n.getContent() == CONTENT_SIGN_WALL)
                {
                        u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
                        video::SColor c(255,l,l,l);
                                video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
                        };
  
 -                      v3s16 dir = unpackDir(n.dir);
 +                      v3s16 dir = unpackDir(n.param2);
  
                        for(s32 i=0; i<4; i++)
                        {
                /*
                        Add flowing water to mesh
                */
 -              else if(n.d == CONTENT_WATER)
 +              else if(n.getContent() == CONTENT_WATER)
                {
                        bool top_is_water = false;
                        MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
 -                      if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
 +                      if(ntop.getContent() == CONTENT_WATER || ntop.getContent() == CONTENT_WATERSOURCE)
                                top_is_water = true;
                        
                        u8 l = 0;
                        // Use the light of the node on top if possible
 -                      if(content_features(ntop.d).param_type == CPT_LIGHT)
 +                      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)
                        else
                        // Neighbor water levels (key = relative position)
                        // Includes current node
                        core::map<v3s16, f32> neighbor_levels;
 -                      core::map<v3s16, u8> neighbor_contents;
 +                      core::map<v3s16, content_t> neighbor_contents;
                        core::map<v3s16, u8> neighbor_flags;
                        const u8 neighborflag_top_is_water = 0x01;
                        v3s16 neighbor_dirs[9] = {
                                // Check neighbor
                                v3s16 p2 = p + neighbor_dirs[i];
                                MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
 -                              if(n2.d != CONTENT_IGNORE)
 +                              if(n2.getContent() != CONTENT_IGNORE)
                                {
 -                                      content = n2.d;
 +                                      content = n2.getContent();
  
 -                                      if(n2.d == CONTENT_WATERSOURCE)
 +                                      if(n2.getContent() == CONTENT_WATERSOURCE)
                                                level = (-0.5+node_water_level) * BS;
 -                                      else if(n2.d == CONTENT_WATER)
 -                                              level = (-0.5 + ((float)(n2.param2 & LIQUID_LEVEL_MASK) + 0.5) / 8.0
 +                                      else if(n2.getContent() == CONTENT_WATER)
 +                                              level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
                                                                * node_water_level) * BS;
  
                                        // Check node above neighbor.
                                        //       doesn't exist
                                        p2.Y += 1;
                                        n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
 -                                      if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
 +                                      if(n2.getContent() == CONTENT_WATERSOURCE || n2.getContent() == CONTENT_WATER)
                                                flags |= neighborflag_top_is_water;
                                }
                                
                /*
                        Add water sources to mesh if using new style
                */
 -              else if(n.d == CONTENT_WATERSOURCE && new_style_water)
 +              else if(n.getContent() == CONTENT_WATERSOURCE && new_style_water)
                {
                        //bool top_is_water = false;
                        bool top_is_air = false;
                        MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
 -                      /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
 +                      /*if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
                                top_is_water = true;*/
 -                      if(n.d == CONTENT_AIR)
 +                      if(n.getContent() == CONTENT_AIR)
                                top_is_air = true;
                        
                        /*if(top_is_water == true)
                /*
                        Add leaves if using new style
                */
 -              else if(n.d == CONTENT_LEAVES && new_style_leaves)
 +              else if(n.getContent() == CONTENT_LEAVES && new_style_leaves)
                {
                        /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
                        u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
                /*
                        Add glass
                */
 -              else if(n.d == CONTENT_GLASS)
 +              else if(n.getContent() == CONTENT_GLASS)
                {
                        u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
                        video::SColor c(255,l,l,l);
                /*
                        Add fence
                */
 -              else if(n.d == CONTENT_FENCE)
 +              else if(n.getContent() == CONTENT_FENCE)
                {
                        u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
                        video::SColor c(255,l,l,l);
                        v3s16 p2 = p;
                        p2.X++;
                        MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
 -                      if(n2.d == CONTENT_FENCE)
 +                      if(n2.getContent() == CONTENT_FENCE)
                        {
                                pos = intToFloat(p+blockpos_nodes, BS);
                                pos.X += BS/2;
                        p2 = p;
                        p2.Z++;
                        n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
 -                      if(n2.d == CONTENT_FENCE)
 +                      if(n2.getContent() == CONTENT_FENCE)
                        {
                                pos = intToFloat(p+blockpos_nodes, BS);
                                pos.Z += BS/2;
                /*
                        Add stones with minerals if stone is invisible
                */
 -              else if(n.d == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE)
 +              else if(n.getContent() == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE)
                {
                        for(u32 j=0; j<6; j++)
                        {
                                v3s16 dir = g_6dirs[j];
                                /*u8 l = 0;
                                MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + dir);
 -                              if(content_features(n2.d).param_type == CPT_LIGHT)
 +                              if(content_features(n2).param_type == CPT_LIGHT)
                                        l = decode_light(n2.getLightBlend(data->m_daynight_ratio));
                                else
                                        l = 255;*/
                                TileSpec ts = n.getTile(dir);
                                AtlasPointer ap = ts.texture;
                                material_general.setTexture(0, ap.atlas);
 +
                                video::S3DVertex vertices[4] =
 -                              {
 +                              {
                                        /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
                                        video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
                                        video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
                                                vertices[i].Pos.rotateXZBy(90);
                                }
                                else if(j == 4)
 -              else if(n.d == CONTENT_PAPYRUS)
+                               for(u16 i=0; i<4; i++)
+                               {
+                                       vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                               }
+                               u16 indices[] = {0,1,2,2,3,0};
+                               // Add to mesh collector
+                               collector.append(material_general, vertices, 4, indices, 6);
+                       }
+               }
+ #endif
++              else if(n.getContent() == CONTENT_PAPYRUS)
+               {
+                       u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
+                       video::SColor c(255,l,l,l);
+                       for(u32 j=0; j<4; j++)
+                       {
+                               video::S3DVertex vertices[4] =
+                               {
+                                       video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
+                                               pa_papyrus.x0(), pa_papyrus.y1()),
+                                       video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
+                                               pa_papyrus.x1(), pa_papyrus.y1()),
+                                       video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
+                                               pa_papyrus.x1(), pa_papyrus.y0()),
+                                       video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
+                                               pa_papyrus.x0(), pa_papyrus.y0()),
+                               };
+                               if(j == 0)
                                {
                                        for(u16 i=0; i<4; i++)
-                                               vertices[i].Pos.rotateYZBy(-90);
+                                               vertices[i].Pos.rotateXZBy(45);
                                }
-                               else if(j == 5)
+                               else if(j == 1)
                                {
                                        for(u16 i=0; i<4; i++)
-                                               vertices[i].Pos.rotateYZBy(90);
+                                               vertices[i].Pos.rotateXZBy(-45);
+                               }
+                               else if(j == 2)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(135);
+                               }
+                               else if(j == 3)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(-135);
                                }
  
                                for(u16 i=0; i<4; i++)
  
                                u16 indices[] = {0,1,2,2,3,0};
                                // Add to mesh collector
-                               collector.append(material_general, vertices, 4, indices, 6);
+                               collector.append(material_papyrus, vertices, 4, indices, 6);
                        }
                }
- #endif
 -              else if(n.d == CONTENT_RAIL)
++              else if(n.getContent() == CONTENT_RAIL)
+               {
+                       u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+                       video::SColor c(255,l,l,l);
+                       bool is_rail_x [] = { false, false };  /* x-1, x+1 */
+                       bool is_rail_z [] = { false, false };  /* z-1, z+1 */
+                       MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
+                       MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
+                       MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
+                       MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
 -                      if(n_minus_x.d == CONTENT_RAIL)
++                      if(n_minus_x.getContent() == CONTENT_RAIL)
+                               is_rail_x[0] = true;
 -                      if(n_plus_x.d == CONTENT_RAIL)
++                      if(n_plus_x.getContent() == CONTENT_RAIL)
+                               is_rail_x[1] = true;
 -                      if(n_minus_z.d == CONTENT_RAIL)
++                      if(n_minus_z.getContent() == CONTENT_RAIL)
+                               is_rail_z[0] = true;
 -                      if(n_plus_z.d == CONTENT_RAIL)
++                      if(n_plus_z.getContent() == CONTENT_RAIL)
+                               is_rail_z[1] = true;
+                       float d = (float)BS/16;
+                       video::S3DVertex vertices[4] =
+                       {
+                               video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
+                                       0, 1),
+                               video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
+                                       1, 1),
+                               video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
+                                       1, 0),
+                               video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
+                                       0, 0),
+                       };
+                       video::SMaterial material_rail;
+                       material_rail.setFlag(video::EMF_LIGHTING, false);
+                       material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
+                       material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
+                       material_rail.setFlag(video::EMF_FOG_ENABLE, true);
+                       material_rail.MaterialType
+                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+                       int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
+                       // Assign textures
+                       if(adjacencies < 2)
+                               material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
+                       else if(adjacencies == 2)
+                       {
+                               if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
+                                       material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
+                               else
+                                       material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png"));
+                       }
+                       else if(adjacencies == 3)
+                               material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png"));
+                       else if(adjacencies == 4)
+                               material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png"));
  
+                       // Rotate textures
+                       int angle = 0;
+                       if(adjacencies == 1)
+                       {
+                               if(is_rail_x[0] || is_rail_x[1])
+                                       angle = 90;
+                       }
+                       else if(adjacencies == 2)
+                       {
+                               if(is_rail_x[0] && is_rail_x[1])
+                                       angle = 90;
+                               else if(is_rail_x[0] && is_rail_z[0])
+                                       angle = 270;
+                               else if(is_rail_x[0] && is_rail_z[1])
+                                       angle = 180;
+                               else if(is_rail_x[1] && is_rail_z[1])
+                                       angle = 90;
+                       }
+                       else if(adjacencies == 3)
+                       {
+                               if(!is_rail_x[0])
+                                       angle=0;
+                               if(!is_rail_x[1])
+                                       angle=180;
+                               if(!is_rail_z[0])
+                                       angle=90;
+                               if(!is_rail_z[1])
+                                       angle=270;
+                       }
+                       if(angle != 0) {
+                               for(u16 i=0; i<4; i++)
+                                       vertices[i].Pos.rotateXZBy(angle);
+                       }
+                       for(s32 i=0; i<4; i++)
+                       {
+                               vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                       }
+                       u16 indices[] = {0,1,2,2,3,0};
+                       collector.append(material_rail, vertices, 4, indices, 6);
+               }
        }
  }
  #endif
diff --combined src/content_mapnode.cpp
index e2aed24581e208ca1bbe2772c8c3bf6465e10bc1,79e10fd617abfd4dbdbffb4b61f8ce866fdc0dfd..8701ab887ff84e56b65110cf69a7e23938bbca26
@@@ -31,59 -31,6 +31,65 @@@ void setStoneLikeDiggingProperties(Digg
  void setDirtLikeDiggingProperties(DiggingPropertiesList &list, float toughness);
  void setWoodLikeDiggingProperties(DiggingPropertiesList &list, float toughness);
  
 +content_t trans_table_19[][2] = {
 +      {CONTENT_GRASS, 1},
 +      {CONTENT_TREE, 4},
 +      {CONTENT_LEAVES, 5},
 +      {CONTENT_GRASS_FOOTSTEPS, 6},
 +      {CONTENT_MESE, 7},
 +      {CONTENT_MUD, 8},
 +      {CONTENT_CLOUD, 10},
 +      {CONTENT_COALSTONE, 11},
 +      {CONTENT_WOOD, 12},
 +      {CONTENT_SAND, 13},
 +      {CONTENT_COBBLE, 18},
 +      {CONTENT_STEEL, 19},
 +      {CONTENT_GLASS, 20},
 +      {CONTENT_MOSSYCOBBLE, 22},
 +      {CONTENT_GRAVEL, 23},
++      {CONTENT_SANDSTONE, 24},
++      {CONTENT_CACTUS, 25},
++      {CONTENT_BRICK, 26},
++      {CONTENT_CLAY, 27},
++      {CONTENT_PAPYRUS, 28},
++      {CONTENT_BOOKSHELF, 29},
 +};
 +
 +MapNode mapnode_translate_from_internal(MapNode n_from, u8 version)
 +{
 +      MapNode result = n_from;
 +      if(version <= 19)
 +      {
 +              content_t c_from = n_from.getContent();
 +              for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
 +              {
 +                      if(trans_table_19[i][0] == c_from)
 +                      {
 +                              result.setContent(trans_table_19[i][1]);
 +                              break;
 +                      }
 +              }
 +      }
 +      return result;
 +}
 +MapNode mapnode_translate_to_internal(MapNode n_from, u8 version)
 +{
 +      MapNode result = n_from;
 +      if(version <= 19)
 +      {
 +              content_t c_from = n_from.getContent();
 +              for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
 +              {
 +                      if(trans_table_19[i][1] == c_from)
 +                      {
 +                              result.setContent(trans_table_19[i][0]);
 +                              break;
 +                      }
 +              }
 +      }
 +      return result;
 +}
 +
  void content_mapnode_init()
  {
        // Read some settings
@@@ -91,7 -38,7 +97,7 @@@
        bool new_style_leaves = g_settings.getBool("new_style_leaves");
        bool invisible_stone = g_settings.getBool("invisible_stone");
  
 -      u8 i;
 +      content_t i;
        ContentFeatures *f = NULL;
  
        i = CONTENT_STONE;
        f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
        setDirtLikeDiggingProperties(f->digging_properties, 1.75);
        
+       i = CONTENT_SANDSTONE;
+       f = &content_features(i);
+       f->setAllTextures("sandstone.png");
+       f->setInventoryTextureCube("sandstone.png", "sandstone.png", "sandstone.png");
+       f->param_type = CPT_MINERAL;
+       f->is_ground_content = true;
+       f->dug_item = std::string("MaterialItem ")+itos(CONTENT_SAND)+" 1";
+       setDirtLikeDiggingProperties(f->digging_properties, 1.0);
+       i = CONTENT_CLAY;
+       f = &content_features(i);
+       f->setAllTextures("clay.png");
+       f->setInventoryTextureCube("clay.png", "clay.png", "clay.png");
+       f->param_type = CPT_MINERAL;
+       f->is_ground_content = true;
+       f->dug_item = std::string("CraftItem lump_of_clay 4");
+       setDirtLikeDiggingProperties(f->digging_properties, 1.0);
+       i = CONTENT_BRICK;
+       f = &content_features(i);
+       f->setAllTextures("brick.png");
+       f->setInventoryTextureCube("brick.png", "brick.png", "brick.png");
+       f->param_type = CPT_MINERAL;
+       f->is_ground_content = true;
+       f->dug_item = std::string("CraftItem clay_brick 4");
+       setStoneLikeDiggingProperties(f->digging_properties, 1.0);
        i = CONTENT_TREE;
        f = &content_features(i);
        f->setAllTextures("tree.png");
        f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
        setWoodLikeDiggingProperties(f->digging_properties, 0.15);
  
+       i = CONTENT_CACTUS;
+       f = &content_features(i);
+       f->setAllTextures("cactus_side.png");
+       f->setTexture(0, "cactus_top.png");
+       f->setTexture(1, "cactus_top.png");
+       f->setInventoryTextureCube("cactus_top.png", "cactus_side.png", "cactus_side.png");
+       f->param_type = CPT_MINERAL;
+       f->is_ground_content = true;
+       f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
+       setWoodLikeDiggingProperties(f->digging_properties, 0.75);
+       i = CONTENT_PAPYRUS;
+       f = &content_features(i);
+       f->setInventoryTexture("papyrus.png");
+       f->light_propagates = true;
+       f->param_type = CPT_LIGHT;
+       f->is_ground_content = true;
+       f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
+       f->solidness = 0; // drawn separately, makes no faces
+       f->walkable = false;
+       setWoodLikeDiggingProperties(f->digging_properties, 0.25);
+       i = CONTENT_BOOKSHELF;
+       f = &content_features(i);
+       f->setAllTextures("bookshelf.png");
+       f->setTexture(0, "wood.png");
+       f->setTexture(1, "wood.png");
+       // FIXME: setInventoryTextureCube() only cares for the first texture
+       f->setInventoryTextureCube("bookshelf.png", "bookshelf.png", "bookshelf.png");
+       //f->setInventoryTextureCube("wood.png", "bookshelf.png", "bookshelf.png");
+       f->param_type = CPT_MINERAL;
+       f->is_ground_content = true;
+       setWoodLikeDiggingProperties(f->digging_properties, 0.75);
        i = CONTENT_GLASS;
        f = &content_features(i);
        f->light_propagates = true;
        f->setInventoryTexture("item_fence.png");
        setWoodLikeDiggingProperties(f->digging_properties, 0.75);
  
+       i = CONTENT_RAIL;
+       f = &content_features(i);
+       f->setInventoryTexture("rail.png");
+       f->light_propagates = true;
+       f->param_type = CPT_LIGHT;
+       f->is_ground_content = true;
+       f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
+       f->solidness = 0; // drawn separately, makes no faces
+       f->air_equivalent = true; // grass grows underneath
+       f->walkable = false;
+       setDirtLikeDiggingProperties(f->digging_properties, 0.75);
        // Deprecated
        i = CONTENT_COALSTONE;
        f = &content_features(i);
        f->buildable_to = true;
        f->liquid_type = LIQUID_FLOWING;
        f->liquid_alternative_flowing = CONTENT_WATER;
+       f->liquid_alternative_source = CONTENT_WATERSOURCE;
        
        i = CONTENT_WATERSOURCE;
        f = &content_features(i);
        f->liquid_type = LIQUID_SOURCE;
        f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
        f->liquid_alternative_flowing = CONTENT_WATER;
+       f->liquid_alternative_source = CONTENT_WATERSOURCE;
        
        i = CONTENT_TORCH;
        f = &content_features(i);
diff --combined src/content_mapnode.h
index b484cb2255be8be0ed694f83ac84ba2aa7753e5c,e53624c21597d5cb19d6e62d1299d387254d253a..02c604c60d3279971a6dbdafe14275b553d00e40
@@@ -20,48 -20,43 +20,55 @@@ with this program; if not, write to th
  #ifndef CONTENT_MAPNODE_HEADER
  #define CONTENT_MAPNODE_HEADER
  
 +#include "mapnode.h"
 +
  void content_mapnode_init();
  
 +MapNode mapnode_translate_from_internal(MapNode n_from, u8 version);
 +MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
 +
  /*
        Node content type IDs
 +      Ranges:
  */
- // Use these sparingly, only when the extra space in param2 is needed.
 +
 +// 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.
  #define CONTENT_STONE 0
 -#define CONTENT_GRASS 1
  #define CONTENT_WATER 2
  #define CONTENT_TORCH 3
 -#define CONTENT_TREE 4
 -#define CONTENT_LEAVES 5
 -#define CONTENT_GRASS_FOOTSTEPS 6
 -#define CONTENT_MESE 7
 -#define CONTENT_MUD 8
  #define CONTENT_WATERSOURCE 9
 -// Pretty much useless, clouds won't be drawn this way
 -#define CONTENT_CLOUD 10
 -#define CONTENT_COALSTONE 11
 -#define CONTENT_WOOD 12
 -#define CONTENT_SAND 13
  #define CONTENT_SIGN_WALL 14
  #define CONTENT_CHEST 15
  #define CONTENT_FURNACE 16
--//#define CONTENT_WORKBENCH 17
 -#define CONTENT_COBBLE 18
 -#define CONTENT_STEEL 19
 -#define CONTENT_GLASS 20
  #define CONTENT_FENCE 21
 -#define CONTENT_MOSSYCOBBLE 22
 -#define CONTENT_GRAVEL 23
 -#define CONTENT_SANDSTONE 24
 -#define CONTENT_CACTUS 25
 -#define CONTENT_BRICK 26
 -#define CONTENT_CLAY 27
 -#define CONTENT_PAPYRUS 28
 -#define CONTENT_BOOKSHELF 29
+ #define CONTENT_RAIL 30
  
- // 0x800...0xfff: param2 higher 4 bytes are not usable
++// 0x800...0xfff (2048...4095): higher 4 bytes of param2 are not usable
 +#define CONTENT_GRASS 0x800 //1
 +#define CONTENT_TREE 0x801 //4
 +#define CONTENT_LEAVES 0x802 //5
 +#define CONTENT_GRASS_FOOTSTEPS 0x803 //6
 +#define CONTENT_MESE 0x804 //7
 +#define CONTENT_MUD 0x805 //8
 +// Pretty much useless, clouds won't be drawn this way
 +#define CONTENT_CLOUD 0x806 //10
 +#define CONTENT_COALSTONE 0x807 //11
 +#define CONTENT_WOOD 0x808 //12
 +#define CONTENT_SAND 0x809 //13
 +#define CONTENT_COBBLE 0x80a //18
 +#define CONTENT_STEEL 0x80b //19
 +#define CONTENT_GLASS 0x80c //20
 +#define CONTENT_MOSSYCOBBLE 0x80d //22
 +#define CONTENT_GRAVEL 0x80e //23
++#define CONTENT_SANDSTONE 0x80f //24
++#define CONTENT_CACTUS 0x810 //25
++#define CONTENT_BRICK 0x811 //26
++#define CONTENT_CLAY 0x812 //27
++#define CONTENT_PAPYRUS 0x813 //28
++#define CONTENT_BOOKSHELF 0x814 //29
++
 +
  #endif
  
diff --combined src/environment.cpp
index b16ee59abda367791e603759c4bd9ada6779cb83,df41dc63f24fc40feb39300b772853bd5799ce49..f5e4b071a08aec49653c622cbad0a4569b5c10d7
@@@ -543,11 -543,11 +543,11 @@@ void spawnRandomObjects(MapBlock *block
                {
                        v3s16 p(x0,y0,z0);
                        MapNode n = block->getNodeNoEx(p);
 -                      if(n.d == CONTENT_IGNORE)
 +                      if(n.getContent() == CONTENT_IGNORE)
                                continue;
 -                      if(content_features(n.d).liquid_type != LIQUID_NONE)
 +                      if(content_features(n).liquid_type != LIQUID_NONE)
                                continue;
 -                      if(content_features(n.d).walkable)
 +                      if(content_features(n).walkable)
                        {
                                last_node_walkable = true;
                                continue;
                        if(last_node_walkable)
                        {
                                // If block contains light information
 -                              if(content_features(n.d).param_type == CPT_LIGHT)
 +                              if(content_features(n).param_type == CPT_LIGHT)
                                {
                                        if(n.getLight(LIGHTBANK_DAY) <= 5)
                                        {
@@@ -624,15 -624,15 +624,15 @@@ void ServerEnvironment::activateBlock(M
  #if 1
                // Test something:
                // Convert all mud under proper day lighting to grass
 -              if(n.d == CONTENT_MUD)
 +              if(n.getContent() == CONTENT_MUD)
                {
                        if(dtime_s > 300)
                        {
                                MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
 -                              if(content_features(n_top.d).air_equivalent &&
 +                              if(content_features(n_top).air_equivalent &&
                                                n_top.getLight(LIGHTBANK_DAY) >= 13)
                                {
 -                                      n.d = CONTENT_GRASS;
 +                                      n.setContent(CONTENT_GRASS);
                                        m_map->addNodeWithEvent(p, n);
                                }
                        }
@@@ -686,9 -686,9 +686,9 @@@ void ServerEnvironment::step(float dtim
                        v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
                        try{
                                MapNode n = m_map->getNode(bottompos);
 -                              if(n.d == CONTENT_GRASS)
 +                              if(n.getContent() == CONTENT_GRASS)
                                {
 -                                      n.d = CONTENT_GRASS_FOOTSTEPS;
 +                                      n.setContent(CONTENT_GRASS_FOOTSTEPS);
                                        m_map->setNode(bottompos, n);
                                }
                        }
                                        Test something:
                                        Convert mud under proper lighting to grass
                                */
 -                              if(n.d == CONTENT_MUD)
 +                              if(n.getContent() == CONTENT_MUD)
                                {
                                        if(myrand()%20 == 0)
                                        {
                                                MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
 -                                              if(content_features(n_top.d).air_equivalent &&
 +                                              if(content_features(n_top).air_equivalent &&
                                                                n_top.getLightBlend(getDayNightRatio()) >= 13)
                                                {
 -                                                      n.d = CONTENT_GRASS;
 +                                                      n.setContent(CONTENT_GRASS);
                                                        m_map->addNodeWithEvent(p, n);
                                                }
                                        }
                                /*
                                        Convert grass into mud if under something else than air
                                */
 -                              else if(n.d == CONTENT_GRASS)
 +                              else if(n.getContent() == CONTENT_GRASS)
                                {
                                        //if(myrand()%20 == 0)
                                        {
                                                MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
 -                                              if(n_top.d != CONTENT_AIR
 -                                                              && n_top.d != CONTENT_IGNORE)
 +                                              if(n_top.getContent() != CONTENT_AIR
 +                                                              && n_top.getContent() != CONTENT_IGNORE)
                                                {
 -                                                      n.d = CONTENT_MUD;
 +                                                      n.setContent(CONTENT_MUD);
                                                        m_map->addNodeWithEvent(p, n);
                                                }
                                        }
                //TestSAO *obj = new TestSAO(this, 0, pos);
                //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
                //ServerActiveObject *obj = new RatSAO(this, 0, pos);
-               ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
+               //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
+               ServerActiveObject *obj = new FireflySAO(this, 0, pos);
                addActiveObject(obj);
        }
  #endif
@@@ -1632,9 -1633,9 +1633,9 @@@ void ClientEnvironment::step(float dtim
                        v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
                        try{
                                MapNode n = m_map->getNode(bottompos);
 -                              if(n.d == CONTENT_GRASS)
 +                              if(n.getContent() == CONTENT_GRASS)
                                {
 -                                      n.d = CONTENT_GRASS_FOOTSTEPS;
 +                                      n.setContent(CONTENT_GRASS_FOOTSTEPS);
                                        m_map->setNode(bottompos, n);
                                        // Update mesh on client
                                        if(m_map->mapType() == MAPTYPE_CLIENT)
@@@ -1873,7 -1874,7 +1874,7 @@@ void ClientEnvironment::drawPostFx(vide
        v3f pos_f = camera_pos;
        v3s16 p_nodes = floatToInt(pos_f, BS);
        MapNode n = m_map->getNodeNoEx(p_nodes);
 -      if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
 +      if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
        {
                v2u32 ss = driver->getScreenSize();
                core::rect<s32> rect(0,0, ss.X, ss.Y);
diff --combined src/game.cpp
index 7e2ee44fc25b39faac46205ebaa819fdfc7e3a3b,0f858e879694cc3404a896e207ec0223d91e8afe..b26d489673e600cd4a9aec664db06b4551421477
@@@ -417,7 -417,7 +417,7 @@@ void getPointedNode(Client *client, v3
                try
                {
                        n = client->getNode(v3s16(x,y,z));
 -                      if(content_pointable(n.d) == false)
 +                      if(content_pointable(n.getContent()) == false)
                                continue;
                }
                catch(InvalidPositionException &e)
                /*
                        Meta-objects
                */
 -              if(n.d == CONTENT_TORCH)
 +              if(n.getContent() == CONTENT_TORCH)
                {
 -                      v3s16 dir = unpackDir(n.dir);
 +                      v3s16 dir = unpackDir(n.param2);
                        v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
                        dir_f *= BS/2 - BS/6 - BS/20;
                        v3f cpf = npf + dir_f;
                                }
                        }
                }
 -              else if(n.d == CONTENT_SIGN_WALL)
 +              else if(n.getContent() == CONTENT_SIGN_WALL)
                {
 -                      v3s16 dir = unpackDir(n.dir);
 +                      v3s16 dir = unpackDir(n.param2);
                        v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
                        dir_f *= BS/2 - BS/6 - BS/20;
                        v3f cpf = npf + dir_f;
                                }
                        }
                }
 -              else if(n.d == CONTENT_RAIL)
++              else if(n.getContent() == CONTENT_RAIL)
+               {
 -                      v3s16 dir = unpackDir(n.dir);
++                      v3s16 dir = unpackDir(n.param0);
+                       v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
+                       dir_f *= BS/2 - BS/6 - BS/20;
+                       v3f cpf = npf + dir_f;
+                       f32 distance = (cpf - camera_position).getLength();
+                       float d = (float)BS/16;
+                       v3f vertices[4] =
+                       {
+                               v3f(BS/2, -BS/2+d, -BS/2),
+                               v3f(-BS/2, -BS/2, BS/2),
+                       };
+                       for(s32 i=0; i<2; i++)
+                       {
+                               vertices[i] += npf;
+                       }
+                       core::aabbox3d<f32> box;
+                       box = core::aabbox3d<f32>(vertices[0]);
+                       box.addInternalPoint(vertices[1]);
+                       if(distance < mindistance)
+                       {
+                               if(box.intersectsWithLine(shootline))
+                               {
+                                       nodefound = true;
+                                       nodepos = np;
+                                       neighbourpos = np;
+                                       mindistance = distance;
+                                       nodehilightbox = box;
+                               }
+                       }
+               }
                /*
                        Regular blocks
                */
@@@ -1722,7 -1759,7 +1759,7 @@@ void the_game
                                        }
  
                                        // Get digging properties for material and tool
 -                                      u8 material = n.d;
 +                                      content_t material = n.getContent();
                                        DiggingProperties prop =
                                                        getDiggingProperties(material, toolname);
                                        
                */
                if(farmesh)
                {
-                       farmesh_range = draw_control.wanted_range * 10;
-                       if(draw_control.range_all && farmesh_range < 500)
-                               farmesh_range = 500;
-                       if(farmesh_range > 1000)
-                               farmesh_range = 1000;
                        farmesh->step(dtime);
                        farmesh->update(v2f(player_position.X, player_position.Z),
-                                       0.05+brightness*0.95, farmesh_range);
+                                       0.05+brightness*0.95);
                }
                
                // Store brightness value
                        endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
                        
                        char temptext[300];
-                       snprintf(temptext, 300, "Minetest-c55 %s ("
+                       snprintf(temptext, 300, "Minetest-delta %s ("
                                        "R: range_all=%i"
                                        ")"
                                        " drawtime=%.0f, beginscenetime=%.0f"
diff --combined src/main.cpp
index 5aff62bf29b776153ee77d75d1d7640515816670,9a1e1960f049ef5233a9a456020f89ac375dad6a..783faa4e24a83cd68a82607fcc46573729f74db5
@@@ -54,24 -54,6 +54,24 @@@ A list of "active blocks" in which stuf
                + This was left to be done by the old system and it sends only the\r
                  nearest ones.\r
  \r
 +Vim conversion regexpes for moving to extended content type storage:\r
 +%s/\(\.\|->\)d \([!=]=\)/\1getContent() \2/g\r
 +%s/content_features(\([^.]*\)\.d)/content_features(\1)/g\r
 +%s/\(\.\|->\)d = \([^;]*\);/\1setContent(\2);/g\r
 +%s/\(getNodeNoExNoEmerge([^)]*)\)\.d/\1.getContent()/g\r
 +%s/\(getNodeNoExNoEmerge(.*)\)\.d/\1.getContent()/g\r
 +%s/\.d;/.getContent();/g\r
 +%s/\(content_liquid\|content_flowing_liquid\|make_liquid_flowing\|content_pointable\)(\([^.]*\).d)/\1(\2.getContent())/g\r
 +Other things to note:\r
 +- node.d = node.param0 (only in raw serialization; use getContent() otherwise)\r
 +- node.param = node.param1\r
 +- node.dir = node.param2\r
 +- content_walkable(node.d) etc should be changed to\r
 +  content_features(node).walkable etc\r
 +- Also check for lines that store the result of getContent to a 8-bit\r
 +  variable and fix them (result of getContent() must be stored in\r
 +  content_t, which is 16-bit)\r
 +\r
  Old, wild and random suggestions that probably won't be done:\r
  -------------------------------------------------------------\r
  \r
@@@ -355,8 -337,14 +355,14 @@@ TODO: Restart irrlicht completely when 
  \r
  TODO: Merge bahamada's audio stuff (clean patch available)\r
  \r
 -TODO: Merge key configuration menu (no clean patch available)\r
 +TODO: Move content_features to mapnode_content_features.{h,cpp} or so\r
  \r
+ TODO: Add some kind of content range validation to mapnode serialization\r
\r
+ TODO: Make sure menu text position is fixed\r
\r
+ TODO: Fix sector over limits error\r
\r
  Making it more portable:\r
  ------------------------\r
   \r
@@@ -419,6 -407,8 +425,8 @@@ Doing currently
  #include "keycode.h"\r
  #include "tile.h"\r
  \r
+ #include "gettext.h"\r
\r
  // This makes textures\r
  ITextureSource *g_texturesource = NULL;\r
  \r
@@@ -1148,6 -1138,10 +1156,10 @@@ int main(int argc, char *argv[]
  \r
        // Create user data directory\r
        fs::CreateDir(porting::path_userdata);\r
\r
+       setlocale(LC_MESSAGES, "");
+       bindtextdomain("minetest", (porting::path_userdata+"/locale").c_str());\r
+       textdomain("minetest");\r
        \r
        // Initialize debug streams\r
  #ifdef RUN_IN_PLACE\r
        \r
        // Set device in game parameters\r
        device = device;\r
\r
+       // Set the window caption\r
+       device->setWindowCaption(L"Minetest [Main Menu]");\r
        \r
        // Create time getter\r
        g_timegetter = new IrrlichtTimeGetter(device);\r
diff --combined src/map.cpp
index dd32e55ba83dd9c4937f6910719460502f5ef2ca,ab4acd4d55e1d36d2301128d7b98f59f0e8cad23..1c63943c4c775f696a8bebef3b428b5b8f6bb85b
@@@ -630,9 -630,9 +630,9 @@@ s16 Map::propagateSunlight(v3s16 start
                else
                {
                        /*// Turn mud into grass
 -                      if(n.d == CONTENT_MUD)
 +                      if(n.getContent() == CONTENT_MUD)
                        {
 -                              n.d = CONTENT_GRASS;
 +                              n.setContent(CONTENT_GRASS);
                                block->setNode(relpos, n);
                                modified_blocks.insert(blockpos, block);
                        }*/
@@@ -920,15 -920,15 +920,15 @@@ void Map::addNodeAndUpdate(v3s16 p, Map
        /*
                If the new node is solid and there is grass below, change it to mud
        */
 -      if(content_features(n.d).walkable == true)
 +      if(content_features(n).walkable == true)
        {
                try{
                        MapNode bottomnode = getNode(bottompos);
  
 -                      if(bottomnode.d == CONTENT_GRASS
 -                                      || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
 +                      if(bottomnode.getContent() == CONTENT_GRASS
 +                                      || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
                        {
 -                              bottomnode.d = CONTENT_MUD;
 +                              bottomnode.setContent(CONTENT_MUD);
                                setNode(bottompos, bottomnode);
                        }
                }
                If the new node is mud and it is under sunlight, change it
                to grass
        */
 -      if(n.d == CONTENT_MUD && node_under_sunlight)
 +      if(n.getContent() == CONTENT_MUD && node_under_sunlight)
        {
 -              n.d = CONTENT_GRASS;
 +              n.setContent(CONTENT_GRASS);
        }
  #endif
  
                If node lets sunlight through and is under sunlight, it has
                sunlight too.
        */
 -      if(node_under_sunlight && content_features(n.d).sunlight_propagates)
 +      if(node_under_sunlight && content_features(n).sunlight_propagates)
        {
                n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
        }
                Add intial metadata
        */
  
 -      NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
 +      NodeMetadata *meta_proto = content_features(n).initial_metadata;
        if(meta_proto)
        {
                NodeMetadata *meta = meta_proto->clone();
                TODO: This could be optimized by mass-unlighting instead
                          of looping
        */
 -      if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
 +      if(node_under_sunlight && !content_features(n).sunlight_propagates)
        {
                s16 y = p.Y - 1;
                for(;; y--){
                v3s16 p2 = p + dirs[i];
  
                MapNode n2 = getNode(p2);
 -              if(content_liquid(n2.d) || n2.d == CONTENT_AIR)
 +              if(content_liquid(n2.getContent()))
                {
                        m_transforming_liquid.push_back(p2);
                }
@@@ -1111,7 -1111,7 +1111,7 @@@ void Map::removeNodeAndUpdate(v3s16 p
        v3s16 toppos = p + v3s16(0,1,0);
  
        // Node will be replaced with this
 -      u8 replace_material = CONTENT_AIR;
 +      content_t replace_material = CONTENT_AIR;
  
        /*
                If there is a node at top and it doesn't have sunlight,
        */
  
        MapNode n;
 -      n.d = replace_material;
 +      n.setContent(replace_material);
        setNode(p, n);
  
        for(s32 i=0; i<2; i++)
        }
  
        /*
-               Add neighboring liquid nodes to transform queue.
+               Add neighboring liquid nodes and this node to transform queue.
+               (it's vital for the node itself to get updated last.)
        */
-       v3s16 dirs[6] = {
+       v3s16 dirs[7] = {
                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
+               v3s16(0,0,0), // self
        };
-       for(u16 i=0; i<6; i++)
+       for(u16 i=0; i<7; i++)
        {
                try
                {
                v3s16 p2 = p + dirs[i];
  
                MapNode n2 = getNode(p2);
 -              if(content_liquid(n2.d) || n2.d == CONTENT_AIR)
 +              if(content_liquid(n2.getContent()))
                {
                        m_transforming_liquid.push_back(p2);
                }
@@@ -1559,17 -1561,17 +1561,17 @@@ void Map::transformLiquids(core::map<v3
                MapNode n0 = getNodeNoEx(p0);
  
                // Don't deal with non-liquids
 -              if(content_liquid(n0.d) == false)
 +              if(content_liquid(n0.getContent()) == false)
                        continue;
  
 -              bool is_source = !content_flowing_liquid(n0.d);
 +              bool is_source = !content_flowing_liquid(n0.getContent());
  
                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);
 +              u8 nonsource_c = make_liquid_flowing(n0.getContent());
  
                /*
                        If not source, check that some node flows into this one
                                v3s16 p2 = p0 + dirs_from[i];
                                MapNode n2 = getNodeNoEx(p2);
  
 -                              if(content_liquid(n2.d))
 +                              if(content_liquid(n2.getContent()))
                                {
 -                                      u8 n2_nonsource_c = make_liquid_flowing(n2.d);
 +                                      u8 n2_nonsource_c = make_liquid_flowing(n2.getContent());
                                        // Check that the liquids are the same type
                                        if(n2_nonsource_c != nonsource_c)
                                        {
                                                                " collide"<<std::endl;
                                                continue;
                                        }
 -                                      bool n2_is_source = !content_flowing_liquid(n2.d);
 +                                      bool n2_is_source = !content_flowing_liquid(n2.getContent());
                                        s8 n2_liquid_level = 8;
                                        if(n2_is_source == false)
                                                n2_liquid_level = n2.param2 & 0x07;
                                if(new_liquid_level_max == -1)
                                {
                                        // Remove water alltoghether
 -                                      n0.d = CONTENT_AIR;
 +                                      n0.setContent(CONTENT_AIR);
                                        n0.param2 = 0;
                                        setNode(p0, n0);
                                }
                                        v3s16 p2 = p0 + dirs[i];
  
                                        MapNode n2 = getNodeNoEx(p2);
 -                                      if(content_flowing_liquid(n2.d))
 +                                      if(content_flowing_liquid(n2.getContent()))
                                        {
                                                m_transforming_liquid.push_back(p2);
                                        }
                }
  
                // Get a new one from queue if the node has turned into non-water
 -              if(content_liquid(n0.d) == false)
 +              if(content_liquid(n0.getContent()) == false)
                        continue;
  
                /*
                        MapNode n2 = getNodeNoEx(p2);
                        //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
  
 -                      if(content_liquid(n2.d))
 +                      if(content_liquid(n2.getContent()))
                        {
 -                              u8 n2_nonsource_c = make_liquid_flowing(n2.d);
 +                              u8 n2_nonsource_c = make_liquid_flowing(n2.getContent());
                                // Check that the liquids are the same type
                                if(n2_nonsource_c != nonsource_c)
                                {
                                                        " collide"<<std::endl;
                                        continue;
                                }
 -                              bool n2_is_source = !content_flowing_liquid(n2.d);
 +                              bool n2_is_source = !content_flowing_liquid(n2.getContent());
                                u8 n2_liquid_level = 8;
                                if(n2_is_source == false)
                                        n2_liquid_level = n2.param2 & 0x07;
                                        }
                                }
                        }
 -                      else if(n2.d == CONTENT_AIR)
 +                      else if(n2.getContent() == CONTENT_AIR)
                        {
 -                              n2.d = nonsource_c;
 +                              n2.setContent(nonsource_c);
                                n2.param2 = liquid_next_level;
                                setNode(p2, n2);
  
@@@ -2362,7 -2364,7 +2364,7 @@@ MapBlock * ServerMap::generateBlock
                {
                        v3s16 p(x0,y0,z0);
                        MapNode n = block->getNode(p);
 -                      if(n.d == CONTENT_IGNORE)
 +                      if(n.getContent() == CONTENT_IGNORE)
                        {
                                dstream<<"CONTENT_IGNORE at "
                                                <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
                        {
                                MapNode n;
                                if(y0%2==0)
 -                                      n.d = CONTENT_AIR;
 +                                      n.setContent(CONTENT_AIR);
                                else
 -                                      n.d = CONTENT_STONE;
 +                                      n.setContent(CONTENT_STONE);
                                block->setNode(v3s16(x0,y0,z0), n);
                        }
                }
@@@ -2674,19 -2676,19 +2676,19 @@@ s16 ServerMap::findGroundLevel(v2s16 p2
        for(; p.Y>min; p.Y--)
        {
                MapNode n = getNodeNoEx(p);
 -              if(n.d != CONTENT_IGNORE)
 +              if(n.getContent() != CONTENT_IGNORE)
                        break;
        }
        if(p.Y == min)
                goto plan_b;
        // If this node is not air, go to plan b
 -      if(getNodeNoEx(p).d != CONTENT_AIR)
 +      if(getNodeNoEx(p).getContent() != CONTENT_AIR)
                goto plan_b;
        // Search existing walkable and return it
        for(; p.Y>min; p.Y--)
        {
                MapNode n = getNodeNoEx(p);
 -              if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
 +              if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
                        return p.Y;
        }
  
diff --combined src/mapgen.cpp
index d4143b6d6a93199594591f4d86b5693269c5f915,8dda93c960e30a74cd4cd2d55e30b11ba741e882..bc4f822808273ff7aace824cbc3a9dde4ca66622
@@@ -67,8 -67,8 +67,8 @@@ static s16 find_ground_level_clever(Vox
        {
                MapNode &n = vmanip.m_data[i];
                if(content_walkable(n.d)
 -                              && n.d != CONTENT_TREE
 -                              && n.d != CONTENT_LEAVES)
 +                              && n.getContent() != CONTENT_TREE
 +                              && n.getContent() != CONTENT_LEAVES)
                        break;
  
                vmanip.m_area.add_y(em, i, -1);
@@@ -85,7 -85,7 +85,7 @@@ static void make_tree(VoxelManipulator 
        MapNode treenode(CONTENT_TREE);
        MapNode leavesnode(CONTENT_LEAVES);
  
-       s16 trunk_h = myrand_range(3, 6);
+       s16 trunk_h = myrand_range(4, 5);
        v3s16 p1 = p0;
        for(s16 ii=0; ii<trunk_h; ii++)
        {
@@@ -97,7 -97,7 +97,7 @@@
        // p1 is now the last piece of the trunk
        p1.Y -= 1;
  
-       VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
+       VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
        //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
        Buffer<u8> leaves_d(leaves_a.getVolume());
        for(s32 i=0; i<leaves_a.getVolume(); i++)
                if(vmanip.m_area.contains(p) == false)
                        continue;
                u32 vi = vmanip.m_area.index(p);
 -              if(vmanip.m_data[vi].d != CONTENT_AIR
 -                              && vmanip.m_data[vi].d != CONTENT_IGNORE)
 +              if(vmanip.m_data[vi].getContent() != CONTENT_AIR
 +                              && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
                        continue;
                u32 i = leaves_a.index(x,y,z);
                if(leaves_d[i] == 1)
        }
  }
  
+ void make_papyrus(VoxelManipulator &vmanip, v3s16 p0)
+ {
+       MapNode papyrusnode(CONTENT_PAPYRUS);
+       s16 trunk_h = myrand_range(2, 3);
+       v3s16 p1 = p0;
+       for(s16 ii=0; ii<trunk_h; ii++)
+       {
+               if(vmanip.m_area.contains(p1))
+                       vmanip.m_data[vmanip.m_area.index(p1)] = papyrusnode;
+               p1.Y++;
+       }
+ }
+ void make_cactus(VoxelManipulator &vmanip, v3s16 p0)
+ {
+       MapNode cactusnode(CONTENT_CACTUS);
+       s16 trunk_h = 3;
+       v3s16 p1 = p0;
+       for(s16 ii=0; ii<trunk_h; ii++)
+       {
+               if(vmanip.m_area.contains(p1))
+                       vmanip.m_data[vmanip.m_area.index(p1)] = cactusnode;
+               p1.Y++;
+       }
+ }
  #if 0
  static void make_randomstone(VoxelManipulator &vmanip, v3s16 p0)
  {
                if(vmanip.m_area.contains(p) == false)
                        continue;
                u32 vi = vmanip.m_area.index(p);
 -              if(vmanip.m_data[vi].d != CONTENT_AIR
 -                              && vmanip.m_data[vi].d != CONTENT_IGNORE)
 +              if(vmanip.m_data[vi].getContent() != CONTENT_AIR
 +                              && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
                        continue;
                u32 i = stone_a.index(x,y,z);
                if(stone_d[i] == 1)
@@@ -307,8 -335,8 +335,8 @@@ static void make_largestone(VoxelManipu
                if(vmanip.m_area.contains(p) == false)
                        continue;
                u32 vi = vmanip.m_area.index(p);
 -              /*if(vmanip.m_data[vi].d != CONTENT_AIR
 -                              && vmanip.m_data[vi].d != CONTENT_IGNORE)
 +              /*if(vmanip.m_data[vi].getContent() != CONTENT_AIR
 +                              && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
                        continue;*/
                u32 i = stone_a.index(x,y,z);
                if(stone_d[i] == 1)
@@@ -516,9 -544,9 +544,9 @@@ static void make_corridor(VoxelManipula
                        p.Y += make_stairs;
  
                /*// If already empty
 -              if(vmanip.getNodeNoExNoEmerge(p).d
 +              if(vmanip.getNodeNoExNoEmerge(p).getContent()
                                == CONTENT_AIR
 -              && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
 +              && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
                                == CONTENT_AIR)
                {
                }*/
@@@ -614,9 -642,9 +642,9 @@@ public
                                randomizeDir();
                                continue;
                        }
 -                      if(vmanip.getNodeNoExNoEmerge(p).d
 +                      if(vmanip.getNodeNoExNoEmerge(p).getContent()
                                        == CONTENT_COBBLE
 -                      && vmanip.getNodeNoExNoEmerge(p1).d
 +                      && vmanip.getNodeNoExNoEmerge(p1).getContent()
                                        == CONTENT_COBBLE)
                        {
                                // Found wall, this is a good place!
                                Determine where to move next
                        */
                        // Jump one up if the actual space is there
 -                      if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d
 +                      if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
                                        == CONTENT_COBBLE
 -                      && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
 +                      && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
                                        == CONTENT_AIR
 -                      && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).d
 +                      && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent()
                                        == CONTENT_AIR)
                                p += v3s16(0,1,0);
                        // Jump one down if the actual space is there
 -                      if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
 +                      if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
                                        == CONTENT_COBBLE
 -                      && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d
 +                      && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
                                        == CONTENT_AIR
 -                      && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).d
 +                      && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent()
                                        == CONTENT_AIR)
                                p += v3s16(0,-1,0);
                        // Check if walking is now possible
 -                      if(vmanip.getNodeNoExNoEmerge(p).d
 +                      if(vmanip.getNodeNoExNoEmerge(p).getContent()
                                        != CONTENT_AIR
 -                      || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
 +                      || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
                                        != CONTENT_AIR)
                        {
                                // Cannot continue walking here
@@@ -770,7 -798,7 +798,7 @@@ static void make_dungeon1(VoxelManipula
                                fits = false;
                                break;
                        }
 -                      if(vmanip.m_data[vi].d == CONTENT_IGNORE)
 +                      if(vmanip.m_data[vi].getContent() == CONTENT_IGNORE)
                        {
                                fits = false;
                                break;
@@@ -909,8 -937,8 +937,8 @@@ NoiseParams get_cave_noise2_params(u64 
  
  NoiseParams get_ground_noise1_params(u64 seed)
  {
-       return NoiseParams(NOISE_PERLIN, seed+983240, 5,
-                       0.60, 100.0, 30.0);
+       return NoiseParams(NOISE_PERLIN, seed+983240, 4,
+                       0.55, 80.0, 40.0);
  }
  
  NoiseParams get_ground_crumbleness_params(u64 seed)
@@@ -945,7 -973,7 +973,7 @@@ bool val_is_ground(double ground_noise1
  
        double f = 0.55 + noise2d_perlin(
                        0.5+(float)p.X/250, 0.5+(float)p.Z/250,
-                       seed+920381, 3, 0.5);
+                       seed+920381, 3, 0.45);
        if(f < 0.01)
                f = 0.01;
        else if(f >= 1.0)
@@@ -1245,11 -1273,11 +1273,11 @@@ void add_random_objects(MapBlock *block
                {
                        v3s16 p(x0,y0,z0);
                        MapNode n = block->getNodeNoEx(p);
 -                      if(n.d == CONTENT_IGNORE)
 +                      if(n.getContent() == CONTENT_IGNORE)
                                continue;
 -                      if(content_features(n.d).liquid_type != LIQUID_NONE)
 +                      if(content_features(n).liquid_type != LIQUID_NONE)
                                continue;
 -                      if(content_features(n.d).walkable)
 +                      if(content_features(n).walkable)
                        {
                                last_node_walkable = true;
                                continue;
                        if(last_node_walkable)
                        {
                                // If block contains light information
 -                              if(content_features(n.d).param_type == CPT_LIGHT)
 +                              if(content_features(n).param_type == CPT_LIGHT)
                                {
                                        if(n.getLight(LIGHTBANK_DAY) <= 3)
                                        {
@@@ -1345,7 -1373,8 +1373,8 @@@ void make_block(BlockMakeData *data
                        data->seed, v2s16(blockpos.X, blockpos.Z), 1);
        // Maximum amount of ground above the bottom of the central block
        s16 maximum_ground_depth = maximum_groundlevel - node_min.Y;
-       
+       #if 0
        /*
                Special case for high air or water: Just fill with air and water.
        */
                                for(s16 y=node_min.Y; y<=node_max.Y; y++)
                                {
                                        // Only modify places that have no content
 -                                      if(vmanip.m_data[i].d == CONTENT_IGNORE)
 +                                      if(vmanip.m_data[i].getContent() == CONTENT_IGNORE)
                                        {
                                                if(y <= WATER_LEVEL)
                                                        vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE);
                // We're done
                return;
        }
+       #endif
  
        /*
                If block is deep underground, this is set to true and ground
                        for(s16 y=node_min.Y; y<=node_max.Y; y++)
                        {
                                // Only modify places that have no content
 -                              if(vmanip.m_data[i].d == CONTENT_IGNORE)
 +                              if(vmanip.m_data[i].getContent() == CONTENT_IGNORE)
                                {
                                        // First priority: make air and water.
                                        // This avoids caves inside water.
                                {
                                        v3s16 p = v3s16(x,y,z) + g_27dirs[i];
                                        u32 vi = vmanip.m_area.index(p);
 -                                      if(vmanip.m_data[vi].d == CONTENT_STONE)
 +                                      if(vmanip.m_data[vi].getContent() == CONTENT_STONE)
                                                if(mineralrandom.next()%8 == 0)
                                                        vmanip.m_data[vi] = MapNode(CONTENT_MESE);
                                }
                                {
                                }*/
  
 -                              if(new_content.d != CONTENT_IGNORE)
 +                              if(new_content.getContent() != CONTENT_IGNORE)
                                {
                                        for(u16 i=0; i<27; i++)
                                        {
                                                v3s16 p = v3s16(x,y,z) + g_27dirs[i];
                                                u32 vi = vmanip.m_area.index(p);
 -                                              if(vmanip.m_data[vi].d == base_content)
 +                                              if(vmanip.m_data[vi].getContent() == base_content)
                                                {
                                                        if(mineralrandom.next()%sparseness == 0)
                                                                vmanip.m_data[vi] = new_content;
                                {
                                        v3s16 p = v3s16(x,y,z) + g_27dirs[i];
                                        u32 vi = vmanip.m_area.index(p);
 -                                      if(vmanip.m_data[vi].d == CONTENT_STONE)
 +                                      if(vmanip.m_data[vi].getContent() == CONTENT_STONE)
                                                if(mineralrandom.next()%8 == 0)
                                                        vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_COAL);
                                }
                                {
                                        v3s16 p = v3s16(x,y,z) + g_27dirs[i];
                                        u32 vi = vmanip.m_area.index(p);
 -                                      if(vmanip.m_data[vi].d == CONTENT_STONE)
 +                                      if(vmanip.m_data[vi].getContent() == CONTENT_STONE)
                                                if(mineralrandom.next()%8 == 0)
                                                        vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_IRON);
                                }
                        u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
                        for(s16 y=node_max.Y; y>=node_min.Y; y--)
                        {
 -                              if(vmanip.m_data[i].d == CONTENT_STONE)
 +                              if(vmanip.m_data[i].getContent() == CONTENT_STONE)
                                {
                                        if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3)
                                        {
                                u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
                                for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
                                {
 -                                      if(vmanip.m_data[i].d == CONTENT_AIR)
 +                                      if(vmanip.m_data[i].getContent() == CONTENT_AIR)
                                                vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
 -                                      else if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
 +                                      else if(vmanip.m_data[i].getContent() == CONTENT_WATERSOURCE)
                                                vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
                                        data->vmanip->m_area.add_y(em, i, -1);
                                }
                                        double d = noise3d_perlin((float)x/2.5,
                                                        (float)y/2.5,(float)z/2.5,
                                                        blockseed, 2, 1.4);
 -                                      if(vmanip.m_data[i].d == CONTENT_COBBLE)
 +                                      if(vmanip.m_data[i].getContent() == CONTENT_COBBLE)
                                        {
                                                if(d < wetness/3.0)
                                                {
 -                                                      vmanip.m_data[i].d = CONTENT_MOSSYCOBBLE;
 +                                                      vmanip.m_data[i].setContent(CONTENT_MOSSYCOBBLE);
                                                }
                                        }
                                        /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE)
                                        {
                                                if(wetness > 1.2)
 -                                                      vmanip.m_data[i].d = CONTENT_MUD;
 +                                                      vmanip.m_data[i].setContent(CONTENT_MUD);
                                        }*/
                                        data->vmanip->m_area.add_y(em, i, -1);
                                }
                        {
                                if(water_found == false)
                                {
 -                                      if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
 +                                      if(vmanip.m_data[i].getContent() == CONTENT_WATERSOURCE)
                                        {
                                                v3s16 p = v3s16(p2d.X, y, p2d.Y);
                                                data->transforming_liquid.push_back(p);
                                        // This can be done because water_found can only
                                        // turn to true and end up here after going through
                                        // a single block.
 -                                      if(vmanip.m_data[i+1].d != CONTENT_WATERSOURCE)
 +                                      if(vmanip.m_data[i+1].getContent() != CONTENT_WATERSOURCE)
                                        {
                                                v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
                                                data->transforming_liquid.push_back(p);
                                u32 current_depth = 0;
                                bool air_detected = false;
                                bool water_detected = false;
+                               bool have_clay = false;
                                // Use fast index incrementing
                                s16 start_y = node_max.Y+2;
                                v3s16 em = vmanip.m_area.getExtent();
                                u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y));
                                for(s16 y=start_y; y>=node_min.Y-3; y--)
                                {
 -                                      if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
 +                                      if(vmanip.m_data[i].getContent() == CONTENT_WATERSOURCE)
                                                water_detected = true;
 -                                      if(vmanip.m_data[i].d == CONTENT_AIR)
 +                                      if(vmanip.m_data[i].getContent() == CONTENT_AIR)
                                                air_detected = true;
  
 -                                      if((vmanip.m_data[i].d == CONTENT_STONE
 -                                                      || vmanip.m_data[i].d == CONTENT_GRASS
 -                                                      || vmanip.m_data[i].d == CONTENT_MUD
 -                                                      || vmanip.m_data[i].d == CONTENT_SAND
 -                                                      || vmanip.m_data[i].d == CONTENT_GRAVEL
 +                                      if((vmanip.m_data[i].getContent() == CONTENT_STONE
 +                                                      || vmanip.m_data[i].getContent() == CONTENT_GRASS
 +                                                      || vmanip.m_data[i].getContent() == CONTENT_MUD
 +                                                      || vmanip.m_data[i].getContent() == CONTENT_SAND
 +                                                      || vmanip.m_data[i].getContent() == CONTENT_GRAVEL
                                                        ) && (air_detected || water_detected))
                                        {
                                                if(current_depth == 0 && y <= WATER_LEVEL+2
                                                {
                                                        if(have_sand)
                                                        {
-                                                               vmanip.m_data[i] = MapNode(CONTENT_SAND);
+                                                               // Determine whether to have clay in the sand here
+                                                               double claynoise = noise2d_perlin(
+                                                                               0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
+                                                                               data->seed+4321, 6, 0.95) + 0.5;
+                               
+                                                               have_clay = (y <= WATER_LEVEL) && (y >= WATER_LEVEL-2) && (
+                                                                       ((claynoise > 0) && (claynoise < 0.04) && (current_depth == 0)) ||
+                                                                       ((claynoise > 0) && (claynoise < 0.12) && (current_depth == 1))
+                                                                       );
+                                                               if (have_clay)
+                                                                       vmanip.m_data[i] = MapNode(CONTENT_CLAY);
+                                                               else
+                                                                       vmanip.m_data[i] = MapNode(CONTENT_SAND);
                                                        }
                                                        #if 1
                                                        else if(current_depth==0 && !water_detected
                                                }
                                                else
                                                {
 -                                                      if(vmanip.m_data[i].d == CONTENT_MUD
 -                                                              || vmanip.m_data[i].d == CONTENT_GRASS)
 +                                                      if(vmanip.m_data[i].getContent() == CONTENT_MUD
 +                                                              || vmanip.m_data[i].getContent() == CONTENT_GRASS)
                                                                vmanip.m_data[i] = MapNode(CONTENT_STONE);
                                                }
  
                        {
                                u32 i = data->vmanip->m_area.index(p);
                                MapNode *n = &data->vmanip->m_data[i];
-                               if(n->getContent() != CONTENT_AIR && n->getContent() != CONTENT_IGNORE)
 -                              if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE && n->d != CONTENT_IGNORE)
++                              if(n->getContent() != CONTENT_AIR && n->getContent() != CONTENT_WATERSOURCE && n->getContent() != CONTENT_IGNORE)
                                {
                                        found = true;
                                        break;
                        // If not found, handle next one
                        if(found == false)
                                continue;
-                       /*
-                               Trees grow only on mud and grass
-                       */
                        {
                                u32 i = data->vmanip->m_area.index(p);
                                MapNode *n = &data->vmanip->m_data[i];
-                               if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS)
-                                       continue;
 -                              if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS && n->d != CONTENT_SAND)
++                              if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS && n->getContent() != CONTENT_SAND)
+                                               continue;
+                               // Papyrus grows only on mud and in water
 -                              if(n->d == CONTENT_MUD && y <= WATER_LEVEL)
++                              if(n->getContent() == CONTENT_MUD && y <= WATER_LEVEL)
+                               {
+                                       p.Y++;
+                                       make_papyrus(vmanip, p);
+                               }
+                               // Trees grow only on mud and grass, on land
 -                              else if((n->d == CONTENT_MUD || n->d == CONTENT_GRASS) && y > WATER_LEVEL + 2)
++                              else if((n->getContent() == CONTENT_MUD || n->getContent() == CONTENT_GRASS) && y > WATER_LEVEL + 2)
+                               {
+                                       p.Y++;
+                                       make_tree(vmanip, p);
+                               }
+                               // Cactii grow only on sand, on land
 -                              else if(n->d == CONTENT_SAND && y > WATER_LEVEL + 2)
++                              else if(n->getContent() == CONTENT_SAND && y > WATER_LEVEL + 2)
+                               {
+                                       p.Y++;
+                                       make_cactus(vmanip, p);
+                               }
                        }
-                       // Tree will be placed one higher
-                       p.Y++;
-                       // Make a tree
-                       make_tree(vmanip, p);
                }
  
  #if 0
                        /*{
                                u32 i = data->vmanip->m_area.index(v3s16(p));
                                MapNode *n = &data->vmanip->m_data[i];
 -                              if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
 +                              if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS)
                                        continue;
                        }*/
                        // Will be placed one higher
                        /*{
                                u32 i = data->vmanip->m_area.index(v3s16(p));
                                MapNode *n = &data->vmanip->m_data[i];
 -                              if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
 +                              if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS)
                                        continue;
                        }*/
                        // Will be placed one lower
diff --combined src/mapnode.h
index 1b10a546b4d0470ac442eaba2496ce2698e8473c,33128049a2599692bf35212d671991e7b6401e2d..3b7ef58787899aa0780f3a0d8fd5e227c9c57cc0
@@@ -32,15 -32,16 +32,15 @@@ with this program; if not, write to th
  /*
        Naming scheme:
        - Material = irrlicht's Material class
 -      - Content = (u8) content of a node
 +      - Content = (content_t) content of a node
        - Tile = TileSpec at some side of a node of some content type
 -*/
  
 -/*
 -      Ranges:
 +      Content ranges:
                0x000...0x07f: param2 is fully usable
                0x800...0xfff: param2 lower 4 bytes are free
  */
  typedef u16 content_t;
 +#define MAX_CONTENT 0xfff
  
  /*
        Initializes all kind of stuff in here.
@@@ -101,7 -102,10 +101,7 @@@ class NodeMetadata
  
  struct ContentFeatures
  {
 -      // If non-NULL, content is translated to this when deserialized
 -      //MapNode *translate_to;
 -
 -      // Type of MapNode::param
 +      // Type of MapNode::param1
        ContentParamType param_type;
  
        /*
        NodeMetadata *initial_metadata;
        
        // If the content is liquid, this is the flowing version of the liquid.
 -      // If content is flowing liquid, this is the same content.
 -      u8 liquid_alternative_flowing;
 +      // If content is liquid, this is the same content.
 +      content_t liquid_alternative_flowing;
+       // If the content is liquid, this is the source version of the liquid.
 -      u8 liquid_alternative_source;
++      content_t liquid_alternative_source;
        
        // Amount of light the node emits
        u8 light_source;
  
        void reset()
        {
 -              //translate_to = NULL;
                param_type = CPT_NONE;
                inventory_texture = NULL;
                is_ground_content = false;
  /*
        Call this to access the ContentFeature list
  */
 -ContentFeatures & content_features(u8 i);
 -
 +ContentFeatures & content_features(content_t i);
 +ContentFeatures & content_features(MapNode &n);
  
  /*
        Here is a bunch of DEPRECATED functions.
        in param.
        NOTE: Don't use, use "content_features(m).whatever" instead
  */
 -inline bool light_propagates_content(u8 m)
 +inline bool light_propagates_content(content_t m)
  {
        return content_features(m).light_propagates;
  }
        NOTE: It doesn't seem to go through torches regardlessly of this
        NOTE: Don't use, use "content_features(m).whatever" instead
  */
 -inline bool sunlight_propagates_content(u8 m)
 +inline bool sunlight_propagates_content(content_t m)
  {
        return content_features(m).sunlight_propagates;
  }
        2: Opaque
        NOTE: Don't use, use "content_features(m).whatever" instead
  */
 -inline u8 content_solidness(u8 m)
 +inline u8 content_solidness(content_t m)
  {
        return content_features(m).solidness;
  }
  // Objects collide with walkable contents
  // NOTE: Don't use, use "content_features(m).whatever" instead
 -inline bool content_walkable(u8 m)
 +inline bool content_walkable(content_t m)
  {
        return content_features(m).walkable;
  }
  // NOTE: Don't use, use "content_features(m).whatever" instead
 -inline bool content_liquid(u8 m)
 +inline bool content_liquid(content_t m)
  {
        return content_features(m).liquid_type != LIQUID_NONE;
  }
  // NOTE: Don't use, use "content_features(m).whatever" instead
 -inline bool content_flowing_liquid(u8 m)
 +inline bool content_flowing_liquid(content_t m)
  {
        return content_features(m).liquid_type == LIQUID_FLOWING;
  }
  // NOTE: Don't use, use "content_features(m).whatever" instead
 -inline bool content_liquid_source(u8 m)
 +inline bool content_liquid_source(content_t m)
  {
        return content_features(m).liquid_type == LIQUID_SOURCE;
  }
  // CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER
  // CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA
  // NOTE: Don't use, use "content_features(m).whatever" instead
 -inline u8 make_liquid_flowing(u8 m)
 +inline content_t make_liquid_flowing(content_t m)
  {
        u8 c = content_features(m).liquid_alternative_flowing;
        assert(c != CONTENT_IGNORE);
  }
  // Pointable contents can be pointed to in the map
  // NOTE: Don't use, use "content_features(m).whatever" instead
 -inline bool content_pointable(u8 m)
 +inline bool content_pointable(content_t m)
  {
        return content_features(m).pointable;
  }
  // NOTE: Don't use, use "content_features(m).whatever" instead
 -inline bool content_diggable(u8 m)
 +inline bool content_diggable(content_t m)
  {
        return content_features(m).diggable;
  }
  // NOTE: Don't use, use "content_features(m).whatever" instead
 -inline bool content_buildable_to(u8 m)
 +inline bool content_buildable_to(content_t m)
  {
        return content_features(m).buildable_to;
  }
                1: Face uses m1's content
                2: Face uses m2's content
  */
 -inline u8 face_contents(u8 m1, u8 m2)
 +inline u8 face_contents(content_t m1, content_t m2)
  {
        if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
                return 0;
@@@ -397,10 -404,17 +399,10 @@@ enum LightBan
        LIGHTBANK_NIGHT
  };
  
 -/*
 -      Masks for MapNode.param2 of flowing liquids
 - */
 -#define LIQUID_LEVEL_MASK 0x07
 -#define LIQUID_FLOW_DOWN_MASK 0x08
 -
  /*
        This is the stuff what the whole world consists of.
  */
  
 -
  struct MapNode
  {
        /*
        union
        {
                u8 param0;
 -              u8 d;
 +              //u8 d;
        };
  
        /*
        union
        {
                u8 param1;
 -              s8 param;
 +              //s8 param;
        };
        
        /*
                The second parameter. Initialized to 0.
                E.g. direction for torches and flowing water.
 +              If param0 >= 0x80, bits 0xf0 of this is extended content type data
        */
        union
        {
                u8 param2;
 -              u8 dir;
 +              //u8 dir;
        };
  
        MapNode(const MapNode & n)
                *this = n;
        }
        
 -      MapNode(u8 data=CONTENT_AIR, u8 a_param=0, u8 a_param2=0)
 +      MapNode(content_t content=CONTENT_AIR, u8 a_param1=0, u8 a_param2=0)
        {
 -              d = data;
 -              param = a_param;
 +              //param0 = a_param0;
 +              param1 = a_param1;
                param2 = a_param2;
 +              // Set after other params because this needs to override part of param2
 +              setContent(content);
        }
  
        bool operator==(const MapNode &other)
        {
 -              return (d == other.d
 -                              && param == other.param
 +              return (param0 == other.param0
 +                              && param1 == other.param1
                                && param2 == other.param2);
        }
        
        // To be used everywhere
        content_t getContent()
        {
 -              return d;
 +              if(param0 < 0x80)
 +                      return param0;
 +              else
 +                      return (param0<<4) + (param2>>4);
        }
        void setContent(content_t c)
        {
 -              d = c;
 +              if(c < 0x80)
 +              {
 +                      if(param0 >= 0x80)
 +                              param2 &= ~(0xf0);
 +                      param0 = c;
 +              }
 +              else
 +              {
 +                      param0 = c>>4;
 +                      param2 &= ~(0xf0);
 +                      param2 |= (c&0x0f)<<4;
 +              }
        }
        
        /*
        */
        bool light_propagates()
        {
 -              return light_propagates_content(d);
 +              return light_propagates_content(getContent());
        }
        bool sunlight_propagates()
        {
 -              return sunlight_propagates_content(d);
 +              return sunlight_propagates_content(getContent());
        }
        u8 solidness()
        {
 -              return content_solidness(d);
 +              return content_solidness(getContent());
        }
        u8 light_source()
        {
 -              return content_features(d).light_source;
 +              return content_features(*this).light_source;
        }
  
        u8 getLightBanksWithSource()
                // Select the brightest of [light source, propagated light]
                u8 lightday = 0;
                u8 lightnight = 0;
 -              if(content_features(d).param_type == CPT_LIGHT)
 +              if(content_features(*this).param_type == CPT_LIGHT)
                {
 -                      lightday = param & 0x0f;
 -                      lightnight = (param>>4)&0x0f;
 +                      lightday = param1 & 0x0f;
 +                      lightnight = (param1>>4)&0x0f;
                }
                if(light_source() > lightday)
                        lightday = light_source();
        {
                // Select the brightest of [light source, propagated light]
                u8 light = 0;
 -              if(content_features(d).param_type == CPT_LIGHT)
 +              if(content_features(*this).param_type == CPT_LIGHT)
                {
                        if(bank == LIGHTBANK_DAY)
 -                              light = param & 0x0f;
 +                              light = param1 & 0x0f;
                        else if(bank == LIGHTBANK_NIGHT)
 -                              light = (param>>4)&0x0f;
 +                              light = (param1>>4)&0x0f;
                        else
                                assert(0);
                }
        void setLight(enum LightBank bank, u8 a_light)
        {
                // If node doesn't contain light data, ignore this
 -              if(content_features(d).param_type != CPT_LIGHT)
 +              if(content_features(*this).param_type != CPT_LIGHT)
                        return;
                if(bank == LIGHTBANK_DAY)
                {
 -                      param &= 0xf0;
 -                      param |= a_light & 0x0f;
 +                      param1 &= 0xf0;
 +                      param1 |= a_light & 0x0f;
                }
                else if(bank == LIGHTBANK_NIGHT)
                {
 -                      param &= 0x0f;
 -                      param |= (a_light & 0x0f)<<4;
 +                      param1 &= 0x0f;
 +                      param1 |= (a_light & 0x0f)<<4;
                }
                else
                        assert(0);