Merge branch 'master' of https://github.com/erlehmann/minetest-delta.git into upstrea...
authorSebastian Rühl <bahamada_basti@yahoo.de>
Sun, 26 Jun 2011 10:24:32 +0000 (12:24 +0200)
committerSebastian Rühl <bahamada_basti@yahoo.de>
Sun, 26 Jun 2011 10:24:32 +0000 (12:24 +0200)
Conflicts:
.gitignore
CMakeLists.txt
data/heart.png
src/CMakeLists.txt
src/game.cpp
src/guiMainMenu.cpp
src/inventory.cpp
src/map.cpp
src/mapblock.cpp
src/mapnode.cpp
src/mapnode.h
src/materials.cpp
src/server.cpp

Signed-off-by: Sebastian Rühl <bahamada_basti@yahoo.de>
16 files changed:
1  2 
.gitignore
CMakeLists.txt
genmap.py
heart.png
src/CMakeLists.txt
src/client.cpp
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/guiMainMenu.h
src/keycode.cpp
src/keycode.h

diff --cc .gitignore
index ce00d585ed9ee5194fd8b81b0a99ab1f4ede0d19,ee3aa43c9d8eb07dbdd7ec750e844bfda04bab6e..f0c372ab7785cc160eeee597a15285a4b0ed26dc
@@@ -18,5 -17,4 +18,5 @@@ Makefil
  cmake_install.cmake
  src/jthread/libjthread.a
  debug.txt
 +bin/debug.txt
+ minetestmapper/map.png
diff --cc CMakeLists.txt
Simple merge
diff --cc genmap.py
index 0000000000000000000000000000000000000000,aaa35690fd6f0f83937c3ca78693a12fce85d8b3..a60509403d65c291ca000178286aca635812f02f
mode 000000,100755..100755
--- /dev/null
+++ b/genmap.py
@@@ -1,0 -1,91 +1,271 @@@
 -"""
 -Map format:
 -map/sectors/XXXXZZZZ/YYYY
+ #!/usr/bin/python2
+ # This is an example script that generates some valid map data.
+ import struct
+ import random
+ import os
+ import sys
++import zlib
++import array
+ from pnoise import pnoise
 -XXXX,YYYY,ZZZZ = coordinates in hexadecimal
++# Old directory format:
++# world/sectors/XXXXZZZZ/YYYY
++# XXXX,YYYY,ZZZZ = coordinates in hexadecimal
++# fffe = -2
++# ffff = -1
++# 0000 =  0
++# 0001 =  1
++# 
++# New directory format:
++# world/sectors2/XXX/ZZZ/YYYY
++# XXX,YYYY,ZZZ = coordinates in hexadecimal
++# fffe = -2
++# ffff = -1
++# 0000 =  0
++# 0001 =  1
++# ffe = -2
++# fff = -1
++# 000 =  0
++# 001 =  1
++#
++# For more proper file format documentation, refer to mapformat.txt
++# For node type documentation, refer to mapnode.h
++# NodeMetadata documentation is not complete, refer to nodemeta.cpp
++#
 -fffe = -2
 -ffff = -1
 -0000 =  0
 -0001 =  1
 -"""
++# Seed for generating terrain
++SEED = 0
 -def getrand():
++# 0=old, 1=new
++SECTOR_DIR_FORMAT = 1
++
++mapdir = "world"
+ def to4h(i):
+       s = "";
+       s += '{0:1x}'.format((i>>12) & 0x000f)
+       s += '{0:1x}'.format((i>>8) & 0x000f)
+       s += '{0:1x}'.format((i>>4) & 0x000f)
+       s += '{0:1x}'.format((i>>0) & 0x000f)
+       return s
 -def writeblock(mapdir, px,py,pz, version):
 -      sectordir = mapdir + "/sectors/" + to4h(px) + to4h(pz)
++def to3h(i):
++      s = "";
++      s += '{0:1x}'.format((i>>8) & 0x000f)
++      s += '{0:1x}'.format((i>>4) & 0x000f)
++      s += '{0:1x}'.format((i>>0) & 0x000f)
++      return s
++
++def get_sector_dir(px, pz):
++      global SECTOR_DIR_FORMAT
++      if SECTOR_DIR_FORMAT == 0:
++              return "/sectors/"+to4h(px)+to4h(pz)
++      elif SECTOR_DIR_FORMAT == 1:
++              return "/sectors2/"+to3h(px)+"/"+to3h(pz)
++      else:
++              assert(0)
++
++def getrand_air_stone():
+       i = random.randrange(0,2)
+       if i==0:
+               return 0
+       return 254
 -      if version == 0:
 -              # version
 -              f.write(struct.pack('B', 0))
 -              # is_underground
 -              f.write(struct.pack('B', 0))
 -      elif version == 2:
 -              # version
 -              f.write(struct.pack('B', 2))
 -              # is_underground
 -              f.write(struct.pack('B', 0))
++# 3-dimensional vector (position)
++class v3:
++      def __init__(self, x=0, y=0, z=0):
++              self.X = x
++              self.Y = y
++              self.Z = z
++
++class NodeMeta:
++      def __init__(self, type_id, data):
++              self.type_id = type_id
++              self.data = data
++
++class StaticObject:
++      def __init__(self):
++              self.type_id = 0
++              self.data = ""
++
++def ser_u16(i):
++      return chr((i>>8)&0xff) + chr((i>>0)&0xff)
++def ser_u32(i):
++      return (chr((i>>24)&0xff) + chr((i>>16)&0xff)
++                      + chr((i>>8)&0xff) + chr((i>>0)&0xff))
++
++# A 16x16x16 chunk of map
++class MapBlock:
++      def __init__(self):
++              self.content = array.array('B')
++              self.param1 = array.array('B')
++              self.param2 = array.array('B')
++              for i in range(16*16*16):
++                      # Initialize to air
++                      self.content.append(254)
++                      # Full light on sunlight, none when no sunlight
++                      self.param1.append(15)
++                      # No additional parameters
++                      self.param2.append(0)
++              
++              # key = v3 pos
++              # value = NodeMeta
++              self.nodemeta = {}
++      
++              # key = v3 pos
++              # value = StaticObject
++              self.static_objects = {}
++      
++      def set_content(self, v3, b):
++              self.content[v3.Z*16*16+v3.Y*16+v3.X] = b
++      def set_param1(self, v3, b):
++              self.param1[v3.Z*16*16+v3.Y*16+v3.X] = b
++      def set_param2(self, v3, b):
++              self.param2[v3.Z*16*16+v3.Y*16+v3.X] = b
++      
++      # Get data for serialization. Returns a string.
++      def serialize_data(self):
++              s = ""
++              for i in range(16*16*16):
++                      s += chr(self.content[i])
++              for i in range(16*16*16):
++                      s += chr(self.param1[i])
++              for i in range(16*16*16):
++                      s += chr(self.param2[i])
++              return s
++
++      def serialize_nodemeta(self):
++              s = ""
++              s += ser_u16(1)
++              s += ser_u16(len(self.nodemeta))
++              for pos, meta in self.nodemeta.items():
++                      pos_i = pos.Z*16*16 + pos.Y*16 + pos.X
++                      s += ser_u16(pos_i)
++                      s += ser_u16(meta.type_id)
++                      s += ser_u16(len(meta.data))
++                      s += meta.data
++              return s
++
++      def serialize_staticobj(self):
++              s = ""
++              s += chr(0)
++              s += ser_u16(len(self.static_objects))
++              for pos, obj in self.static_objects.items():
++                      pos_i = pos.Z*16*16 + pos.Y*16 + pos.X
++                      s += ser_s32(pos.X*1000)
++                      s += ser_s32(pos.Y*1000)
++                      s += ser_s32(pos.Z*1000)
++                      s += ser_u16(obj.type_id)
++                      s += ser_u16(len(obj.data))
++                      s += obj.data
++              return s
++
++def writeblock(mapdir, px,py,pz, block):
++
++      sectordir = mapdir + get_sector_dir(px, pz);
+       
+       try:
+               os.makedirs(sectordir)
+       except OSError:
+               pass
++      
++      path = sectordir+"/"+to4h(py)
++
++      print("writing block file "+path)
+       f = open(sectordir+"/"+to4h(py), "wb")
 -      for z in range(0,16):
 -              for y in range(0,16):
 -                      for x in range(0,16):
 -                              b = 254
 -                              r = 20.0*pnoise((px*16+x)/100.,(pz*16+z)/100.,0)
 -                              r += 5.0*pnoise((px*16+x)/25.,(pz*16+z)/25.,0)
 -                              #print("r="+str(r))
 -                              y1 = py*16+y
 -                              if y1 <= r-3:
 -                                      b = 0 #stone
 -                              elif y1 <= r:
 -                                      b = 1 #grass
 -                              elif y1 <= 1:
 -                                      b = 9 #water
 -                              if version == 0:
 -                                      # Material content
 -                                      f.write(struct.pack('B', b))
 -                              elif version == 2:
 -                                      # Material content
 -                                      f.write(struct.pack('B', b))
 -                                      # Brightness
 -                                      f.write(struct.pack('B', 15))
++      if f == None:
++              return
++
++      # version
++      version = 17
++      f.write(struct.pack('B', version))
++
++      # flags
++      # 0x01=is_undg, 0x02=dn_diff, 0x04=lighting_expired
++      flags = 0 + 0x02 + 0x04
++      f.write(struct.pack('B', flags))
+       
 -mapdir = "map"
++      # data
++      c_obj = zlib.compressobj()
++      c_obj.compress(block.serialize_data())
++      f.write(struct.pack('BB', 0x78, 0x9c)) # zlib magic number
++      f.write(c_obj.flush())
++
++      # node metadata
++      c_obj = zlib.compressobj()
++      c_obj.compress(block.serialize_nodemeta())
++      f.write(struct.pack('BB', 0x78, 0x9c)) # zlib magic number
++      f.write(c_obj.flush())
++
++      # mapblockobject count
++      f.write(ser_u16(0))
++
++      # static objects
++      f.write(block.serialize_staticobj())
++
++      # timestamp
++      f.write(ser_u32(0xffffffff))
+       f.close()
 -for z in range(-2,3):
 -      for y in range(-1,2):
 -              for x in range(-2,3):
 -                      print("generating block "+str(x)+","+str(y)+","+str(z))
 -                      writeblock(mapdir, x,y,z, 0)
++for z0 in range(-1,3):
++      for x0 in range(-1,3):
++              for y0 in range(-1,3):
++                      print("generating block "+str(x0)+","+str(y0)+","+str(z0))
++                      #v3 blockp = v3(x0,y0,z0)
++                      
++                      # Create a MapBlock
++                      block = MapBlock()
++                      
++                      # Generate stuff in it
++                      for z in range(0,16):
++                              for x in range(0,16):
++                                      h = 20.0*pnoise((x0*16+x)/100.,(z0*16+z)/100.,SEED+0)
++                                      h += 5.0*pnoise((x0*16+x)/25.,(z0*16+z)/25.,SEED+0)
++                                      if pnoise((x0*16+x)/25.,(z0*16+z)/25.,SEED+92412) > 0.05:
++                                              h += 10
++                                      #print("r="+str(r))
++                                      # This enables comparison by ==
++                                      h = int(h)
++                                      for y in range(0,16):
++                                              p = v3(x,y,z)
++                                              b = 254
++                                              y1 = y0*16+y
++                                              if y1 <= h-3:
++                                                      b = 0 #stone
++                                              elif y1 <= h and y1 <= 0:
++                                                      b = 8 #mud
++                                              elif y1 == h:
++                                                      b = 1 #grass
++                                              elif y1 < h:
++                                                      b = 8 #mud
++                                              elif y1 <= 1:
++                                                      b = 9 #water
++
++                                              # Material content
++                                              block.set_content(p, b)
++
++                                      # Place a sign at the center at surface level.
++                                      # Placing a sign means placing the sign node and
++                                      # adding node metadata to the mapblock.
++                                      if x == 8 and z == 8 and y0*16 <= h-1 and (y0+1)*16-1 > h:
++                                              p = v3(8,h+1-y0*16,8)
++                                              # 14 = Sign
++                                              content_type = 14
++                                              block.set_content(p, content_type)
++                                              # This places the sign to the bottom of the cube.
++                                              # Working values: 0x01, 0x02, 0x04, 0x08, 0x10, 0x20
++                                              block.set_param2(p, 0x08)
++                                              # Then add metadata to hold the text of the sign
++                                              s = "Hello at sector ("+str(x0)+","+str(z0)+")"
++                                              meta = NodeMeta(content_type, ser_u16(len(s))+s)
++                                              block.nodemeta[p] = meta
++                      # Write it on disk
++                      writeblock(mapdir, x0,y0,z0, block)
+ #END
diff --cc heart.png
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bea1aefd6b22e3268f88db0eb26188b1cb362f81
new file mode 100644 (file)
Binary files differ
index 73a960ecc64b6a3e70dc9b7715c2719b9b89fae3,9d5cf7d2cb9f7ce2b04153195dd9e20871f5dd25..f62a8626bef71ca8030934f285262eaaaf2b304f
@@@ -107,7 -98,9 +107,8 @@@ set(minetest_SRC
        keycode.cpp
        clouds.cpp
        clientobject.cpp
 -      guiFurnaceMenu.cpp
        guiMainMenu.cpp
+       guiKeyChangeMenu.cpp
        guiMessageMenu.cpp
        guiTextInputMenu.cpp
        guiInventoryMenu.cpp
diff --cc src/client.cpp
index abc05650541e5bb7e9ceae557085131ef5b44bd4,5869dc77bbab1714d1e12d6395b890811578327a..e86b3a4f85ad0fafd9575022c46300372e65a193
@@@ -772,10 -780,7 +795,9 @@@ void Client::ProcessData(u8 *data, u32 
                */
  
                //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
--              
 +              /*
 +                      Add it to mesh update queue and set it to be acknowledged after update.
 +              */
                addUpdateMeshTaskWithEdge(p, true);
        }
        else if(command == TOCLIENT_PLAYERPOS)
index 32d2e6d489b6f4d5cdff8ff952463a6e77822268,0000000000000000000000000000000000000000..069e68300b9bdf412c082a84b7aaa2fdc250c19e
mode 100644,000000..100644
--- /dev/null
@@@ -1,480 -1,0 +1,586 @@@
 +/*
 +Minetest-c55
 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 +
 +This program is free software; you can redistribute it and/or modify
 +it under the terms of the GNU General Public License as published by
 +the Free Software Foundation; either version 2 of the License, or
 +(at your option) any later version.
 +
 +This program is distributed in the hope that it will be useful,
 +but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License along
 +with this program; if not, write to the Free Software Foundation, Inc.,
 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 +*/
 +
 +#include "content_craft.h"
 +#include "inventory.h"
 +#include "content_mapnode.h"
 +#include "player.h"
 +
 +/*
 +      items: actually *items[9]
 +      return value: allocates a new item, or returns NULL.
 +*/
 +InventoryItem *craft_get_result(InventoryItem **items)
 +{
 +      // Wood
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new MaterialItem(CONTENT_WOOD, 4);
 +              }
 +      }
 +
 +      // Stick
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new CraftItem("Stick", 4);
 +              }
 +      }
 +
 +      // Fence
 +      {
 +              ItemSpec specs[9];
 +              specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new MaterialItem(CONTENT_FENCE, 2);
 +              }
 +      }
 +
 +      // Sign
 +      {
 +              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_MATERIAL, CONTENT_WOOD);
 +              specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      //return new MapBlockObjectItem("Sign");
 +                      return new MaterialItem(CONTENT_SIGN_WALL, 1);
 +              }
 +      }
 +
 +      // Torch
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
 +              specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new MaterialItem(CONTENT_TORCH, 4);
 +              }
 +      }
 +
 +      // Wooden pick
 +      {
 +              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[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("WPick", 0);
 +              }
 +      }
 +
 +      // Stone pick
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("STPick", 0);
 +              }
 +      }
 +
 +      // Steel pick
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("SteelPick", 0);
 +              }
 +      }
 +
 +      // Mese pick
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
 +              specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("MesePick", 0);
 +              }
 +      }
 +
 +      // Wooden shovel
 +      {
 +              ItemSpec specs[9];
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("WShovel", 0);
 +              }
 +      }
 +
 +      // Stone shovel
 +      {
 +              ItemSpec specs[9];
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("STShovel", 0);
 +              }
 +      }
 +
 +      // Steel shovel
 +      {
 +              ItemSpec specs[9];
 +              specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("SteelShovel", 0);
 +              }
 +      }
 +
 +      // Wooden axe
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("WAxe", 0);
 +              }
 +      }
 +
 +      // Stone axe
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("STAxe", 0);
 +              }
 +      }
 +
 +      // Steel axe
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("SteelAxe", 0);
 +              }
 +      }
 +
 +      // Wooden sword
 +      {
 +              ItemSpec specs[9];
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("WSword", 0);
 +              }
 +      }
 +
 +      // Stone sword
 +      {
 +              ItemSpec specs[9];
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("STSword", 0);
 +              }
 +      }
 +
 +      // Steel sword
 +      {
 +              ItemSpec specs[9];
 +              specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new ToolItem("SteelSword", 0);
 +              }
 +      }
 +
++      // 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];
 +              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_MATERIAL, CONTENT_WOOD);
 +              specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
 +              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_CHEST, 1);
 +              }
 +      }
 +
 +      // Furnace
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new MaterialItem(CONTENT_FURNACE, 1);
 +              }
 +      }
 +
 +      // Steel block
 +      {
 +              ItemSpec specs[9];
 +              specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
 +              if(checkItemCombination(items, specs))
 +              {
 +                      return new MaterialItem(CONTENT_STEEL, 1);
 +              }
 +      }
 +
++      // 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;
 +}
 +
 +void craft_set_creative_inventory(Player *player)
 +{
 +      player->resetInventory();
 +      
 +      // Give some good tools
 +      {
 +              InventoryItem *item = new ToolItem("MesePick", 0);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new ToolItem("SteelPick", 0);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new ToolItem("SteelAxe", 0);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new ToolItem("SteelShovel", 0);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +
 +      /*
 +              Give materials
 +      */
 +      
 +      // CONTENT_IGNORE-terminated list
 +      u8 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_CHEST,
 +              CONTENT_FURNACE,
 +              CONTENT_SIGN_WALL,
 +              CONTENT_IGNORE
 +      };
 +      
 +      u8 *mip = material_items;
 +      for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
 +      {
 +              if(*mip == CONTENT_IGNORE)
 +                      break;
 +
 +              InventoryItem *item = new MaterialItem(*mip, 1);
 +              player->inventory.addItem("main", item);
 +
 +              mip++;
 +      }
 +
 +#if 0
 +      assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
 +      
 +      // add torch first
 +      InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
 +      player->inventory.addItem("main", item);
 +      
 +      // Then others
 +      for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
 +      {
 +              // Skip some materials
 +              if(i == CONTENT_WATER || i == CONTENT_TORCH
 +                      || i == CONTENT_COALSTONE)
 +                      continue;
 +
 +              InventoryItem *item = new MaterialItem(i, 1);
 +              player->inventory.addItem("main", item);
 +      }
 +#endif
 +
 +      /*// Sign
 +      {
 +              InventoryItem *item = new MapBlockObjectItem("Sign Example text");
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }*/
 +}
 +
 +void craft_give_initial_stuff(Player *player)
 +{
 +      {
 +              InventoryItem *item = new ToolItem("SteelPick", 0);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new ToolItem("SteelAxe", 0);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new ToolItem("SteelShovel", 0);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      /*{
 +              InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new CraftItem("Stick", 4);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new ToolItem("WPick", 32000);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }
 +      {
 +              InventoryItem *item = new ToolItem("STPick", 32000);
 +              void* r = player->inventory.addItem("main", item);
 +              assert(r == NULL);
 +      }*/
 +      /*// and some signs
 +      for(u16 i=0; i<4; i++)
 +      {
 +              InventoryItem *item = new MapBlockObjectItem("Sign Example text");
 +              bool r = player->inventory.addItem("main", item);
 +              assert(r == true);
 +      }*/
 +      /*// Give some other stuff
 +      {
 +              InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
 +              bool r = player->inventory.addItem("main", item);
 +              assert(r == true);
 +      }*/
 +}
 +
index 3b72b31f1a21f72def7f4ab39b72b5c69ab29d97,0000000000000000000000000000000000000000..357c8ef26c05d0511b32f1a4653709d37d7c3536
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,108 @@@
-       if(subname == "lump_of_iron")
 +/*
 +Minetest-c55
 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 +
 +This program is free software; you can redistribute it and/or modify
 +it under the terms of the GNU General Public License as published by
 +the Free Software Foundation; either version 2 of the License, or
 +(at your option) any later version.
 +
 +This program is distributed in the hope that it will be useful,
 +but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License along
 +with this program; if not, write to the Free Software Foundation, Inc.,
 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 +*/
 +
 +#include "content_inventory.h"
 +#include "inventory.h"
 +#include "serverobject.h"
 +#include "content_mapnode.h"
 +
 +bool item_material_is_cookable(u8 content)
 +{
 +      if(content == CONTENT_TREE)
 +              return true;
 +      else if(content == CONTENT_COBBLE)
 +              return true;
 +      else if(content == CONTENT_SAND)
 +              return true;
 +      return false;
 +}
 +
 +InventoryItem* item_material_create_cook_result(u8 content)
 +{
 +      if(content == CONTENT_TREE)
 +              return new CraftItem("lump_of_coal", 1);
 +      else if(content == CONTENT_COBBLE)
 +              return new MaterialItem(CONTENT_STONE, 1);
 +      else if(content == CONTENT_SAND)
 +              return new MaterialItem(CONTENT_GLASS, 1);
 +      return NULL;
 +}
 +
 +std::string item_craft_get_image_name(const std::string &subname)
 +{
 +      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
 +              return "cloud.png"; // just something
 +}
 +
 +ServerActiveObject* item_craft_create_object(const std::string &subname,
 +              ServerEnvironment *env, u16 id, v3f pos)
 +{
 +      if(subname == "rat")
 +      {
 +              ServerActiveObject *obj = new RatSAO(env, id, pos);
 +              return obj;
 +      }
 +
 +      return NULL;
 +}
 +
 +s16 item_craft_get_drop_count(const std::string &subname)
 +{
 +      if(subname == "rat")
 +              return 1;
 +
 +      return -1;
 +}
 +
 +bool item_craft_is_cookable(const std::string &subname)
 +{
++      if(subname == "lump_of_iron" || subname == "lump_of_clay")
 +              return true;
 +              
 +      return false;
 +}
 +
 +InventoryItem* item_craft_create_cook_result(const std::string &subname)
 +{
 +      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;
 +}
 +
index bc701aadf4c32927cd83372daaaf7072a104c736,0000000000000000000000000000000000000000..9ef0599d17911689fbe0682daea707200c84db6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,923 -1,0 +1,1076 @@@
 +/*
 +Minetest-c55
 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 +
 +This program is free software; you can redistribute it and/or modify
 +it under the terms of the GNU General Public License as published by
 +the Free Software Foundation; either version 2 of the License, or
 +(at your option) any later version.
 +
 +This program is distributed in the hope that it will be useful,
 +but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License along
 +with this program; if not, write to the Free Software Foundation, Inc.,
 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 +*/
 +
 +#include "content_mapblock.h"
 +#include "content_mapnode.h"
 +#include "main.h" // For g_settings and g_texturesource
 +#include "mineral.h"
 +
 +#ifndef SERVER
 +// Create a cuboid.
 +//  material  - the material to use (for all 6 faces)
 +//  collector - the MeshCollector for the resulting polygons
 +//  pa        - texture atlas pointer for the material
 +//  c         - vertex colour - used for all
 +//  pos       - the position of the centre of the cuboid
 +//  rz,ry,rz  - the radius of the cuboid in each dimension
 +//  txc       - texture coordinates - this is a list of texture coordinates
 +//              for the opposite corners of each face - therefore, there
 +//              should be (2+2)*6=24 values in the list. Alternatively, pass
 +//              NULL to use the entire texture for each face. The order of
 +//              the faces in the list is top-backi-right-front-left-bottom
 +//              If you specified 0,0,1,1 for each face, that would be the
 +//              same as passing NULL.
 +void makeCuboid(video::SMaterial &material, MeshCollector *collector,
 +      AtlasPointer* pa, video::SColor &c,
 +      v3f &pos, f32 rx, f32 ry, f32 rz, f32* txc)
 +{
 +      f32 tu0=pa->x0();
 +      f32 tu1=pa->x1();
 +      f32 tv0=pa->y0();
 +      f32 tv1=pa->y1();
 +      f32 txus=tu1-tu0;
 +      f32 txvs=tv1-tv0;
 +
 +      video::S3DVertex v[4] =
 +      {
 +              video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv1),
 +              video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv1),
 +              video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv0),
 +              video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv0)
 +      };
 +
 +      for(int i=0;i<6;i++)
 +      {
 +              switch(i)
 +              {
 +                      case 0: // top
 +                              v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
 +                              v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
 +                              v[2].Pos.X= rx; v[2].Pos.Y= ry; v[2].Pos.Z= rz;
 +                              v[3].Pos.X= rx; v[3].Pos.Y= ry, v[3].Pos.Z=-rz;
 +                              break;
 +                      case 1: // back
 +                              v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
 +                              v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
 +                              v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
 +                              v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
 +                              break;
 +                      case 2: //right
 +                              v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
 +                              v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
 +                              v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
 +                              v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
 +                              break;
 +                      case 3: // front
 +                              v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
 +                              v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
 +                              v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
 +                              v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
 +                              break;
 +                      case 4: // left
 +                              v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
 +                              v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
 +                              v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
 +                              v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
 +                              break;
 +                      case 5: // bottom
 +                              v[0].Pos.X= rx; v[0].Pos.Y=-ry; v[0].Pos.Z= rz;
 +                              v[1].Pos.X=-rx; v[1].Pos.Y=-ry; v[1].Pos.Z= rz;
 +                              v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
 +                              v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
 +                              break;
 +              }
 +
 +              if(txc!=NULL)
 +              {
 +                      v[0].TCoords.X=tu0+txus*txc[0]; v[0].TCoords.Y=tv0+txvs*txc[3];
 +                      v[1].TCoords.X=tu0+txus*txc[2]; v[1].TCoords.Y=tv0+txvs*txc[3];
 +                      v[2].TCoords.X=tu0+txus*txc[2]; v[2].TCoords.Y=tv0+txvs*txc[1];
 +                      v[3].TCoords.X=tu0+txus*txc[0]; v[3].TCoords.Y=tv0+txvs*txc[1];
 +                      txc+=4;
 +              }
 +
 +              for(u16 i=0; i<4; i++)
 +                      v[i].Pos += pos;
 +              u16 indices[] = {0,1,2,2,3,0};
 +              collector->append(material, v, 4, indices, 6);
 +
 +      }
 +
 +}
 +#endif
 +
 +#ifndef SERVER
 +void mapblock_mesh_generate_special(MeshMakeData *data,
 +              MeshCollector &collector)
 +{
 +      // 0ms
 +      //TimeTaker timer("mapblock_mesh_generate_special()");
 +
 +      /*
 +              Some settings
 +      */
 +      bool new_style_water = g_settings.getBool("new_style_water");
 +      bool new_style_leaves = g_settings.getBool("new_style_leaves");
 +      //bool smooth_lighting = g_settings.getBool("smooth_lighting");
 +      bool invisible_stone = g_settings.getBool("invisible_stone");
 +      
 +      float node_water_level = 1.0;
 +      if(new_style_water)
 +              node_water_level = 0.85;
 +      
 +      v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
 +
 +      // Flowing water material
 +      video::SMaterial material_water1;
 +      material_water1.setFlag(video::EMF_LIGHTING, false);
 +      material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
 +      material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
 +      material_water1.setFlag(video::EMF_FOG_ENABLE, true);
 +      material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
 +      AtlasPointer pa_water1 = g_texturesource->getTexture(
 +                      g_texturesource->getTextureId("water.png"));
 +      material_water1.setTexture(0, pa_water1.atlas);
 +
 +      // New-style leaves material
 +      video::SMaterial material_leaves1;
 +      material_leaves1.setFlag(video::EMF_LIGHTING, false);
 +      //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
 +      material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
 +      material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
 +      material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
 +      AtlasPointer pa_leaves1 = g_texturesource->getTexture(
 +                      g_texturesource->getTextureId("leaves.png"));
 +      material_leaves1.setTexture(0, pa_leaves1.atlas);
 +
 +      // Glass material
 +      video::SMaterial material_glass;
 +      material_glass.setFlag(video::EMF_LIGHTING, false);
 +      material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
 +      material_glass.setFlag(video::EMF_FOG_ENABLE, true);
 +      material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
 +      AtlasPointer pa_glass = g_texturesource->getTexture(
 +                      g_texturesource->getTextureId("glass.png"));
 +      material_glass.setTexture(0, pa_glass.atlas);
 +
 +      // Wood material
 +      video::SMaterial material_wood;
 +      material_wood.setFlag(video::EMF_LIGHTING, false);
 +      material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
 +      material_wood.setFlag(video::EMF_FOG_ENABLE, true);
 +      material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
 +      AtlasPointer pa_wood = g_texturesource->getTexture(
 +                      g_texturesource->getTextureId("wood.png"));
 +      material_wood.setTexture(0, pa_wood.atlas);
 +
 +      // General ground material for special output
 +      // Texture is modified just before usage
 +      video::SMaterial material_general;
 +      material_general.setFlag(video::EMF_LIGHTING, false);
 +      material_general.setFlag(video::EMF_BILINEAR_FILTER, false);
 +      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++)
 +      {
 +              v3s16 p(x,y,z);
 +
 +              MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
 +              
 +              /*
 +                      Add torches to mesh
 +              */
 +              if(n.d == CONTENT_TORCH)
 +              {
 +                      video::SColor c(255,255,255,255);
 +
 +                      // Wall at X+ of node
 +                      video::S3DVertex vertices[4] =
 +                      {
 +                              video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
 +                              video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
 +                              video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
 +                              video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
 +                      };
 +
 +                      v3s16 dir = unpackDir(n.dir);
 +
 +                      for(s32 i=0; i<4; i++)
 +                      {
 +                              if(dir == v3s16(1,0,0))
 +                                      vertices[i].Pos.rotateXZBy(0);
 +                              if(dir == v3s16(-1,0,0))
 +                                      vertices[i].Pos.rotateXZBy(180);
 +                              if(dir == v3s16(0,0,1))
 +                                      vertices[i].Pos.rotateXZBy(90);
 +                              if(dir == v3s16(0,0,-1))
 +                                      vertices[i].Pos.rotateXZBy(-90);
 +                              if(dir == v3s16(0,-1,0))
 +                                      vertices[i].Pos.rotateXZBy(45);
 +                              if(dir == v3s16(0,1,0))
 +                                      vertices[i].Pos.rotateXZBy(-45);
 +
 +                              vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
 +                      }
 +
 +                      // Set material
 +                      video::SMaterial material;
 +                      material.setFlag(video::EMF_LIGHTING, false);
 +                      material.setFlag(video::EMF_BACK_FACE_CULLING, false);
 +                      material.setFlag(video::EMF_BILINEAR_FILTER, false);
 +                      //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
 +                      material.MaterialType
 +                                      = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
 +
 +                      if(dir == v3s16(0,-1,0))
 +                              material.setTexture(0,
 +                                              g_texturesource->getTextureRaw("torch_on_floor.png"));
 +                      else if(dir == v3s16(0,1,0))
 +                              material.setTexture(0,
 +                                              g_texturesource->getTextureRaw("torch_on_ceiling.png"));
 +                      // For backwards compatibility
 +                      else if(dir == v3s16(0,0,0))
 +                              material.setTexture(0,
 +                                              g_texturesource->getTextureRaw("torch_on_floor.png"));
 +                      else
 +                              material.setTexture(0, 
 +                                              g_texturesource->getTextureRaw("torch.png"));
 +
 +                      u16 indices[] = {0,1,2,2,3,0};
 +                      // Add to mesh collector
 +                      collector.append(material, vertices, 4, indices, 6);
 +              }
 +              /*
 +                      Signs on walls
 +              */
 +              if(n.d == CONTENT_SIGN_WALL)
 +              {
 +                      u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
 +                      video::SColor c(255,l,l,l);
 +                              
 +                      float d = (float)BS/16;
 +                      // Wall at X+ of node
 +                      video::S3DVertex vertices[4] =
 +                      {
 +                              video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
 +                              video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
 +                              video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
 +                              video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
 +                      };
 +
 +                      v3s16 dir = unpackDir(n.dir);
 +
 +                      for(s32 i=0; i<4; i++)
 +                      {
 +                              if(dir == v3s16(1,0,0))
 +                                      vertices[i].Pos.rotateXZBy(0);
 +                              if(dir == v3s16(-1,0,0))
 +                                      vertices[i].Pos.rotateXZBy(180);
 +                              if(dir == v3s16(0,0,1))
 +                                      vertices[i].Pos.rotateXZBy(90);
 +                              if(dir == v3s16(0,0,-1))
 +                                      vertices[i].Pos.rotateXZBy(-90);
 +                              if(dir == v3s16(0,-1,0))
 +                                      vertices[i].Pos.rotateXYBy(-90);
 +                              if(dir == v3s16(0,1,0))
 +                                      vertices[i].Pos.rotateXYBy(90);
 +
 +                              vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
 +                      }
 +
 +                      // Set material
 +                      video::SMaterial material;
 +                      material.setFlag(video::EMF_LIGHTING, false);
 +                      material.setFlag(video::EMF_BACK_FACE_CULLING, false);
 +                      material.setFlag(video::EMF_BILINEAR_FILTER, false);
 +                      material.setFlag(video::EMF_FOG_ENABLE, true);
 +                      //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
 +                      material.MaterialType
 +                                      = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
 +
 +                      material.setTexture(0, 
 +                                      g_texturesource->getTextureRaw("sign_wall.png"));
 +
 +                      u16 indices[] = {0,1,2,2,3,0};
 +                      // Add to mesh collector
 +                      collector.append(material, vertices, 4, indices, 6);
 +              }
 +              /*
 +                      Add flowing water to mesh
 +              */
 +              else if(n.d == 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)
 +                              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)
 +                              l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
 +                      // Otherwise use the light of this node (the water)
 +                      else
 +                              l = decode_light(n.getLightBlend(data->m_daynight_ratio));
 +                      video::SColor c(WATER_ALPHA,l,l,l);
 +                      
 +                      // Neighbor water levels (key = relative position)
 +                      // Includes current node
 +                      core::map<v3s16, f32> neighbor_levels;
 +                      core::map<v3s16, u8> neighbor_contents;
 +                      core::map<v3s16, u8> neighbor_flags;
 +                      const u8 neighborflag_top_is_water = 0x01;
 +                      v3s16 neighbor_dirs[9] = {
 +                              v3s16(0,0,0),
 +                              v3s16(0,0,1),
 +                              v3s16(0,0,-1),
 +                              v3s16(1,0,0),
 +                              v3s16(-1,0,0),
 +                              v3s16(1,0,1),
 +                              v3s16(-1,0,-1),
 +                              v3s16(1,0,-1),
 +                              v3s16(-1,0,1),
 +                      };
 +                      for(u32 i=0; i<9; i++)
 +                      {
 +                              u8 content = CONTENT_AIR;
 +                              float level = -0.5 * BS;
 +                              u8 flags = 0;
 +                              // Check neighbor
 +                              v3s16 p2 = p + neighbor_dirs[i];
 +                              MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
 +                              if(n2.d != CONTENT_IGNORE)
 +                              {
 +                                      content = n2.d;
 +
 +                                      if(n2.d == CONTENT_WATERSOURCE)
 +                                              level = (-0.5+node_water_level) * BS;
 +                                      else if(n2.d == CONTENT_WATER)
 +                                              level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
 +                                                              * node_water_level) * BS;
 +
 +                                      // Check node above neighbor.
 +                                      // NOTE: This doesn't get executed if neighbor
 +                                      //       doesn't exist
 +                                      p2.Y += 1;
 +                                      n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
 +                                      if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
 +                                              flags |= neighborflag_top_is_water;
 +                              }
 +                              
 +                              neighbor_levels.insert(neighbor_dirs[i], level);
 +                              neighbor_contents.insert(neighbor_dirs[i], content);
 +                              neighbor_flags.insert(neighbor_dirs[i], flags);
 +                      }
 +
 +                      //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
 +                      //float water_level = neighbor_levels[v3s16(0,0,0)];
 +
 +                      // Corner heights (average between four waters)
 +                      f32 corner_levels[4];
 +                      
 +                      v3s16 halfdirs[4] = {
 +                              v3s16(0,0,0),
 +                              v3s16(1,0,0),
 +                              v3s16(1,0,1),
 +                              v3s16(0,0,1),
 +                      };
 +                      for(u32 i=0; i<4; i++)
 +                      {
 +                              v3s16 cornerdir = halfdirs[i];
 +                              float cornerlevel = 0;
 +                              u32 valid_count = 0;
 +                              for(u32 j=0; j<4; j++)
 +                              {
 +                                      v3s16 neighbordir = cornerdir - halfdirs[j];
 +                                      u8 content = neighbor_contents[neighbordir];
 +                                      // Special case for source nodes
 +                                      if(content == CONTENT_WATERSOURCE)
 +                                      {
 +                                              cornerlevel = (-0.5+node_water_level)*BS;
 +                                              valid_count = 1;
 +                                              break;
 +                                      }
 +                                      else if(content == CONTENT_WATER)
 +                                      {
 +                                              cornerlevel += neighbor_levels[neighbordir];
 +                                              valid_count++;
 +                                      }
 +                                      else if(content == CONTENT_AIR)
 +                                      {
 +                                              cornerlevel += -0.5*BS;
 +                                              valid_count++;
 +                                      }
 +                              }
 +                              if(valid_count > 0)
 +                                      cornerlevel /= valid_count;
 +                              corner_levels[i] = cornerlevel;
 +                      }
 +
 +                      /*
 +                              Generate sides
 +                      */
 +
 +                      v3s16 side_dirs[4] = {
 +                              v3s16(1,0,0),
 +                              v3s16(-1,0,0),
 +                              v3s16(0,0,1),
 +                              v3s16(0,0,-1),
 +                      };
 +                      s16 side_corners[4][2] = {
 +                              {1, 2},
 +                              {3, 0},
 +                              {2, 3},
 +                              {0, 1},
 +                      };
 +                      for(u32 i=0; i<4; i++)
 +                      {
 +                              v3s16 dir = side_dirs[i];
 +
 +                              /*
 +                                      If our topside is water and neighbor's topside
 +                                      is water, don't draw side face
 +                              */
 +                              if(top_is_water &&
 +                                              neighbor_flags[dir] & neighborflag_top_is_water)
 +                                      continue;
 +
 +                              u8 neighbor_content = neighbor_contents[dir];
 +                              
 +                              // Don't draw face if neighbor is not air or water
 +                              if(neighbor_content != CONTENT_AIR
 +                                              && neighbor_content != CONTENT_WATER)
 +                                      continue;
 +                              
 +                              bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
 +                              
 +                              // Don't draw any faces if neighbor is water and top is water
 +                              if(neighbor_is_water == true && top_is_water == false)
 +                                      continue;
 +                              
 +                              video::S3DVertex vertices[4] =
 +                              {
 +                                      /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
 +                                      video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
 +                                      video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
 +                                      video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
 +                                      video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
 +                                                      pa_water1.x0(), pa_water1.y1()),
 +                                      video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
 +                                                      pa_water1.x1(), pa_water1.y1()),
 +                                      video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
 +                                                      pa_water1.x1(), pa_water1.y0()),
 +                                      video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
 +                                                      pa_water1.x0(), pa_water1.y0()),
 +                              };
 +                              
 +                              /*
 +                                      If our topside is water, set upper border of face
 +                                      at upper border of node
 +                              */
 +                              if(top_is_water)
 +                              {
 +                                      vertices[2].Pos.Y = 0.5*BS;
 +                                      vertices[3].Pos.Y = 0.5*BS;
 +                              }
 +                              /*
 +                                      Otherwise upper position of face is corner levels
 +                              */
 +                              else
 +                              {
 +                                      vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
 +                                      vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
 +                              }
 +                              
 +                              /*
 +                                      If neighbor is water, lower border of face is corner
 +                                      water levels
 +                              */
 +                              if(neighbor_is_water)
 +                              {
 +                                      vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
 +                                      vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
 +                              }
 +                              /*
 +                                      If neighbor is not water, lower border of face is
 +                                      lower border of node
 +                              */
 +                              else
 +                              {
 +                                      vertices[0].Pos.Y = -0.5*BS;
 +                                      vertices[1].Pos.Y = -0.5*BS;
 +                              }
 +                              
 +                              for(s32 j=0; j<4; j++)
 +                              {
 +                                      if(dir == v3s16(0,0,1))
 +                                              vertices[j].Pos.rotateXZBy(0);
 +                                      if(dir == v3s16(0,0,-1))
 +                                              vertices[j].Pos.rotateXZBy(180);
 +                                      if(dir == v3s16(-1,0,0))
 +                                              vertices[j].Pos.rotateXZBy(90);
 +                                      if(dir == v3s16(1,0,-0))
 +                                              vertices[j].Pos.rotateXZBy(-90);
 +
 +                                      vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
 +                              }
 +
 +                              u16 indices[] = {0,1,2,2,3,0};
 +                              // Add to mesh collector
 +                              collector.append(material_water1, vertices, 4, indices, 6);
 +                      }
 +                      
 +                      /*
 +                              Generate top side, if appropriate
 +                      */
 +                      
 +                      if(top_is_water == false)
 +                      {
 +                              video::S3DVertex vertices[4] =
 +                              {
 +                                      /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
 +                                      video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
 +                                      video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
 +                                      video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
 +                                      video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
 +                                                      pa_water1.x0(), pa_water1.y1()),
 +                                      video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
 +                                                      pa_water1.x1(), pa_water1.y1()),
 +                                      video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
 +                                                      pa_water1.x1(), pa_water1.y0()),
 +                                      video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
 +                                                      pa_water1.x0(), pa_water1.y0()),
 +                              };
 +                              
 +                              // This fixes a strange bug
 +                              s32 corner_resolve[4] = {3,2,1,0};
 +
 +                              for(s32 i=0; i<4; i++)
 +                              {
 +                                      //vertices[i].Pos.Y += water_level;
 +                                      //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
 +                                      s32 j = corner_resolve[i];
 +                                      vertices[i].Pos.Y += corner_levels[j];
 +                                      vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
 +                              }
 +
 +                              u16 indices[] = {0,1,2,2,3,0};
 +                              // Add to mesh collector
 +                              collector.append(material_water1, vertices, 4, indices, 6);
 +                      }
 +              }
 +              /*
 +                      Add water sources to mesh if using new style
 +              */
 +              else if(n.d == 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)
 +                              top_is_water = true;*/
 +                      if(n.d == CONTENT_AIR)
 +                              top_is_air = true;
 +                      
 +                      /*if(top_is_water == true)
 +                              continue;*/
 +                      if(top_is_air == false)
 +                              continue;
 +
 +                      u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
 +                      video::SColor c(WATER_ALPHA,l,l,l);
 +                      
 +                      video::S3DVertex vertices[4] =
 +                      {
 +                              /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
 +                              video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
 +                              video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
 +                              video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
 +                              video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
 +                                              pa_water1.x0(), pa_water1.y1()),
 +                              video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
 +                                              pa_water1.x1(), pa_water1.y1()),
 +                              video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
 +                                              pa_water1.x1(), pa_water1.y0()),
 +                              video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
 +                                              pa_water1.x0(), pa_water1.y0()),
 +                      };
 +
 +                      for(s32 i=0; i<4; i++)
 +                      {
 +                              vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
 +                              vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
 +                      }
 +
 +                      u16 indices[] = {0,1,2,2,3,0};
 +                      // Add to mesh collector
 +                      collector.append(material_water1, vertices, 4, indices, 6);
 +              }
 +              /*
 +                      Add leaves if using new style
 +              */
 +              else if(n.d == 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)));
 +                      video::SColor c(255,l,l,l);
 +
 +                      for(u32 j=0; j<6; j++)
 +                      {
 +                              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),
 +                                      video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
 +                                      video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
 +                                              pa_leaves1.x0(), pa_leaves1.y1()),
 +                                      video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
 +                                              pa_leaves1.x1(), pa_leaves1.y1()),
 +                                      video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
 +                                              pa_leaves1.x1(), pa_leaves1.y0()),
 +                                      video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
 +                                              pa_leaves1.x0(), pa_leaves1.y0()),
 +                              };
 +
 +                              if(j == 0)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(0);
 +                              }
 +                              else if(j == 1)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(180);
 +                              }
 +                              else if(j == 2)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(-90);
 +                              }
 +                              else if(j == 3)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(90);
 +                              }
 +                              else if(j == 4)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateYZBy(-90);
 +                              }
 +                              else if(j == 5)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateYZBy(90);
 +                              }
 +
 +                              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_leaves1, vertices, 4, indices, 6);
 +                      }
 +              }
 +              /*
 +                      Add glass
 +              */
 +              else if(n.d == CONTENT_GLASS)
 +              {
 +                      u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
 +                      video::SColor c(255,l,l,l);
 +
 +                      for(u32 j=0; j<6; j++)
 +                      {
 +                              video::S3DVertex vertices[4] =
 +                              {
 +                                      video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
 +                                              pa_glass.x0(), pa_glass.y1()),
 +                                      video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
 +                                              pa_glass.x1(), pa_glass.y1()),
 +                                      video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
 +                                              pa_glass.x1(), pa_glass.y0()),
 +                                      video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
 +                                              pa_glass.x0(), pa_glass.y0()),
 +                              };
 +
 +                              if(j == 0)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(0);
 +                              }
 +                              else if(j == 1)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(180);
 +                              }
 +                              else if(j == 2)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(-90);
 +                              }
 +                              else if(j == 3)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(90);
 +                              }
 +                              else if(j == 4)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateYZBy(-90);
 +                              }
 +                              else if(j == 5)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateYZBy(90);
 +                              }
 +
 +                              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_glass, vertices, 4, indices, 6);
 +                      }
 +              }
 +              /*
 +                      Add fence
 +              */
 +              else if(n.d == CONTENT_FENCE)
 +              {
 +                      u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
 +                      video::SColor c(255,l,l,l);
 +
 +                      const f32 post_rad=(f32)BS/10;
 +                      const f32 bar_rad=(f32)BS/20;
 +                      const f32 bar_len=(f32)(BS/2)-post_rad;
 +
 +                      // The post - always present
 +                      v3f pos = intToFloat(p+blockpos_nodes, BS);
 +                      f32 postuv[24]={
 +                                      0.4,0.4,0.6,0.6,
 +                                      0.35,0,0.65,1,
 +                                      0.35,0,0.65,1,
 +                                      0.35,0,0.65,1,
 +                                      0.35,0,0.65,1,
 +                                      0.4,0.4,0.6,0.6};
 +                      makeCuboid(material_wood, &collector,
 +                              &pa_wood, c, pos,
 +                              post_rad,BS/2,post_rad, postuv);
 +
 +                      // Now a section of fence, +X, if there's a post there
 +                      v3s16 p2 = p;
 +                      p2.X++;
 +                      MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
 +                      if(n2.d == CONTENT_FENCE)
 +                      {
 +                              pos = intToFloat(p+blockpos_nodes, BS);
 +                              pos.X += BS/2;
 +                              pos.Y += BS/4;
 +                              f32 xrailuv[24]={
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6};
 +                              makeCuboid(material_wood, &collector,
 +                                      &pa_wood, c, pos,
 +                                      bar_len,bar_rad,bar_rad, xrailuv);
 +
 +                              pos.Y -= BS/2;
 +                              makeCuboid(material_wood, &collector,
 +                                      &pa_wood, c, pos,
 +                                      bar_len,bar_rad,bar_rad, xrailuv);
 +                      }
 +
 +                      // Now a section of fence, +Z, if there's a post there
 +                      p2 = p;
 +                      p2.Z++;
 +                      n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
 +                      if(n2.d == CONTENT_FENCE)
 +                      {
 +                              pos = intToFloat(p+blockpos_nodes, BS);
 +                              pos.Z += BS/2;
 +                              pos.Y += BS/4;
 +                              f32 zrailuv[24]={
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6,
 +                                      0,0.4,1,0.6};
 +                              makeCuboid(material_wood, &collector,
 +                                      &pa_wood, c, pos,
 +                                      bar_rad,bar_rad,bar_len, zrailuv);
 +                              pos.Y -= BS/2;
 +                              makeCuboid(material_wood, &collector,
 +                                      &pa_wood, c, pos,
 +                                      bar_rad,bar_rad,bar_len, zrailuv);
 +
 +                      }
 +
 +              }
 +#if 1
 +              /*
 +                      Add stones with minerals if stone is invisible
 +              */
 +              else if(n.d == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE)
 +              {
 +                      for(u32 j=0; j<6; j++)
 +                      {
 +                              // NOTE: Hopefully g_6dirs[j] is the right direction...
 +                              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)
 +                                      l = decode_light(n2.getLightBlend(data->m_daynight_ratio));
 +                              else
 +                                      l = 255;*/
 +                              u8 l = 255;
 +                              video::SColor c(255,l,l,l);
 +                              
 +                              // Get the right texture
 +                              TileSpec ts = n.getTile(dir);
 +                              AtlasPointer ap = ts.texture;
 +                              material_general.setTexture(0, ap.atlas);
-                               {
 +                              video::S3DVertex vertices[4] =
-                                               vertices[i].Pos.rotateYZBy(-90);
++                              {
 +                                      /*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),
 +                                      video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
 +                                      video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
 +                                              ap.x0(), ap.y1()),
 +                                      video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
 +                                              ap.x1(), ap.y1()),
 +                                      video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
 +                                              ap.x1(), ap.y0()),
 +                                      video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
 +                                              ap.x0(), ap.y0()),
 +                              };
 +
 +                              if(j == 0)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(0);
 +                              }
 +                              else if(j == 1)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(180);
 +                              }
 +                              else if(j == 2)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(-90);
 +                              }
 +                              else if(j == 3)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
 +                                              vertices[i].Pos.rotateXZBy(90);
 +                              }
 +                              else if(j == 4)
++
++                              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.d == 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++)
-                               else if(j == 5)
++                                              vertices[i].Pos.rotateXZBy(45);
 +                              }
-                                               vertices[i].Pos.rotateYZBy(90);
++                              else if(j == 1)
 +                              {
 +                                      for(u16 i=0; i<4; i++)
-                               collector.append(material_general, vertices, 4, indices, 6);
++                                              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++)
 +                              {
 +                                      vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
 +                              }
 +
 +                              u16 indices[] = {0,1,2,2,3,0};
 +                              // Add to mesh collector
- #endif
++                              collector.append(material_papyrus, vertices, 4, indices, 6);
 +                      }
 +              }
++              else if(n.d == 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)
++                              is_rail_x[0] = true;
++                      if(n_plus_x.d == CONTENT_RAIL)
++                              is_rail_x[1] = true;
++                      if(n_minus_z.d == CONTENT_RAIL)
++                              is_rail_z[0] = true;
++                      if(n_plus_z.d == 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
 +
index 403fb66d398fc86a750e9f5564553e1165ecdf12,0000000000000000000000000000000000000000..2e7a240f308aea55e15639f4ddf20cfe181a67da
mode 100644,000000..100644
--- /dev/null
@@@ -1,377 -1,0 +1,450 @@@
 +/*
 +Minetest-c55
 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 +
 +This program is free software; you can redistribute it and/or modify
 +it under the terms of the GNU General Public License as published by
 +the Free Software Foundation; either version 2 of the License, or
 +(at your option) any later version.
 +
 +This program is distributed in the hope that it will be useful,
 +but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License along
 +with this program; if not, write to the Free Software Foundation, Inc.,
 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 +*/
 +
 +// For g_settings
 +#include "main.h"
 +
 +#include "content_mapnode.h"
 +#include "mapnode.h"
 +#include "content_nodemeta.h"
 +
 +// TODO: Get rid of these and set up some attributes like toughness,
 +//       fluffyness, and a funciton to calculate time and durability loss
 +//       (and sound? and whatever else) from them
 +void setStoneLikeDiggingProperties(DiggingPropertiesList &list, float toughness);
 +void setDirtLikeDiggingProperties(DiggingPropertiesList &list, float toughness);
 +void setWoodLikeDiggingProperties(DiggingPropertiesList &list, float toughness);
 +
 +void content_mapnode_init()
 +{
 +      // Read some settings
 +      bool new_style_water = g_settings.getBool("new_style_water");
 +      bool new_style_leaves = g_settings.getBool("new_style_leaves");
 +      bool invisible_stone = g_settings.getBool("invisible_stone");
 +
 +      u8 i;
 +      ContentFeatures *f = NULL;
 +
 +      i = CONTENT_STONE;
 +      f = &content_features(i);
 +      f->setAllTextures("stone.png");
 +      f->setInventoryTextureCube("stone.png", "stone.png", "stone.png");
 +      f->param_type = CPT_MINERAL;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(CONTENT_COBBLE)+" 1";
 +      setStoneLikeDiggingProperties(f->digging_properties, 1.0);
 +      if(invisible_stone)
 +              f->solidness = 0; // For debugging, hides regular stone
 +      
 +      i = CONTENT_GRASS;
 +      f = &content_features(i);
 +      f->setAllTextures("mud.png^grass_side.png");
 +      f->setTexture(0, "grass.png");
 +      f->setTexture(1, "mud.png");
 +      f->param_type = CPT_MINERAL;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(CONTENT_MUD)+" 1";
 +      setDirtLikeDiggingProperties(f->digging_properties, 1.0);
 +      
 +      i = CONTENT_GRASS_FOOTSTEPS;
 +      f = &content_features(i);
 +      f->setAllTextures("mud.png^grass_side.png");
 +      f->setTexture(0, "grass_footsteps.png");
 +      f->setTexture(1, "mud.png");
 +      f->param_type = CPT_MINERAL;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(CONTENT_MUD)+" 1";
 +      setDirtLikeDiggingProperties(f->digging_properties, 1.0);
 +      
 +      i = CONTENT_MUD;
 +      f = &content_features(i);
 +      f->setAllTextures("mud.png");
 +      f->setInventoryTextureCube("mud.png", "mud.png", "mud.png");
 +      f->param_type = CPT_MINERAL;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      setDirtLikeDiggingProperties(f->digging_properties, 1.0);
 +      
 +      i = CONTENT_SAND;
 +      f = &content_features(i);
 +      f->setAllTextures("sand.png");
 +      f->setInventoryTextureCube("sand.png", "sand.png", "sand.png");
 +      f->param_type = CPT_MINERAL;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      setDirtLikeDiggingProperties(f->digging_properties, 1.0);
 +      
 +      i = CONTENT_GRAVEL;
 +      f = &content_features(i);
 +      f->setAllTextures("gravel.png");
 +      f->setInventoryTextureCube("gravel.png", "gravel.png", "gravel.png");
 +      f->param_type = CPT_MINERAL;
 +      f->is_ground_content = true;
 +      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->setTexture(0, "tree_top.png");
 +      f->setTexture(1, "tree_top.png");
 +      f->param_type = CPT_MINERAL;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      setWoodLikeDiggingProperties(f->digging_properties, 1.0);
 +      
 +      i = CONTENT_LEAVES;
 +      f = &content_features(i);
 +      f->light_propagates = true;
 +      //f->param_type = CPT_MINERAL;
 +      f->param_type = CPT_LIGHT;
 +      f->is_ground_content = true;
 +      if(new_style_leaves)
 +      {
 +              f->solidness = 0; // drawn separately, makes no faces
 +              f->setInventoryTextureCube("leaves.png", "leaves.png", "leaves.png");
 +      }
 +      else
 +      {
 +              f->setAllTextures("[noalpha:leaves.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->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->setInventoryTextureCube("glass.png", "glass.png", "glass.png");
 +      setWoodLikeDiggingProperties(f->digging_properties, 0.15);
 +
 +      i = CONTENT_FENCE;
 +      f = &content_features(i);
 +      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->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->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL);
 +      f->setAllTextures("stone.png^mineral_coal.png");
 +      f->is_ground_content = true;
 +      setStoneLikeDiggingProperties(f->digging_properties, 1.5);
 +      
 +      i = CONTENT_WOOD;
 +      f = &content_features(i);
 +      f->setAllTextures("wood.png");
 +      f->setInventoryTextureCube("wood.png", "wood.png", "wood.png");
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      setWoodLikeDiggingProperties(f->digging_properties, 0.75);
 +      
 +      i = CONTENT_MESE;
 +      f = &content_features(i);
 +      f->setAllTextures("mese.png");
 +      f->setInventoryTextureCube("mese.png", "mese.png", "mese.png");
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      setStoneLikeDiggingProperties(f->digging_properties, 0.5);
 +      
 +      i = CONTENT_CLOUD;
 +      f = &content_features(i);
 +      f->setAllTextures("cloud.png");
 +      f->setInventoryTextureCube("cloud.png", "cloud.png", "cloud.png");
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      
 +      i = CONTENT_AIR;
 +      f = &content_features(i);
 +      f->param_type = CPT_LIGHT;
 +      f->light_propagates = true;
 +      f->sunlight_propagates = true;
 +      f->solidness = 0;
 +      f->walkable = false;
 +      f->pointable = false;
 +      f->diggable = false;
 +      f->buildable_to = true;
 +      f->air_equivalent = true;
 +      
 +      i = CONTENT_WATER;
 +      f = &content_features(i);
 +      f->setInventoryTextureCube("water.png", "water.png", "water.png");
 +      f->param_type = CPT_LIGHT;
 +      f->light_propagates = true;
 +      f->solidness = 0; // Drawn separately, makes no faces
 +      f->walkable = false;
 +      f->pointable = false;
 +      f->diggable = false;
 +      f->buildable_to = true;
 +      f->liquid_type = LIQUID_FLOWING;
 +      f->liquid_alternative_flowing = CONTENT_WATER;
 +      
 +      i = CONTENT_WATERSOURCE;
 +      f = &content_features(i);
 +      //f->setInventoryTexture("water.png");
 +      f->setInventoryTextureCube("water.png", "water.png", "water.png");
 +      if(new_style_water)
 +      {
 +              f->solidness = 0; // drawn separately, makes no faces
 +      }
 +      else // old style
 +      {
 +              f->solidness = 1;
 +
 +              TileSpec t;
 +              if(g_texturesource)
 +                      t.texture = g_texturesource->getTexture("water.png");
 +              
 +              t.alpha = WATER_ALPHA;
 +              t.material_type = MATERIAL_ALPHA_VERTEX;
 +              t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
 +              f->setAllTiles(t);
 +      }
 +      f->param_type = CPT_LIGHT;
 +      f->light_propagates = true;
 +      f->walkable = false;
 +      f->pointable = false;
 +      f->diggable = false;
 +      f->buildable_to = true;
 +      f->liquid_type = LIQUID_SOURCE;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      f->liquid_alternative_flowing = CONTENT_WATER;
 +      
 +      i = CONTENT_TORCH;
 +      f = &content_features(i);
 +      f->setInventoryTexture("torch_on_floor.png");
 +      f->param_type = CPT_LIGHT;
 +      f->light_propagates = true;
 +      f->sunlight_propagates = true;
 +      f->solidness = 0; // drawn separately, makes no faces
 +      f->walkable = false;
 +      f->wall_mounted = true;
 +      f->air_equivalent = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      f->light_source = LIGHT_MAX-1;
 +      f->digging_properties.set("", DiggingProperties(true, 0.0, 0));
 +      
 +      i = CONTENT_SIGN_WALL;
 +      f = &content_features(i);
 +      f->setInventoryTexture("sign_wall.png");
 +      f->param_type = CPT_LIGHT;
 +      f->light_propagates = true;
 +      f->sunlight_propagates = true;
 +      f->solidness = 0; // drawn separately, makes no faces
 +      f->walkable = false;
 +      f->wall_mounted = true;
 +      f->air_equivalent = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      if(f->initial_metadata == NULL)
 +              f->initial_metadata = new SignNodeMetadata("Some sign");
 +      f->digging_properties.set("", DiggingProperties(true, 0.5, 0));
 +      
 +      i = CONTENT_CHEST;
 +      f = &content_features(i);
 +      f->param_type = CPT_FACEDIR_SIMPLE;
 +      f->setAllTextures("chest_side.png");
 +      f->setTexture(0, "chest_top.png");
 +      f->setTexture(1, "chest_top.png");
 +      f->setTexture(5, "chest_front.png"); // Z-
 +      f->setInventoryTexture("chest_top.png");
 +      //f->setInventoryTextureCube("chest_top.png", "chest_side.png", "chest_side.png");
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      if(f->initial_metadata == NULL)
 +              f->initial_metadata = new ChestNodeMetadata();
 +      setWoodLikeDiggingProperties(f->digging_properties, 1.0);
 +      
 +      i = CONTENT_FURNACE;
 +      f = &content_features(i);
 +      f->param_type = CPT_FACEDIR_SIMPLE;
 +      f->setAllTextures("furnace_side.png");
 +      f->setTexture(5, "furnace_front.png"); // Z-
 +      f->setInventoryTexture("furnace_front.png");
 +      //f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      f->dug_item = std::string("MaterialItem ")+itos(CONTENT_COBBLE)+" 6";
 +      if(f->initial_metadata == NULL)
 +              f->initial_metadata = new FurnaceNodeMetadata();
 +      setStoneLikeDiggingProperties(f->digging_properties, 3.0);
 +      
 +      i = CONTENT_COBBLE;
 +      f = &content_features(i);
 +      f->setAllTextures("cobble.png");
 +      f->setInventoryTextureCube("cobble.png", "cobble.png", "cobble.png");
 +      f->param_type = CPT_NONE;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      setStoneLikeDiggingProperties(f->digging_properties, 0.9);
 +
 +      i = CONTENT_MOSSYCOBBLE;
 +      f = &content_features(i);
 +      f->setAllTextures("mossycobble.png");
 +      f->setInventoryTextureCube("mossycobble.png", "mossycobble.png", "mossycobble.png");
 +      f->param_type = CPT_NONE;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      setStoneLikeDiggingProperties(f->digging_properties, 0.8);
 +      
 +      i = CONTENT_STEEL;
 +      f = &content_features(i);
 +      f->setAllTextures("steel_block.png");
 +      f->setInventoryTextureCube("steel_block.png", "steel_block.png",
 +                      "steel_block.png");
 +      f->param_type = CPT_NONE;
 +      f->is_ground_content = true;
 +      f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 +      setStoneLikeDiggingProperties(f->digging_properties, 5.0);
 +      
 +      // NOTE: Remember to add frequently used stuff to the texture atlas in tile.cpp
 +      
 +
 +      /*
 +              Add MesePick to everything
 +      */
 +      for(u16 i=0; i<256; i++)
 +      {
 +              content_features(i).digging_properties.set("MesePick",
 +                              DiggingProperties(true, 0.0, 65535./1337));
 +      }
 +
 +}
 +
 +void setStoneLikeDiggingProperties(DiggingPropertiesList &list, float toughness)
 +{
 +      list.set("",
 +                      DiggingProperties(true, 15.0*toughness, 0));
 +      
 +      list.set("WPick",
 +                      DiggingProperties(true, 1.3*toughness, 65535./30.*toughness));
 +      list.set("STPick",
 +                      DiggingProperties(true, 0.75*toughness, 65535./100.*toughness));
 +      list.set("SteelPick",
 +                      DiggingProperties(true, 0.50*toughness, 65535./333.*toughness));
 +
 +      /*list.set("MesePick",
 +                      DiggingProperties(true, 0.0*toughness, 65535./20.*toughness));*/
 +}
 +
 +void setDirtLikeDiggingProperties(DiggingPropertiesList &list, float toughness)
 +{
 +      list.set("",
 +                      DiggingProperties(true, 0.75*toughness, 0));
 +      
 +      list.set("WShovel",
 +                      DiggingProperties(true, 0.4*toughness, 65535./50.*toughness));
 +      list.set("STShovel",
 +                      DiggingProperties(true, 0.2*toughness, 65535./150.*toughness));
 +      list.set("SteelShovel",
 +                      DiggingProperties(true, 0.15*toughness, 65535./400.*toughness));
 +}
 +
 +void setWoodLikeDiggingProperties(DiggingPropertiesList &list, float toughness)
 +{
 +      list.set("",
 +                      DiggingProperties(true, 3.0*toughness, 0));
 +      
 +      list.set("WAxe",
 +                      DiggingProperties(true, 1.5*toughness, 65535./30.*toughness));
 +      list.set("STAxe",
 +                      DiggingProperties(true, 0.75*toughness, 65535./100.*toughness));
 +      list.set("SteelAxe",
 +                      DiggingProperties(true, 0.5*toughness, 65535./333.*toughness));
 +}
 +
 +
index e314807f94dc7c92a37c8871a068a1f1b0b2187c,0000000000000000000000000000000000000000..e53624c21597d5cb19d6e62d1299d387254d253a
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,62 @@@
 +/*
 +Minetest-c55
 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 +
 +This program is free software; you can redistribute it and/or modify
 +it under the terms of the GNU General Public License as published by
 +the Free Software Foundation; either version 2 of the License, or
 +(at your option) any later version.
 +
 +This program is distributed in the hope that it will be useful,
 +but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License along
 +with this program; if not, write to the Free Software Foundation, Inc.,
 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 +*/
 +
 +#ifndef CONTENT_MAPNODE_HEADER
 +#define CONTENT_MAPNODE_HEADER
 +
 +void content_mapnode_init();
 +
 +/*
 +      Node content type IDs
 +*/
 +#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
 +
 +#endif
 +
index cd255341f5d8b996c1600e61a14fa2e46d9665ba,3ebfef0c539e185826788e6063efd9314193b39a..e2c704259888323064063c0bf7a4a78b8e7aeab0
@@@ -579,66 -571,6 +579,66 @@@ void spawnRandomObjects(MapBlock *block
  }
  #endif
  
-       
 +void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
 +{
 +      // Get time difference
 +      u32 dtime_s = 0;
 +      u32 stamp = block->getTimestamp();
 +      if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
 +              dtime_s = m_game_time - block->getTimestamp();
 +      dtime_s += additional_dtime;
 +
 +      // Set current time as timestamp (and let it set ChangedFlag)
 +      block->setTimestamp(m_game_time);
 +
 +      //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
-       
++
 +      // Activate stored objects
 +      activateObjects(block);
 +
 +      // Run node metadata
 +      bool changed = block->m_node_metadata.step((float)dtime_s);
 +      if(changed)
 +      {
 +              MapEditEvent event;
 +              event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
 +              event.p = block->getPos();
 +              m_map->dispatchEvent(&event);
 +
 +              block->setChangedFlag();
 +      }
 +
 +      // TODO: Do something
 +      // TODO: Implement usage of ActiveBlockModifier
++
 +      // Here's a quick demonstration
 +      v3s16 p0;
 +      for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
 +      for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
 +      for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
 +      {
 +              v3s16 p = p0 + block->getPosRelative();
 +              MapNode n = block->getNodeNoEx(p0);
 +#if 1
 +              // Test something:
 +              // Convert all mud under proper day lighting to grass
 +              if(n.d == CONTENT_MUD)
 +              {
 +                      if(dtime_s > 300)
 +                      {
 +                              MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
 +                              if(content_features(n_top.d).air_equivalent &&
 +                                              n_top.getLight(LIGHTBANK_DAY) >= 13)
 +                              {
 +                                      n.d = CONTENT_GRASS;
 +                                      m_map->addNodeWithEvent(p, n);
 +                              }
 +                      }
 +              }
 +#endif
 +      }
 +}
 +
  void ServerEnvironment::step(float dtime)
  {
        DSTACK(__FUNCTION_NAME);
                        MapBlock *block = m_map->getBlockNoCreateNoEx(p);
                        if(block==NULL)
                                continue;
--                      
 -                      // Set current time as timestamp
 +                      // Set current time as timestamp (and let it set ChangedFlag)
++
                        block->setTimestamp(m_game_time);
                }
  
                        MapBlock *block = m_map->getBlockNoCreateNoEx(p);
                        if(block==NULL)
                                continue;
 -                      
 +
-                       activateBlock(block);
+                       // Get time difference
+                       u32 dtime_s = 0;
+                       u32 stamp = block->getTimestamp();
+                       if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
+                               dtime_s = m_game_time - block->getTimestamp();
 -                      // Set current time as timestamp
++                      // Set current time as timestamp (and let it set ChangedFlag)
+                       block->setTimestamp(m_game_time);
+                       //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
 -                      
++
+                       // Activate stored objects
+                       activateObjects(block);
++                      // Run node metadata
++                      bool changed = block->m_node_metadata.step((float)dtime_s);
++                      if(changed)
++                      {
++                              MapEditEvent event;
++                              event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
++                              event.p = p;
++                              m_map->dispatchEvent(&event);
++
++                              block->setChangedFlag();
++                      }
++
+                       // TODO: Do something
 -                      
++                      // TODO: Implement usage of ActiveBlockModifier
++
+                       // Here's a quick demonstration
+                       v3s16 p0;
+                       for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
+                       for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
+                       for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
+                       {
+                               v3s16 p = p0 + block->getPosRelative();
+                               MapNode n = block->getNodeNoEx(p0);
+                               // Test something:
+                               // Convert all mud under proper day lighting to grass
+                               if(n.d == CONTENT_MUD)
+                               {
 -                                      if(1)
++                                      if(dtime_s > 300)
+                                       {
+                                               MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
+                                               if(content_features(n_top.d).air_equivalent &&
+                                                               n_top.getLight(LIGHTBANK_DAY) >= 13)
+                                               {
+                                                       n.d = CONTENT_GRASS;
+                                                       m_map->addNodeWithEvent(p, n);
+                                               }
+                                       }
+                               }
++                              /*
++                                      Convert grass into mud if under something else than air
++                              */
++                              else if(n.d == 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)
++                                              {
++                                                      n.d = CONTENT_MUD;
++                                                      m_map->addNodeWithEvent(p, n);
++                                              }
++                                      }
++                              }
+                       }
                }
        }
  
                                                }
                                        }
                                }
-                               }
 +                              /*
 +                                      Convert grass into mud if under something else than air
 +                              */
 +                              else if(n.d == 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)
 +                                              {
 +                                                      n.d = CONTENT_MUD;
 +                                                      m_map->addNodeWithEvent(p, n);
 +                                              }
++                                              }
 +                                      }
                        }
                }
        }
diff --cc src/game.cpp
Simple merge
index edd519024fb6a56879538d6cf144183f92f4c18b,6c028c6e5e60f18bed4a690adaa8bf28ea6affb4..87561f7974fd075dd4bea9c6fc440a5470fffcfb
@@@ -27,21 -27,35 +27,36 @@@ with this program; if not, write to th
  // For IGameCallback
  #include "guiPauseMenu.h"
  
+ enum
+ {
+       GUI_ID_QUIT_BUTTON = 101,
+       GUI_ID_NAME_INPUT,
+       GUI_ID_ADDRESS_INPUT,
+       GUI_ID_PORT_INPUT,
+       GUI_ID_FANCYTREE_CB,
+       GUI_ID_SMOOTH_LIGHTING_CB,
+       GUI_ID_DAMAGE_CB,
+       GUI_ID_CREATIVE_CB,
+       GUI_ID_JOIN_GAME_BUTTON,
+       GUI_ID_CHANGE_KEYS_BUTTON,
+       GUI_ID_DELETE_MAP_BUTTON
+ };
  struct MainMenuData
  {
 -      MainMenuData() :
 +      MainMenuData():
                // Client opts
 -                              fancy_trees(false), smooth_lighting(false),
 -                              // Server opts
 -                              creative_mode(false), enable_damage(false),
 -                              // Actions
 -                              delete_map(false)
 -      {
 -      }
 +              fancy_trees(false),
 +              smooth_lighting(false),
 +              // Server opts
 +              creative_mode(false),
 +              enable_damage(false),
 +              // Actions
 +              delete_map(false)
 +      {}
  
        // These are in the native format of the gui elements
 -
 +      
        // Client options
        std::wstring address;
        std::wstring port;
@@@ -87,6 -104,6 +102,11 @@@ private
        MainMenuData *m_data;
        bool m_accepted;
        IGameCallback *m_gamecallback;
++
++      gui::IGUIEnvironment* env;
++      gui::IGUIElement* parent;
++      s32 id;
++      IMenuManager *menumgr;
  };
  
  #endif
diff --cc src/keycode.cpp
index ad3c0b4018bf074dabf120eb5bb18f479fe50fe8,993426053cebea4b074053d33c2dbc5d758b8e67..f014914d02a6f27e3070a60aa4693e9affc36a36
@@@ -171,9 -170,48 +171,49 @@@ irr::EKEY_CODE keyname_to_keycode(cons
        return irr::KEY_KEY_CODES_COUNT;
  }
  
+ static const char *KeyNames[] =
+ { "-", "KEY_LBUTTON", "KEY_RBUTTON", "Cancel", "Middle Button", "X Button 1",
+               "X Button 2", "-", "Back", "Tab", "-", "-", "Clear", "Return", "-",
+               "-", "KEY_SHIFT", "Control", "Menu", "Pause", "Capital", "Kana", "-",
+               "Junja", "Final", "Kanji", "-", "Escape", "Convert", "Nonconvert",
+               "Accept", "Mode Change", "KEY_SPACE", "Priot", "Next", "KEY_END",
+               "KEY_HOME", "Left", "Up", "Right", "Down", "Select", "KEY_PRINT",
+               "Execute", "Snapshot", "Insert", "Delete", "Help", "KEY_KEY_0",
+               "KEY_KEY_1", "KEY_KEY_2", "KEY_KEY_3", "KEY_KEY_4", "KEY_KEY_5",
+               "KEY_KEY_6", "KEY_KEY_7", "KEY_KEY_8", "KEY_KEY_9", "-", "-", "-", "-",
+               "-", "-", "-", "KEY_KEY_A", "KEY_KEY_B", "KEY_KEY_C", "KEY_KEY_D",
+               "KEY_KEY_E", "KEY_KEY_F", "KEY_KEY_G", "KEY_KEY_H", "KEY_KEY_I",
+               "KEY_KEY_J", "KEY_KEY_K", "KEY_KEY_L", "KEY_KEY_M", "KEY_KEY_N",
+               "KEY_KEY_O", "KEY_KEY_P", "KEY_KEY_Q", "KEY_KEY_R", "KEY_KEY_S",
+               "KEY_KEY_T", "KEY_KEY_U", "KEY_KEY_V", "KEY_KEY_W", "KEY_KEY_X",
+               "KEY_KEY_Y", "KEY_KEY_Z", "Left Windows", "Right Windows", "Apps", "-",
+               "Sleep", "KEY_NUMPAD0", "KEY_NUMPAD1", "KEY_NUMPAD2", "KEY_NUMPAD3",
+               "KEY_NUMPAD4", "KEY_NUMPAD5", "KEY_NUMPAD6", "KEY_NUMPAD7",
+               "KEY_NUMPAD8", "KEY_NUMPAD9", "Numpad *", "Numpad +", "Numpad /",
+               "Numpad -", "Numpad .", "Numpad /", "KEY_F1", "KEY_F2", "KEY_F3",
+               "KEY_F4", "KEY_F5", "KEY_F6", "KEY_F7", "KEY_F8", "KEY_F9", "KEY_F10",
+               "KEY_F11", "KEY_F12", "KEY_F13", "KEY_F14", "KEY_F15", "KEY_F16",
+               "KEY_F17", "KEY_F18", "KEY_F19", "KEY_F20", "KEY_F21", "KEY_F22",
+               "KEY_F23", "KEY_F24", "-", "-", "-", "-", "-", "-", "-", "-",
+               "Num Lock", "Scroll Lock", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+               "-", "-", "-", "-", "-", "KEY_LSHIFT", "KEY_RSHIFT", "Left Control",
+               "Right Control", "Left Menu", "Right Menu", "-", "-", "-", "-", "-",
+               "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+               "-", "-", "Plus", "Comma", "Minus", "Period", "-", "-", "-", "-", "-",
+               "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+               "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+               "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+               "-", "-", "-", "-", "-", "-", "-", "-", "Attn", "CrSel", "ExSel",
+               "Erase OEF", "Play", "Zoom", "PA1", "OEM Clear", "-" };
+ std::string keycode_to_keyname(s32 keycode)
+ {
+       return KeyNames[keycode];
+ }
++
  /*
 - Key config
 - */
 +      Key config
 +*/
  
  // A simple cache for quicker lookup
  core::map<std::string, irr::EKEY_CODE> g_key_setting_cache;
@@@ -189,4 -227,8 +229,7 @@@ irr::EKEY_CODE getKeySetting(const cha
        return c;
  }
  
+ void clearKeyCache()
+ {
+       g_key_setting_cache.clear();
+ }
 -
diff --cc src/keycode.h
index f19fe344282c77c8f999b2ddffe7cbb286a9d38b,614f2e584ca45aac937710f786d0219122fb27d5..9c62004d8f744644185d61ad4ded8bfab9abb4a1
@@@ -26,6 -27,7 +27,8 @@@ irr::EKEY_CODE keyname_to_keycode(cons
  
  // Key configuration getter
  irr::EKEY_CODE getKeySetting(const char *settingname);
+ std::string keycode_to_keyname(s32 keycode);
+ void clearCache();
 +
  #endif