diff --git a/.gitignore b/.gitignore
index ce00d585ed9ee5194fd8b81b0a99ab1f4ede0d19..f0c372ab7785cc160eeee597a15285a4b0ed26dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,4 +19,4 @@ cmake_install.cmake
 src/jthread/libjthread.a
 debug.txt
 bin/debug.txt
-
+minetestmapper/map.png
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6403a9dd94ec3d562607fc1e16c5dd02d2211e0b..aae390f38221150eb864d5a9a536cb398465cd8c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,11 +5,11 @@ if(${CMAKE_VERSION} STREQUAL "2.8.2")
 endif(${CMAKE_VERSION} STREQUAL "2.8.2")
 
 # This can be read from ${PROJECT_NAME} after project() is called
-project(minetest)
+project(minetest-delta)
 
 set(VERSION_MAJOR 0)
 set(VERSION_MINOR 2)
-set(VERSION_PATCH 20110704_0)
+set(VERSION_PATCH 20110720_0)
 set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
 
 # Configuration options
@@ -48,18 +48,18 @@ if(WIN32)
 	set(EXAMPLE_CONF_DIR ".")
 elseif(APPLE)
 	# random placeholders
-	set(DATADIR "share/minetest")
+	set(DATADIR "share/${PROJECT_NAME}")
 	set(BINDIR "bin")
-	set(DOCDIR "share/doc/minetest")
+	set(DOCDIR "share/doc/${PROJECT_NAME}")
 	set(EXAMPLE_CONF_DIR ".")
 elseif(UNIX) # Linux, BSD etc
-	set(DATADIR "share/minetest")
+	set(DATADIR "share/${PROJECT_NAME}")
 	set(BINDIR "bin")
-	set(DOCDIR "share/doc/minetest")
-	set(EXAMPLE_CONF_DIR "share/doc/minetest")
+	set(DOCDIR "share/doc/${PROJECT_NAME}")
+	set(EXAMPLE_CONF_DIR "share/doc/${PROJECT_NAME}")
 endif()
 
-install(FILES "README.txt" DESTINATION "${DOCDIR}")
+install(FILES "doc/README.txt" DESTINATION "${DOCDIR}")
 install(FILES "minetest.conf.example" DESTINATION "${DOCDIR}")
 
 #
@@ -103,8 +103,8 @@ elseif(APPLE)
 	set(CPACK_BUNDLE_NAME ${PROJECT_NAME})
 	set(CPACK_BUNDLE_ICON "")
 	set(CPACK_BUNDLE_PLIST "")
-	set(CPACK_BUNDLE_STARTUP_COMMAND "Contents/MacOS/minetest")
-	set(CPACK_GENERATOR Bundle)
+	set(CPACK_BUNDLE_STARTUP_COMMAND "Contents/MacOS/${PROJECT_NAME}")
+	set(CPACK_GENERATOR "Bundle")
 else()
 	set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-linux")
 	set(CPACK_GENERATOR TGZ)
diff --git a/README b/README
new file mode 100644
index 0000000000000000000000000000000000000000..82fcdd33107d65f8c0f5459898600376b87339dc
--- /dev/null
+++ b/README
@@ -0,0 +1,41 @@
+Minetest Δ (“Minetest Delta”) is a fork of Minetest-c55 <http://celeron.55.lt/~celeron55/minetest/>, incorporating experimental features that are not (yet) included in Minetest-c55.
+
+New features:
+* Submenu for key assignment
+* configurable far mesh tree display (boolean)
+* configurable far mesh rendering distance
+* volumetric clouds (using fake shading)
+* translation support (including questionable translation into german)
+* build system uses local jthread & sqlite libraries if available
+
+New bricks:
+* Sandstone (crafted from 4 sand, yields sand)
+* Cactus (plant that grows on sand)
+* Clay (found in sand at sea level, yields 4 lumps of clay)
+* Brick (made from 4 clay bricks, yields 4 clay bricks)
+* Papyrus (plant that grows in shallow water)
+* Book shelf (made from 6 wood and 3 books, sandwhiched)
+* Rail (made from 6 iron ingots and 3 sticks, vertically sandwhiched)
+
+New materials:
+* Lump of clay
+* Clay brick (made from lumps of clay in the furnace)
+* Paper (made from 3 papyrus horizontally)
+* Book (made from 3 paper vertically)
+
+Alternate graphics:
+* Player
+* Omsk birds (instead of Oerkki ghosts)
+* Rat
+* Glass
+* Heart
+
+Building on GNU/Linux or OS X:
+    cmake . -DRUN_IN_PLACE=1
+    make -j2
+
+The “upstream” branch contains vanilla minetest-c55, created using:
+    hg-fast-export -r ~/share/src/games/minetest -o upstream
+    git push origin upstream/master:upstream -f
+
+All new/replaced graphics done by erlehmann are dual-licensed under GPL (version 2 or, at your option, any later version) and CC-BY-SA (version 3.0 or, at your option, any later version).
diff --git a/README.txt b/README.txt
index 41048992e98e10d083b9f413d6789c7005be11e7..662f2770b0734d0ca2a6212959cae376b890f4db 100644
--- a/README.txt
+++ b/README.txt
@@ -90,6 +90,8 @@ Compiling on Windows:
 		http://www.winimage.com/zLibDll/index.html
 	* Zlib library (zlibwapi.lib and zlibwapi.dll from zlib125dll.zip):
 		http://www.winimage.com/zLibDll/index.html
+	* gettext bibrary and tools:
+		http://gnuwin32.sourceforge.net/downlinks/gettext.php
 	* And, of course, Minetest-c55:
 		http://celeron.55.lt/~celeron55/minetest/download
 - Steps:
@@ -117,6 +119,10 @@ Compiling on Windows:
 			+ lib
 			+ include
 			...
+		+ gettext
+			+bin
+			+include
+			+lib
 		+ minetest
 			+ src
 			+ doc
@@ -145,6 +151,10 @@ Compiling on Windows:
 	ZLIB_DLL                 DIR/zlib125dll/dll32/zlibwapi.dll
 	ZLIB_INCLUDE_DIR         DIR/zlib-1.2.5
 	ZLIB_LIBRARIES           DIR/zlib125dll/dll32/zlibwapi.lib
+	GETTEXT_BIN_DIR          DIR/gettext/bin
+	GETTEXT_INCLUDE_DIR      DIR/gettext/include
+	GETTEXT_LIBRARIES        DIR/gettext/lib/intl.lib
+	GETTEXT_MSGFMT           DIR/gettext/bin/msgfmt
 	-----------------
 	- Hit "Configure"
 	- Hit "Generate"
diff --git a/cmake/Modules/FindGettextLib.cmake b/cmake/Modules/FindGettextLib.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..b99fd33b83f7e7e65a5ab3c479dd6abcc038547d
--- /dev/null
+++ b/cmake/Modules/FindGettextLib.cmake
@@ -0,0 +1,80 @@
+# Package finder for gettext libs and include files
+
+SET(CUSTOM_GETTEXT_PATH "${PROJECT_SOURCE_DIR}/../../gettext"
+	CACHE FILEPATH "path to custom gettext")
+
+# by default
+SET(GETTEXT_FOUND FALSE)
+
+FIND_PATH(GETTEXT_INCLUDE_DIR
+	NAMES libintl.h
+	PATHS "${CUSTOM_GETTEXT_PATH}/include"
+	DOC "gettext include directory")
+
+FIND_PROGRAM(GETTEXT_MSGFMT
+	NAMES msgfmt
+	PATHS "${CUSTOM_GETTEXT_PATH}/bin"
+	DOC "path to msgfmt")
+
+FIND_PROGRAM(GETTEXT_MSGMERGE
+	NAMES msgmerge
+	PATHS "${CUSTOM_GETTEXT_PATH}/bin"
+	DOC "path to msgmerge")
+
+FIND_PROGRAM(GETTEXT_MSGEN
+	NAMES msgen
+	PATHS "${CUSTOM_GETTEXT_PATH}/bin"
+	DOC "path to msgen")
+
+FIND_PROGRAM(GETTEXT_EXTRACT
+	NAMES xgettext
+	PATHS "${CUSTOM_GETTEXT_PATH}/bin"
+	DOC "path to xgettext")
+
+# modern Linux, as well as Mac, seem to not need require special linking
+# they do not because gettext is part of glibc
+# TODO check the requirements on other BSDs and older Linux
+IF (WIN32)
+	IF(MSVC)
+		SET(GETTEXT_LIB_NAMES
+			libintl.lib intl.lib libintl3.lib intl3.lib)
+	ELSE()
+		SET(GETTEXT_LIB_NAMES
+			libintl.dll.a intl.dll.a libintl3.dll.a intl3.dll.a)
+	ENDIF()
+	FIND_LIBRARY(GETTEXT_LIBRARY
+		NAMES ${GETTEXT_LIB_NAMES}
+		PATHS "${CUSTOM_GETTEXT_PATH}/lib"
+		DOC "gettext *intl*.lib")
+	FIND_FILE(GETTEXT_DLL
+		NAMES libintl.dll intl.dll libintl3.dll intl3.dll
+		PATHS "${CUSTOM_GETTEXT_PATH}/bin" "${CUSTOM_GETTEXT_PATH}/lib" 
+		DOC "gettext *intl*.dll")
+	FIND_FILE(GETTEXT_ICONV_DLL
+		NAMES libiconv2.dll
+		PATHS "${CUSTOM_GETTEXT_PATH}/bin" "${CUSTOM_GETTEXT_PATH}/lib"
+		DOC "gettext *iconv*.lib")
+ENDIF(WIN32)
+
+
+IF(GETTEXT_INCLUDE_DIR AND GETTEXT_MSGFMT)
+	IF (WIN32)
+		# in the Win32 case check also for the extra linking requirements
+		IF(GETTEXT_LIBRARY AND GETTEXT_DLL AND GETTEXT_ICONV_DLL)
+			SET(GETTEXT_FOUND TRUE)
+		ENDIF()
+	ELSE(WIN32)
+		SET(GETTEXT_FOUND TRUE)
+	ENDIF(WIN32)
+ENDIF()
+
+IF(GETTEXT_FOUND)
+	SET(GETTEXT_PO_PATH ${CMAKE_SOURCE_DIR}/po)
+	SET(GETTEXT_MO_BUILD_PATH ${CMAKE_BINARY_DIR}/locale/<locale>/LC_MESSAGES)
+	SET(GETTEXT_MO_DEST_PATH locale/<locale>/LC_MESSAGES)
+	FILE(GLOB GETTEXT_AVAILABLE_LOCALES RELATIVE ${GETTEXT_PO_PATH} "${GETTEXT_PO_PATH}/*")
+	MACRO(SET_MO_PATHS _buildvar _destvar _locale)
+		STRING(REPLACE "<locale>" ${_locale} ${_buildvar} ${GETTEXT_MO_BUILD_PATH})
+		STRING(REPLACE "<locale>" ${_locale} ${_destvar} ${GETTEXT_MO_DEST_PATH})
+	ENDMACRO(SET_MO_PATHS)
+ENDIF()
diff --git a/cmake/Modules/FindJthread.cmake b/cmake/Modules/FindJthread.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..302a3c22f83f4dab091707850ca8dadebb435208
--- /dev/null
+++ b/cmake/Modules/FindJthread.cmake
@@ -0,0 +1,18 @@
+# Look for jthread, use our own if not found
+
+FIND_PATH(JTHREAD_INCLUDE_DIR jthread.h)
+
+FIND_LIBRARY(JTHREAD_LIBRARY NAMES jthread)
+
+IF(JTHREAD_LIBRARY AND JTHREAD_INCLUDE_DIR)
+	SET( JTHREAD_FOUND TRUE )
+ENDIF(JTHREAD_LIBRARY AND JTHREAD_INCLUDE_DIR)
+
+IF(JTHREAD_FOUND)
+	MESSAGE(STATUS "Found system jthread header file in ${JTHREAD_INCLUDE_DIR}")
+	MESSAGE(STATUS "Found system jthread library ${JTHREAD_LIBRARY}")
+ELSE(JTHREAD_FOUND)
+	SET(JTHREAD_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/jthread)
+	SET(JTHREAD_LIBRARY jthread)
+	MESSAGE(STATUS "Using project jthread library")
+ENDIF(JTHREAD_FOUND)
diff --git a/cmake/Modules/FindSqlite3.cmake b/cmake/Modules/FindSqlite3.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..ecce6e38e2d70c1e565c731d45560c98f048ef88
--- /dev/null
+++ b/cmake/Modules/FindSqlite3.cmake
@@ -0,0 +1,18 @@
+# Look for sqlite3, use our own if not found
+
+FIND_PATH(SQLITE3_INCLUDE_DIR sqlite3.h)
+
+FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3)
+
+IF(SQLITE3_LIBRARY AND SQLITE3_INCLUDE_DIR)
+	SET( SQLITE3_FOUND TRUE )
+ENDIF(SQLITE3_LIBRARY AND SQLITE3_INCLUDE_DIR)
+
+IF(SQLITE3_FOUND)
+	MESSAGE(STATUS "Found system sqlite3 header file in ${SQLITE3_INCLUDE_DIR}")
+	MESSAGE(STATUS "Found system sqlite3 library ${SQLITE3_LIBRARY}")
+ELSE(SQLITE3_FOUND)
+	SET(SQLITE3_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/sqlite)
+	SET(SQLITE3_LIBRARY sqlite3)
+	MESSAGE(STATUS "Using project sqlite3 library")
+ENDIF(SQLITE3_FOUND)
diff --git a/data/book.png b/data/book.png
new file mode 100644
index 0000000000000000000000000000000000000000..176fb6aa96154614c7edb3a7ed80064d23a8fceb
Binary files /dev/null and b/data/book.png differ
diff --git a/data/bookshelf.png b/data/bookshelf.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ecc50ff3ad1d0f633f2fb18cd59801fa2391ed7
Binary files /dev/null and b/data/bookshelf.png differ
diff --git a/data/brick.png b/data/brick.png
new file mode 100644
index 0000000000000000000000000000000000000000..32d77f34787b079f9df729bc398b780be98e83b1
Binary files /dev/null and b/data/brick.png differ
diff --git a/data/cactus_side.png b/data/cactus_side.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc479fde6f9dc859180035ee3974c1ffb198136c
Binary files /dev/null and b/data/cactus_side.png differ
diff --git a/data/cactus_top.png b/data/cactus_top.png
new file mode 100644
index 0000000000000000000000000000000000000000..f9e68df5114b04f48290dc277b9d73f49db941cc
Binary files /dev/null and b/data/cactus_top.png differ
diff --git a/data/clay.png b/data/clay.png
new file mode 100644
index 0000000000000000000000000000000000000000..3557429d81f05837cb54ea3a964392bf28b78440
Binary files /dev/null and b/data/clay.png differ
diff --git a/data/clay_brick.png b/data/clay_brick.png
new file mode 100644
index 0000000000000000000000000000000000000000..e36648e48f68ab3c201132839093a18a7387a72f
Binary files /dev/null and b/data/clay_brick.png differ
diff --git a/data/fence.png b/data/fence.png
new file mode 100644
index 0000000000000000000000000000000000000000..0b99f0eb5dd6b27c9f478cc1eb8a510b59a97ea6
Binary files /dev/null and b/data/fence.png differ
diff --git a/data/firefly.png b/data/firefly.png
new file mode 100644
index 0000000000000000000000000000000000000000..40df7fa1cc62fd8daf90ba6ae24a93992551b9fd
Binary files /dev/null and b/data/firefly.png differ
diff --git a/data/lump_of_clay.png b/data/lump_of_clay.png
new file mode 100644
index 0000000000000000000000000000000000000000..be0bab9d741ce23d10d86a223e5e085d17736c9a
Binary files /dev/null and b/data/lump_of_clay.png differ
diff --git a/data/paper.png b/data/paper.png
new file mode 100644
index 0000000000000000000000000000000000000000..ae5c06bc6fea52d8d78c85592b97405682bf0e9b
Binary files /dev/null and b/data/paper.png differ
diff --git a/data/papyrus.png b/data/papyrus.png
new file mode 100644
index 0000000000000000000000000000000000000000..bf0dec7fe2f2048a4f521c8efce41fd775ef1339
Binary files /dev/null and b/data/papyrus.png differ
diff --git a/data/rail.png b/data/rail.png
new file mode 100644
index 0000000000000000000000000000000000000000..18176d9f3ca96ec9d6e4f6d41ab7a370aa93afeb
Binary files /dev/null and b/data/rail.png differ
diff --git a/data/rail_crossing.png b/data/rail_crossing.png
new file mode 100644
index 0000000000000000000000000000000000000000..98464057704a84ab9dfeac63f13e9b1af8d6f1ae
Binary files /dev/null and b/data/rail_crossing.png differ
diff --git a/data/rail_curved.png b/data/rail_curved.png
new file mode 100644
index 0000000000000000000000000000000000000000..62afa3d2b81c4dc6f85275ea6de89ffb43a1be8b
Binary files /dev/null and b/data/rail_curved.png differ
diff --git a/data/rail_t_junction.png b/data/rail_t_junction.png
new file mode 100644
index 0000000000000000000000000000000000000000..9985f63cd49ce29f636e72fe3b168fe1b6bad236
Binary files /dev/null and b/data/rail_t_junction.png differ
diff --git a/data/rat.png b/data/rat.png
index d1a0e2ae26ad41a751c99ea84e79f45e58c1970b..96d44c3fae171d9d1e59d8137315f0d61697abd5 100644
Binary files a/data/rat.png and b/data/rat.png differ
diff --git a/data/sandstone.png b/data/sandstone.png
new file mode 100644
index 0000000000000000000000000000000000000000..c4759b4d0289346f97ba69c44230d34b409c2a1d
Binary files /dev/null and b/data/sandstone.png differ
diff --git a/doc/README.txt b/doc/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..645e2a560b7047bd16bb485a529184eec419da32
--- /dev/null
+++ b/doc/README.txt
@@ -0,0 +1,238 @@
+Minetest-c55
+---------------
+An InfiniMiner/Minecraft inspired game.
+Copyright (c) 2010-2011 Perttu Ahola <celeron55@gmail.com>
+
+Further documentation:
+----------------------
+- Website: http://celeron.55.lt/~celeron55/minetest/
+- Wiki: http://celeron.55.lt/~celeron55/minetest/wiki/
+- Forum: http://celeron.55.lt/~celeron55/minetest/forum/
+
+This is a development version:
+------------------------------
+- Don't expect it to work as well as a finished game will.
+- Please report any bugs to me. That way I can fix them to the next release.
+	- debug.txt is useful when the game crashes.
+
+Controls:
+---------
+- See the in-game pause menu
+- Settable in the configuration file, see the section below.
+
+Map directory:
+--------------
+- Map is stored in a directory, which can be removed to generate a new map.
+- There is a command-line option for it: --map-dir
+- For a RUN_IN_PLACE build, it is located in:
+		../map
+- Otherwise something like this:
+	Windows: C:\Documents and Settings\user\Application Data\minetest\map
+	Linux: ~/.minetest/map
+	OS X: ~/Library/Application Support/minetest/map
+
+Configuration file:
+-------------------
+- An optional configuration file can be used. See minetest.conf.example.
+- Path to file can be passed as a parameter to the executable:
+	--config <path-to-file>
+- Defaults:
+	- If built with -DRUN_IN_PLACE=1:
+		../minetest.conf
+		../../minetest.conf
+	- Otherwise something like this:
+		Windows: C:\Documents and Settings\user\Application Data\minetest\minetest.conf
+		Linux: ~/.minetest/minetest.conf
+		OS X: ~/Library/Application Support/minetest.conf
+
+Command-line options:
+---------------------
+- Use --help
+
+Compiling on GNU/Linux:
+-----------------------
+
+Install dependencies. Here's an example for Debian/Ubuntu:
+$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev
+
+Download source, extract (this is the URL to the latest of source repository, which might not work at all times):
+$ wget https://bitbucket.org/celeron55/minetest/get/tip.tar.gz
+$ tar xf tip.tar.gz
+$ cd minetest
+
+Build a version that runs directly from the source directory:
+$ cmake . -DRUN_IN_PLACE=1
+$ make -j2
+
+Run it:
+$ cd bin
+$ ./minetest
+
+- Use cmake . -LH to see all CMake options and their current state
+- If you want to install it system-wide (or are making a distribution package), you will want to use -DRUN_IN_PLACE=0
+- You can build a bare server or a bare client by specifying -DBUILD_CLIENT=0 or -DBUILD_SERVER=0
+- You can select between Release and Debug build by -DCMAKE_BUILD_TYPE=<Debug or Release>
+  - Note that the Debug build is considerably slower
+
+Compiling on Windows:
+---------------------
+
+- You need:
+	* CMake:
+		http://www.cmake.org/cmake/resources/software.html
+	* MinGW or Visual Studio
+		http://www.mingw.org/
+		http://msdn.microsoft.com/en-us/vstudio/default
+	* Irrlicht SDK 1.7:
+		http://irrlicht.sourceforge.net/downloads.html
+	* Zlib headers (zlib125.zip)
+		http://www.winimage.com/zLibDll/index.html
+	* Zlib library (zlibwapi.lib and zlibwapi.dll from zlib125dll.zip):
+		http://www.winimage.com/zLibDll/index.html
+	* And, of course, Minetest-c55:
+		http://celeron.55.lt/~celeron55/minetest/download
+- Steps:
+	- Select a directory called DIR hereafter in which you will operate.
+	- Make sure you have CMake and a compiler installed.
+	- Download all the other stuff to DIR and extract them into there. All those
+	  packages contain a nice base directory in them, which should end up being
+	  the direct subdirectories of DIR.
+	- You will end up with a directory structure like this (+=dir, -=file):
+	-----------------
+	+ DIR
+		- zlib-1.2.5.tar.gz
+		- zlib125dll.zip
+		- irrlicht-1.7.1.zip
+		- 110214175330.zip (or whatever, this is the minetest source)
+		+ zlib-1.2.5
+			- zlib.h
+			+ win32
+			...
+		+ zlib125dll
+			- readme.txt
+			+ dll32
+			...
+		+ irrlicht-1.7.1
+			+ lib
+			+ include
+			...
+		+ minetest
+			+ src
+			+ doc
+			- CMakeLists.txt
+			...
+	-----------------
+	- Start up the CMake GUI
+	- Select "Browse Source..." and select DIR/minetest
+	- Now, if using MSVC:
+		- Select "Browse Build..." and select DIR/minetest-build
+	- Else if using MinGW:
+		- Select "Browse Build..." and select DIR/minetest
+	- Select "Configure"
+	- Select your compiler
+	- It will warn about missing stuff, ignore that at this point. (later don't)
+	- Make sure the configuration is as follows
+	  (note that the versions may differ for you):
+	-----------------
+	BUILD_CLIENT             [X]
+	BUILD_SERVER             [ ]
+	CMAKE_BUILD_TYPE         Release
+	CMAKE_INSTALL_PREFIX     DIR/minetest-install
+	IRRLICHT_SOURCE_DIR      DIR/irrlicht-1.7.1
+	RUN_IN_PLACE             [X]
+	WARN_ALL                 [ ]
+	ZLIB_DLL                 DIR/zlib125dll/dll32/zlibwapi.dll
+	ZLIB_INCLUDE_DIR         DIR/zlib-1.2.5
+	ZLIB_LIBRARIES           DIR/zlib125dll/dll32/zlibwapi.lib
+	-----------------
+	- Hit "Configure"
+	- Hit "Generate"
+	If using MSVC:
+		- Open the generated minetest.sln
+		- The project defaults to the "Debug" configuration. Make very sure to
+		  select "Release", unless you want to debug some stuff (it's slower)
+		- Build the ALL_BUILD project
+		- Build the INSTALL project
+		- You should now have a working game with the executable in
+			DIR/minetest-install/bin/minetest.exe
+		- Additionally you may create a zip package by building the PACKAGE
+		  project.
+	If using MinGW:
+		- Using the command line, browse to the build directory and run 'make'
+		  (or mingw32-make or whatever it happens to be)
+		- You should now have a working game with the executable in
+			DIR/minetest/bin/minetest.exe
+
+License of Minetest-c55
+-----------------------
+
+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.
+
+Irrlicht
+---------------
+
+This program uses the Irrlicht Engine. http://irrlicht.sourceforge.net/
+
+ The Irrlicht Engine License
+
+Copyright © 2002-2005 Nikolaus Gebhardt
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute
+it freely, subject to the following restrictions:
+
+   1. The origin of this software must not be misrepresented; you
+      must not claim that you wrote the original software. If you use
+	  this software in a product, an acknowledgment in the product
+	  documentation would be appreciated but is not required.
+   2. Altered source versions must be plainly marked as such, and must
+      not be misrepresented as being the original software.
+   3. This notice may not be removed or altered from any source
+      distribution.
+
+
+JThread
+---------------
+
+This program uses the JThread library. License for JThread follows:
+
+Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
+
diff --git a/genmap.py b/genmap.py
new file mode 100755
index 0000000000000000000000000000000000000000..a60509403d65c291ca000178286aca635812f02f
--- /dev/null
+++ b/genmap.py
@@ -0,0 +1,271 @@
+#!/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
+
+# 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
+#
+
+# Seed for generating terrain
+SEED = 0
+
+# 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 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
+
+# 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")
+
+	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))
+	
+	# 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 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 --git a/makepackage_binary.sh b/makepackage_binary.sh
new file mode 100755
index 0000000000000000000000000000000000000000..32d19865ab98e3585f5a52b379267a4209d68347
--- /dev/null
+++ b/makepackage_binary.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+PROJECT_NAME=minetest-delta
+PACKAGEDIR=../$PROJECT_NAME-packages
+PACKAGENAME=$PROJECT_NAME-binary-`date +%y%m%d%H%M%S`
+PACKAGEPATH=$PACKAGEDIR/$PACKAGENAME
+
+mkdir -p $PACKAGEPATH
+mkdir -p $PACKAGEPATH/bin
+mkdir -p $PACKAGEPATH/data
+mkdir -p $PACKAGEPATH/doc
+mkdir -p $PACKAGEPATH/locale/de/LC_MESSAGES/
+
+cp minetest.conf.example $PACKAGEPATH/
+
+cp bin/$PROJECT_NAME.exe $PACKAGEPATH/bin/
+cp bin/Irrlicht.dll $PACKAGEPATH/bin/
+cp bin/zlibwapi.dll $PACKAGEPATH/bin/
+#cp bin/test $PACKAGEPATH/bin/
+#cp bin/fasttest $PACKAGEPATH/bin/
+#cp bin/server $PACKAGEPATH/bin/
+#cp ../irrlicht/irrlicht-1.7.1/lib/Linux/libIrrlicht.a $PACKAGEPATH/bin/
+#cp ../jthread/jthread-1.2.1/src/.libs/libjthread-1.2.1.so $PACKAGEPATH/bin/
+
+cp -r data/fontlucida.png $PACKAGEPATH/data/
+cp -r data/player.png $PACKAGEPATH/data/
+cp -r data/player_back.png $PACKAGEPATH/data/
+cp -r data/stone.png $PACKAGEPATH/data/
+cp -r data/grass.png $PACKAGEPATH/data/
+cp -r data/grass_footsteps.png $PACKAGEPATH/data/
+cp -r data/water.png $PACKAGEPATH/data/
+cp -r data/tree.png $PACKAGEPATH/data/
+cp -r data/leaves.png $PACKAGEPATH/data/
+cp -r data/mese.png $PACKAGEPATH/data/
+cp -r data/cloud.png $PACKAGEPATH/data/
+cp -r data/sign.png $PACKAGEPATH/data/
+cp -r data/sign_back.png $PACKAGEPATH/data/
+cp -r data/rat.png $PACKAGEPATH/data/
+cp -r data/mud.png $PACKAGEPATH/data/
+cp -r data/torch.png $PACKAGEPATH/data/
+cp -r data/torch_on_floor.png $PACKAGEPATH/data/
+cp -r data/torch_on_ceiling.png $PACKAGEPATH/data/
+cp -r data/tree_top.png $PACKAGEPATH/data/
+cp -r data/coalstone.png $PACKAGEPATH/data/
+cp -r data/crack.png $PACKAGEPATH/data/
+cp -r data/wood.png $PACKAGEPATH/data/
+cp -r data/stick.png $PACKAGEPATH/data/
+cp -r data/tool_wpick.png $PACKAGEPATH/data/
+cp -r data/tool_stpick.png $PACKAGEPATH/data/
+cp -r data/tool_mesepick.png $PACKAGEPATH/data/
+cp -r data/grass_side.png $PACKAGEPATH/data/
+cp -r data/lump_of_coal.png $PACKAGEPATH/data/
+cp -r data/lump_of_iron.png $PACKAGEPATH/data/
+cp -r data/mineral_coal.png $PACKAGEPATH/data/
+cp -r data/mineral_iron.png $PACKAGEPATH/data/
+cp -r data/sand.png $PACKAGEPATH/data/
+
+#cp -r data/pauseMenu.gui $PACKAGEPATH/data/
+
+cp -r doc/README.txt $PACKAGEPATH/doc/README.txt
+
+cp -r locale/de/LC_MESSAGES/$PROJECT_NAME.mo $PACKAGEPATH/locale/de/LC_MESSAGES/
+
+cd $PACKAGEDIR
+rm $PACKAGENAME.zip
+zip -r $PACKAGENAME.zip $PACKAGENAME
+
diff --git a/minetest-icon.svg b/minetest-icon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..46c9ac702d664841309949de7ab6727214ed9d0a
--- /dev/null
+++ b/minetest-icon.svg
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48px"
+   height="48px"
+   id="svg2856"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="minetest-icon.svg"
+   inkscape:export-filename="/home/erlehmann/pics/icons/minetest/minetest-icon-24x24.png"
+   inkscape:export-xdpi="45"
+   inkscape:export-ydpi="45">
+  <defs
+     id="defs2858">
+    <filter
+       inkscape:collect="always"
+       id="filter3864">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.20490381"
+         id="feGaussianBlur3866" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="12.083333"
+     inkscape:cx="24"
+     inkscape:cy="24"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="1233"
+     inkscape:window-height="755"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2866"
+       empspacing="2"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       spacingx="0.5px"
+       spacingy="10px"
+       color="#ff0000"
+       opacity="0.1254902"
+       empcolor="#ff0000"
+       empopacity="0.25098039"
+       dotted="false" />
+    <inkscape:grid
+       type="axonomgrid"
+       id="grid2870"
+       units="px"
+       empspacing="1"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       spacingy="1px"
+       originx="0px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata2861">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       style="fill:#e9b96e;fill-opacity:1;stroke:#573a0d;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       d="M 6.1513775e-7,16 3.2110204e-7,28 21.035899,40.145082 l 21,-12.414519 0,-11.461126 L 20.78461,4 6.1513775e-7,16 z"
+       id="path3047"
+       transform="translate(3.4641013,6)"
+       sodipodi:nodetypes="ccccccc" />
+    <path
+       style="fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       d="m 8.5,30.907477 -2,-1.1547 0,6 L 17.320508,42 l 0,-2 -1.732051,-1 0,-2 L 13.5,35.794229 l 0,-4 -5,-2.886752 0,2 z"
+       id="path3831"
+       sodipodi:nodetypes="ccccccccccc" />
+    <path
+       style="opacity:1;fill:#555753;fill-opacity:1;stroke:#2e3436;stroke-linejoin:miter"
+       d="m 6.9282032,36 3.4641018,-2 3.464101,2 1.643594,0.948929 0,2 2,1.154701 0,2 L 6.9282032,36 z"
+       id="path3870"
+       sodipodi:nodetypes="cccccccc" />
+    <path
+       style="fill:#fce94f;fill-opacity:1;stroke:#625802;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       d="M 25.980762,19 31.5,22.186533 l 0,2 L 38.09375,28 41.5625,26 45.5,23.730563 l 0,2.538874 0,-4 L 32.908965,15 25.980762,19 z"
+       id="path3851"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#e9b96e;fill-opacity:1;stroke:#573a0d;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0.50000000000000000"
+       d="m 24.839746,18.341234 8.660254,-5 0,2 -8.660254,5 0,-2 z"
+       id="path5684"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#73d216;fill-opacity:1;stroke:#325b09;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       d="M 25.980762,5 3.4641016,18 17.5,26.10363 31.5,18.186533 24.839746,14.341234 33.5,9.341234 25.980762,5 z"
+       id="path3821"
+       sodipodi:nodetypes="ccccccc"
+       transform="translate(0,4)" />
+    <path
+       style="fill:#729fcf;fill-opacity:1;stroke:#19314b;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       d="m 17.5,28.10363 0,2 1.552559,0.89637 0,2 5.447441,3.145082 12,-7.071797 0,-2.14657 2,-1.1547 0,-1.54403 -7,-4.041452 -14,7.917097 z"
+       id="path3825"
+       sodipodi:nodetypes="ccccccccccc"
+       transform="translate(0,4)" />
+    <g
+       id="g5691"
+       style="stroke-linejoin:miter">
+      <path
+         sodipodi:nodetypes="ccccc"
+         id="path3862"
+         d="m 13.856406,20 6.928204,4 -6.928204,4 -6.9282028,-4 6.9282028,-4 z"
+         style="fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3864);opacity:0.25000000000000000" />
+      <g
+         id="g3858"
+         style="stroke-linejoin:miter">
+        <path
+           style="fill:#c17d11;fill-opacity:1;stroke:#8f5902;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+           d="m 15.588457,21 1.732051,1 1.732051,-1 0,-6 -1.732051,-1 -1.732051,1 0,6 z"
+           id="path3833"
+           sodipodi:nodetypes="ccccccc"
+           transform="translate(-3.4641015,2)" />
+        <path
+           style="fill:#4e9a06;fill-opacity:1;stroke:#316004;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+           d="M 9.9641015,13.752777 17.320508,18 l 6.643593,-3.835681 0,-8.3286385 L 17.320508,2 9.9641015,6.2472233 l 0,7.5055537 z"
+           id="path3837"
+           transform="translate(-3.4641015,2)"
+           sodipodi:nodetypes="ccccccc" />
+      </g>
+    </g>
+    <g
+       id="g5686"
+       transform="translate(-4.2591582e-7,2)"
+       style="stroke-linejoin:miter">
+      <path
+         transform="translate(24.248712,-2)"
+         style="opacity:0.25000000000000000;fill:#2e3436;fill-opacity:1;stroke:none;filter:url(#filter3864);stroke-linejoin:miter"
+         d="m 13.856406,20 5.196153,3 -5.196153,3 -5.196152,-3 5.196152,-3 z"
+         id="path3868"
+         sodipodi:nodetypes="ccccc" />
+      <path
+         style="fill:#4e9a06;fill-opacity:1;stroke:#316004;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         d="M 15.71539,21.073285 17.320508,22 l 1.394882,-0.805336 0,-8.389328 L 17.320508,12 l -1.605118,1.073285 0,8 z"
+         id="path3853"
+         sodipodi:nodetypes="ccccccc"
+         transform="translate(20.78461,0)" />
+    </g>
+    <path
+       style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:0.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:0.50000000000000000, 0.50000000000000000;stroke-dashoffset:0.25000000000000000"
+       d="M 12.124356,33 11.25833,32.5"
+       id="path3872"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:#888a85;stroke:#2e3436;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0.50000000000000000"
+       d="m 45.5,26.730563 -4,2.309401 0,1 -2,1.1547 0,2 -2,1.154701 0,4 8,-4.618802 0,-7 z"
+       id="path3874"
+       sodipodi:nodetypes="ccccccccc" />
+  </g>
+</svg>
diff --git a/minetestmapper/colors.txt b/minetestmapper/colors.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e70f56e1e8d594dd4c3761fc672e056f346ba106
--- /dev/null
+++ b/minetestmapper/colors.txt
@@ -0,0 +1,25 @@
+0 128 128 128
+1 107 134 51
+2 39 66 106
+3 255 255 0
+4 86 58 31
+5 48 95 8
+6 102 129 38
+7 178 178 0
+8 101 84 36
+9 39 66 106
+12 104 78 42
+13 210 194 156
+14 117 86 41
+15 128 79 0
+16 118 118 118
+18 123 123 123
+19 199 199 199
+20 183 183 222
+21 103 78 42
+22 219 202 178
+23 78 154 6
+24 204 0 0
+25 211 215 207
+26 138 226 52
+27 104 78 42
diff --git a/minetestmapper/minetestmapper2.py b/minetestmapper/minetestmapper2.py
new file mode 100755
index 0000000000000000000000000000000000000000..8dc3de2f4bbf6a0c4d9839650760281ae65bca02
--- /dev/null
+++ b/minetestmapper/minetestmapper2.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Made by j0gge, modified by celeron55
+
+# This program is free software. It comes without any warranty, to
+# the extent permitted by applicable law. You can redistribute it
+# and/or modify it under the terms of the Do What The Fuck You Want
+# To Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/wtfpl/COPYING for more details.
+
+# Requires Python Imaging Library: http://www.pythonware.com/products/pil/
+
+# Some speed-up: ...lol, actually it slows it down.
+#import psyco ; psyco.full()
+#from psyco.classes import *
+
+import zlib
+import Image, ImageDraw
+import os
+import string
+import time
+
+def hex_to_int(h):
+    i = int(h,16)
+    if(i > 2047):
+        i-=4096
+    return i
+
+def hex4_to_int(h):
+    i = int(h,16)
+    if(i > 32767):
+        i-=65536
+    return i
+
+def int_to_hex3(i):
+    if(i < 0):
+        return "%03X" % (i + 4096)
+    else:
+        return "%03X" % i
+
+def int_to_hex4(i):
+    if(i < 0):
+        return "%04X" % (i + 65536)
+    else:
+        return "%04X" % i
+
+def limit(i,l,h):
+    if(i>h):
+        i=h
+    if(i<l):
+        i=l
+    return i
+
+# Fix these!
+path="../map/"
+output="map.png"
+
+sector_xmin = -1000/16
+sector_xmax = 1000/16
+sector_zmin = -1000/16
+sector_zmax = 1000/16
+
+# Load color information for the blocks.
+colors = {}
+f = file("colors.txt")
+for line in f:
+    values = string.split(line)
+    colors[int(values[0])] = (int(values[1]), int(values[2]), int(values[3]))
+f.close()
+
+xlist = []
+zlist = []
+
+# List all sectors to memory and calculate the width and heigth of the resulting picture.
+if os.path.exists(path + "sectors2"):
+    for filename in os.listdir(path + "sectors2"):
+        for filename2 in os.listdir(path + "sectors2/" + filename):
+            x = hex_to_int(filename)
+            z = hex_to_int(filename2)
+            if x < sector_xmin or x > sector_xmax:
+                continue
+            if z < sector_zmin or z > sector_zmax:
+                continue
+            xlist.append(x)
+            zlist.append(z)
+
+if os.path.exists(path + "sectors"):
+    for filename in os.listdir(path + "sectors"):
+        x = hex4_to_int(filename[:4])
+        z = hex4_to_int(filename[-4:])
+        if x < sector_xmin or x > sector_xmax:
+            continue
+        if z < sector_zmin or z > sector_zmax:
+            continue
+        xlist.append(x)
+        zlist.append(z)
+
+w = (max(xlist) - min(xlist)) * 16 + 16
+h = (max(zlist) - min(zlist)) * 16 + 16
+
+print "w="+str(w)+" h="+str(h)
+
+im = Image.new("RGB", (w, h), "white")
+impix = im.load()
+
+stuff={}
+
+starttime = time.time()
+
+# Go through all sectors.
+for n in range(len(xlist)):
+    #if n > 500:
+    #   break
+    if n % 200 == 0:
+        nowtime = time.time()
+        dtime = nowtime - starttime
+        n_per_second = 1.0 * n / dtime
+        if n_per_second != 0:
+            seconds_per_n = 1.0 / n_per_second
+            time_guess = seconds_per_n * len(xlist)
+            remaining_s = time_guess - dtime
+            remaining_minutes = int(remaining_s / 60)
+            remaining_s -= remaining_minutes * 60;
+            print("Processing sector "+str(n)+" of "+str(len(xlist))
+                    +" ("+str(round(100.0*n/len(xlist), 1))+"%)"
+                    +" (ETA: "+str(remaining_minutes)+"m "
+                    +str(int(remaining_s))+"s)")
+
+    xpos = xlist[n]
+    zpos = zlist[n]
+
+    xhex = int_to_hex3(xpos)
+    zhex = int_to_hex3(zpos)
+    xhex4 = int_to_hex4(xpos)
+    zhex4 = int_to_hex4(zpos)
+
+    sector1 = xhex4.lower() + zhex4.lower()
+    sector2 = xhex.lower() + "/" + zhex.lower()
+
+    ylist=[]
+
+    sectortype = ""
+
+    try:
+        for filename in os.listdir(path + "sectors/" + sector1):
+            if(filename != "meta"):
+                pos = int(filename,16)
+                if(pos > 32767):
+                    pos-=65536
+                ylist.append(pos)
+                sectortype = "old"
+    except OSError:
+        pass
+
+    if sectortype != "old":
+        try:
+            for filename in os.listdir(path + "sectors2/" + sector2):
+                if(filename != "meta"):
+                    pos = int(filename,16)
+                    if(pos > 32767):
+                        pos-=65536
+                    ylist.append(pos)
+                    sectortype = "new"
+        except OSError:
+            pass
+
+    if sectortype == "":
+        continue
+
+    ylist.sort()
+
+    # Make a list of pixels of the sector that are to be looked for.
+    pixellist = []
+    for x in range(16):
+        for y in range(16):
+            pixellist.append((x,y))
+
+    # Go through the Y axis from top to bottom.
+    for ypos in reversed(ylist):
+
+        yhex = int_to_hex4(ypos)
+
+        filename = ""
+        if sectortype == "old":
+            filename = path + "sectors/" + sector1 + "/" + yhex.lower()
+        else:
+            filename = path + "sectors2/" + sector2 + "/" + yhex.lower()
+
+        f = file(filename, "rb")
+
+        # Let's just memorize these even though it's not really necessary.
+        version = f.read(1)
+        flags = f.read(1)
+
+        dec_o = zlib.decompressobj()
+        try:
+            mapdata = dec_o.decompress(f.read())
+        except:
+            mapdata = []
+
+        f.close()
+
+        if(len(mapdata)<4096):
+            print "bad: " + xhex+zhex+"/"+yhex + " " + len(mapdata)
+        else:
+            chunkxpos=xpos*16
+            chunkypos=ypos*16
+            chunkzpos=zpos*16
+            for (x,z) in reversed(pixellist):
+                for y in reversed(range(16)):
+                    datapos=x+y*16+z*256
+                    if(ord(mapdata[datapos])!=254):
+                        try:
+                            pixellist.remove((x,z))
+                            # Memorize information on the type and height of the block and for drawing the picture.
+                            stuff[(chunkxpos+x,chunkzpos+z)]=(chunkypos+y,ord(mapdata[datapos]))
+                            break
+                        except:
+                            print "strange block: " + xhex+zhex+"/"+yhex + " x: " + str(x) + " y: " + str(y) + " z: " + str(z) + " block: " + str(ord(mapdata[datapos]))
+
+        # After finding all the pixeld in the sector, we can move on to the next sector without having to continue the Y axis.
+        if(len(pixellist)==0):
+            break
+
+print "Drawing image"
+# Drawing the picture
+starttime = time.time()
+n = 0
+minx = min(xlist)
+minz = min(zlist)
+for (x,z) in stuff.iterkeys():
+    if n % 500000 == 0:
+        nowtime = time.time()
+        dtime = nowtime - starttime
+        n_per_second = 1.0 * n / dtime
+        if n_per_second != 0:
+            listlen = len(stuff)
+            seconds_per_n = 1.0 / n_per_second
+            time_guess = seconds_per_n * listlen
+            remaining_s = time_guess - dtime
+            remaining_minutes = int(remaining_s / 60)
+            remaining_s -= remaining_minutes * 60;
+            print("Drawing pixel "+str(n)+" of "+str(listlen)
+                    +" ("+str(round(100.0*n/listlen, 1))+"%)"
+                    +" (ETA: "+str(remaining_minutes)+"m "
+                    +str(int(remaining_s))+"s)")
+    n += 1
+
+    (r,g,b)=colors[stuff[(x,z)][1]]
+
+    # Comparing heights of a couple of adjacent blocks and changing brightness accordingly.
+    try:
+        y1=stuff[(x-1,z)][0]
+        y2=stuff[(x,z-1)][0]
+        y=stuff[(x,z)][0]
+
+        d=(y-y1+y-y2)*12
+
+        if(d>36):
+            d=36
+
+        r=limit(r+d,0,255)
+        g=limit(g+d,0,255)
+        b=limit(b+d,0,255)
+    except:
+        pass
+    #impix[w-1-(x-minx*16),h-1-(z-minz*16)]=(r,g,b)
+    impix[x-minx*16,h-1-(z-minz*16)]=(r,g,b)
+
+# Flip the picture to make it right and save.
+#print "Transposing"
+#im=im.transpose(Image.FLIP_TOP_BOTTOM)
+print "Saving"
+im.save(output)
diff --git a/pnoise.py b/pnoise.py
new file mode 100644
index 0000000000000000000000000000000000000000..fcab5ac15cb28f38fbb89f9b7ad8a347401bf4ba
--- /dev/null
+++ b/pnoise.py
@@ -0,0 +1,102 @@
+#
+# A python perlin noise implementation, from
+# http://www.fundza.com/c4serious/noise/perlin/perlin.html
+#
+# This is used for testing how to create maps with a python script.
+#
+
+import math
+p = (
+151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,
+30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,
+62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,
+125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,
+83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,
+143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,
+196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
+250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,
+58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,
+221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,
+224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,
+12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,
+199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,
+205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
+151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,
+30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,
+62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,
+125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,
+83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,
+143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,
+196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
+250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,
+58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,
+221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,
+224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,
+12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,
+199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,
+205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180)
+  
+def lerp(t, a, b):
+    return a + t * (b - a)
+  
+def fade(t):
+    return t * t * t * (t * (t * 6 - 15) + 10)
+  
+def grad(hash, x, y, z):
+    h = hash & 15
+    if h < 8:
+        u = x
+    else:
+        u = y
+    if h < 4:
+        v = y
+    elif h == 12 or h == 14:
+        v = x
+    else:
+        v = z
+    if h & 1 != 0:
+        u = -u
+    if h & 2 != 0:
+        v = -v
+    return u + v
+  
+def pnoise(x, y, z):
+    global p
+    X = int(math.floor(x)) & 255
+    Y = int(math.floor(y)) & 255
+    Z = int(math.floor(z)) & 255
+    x -= math.floor(x)
+    y -= math.floor(y)
+    z -= math.floor(z)
+    
+    u = fade(x)
+    v = fade(y)
+    w = fade(z)
+    
+    A =  p[X] + Y
+    AA = p[A] + Z
+    AB = p[A + 1] + Z
+    B =  p[X + 1] + Y
+    BA = p[B] + Z
+    BB = p[B + 1] + Z
+    
+    pAA = p[AA]
+    pAB = p[AB]
+    pBA = p[BA]
+    pBB = p[BB]
+    pAA1 = p[AA + 1]
+    pBA1 = p[BA + 1]
+    pAB1 = p[AB + 1]
+    pBB1 = p[BB + 1]
+    
+    gradAA =  grad(pAA, x,   y,   z)
+    gradBA =  grad(pBA, x-1, y,   z)
+    gradAB =  grad(pAB, x,   y-1, z)
+    gradBB =  grad(pBB, x-1, y-1, z)
+    gradAA1 = grad(pAA1,x,   y,   z-1)
+    gradBA1 = grad(pBA1,x-1, y,   z-1)
+    gradAB1 = grad(pAB1,x,   y-1, z-1)
+    gradBB1 = grad(pBB1,x-1, y-1, z-1)
+    return lerp(w, 
+    lerp(v, lerp(u, gradAA, gradBA), lerp(u, gradAB, gradBB)),
+    lerp(v, lerp(u, gradAA1,gradBA1),lerp(u, gradAB1,gradBB1)))    
diff --git a/po/de/minetest.po b/po/de/minetest.po
new file mode 100644
index 0000000000000000000000000000000000000000..69b57fd95b150849ff032343f9b38ea85b35b5aa
--- /dev/null
+++ b/po/de/minetest.po
@@ -0,0 +1,123 @@
+# German translations for minetest-c55 package.
+# Copyright (C) 2011 celeron
+# This file is distributed under the same license as the minetest-c55 package.
+# Constantin Wenger <constantin.wenger@googlemail.com>, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: 0.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-07-22 11:00+0200\n"
+"PO-Revision-Date: 2011-07-20 16:58+0100\n"
+"Last-Translator: Constantin Wenger <constantin.wenger@googlemail.com>\n"
+"Language-Team: Deutsch <>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+#: src/guiMainMenu.cpp:180
+msgid "Name/Password"
+msgstr "Name/Passwort"
+
+#: src/guiMainMenu.cpp:203
+msgid "Address/Port"
+msgstr "Adresse / Port"
+
+#: src/guiMainMenu.cpp:223
+msgid "Leave address blank to start a local server."
+msgstr "Lasse die Adresse frei um einen eigenen Server zu starten"
+
+#: src/guiMainMenu.cpp:230
+msgid "Fancy trees"
+msgstr "Schöne Bäume"
+
+#: src/guiMainMenu.cpp:236
+msgid "Smooth Lighting"
+msgstr "Besseres Licht"
+
+#: src/guiMainMenu.cpp:244
+msgid "Start Game / Connect"
+msgstr "Spiel starten / Verbinden"
+
+#: src/guiMainMenu.cpp:253
+msgid "Change keys"
+msgstr "Tastenbelegung ändern"
+
+#: src/guiMainMenu.cpp:276
+msgid "Creative Mode"
+msgstr "Kreativitätsmodus"
+
+#: src/guiMainMenu.cpp:282
+msgid "Enable Damage"
+msgstr "Schaden einschalten"
+
+#: src/guiMainMenu.cpp:290
+msgid "Delete map"
+msgstr "Karte löschen"
+
+#: src/guiMessageMenu.cpp:93 src/guiTextInputMenu.cpp:111
+msgid "Proceed"
+msgstr "Fortsetzen"
+
+#: src/guiPasswordChange.cpp:102
+msgid "Old Password"
+msgstr "Altes Passwort"
+
+#: src/guiPasswordChange.cpp:117
+msgid "New Password"
+msgstr "Neues Passwort"
+
+#: src/guiPasswordChange.cpp:131
+msgid "Confirm Password"
+msgstr "Passwort wiederholen"
+
+#: src/guiPasswordChange.cpp:146
+msgid "Change"
+msgstr "Ändern"
+
+#: src/guiPasswordChange.cpp:155
+msgid "Passwords do not match!"
+msgstr "Passwörter passen nicht zusammen"
+
+#: src/guiPauseMenu.cpp:110
+msgid "Continue"
+msgstr "Weiter"
+
+#: src/guiPauseMenu.cpp:117
+msgid "Change Password"
+msgstr "Passwort ändern"
+
+#: src/guiPauseMenu.cpp:124
+msgid "Disconnect"
+msgstr "Verbindung trennen"
+
+#: src/guiPauseMenu.cpp:131
+msgid "Exit to OS"
+msgstr "Programm beenden"
+
+#: src/guiPauseMenu.cpp:138
+msgid ""
+"Keys:\n"
+"- WASD: Walk\n"
+"- Mouse left: dig blocks\n"
+"- Mouse right: place blocks\n"
+"- Mouse wheel: select item\n"
+"- 0...9: select item\n"
+"- Shift: sneak\n"
+"- R: Toggle viewing all loaded chunks\n"
+"- I: Inventory menu\n"
+"- ESC: This menu\n"
+"- T: Chat\n"
+msgstr ""
+"Tastenkürzel:\n"
+"- WASD: Gehen\n"
+"- linke Maustaste: dig blocks\n"
+"- rechte Maustaste: place blocks\n"
+"- Mausrad: Item auswählen\n"
+"- 0...9: Item auswählen\n"
+"- Shift: ducken\n"
+"- R: Alle geladenen Kartenteile anzeigen, umschalten\n"
+"- I: Inventarmenü\n"
+"- T: Chat\n"
diff --git a/po/en/minetest.pot b/po/en/minetest.pot
new file mode 100644
index 0000000000000000000000000000000000000000..e242c494b5f0569e4665866878ee750a0c89ead1
--- /dev/null
+++ b/po/en/minetest.pot
@@ -0,0 +1,113 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: minetest\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-07-22 11:02+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/guiMainMenu.cpp:180
+msgid "Name/Password"
+msgstr ""
+
+#: src/guiMainMenu.cpp:203
+msgid "Address/Port"
+msgstr ""
+
+#: src/guiMainMenu.cpp:223
+msgid "Leave address blank to start a local server."
+msgstr ""
+
+#: src/guiMainMenu.cpp:230
+msgid "Fancy trees"
+msgstr ""
+
+#: src/guiMainMenu.cpp:236
+msgid "Smooth Lighting"
+msgstr ""
+
+#: src/guiMainMenu.cpp:244
+msgid "Start Game / Connect"
+msgstr ""
+
+#: src/guiMainMenu.cpp:253
+msgid "Change keys"
+msgstr ""
+
+#: src/guiMainMenu.cpp:276
+msgid "Creative Mode"
+msgstr ""
+
+#: src/guiMainMenu.cpp:282
+msgid "Enable Damage"
+msgstr ""
+
+#: src/guiMainMenu.cpp:290
+msgid "Delete map"
+msgstr ""
+
+#: src/guiMessageMenu.cpp:93 src/guiTextInputMenu.cpp:111
+msgid "Proceed"
+msgstr ""
+
+#: src/guiPasswordChange.cpp:102
+msgid "Old Password"
+msgstr ""
+
+#: src/guiPasswordChange.cpp:117
+msgid "New Password"
+msgstr ""
+
+#: src/guiPasswordChange.cpp:131
+msgid "Confirm Password"
+msgstr ""
+
+#: src/guiPasswordChange.cpp:146
+msgid "Change"
+msgstr ""
+
+#: src/guiPasswordChange.cpp:155
+msgid "Passwords do not match!"
+msgstr ""
+
+#: src/guiPauseMenu.cpp:110
+msgid "Continue"
+msgstr ""
+
+#: src/guiPauseMenu.cpp:117
+msgid "Change Password"
+msgstr ""
+
+#: src/guiPauseMenu.cpp:124
+msgid "Disconnect"
+msgstr ""
+
+#: src/guiPauseMenu.cpp:131
+msgid "Exit to OS"
+msgstr ""
+
+#: src/guiPauseMenu.cpp:138
+msgid ""
+"Keys:\n"
+"- WASD: Walk\n"
+"- Mouse left: dig blocks\n"
+"- Mouse right: place blocks\n"
+"- Mouse wheel: select item\n"
+"- 0...9: select item\n"
+"- Shift: sneak\n"
+"- R: Toggle viewing all loaded chunks\n"
+"- I: Inventory menu\n"
+"- ESC: This menu\n"
+"- T: Chat\n"
+msgstr ""
diff --git a/po/fr/minetest.po b/po/fr/minetest.po
new file mode 100644
index 0000000000000000000000000000000000000000..b060c424c562c9dc80177e63d337e8052ed566be
--- /dev/null
+++ b/po/fr/minetest.po
@@ -0,0 +1,123 @@
+# French translations for minetest-c55 package.
+# Copyright (C) 2011 celeron
+# This file is distributed under the same license as the minetest-c55 package.
+# Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>, 2011
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: 0.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-07-22 11:00+0200\n"
+"PO-Revision-Date: 2011-07-21 15:48+0200\n"
+"Last-Translator: Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>\n"
+"Language-Team: Français <>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+#: src/guiMainMenu.cpp:180
+msgid "Name/Password"
+msgstr "Nom / MdP"
+
+#: src/guiMainMenu.cpp:203
+msgid "Address/Port"
+msgstr "Adresse / Port"
+
+#: src/guiMainMenu.cpp:223
+msgid "Leave address blank to start a local server."
+msgstr "Laisser l'adresse vide pour lancer un serveur local."
+
+#: src/guiMainMenu.cpp:230
+msgid "Fancy trees"
+msgstr "Arbres spéciaux"
+
+#: src/guiMainMenu.cpp:236
+msgid "Smooth Lighting"
+msgstr "Lumière douce"
+
+#: src/guiMainMenu.cpp:244
+msgid "Start Game / Connect"
+msgstr "Démarrer / Connecter"
+
+#: src/guiMainMenu.cpp:253
+msgid "Change keys"
+msgstr "Changer touches"
+
+#: src/guiMainMenu.cpp:276
+msgid "Creative Mode"
+msgstr "Mode créatif"
+
+#: src/guiMainMenu.cpp:282
+msgid "Enable Damage"
+msgstr "Activer blessures"
+
+#: src/guiMainMenu.cpp:290
+msgid "Delete map"
+msgstr "Supprimer carte"
+
+#: src/guiMessageMenu.cpp:93 src/guiTextInputMenu.cpp:111
+msgid "Proceed"
+msgstr "OK"
+
+#: src/guiPasswordChange.cpp:102
+msgid "Old Password"
+msgstr "Ancien mot de passe"
+
+#: src/guiPasswordChange.cpp:117
+msgid "New Password"
+msgstr "Nouveau mot de passe"
+
+#: src/guiPasswordChange.cpp:131
+msgid "Confirm Password"
+msgstr "Confirmer mot de passe"
+
+#: src/guiPasswordChange.cpp:146
+msgid "Change"
+msgstr "Changer"
+
+#: src/guiPasswordChange.cpp:155
+msgid "Passwords do not match!"
+msgstr "Mauvaise correspondance!"
+
+#: src/guiPauseMenu.cpp:110
+msgid "Continue"
+msgstr "Continuer"
+
+#: src/guiPauseMenu.cpp:117
+msgid "Change Password"
+msgstr "Changer mot de passe"
+
+#: src/guiPauseMenu.cpp:124
+msgid "Disconnect"
+msgstr "Déconnection"
+
+#: src/guiPauseMenu.cpp:131
+msgid "Exit to OS"
+msgstr "Quitter le jeu"
+
+#: src/guiPauseMenu.cpp:138
+msgid ""
+"Keys:\n"
+"- WASD: Walk\n"
+"- Mouse left: dig blocks\n"
+"- Mouse right: place blocks\n"
+"- Mouse wheel: select item\n"
+"- 0...9: select item\n"
+"- Shift: sneak\n"
+"- R: Toggle viewing all loaded chunks\n"
+"- I: Inventory menu\n"
+"- ESC: This menu\n"
+"- T: Chat\n"
+msgstr ""
+"Touches:\n"
+"- WASD: Marcher\n"
+"- Clic gauche: Creuser bloc\n"
+"- Clic droite: Insérer bloc\n"
+"- Roulette: Sélection élément\n"
+"- 0...9: Sélection élément\n"
+"- Shift: S'accroupir\n"
+"- R: Active la vue de tous les blocs\n"
+"- I: Inventaire\n"
+"- T: Chat\n"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 84726945691261aaacbcba5f5a772cb8af8ef42a..c93762cabb2f6dcadf18605cbabc4beaec4e6736 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,10 +1,32 @@
-project(minetest)
+project(minetest-delta)
 cmake_minimum_required( VERSION 2.6 )
 
 if(RUN_IN_PLACE)
 	add_definitions ( -DRUN_IN_PLACE )
 endif(RUN_IN_PLACE)
 
+OPTION(USE_GETTEXT "Use GetText for internationalization" OFF)
+
+if(USE_GETTEXT)
+	find_package(GettextLib REQUIRED)
+	message(STATUS "gettext include path: ${GETTEXT_INCLUDE_DIR}")
+	message(STATUS "gettext msgfmt path: ${GETTEXT_MSGFMT}")
+	if(WIN32)
+		message(STATUS "gettext library: ${GETTEXT_LIBRARY}")
+		message(STATUS "gettext dll: ${GETTEXT_DLL}")
+		message(STATUS "gettext iconv dll: ${GETTEXT_ICONV_DLL}")
+	endif()
+	if (GETTEXT_FOUND)
+		add_definitions( -DUSE_GETTEXT )
+		message(STATUS "GetText enabled; locales found: ${GETTEXT_AVAILABLE_LOCALES}")
+	else()
+		message(STATUS "ERROR: GetText enabled but not found, disabling")
+		set(USE_GETTEXT FALSE)
+	endif(GETTEXT_FOUND)
+else(USE_GETTEXT)
+	message(STATUS "GetText disabled")
+endif(USE_GETTEXT)
+
 if(NOT MSVC)
 	set(USE_GPROF 0 CACHE BOOL "Use -pg flag for g++")
 endif()
@@ -27,6 +49,8 @@ if(WIN32)
 			CACHE FILEPATH "Path to zlibwapi.lib")
 	set(ZLIB_DLL "${PROJECT_SOURCE_DIR}/../../zlib125dll/dll32/zlibwapi.dll"
 			CACHE FILEPATH "Path to zlibwapi.dll (for installation)")
+	set(IRRLICHT_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../../irrlicht-1.7.2"
+			CACHE PATH "irrlicht dir")
 else()
 	# Unix probably
 	if(BUILD_CLIENT)
@@ -55,6 +79,9 @@ else()
 	set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
 endif()
 
+find_package(Jthread REQUIRED)
+find_package(Sqlite3 REQUIRED)
+
 configure_file(
 	"${PROJECT_SOURCE_DIR}/cmake_config.h.in"
 	"${PROJECT_BINARY_DIR}/cmake_config.h"
@@ -110,6 +137,7 @@ set(minetest_SRCS
 	clouds.cpp
 	clientobject.cpp
 	guiMainMenu.cpp
+	guiKeyChangeMenu.cpp
 	guiMessageMenu.cpp
 	guiTextInputMenu.cpp
 	guiInventoryMenu.cpp
@@ -133,16 +161,17 @@ include_directories(
 	${ZLIB_INCLUDE_DIR}
 	${CMAKE_BUILD_TYPE}
 	${PNG_INCLUDE_DIR}
-	"${PROJECT_SOURCE_DIR}/jthread"
-	"${PROJECT_SOURCE_DIR}/sqlite"
+	${GETTEXT_INCLUDE_DIR}
+	${JTHREAD_INCLUDE_DIR}
+	${SQLITE3_INCLUDE_DIR}
 )
 
 set(EXECUTABLE_OUTPUT_PATH ../bin)
 
 if(BUILD_CLIENT)
-	add_executable(minetest ${minetest_SRCS})
+	add_executable(${PROJECT_NAME} ${minetest_SRCS})
 	target_link_libraries(
-		minetest
+		${PROJECT_NAME}
 		${ZLIB_LIBRARIES}
 		${IRRLICHT_LIBRARY}
 		${OPENGL_LIBRARIES}
@@ -150,21 +179,22 @@ if(BUILD_CLIENT)
 		${BZIP2_LIBRARIES}
 		${PNG_LIBRARIES}
 		${X11_LIBRARIES}
+		${GETTEXT_LIBRARY}
 		${PLATFORM_LIBS}
 		${CLIENT_PLATFORM_LIBS}
-		jthread
-		sqlite3
+		${JTHREAD_LIBRARY}
+		${SQLITE3_LIBRARY}
 	)
 endif(BUILD_CLIENT)
 
 if(BUILD_SERVER)
-	add_executable(minetestserver ${minetestserver_SRCS})
+	add_executable(${PROJECT_NAME}server ${minetestserver_SRCS})
 	target_link_libraries(
-		minetestserver
+		${PROJECT_NAME}server
 		${ZLIB_LIBRARIES}
 		${PLATFORM_LIBS}
-		jthread
-		sqlite3
+		${JTHREAD_LIBRARY}
+		${SQLITE3_LIBRARY}
 	)
 endif(BUILD_SERVER)
 
@@ -185,7 +215,7 @@ if(MSVC)
 	set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Zi /Ob0 /Od /RTC1")
 	
 	if(BUILD_SERVER)
-		set_target_properties(minetestserver PROPERTIES
+		set_target_properties(${PROJECT_NAME}server PROPERTIES
 				COMPILE_DEFINITIONS "SERVER")
 	endif(BUILD_SERVER)
 
@@ -215,7 +245,7 @@ else()
 	endif()
 	
 	if(BUILD_SERVER)
-		set_target_properties(minetestserver PROPERTIES
+		set_target_properties(${PROJECT_NAME}server PROPERTIES
 				COMPILE_DEFINITIONS "SERVER")
 	endif(BUILD_SERVER)
 
@@ -232,12 +262,20 @@ endif()
 install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../minetest.conf.example" DESTINATION ${EXAMPLE_CONF_DIR})
 
 if(BUILD_CLIENT)
-	install(TARGETS minetest DESTINATION ${BINDIR})
+	install(TARGETS ${PROJECT_NAME} DESTINATION ${BINDIR})
 
 	file(GLOB images "${CMAKE_CURRENT_SOURCE_DIR}/../data/*.png")
 
 	install(FILES ${images} DESTINATION ${DATADIR})
 
+	if (USE_GETTEXT)
+		foreach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
+			set_mo_paths(MO_BUILD_PATH MO_DEST_PATH ${LOCALE})
+			set(MO_BUILD_PATH "${MO_BUILD_PATH}/${PROJECT_NAME}.mo")
+			install(FILES ${MO_BUILD_PATH} DESTINATION ${MO_DEST_PATH})
+		endforeach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
+	endif()
+
 	if(WIN32)
 		if(DEFINED IRRLICHT_DLL)
 			install(FILES ${IRRLICHT_DLL} DESTINATION ${BINDIR})
@@ -245,16 +283,82 @@ if(BUILD_CLIENT)
 		if(DEFINED ZLIB_DLL)
 			install(FILES ${ZLIB_DLL} DESTINATION ${BINDIR})
 		endif()
+		if(USE_GETTEXT)
+			if(DEFINED GETTEXT_DLL)
+				install(FILES ${GETTEXT_DLL} DESTINATION ${BINDIR})
+			endif()
+			if(DEFINED GETTEXT_ICONV_DLL)
+				install(FILES ${GETTEXT_ICONV_DLL} DESTINATION ${BINDIR})
+			endif()
+		endif(USE_GETTEXT)
 	endif()
 endif(BUILD_CLIENT)
 
 if(BUILD_SERVER)
-	install(TARGETS minetestserver DESTINATION ${BINDIR})
+	install(TARGETS ${PROJECT_NAME}server DESTINATION ${BINDIR})
 endif(BUILD_SERVER)
 
+if (USE_GETTEXT)
+	add_custom_command(OUTPUT "${GETTEXT_PO_PATH}/en"
+		COMMAND ${CMAKE_COMMAND} -E make_directory "${GETTEXT_PO_PATH}/en"
+		COMMENT "po-update [en]: creating translation template base directory")
+	set(POT_FILE "${GETTEXT_PO_PATH}/en/minetest.pot")
+	file(GLOB GETTEXT_POT_DEPS *.cpp *.h)
+	file(GLOB GETTEXT_POT_DEPS_REL RELATIVE ${CMAKE_SOURCE_DIR} *.cpp *.h)
+	add_custom_command(OUTPUT ${POT_FILE}
+		COMMAND ${GETTEXT_EXTRACT} -F -n -o ${POT_FILE} ${GETTEXT_POT_DEPS_REL}
+		DEPENDS "${GETTEXT_PO_PATH}/en" ${GETTEXT_POT_DEPS}
+		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+		COMMENT "po-update [en]: updating translation template")
+
+	set(PO_FILES)
+	set(MO_FILES)
+
+	foreach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
+		# skip the 'en' locale which is treated separately
+		if (NOT LOCALE STREQUAL "en")
+			set(PO_FILE_PATH "${GETTEXT_PO_PATH}/${LOCALE}/minetest.po")
+			add_custom_command(OUTPUT ${PO_FILE_PATH}
+				COMMAND ${GETTEXT_MSGMERGE} -F -U ${PO_FILE_PATH} ${POT_FILE}
+				DEPENDS ${POT_FILE}
+				WORKING_DIRECTORY "${GETTEXT_PO_PATH}/${LOCALE}"
+				COMMENT "po-update [${LOCALE}]: updating strings")
+
+
+			set_mo_paths(MO_BUILD_PATH MO_DEST_PATH ${LOCALE})
+			add_custom_command(OUTPUT ${MO_BUILD_PATH}
+				COMMAND ${CMAKE_COMMAND} -E make_directory ${MO_BUILD_PATH}
+				COMMENT "mo-update [${LOCALE}]: Creating locale directory.")
+
+			set(MO_FILE_PATH "${MO_BUILD_PATH}/minetest.mo")
+
+			add_custom_command(
+				OUTPUT ${MO_FILE_PATH}
+				COMMAND ${GETTEXT_MSGFMT} -o ${MO_FILE_PATH} ${PO_FILE_PATH}
+				DEPENDS ${MO_BUILD_PATH} ${PO_FILE_PATH}
+				WORKING_DIRECTORY "${GETTEXT_PO_PATH}/${LOCALE}"
+				COMMENT "mo-update [${LOCALE}]: Creating mo file."
+				)
+
+			set(MO_FILES ${MO_FILES} ${MO_FILE_PATH})
+			set(PO_FILES ${PO_FILES} ${PO_FILE_PATH})
+		endif(NOT LOCALE STREQUAL "en")
+	endforeach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
+
+	add_custom_target(translations ALL COMMENT "mo update" DEPENDS ${MO_FILES})
+	add_custom_target(updatepo COMMENT "po update" DEPENDS ${PO_FILES})
+endif(USE_GETTEXT)
+
 # Subdirectories
 
-add_subdirectory(jthread)
+if (JTHREAD_FOUND)
+else (JTHREAD_FOUND)
+	add_subdirectory(jthread)
+endif (JTHREAD_FOUND)
+
+if (SQLITE3_FOUND)
+else (SQLITE3_FOUND)
 add_subdirectory(sqlite)
+endif (SQLITE3_FOUND)
 
 #end
diff --git a/src/client.cpp b/src/client.cpp
index 585fce11c3557624ac14ae72ef80e23f1da2bb67..7ebb30fbacd952495fe10f9948713c54e80c16c5 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -5,7 +5,7 @@ Copyright (C) 2010 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
-MeshUpdateQueue::(at your option) any later version.
+(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
@@ -328,7 +328,8 @@ void Client::step(float dtime)
 
 			core::list<v3s16> deleted_blocks;
 
-				g_settings.getFloat("client_unload_unused_data_timeout");
+			float delete_unused_sectors_timeout = 
+				g_settings.getFloat("client_delete_unused_sectors_timeout");
 	
 			// Delete sector blocks
 			/*u32 num = m_env.getMap().unloadUnusedData
diff --git a/src/clouds.cpp b/src/clouds.cpp
index 122beedacd47133c6468c84d8c75725873c68089..d754cc15e2f0b93972a517037c233b720f8c6d7c 100644
--- a/src/clouds.cpp
+++ b/src/clouds.cpp
@@ -84,7 +84,7 @@ void Clouds::render()
 	*/
 
 	const s16 cloud_radius_i = 12;
-	const float cloud_size = BS*50;
+	const float cloud_size = BS*48;
 	const v2f cloud_speed(-BS*2, 0);
 	
 	// Position of cloud noise origin in world coordinates
@@ -123,24 +123,88 @@ void Clouds::render()
 				(float)p_in_noise_i.X*cloud_size/BS/200,
 				(float)p_in_noise_i.Y*cloud_size/BS/200,
 				m_seed, 3, 0.4);
-		if(noise < 0.8)
+		if(noise < 0.95)
 			continue;
-		
-		v2f p1 = p0 + v2f(1,1)*cloud_size;
 
-		//video::SColor c(128,255,255,255);
 		float b = m_brightness;
-		video::SColor c(128,b*230,b*230,b*255);
-		video::S3DVertex vertices[4] =
+		video::SColor c_top(128,b*240,b*240,b*255);
+		video::SColor c_side_1(128,b*230,b*230,b*255);
+		video::SColor c_side_2(128,b*220,b*220,b*245);
+		video::SColor c_bottom(128,b*205,b*205,b*230);
+
+		video::S3DVertex v[4] =
 		{
-			video::S3DVertex(p0.X,m_cloud_y,p0.Y, 0,0,0, c, 0,1),
-			video::S3DVertex(p0.X,m_cloud_y,p1.Y, 0,0,0, c, 1,1),
-			video::S3DVertex(p1.X,m_cloud_y,p1.Y, 0,0,0, c, 1,0),
-			video::S3DVertex(p1.X,m_cloud_y,p0.Y, 0,0,0, c, 0,0),
+			video::S3DVertex(0,0,0, 0,0,0, c_top, 0, 1),
+			video::S3DVertex(0,0,0, 0,0,0, c_top, 1, 1),
+			video::S3DVertex(0,0,0, 0,0,0, c_top, 1, 0),
+			video::S3DVertex(0,0,0, 0,0,0, c_top, 0, 0)
 		};
-		u16 indices[] = {0,1,2,2,3,0};
-		driver->drawVertexPrimitiveList(vertices, 4, indices, 2,
-				video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
+
+		f32 rx = cloud_size;
+		f32 ry = 8*BS;
+		f32 rz = cloud_size;
+
+		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
+					for(int j=0;j<4;j++)
+						v[j].Color=c_side_1;
+					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
+					for(int j=0;j<4;j++)
+						v[j].Color=c_side_2;
+					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
+					for(int j=0;j<4;j++)
+						v[j].Color=c_side_1;
+					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
+					for(int j=0;j<4;j++)
+						v[j].Color=c_side_2;
+					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
+					for(int j=0;j<4;j++)
+						v[j].Color=c_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;
+			}
+
+			v3f pos = v3f(p0.X,m_cloud_y,p0.Y);
+
+			for(u16 i=0; i<4; i++)
+				v[i].Pos += pos;
+			u16 indices[] = {0,1,2,2,3,0};
+			driver->drawVertexPrimitiveList(v, 4, indices, 2,
+					video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
+		}
+
 	}
 }
 
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index dc5ac400f5da3f05f400291ade4911ab7f220866..dfeaea85ab09babf8d62f7a07b22a058046ea353 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -752,4 +752,161 @@ void Oerkki1CAO::initialize(const std::string &data)
 	updateNodePos();
 }
 
+/*
+	FireflyCAO
+*/
+
+// Prototype
+FireflyCAO proto_FireflyCAO;
+
+FireflyCAO::FireflyCAO():
+	ClientActiveObject(0),
+	m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.),
+	m_node(NULL),
+	m_position(v3f(0,10*BS,0)),
+	m_yaw(0)
+{
+	ClientActiveObject::registerType(getType(), create);
+}
 
+FireflyCAO::~FireflyCAO()
+{
+}
+
+ClientActiveObject* FireflyCAO::create()
+{
+	return new FireflyCAO();
+}
+
+void FireflyCAO::addToScene(scene::ISceneManager *smgr)
+{
+	if(m_node != NULL)
+		return;
+	
+	video::IVideoDriver* driver = smgr->getVideoDriver();
+	
+	scene::SMesh *mesh = new scene::SMesh();
+	scene::IMeshBuffer *buf = new scene::SMeshBuffer();
+	video::SColor c(255,255,255,255);
+	video::S3DVertex vertices[4] =
+	{
+		video::S3DVertex(0,0,0, 0,0,0, c, 0,1),
+		video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
+		video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
+		video::S3DVertex(0,BS/2,0, 0,0,0, c, 0,0),
+	};
+	u16 indices[] = {0,1,2,2,3,0};
+	buf->append(vertices, 4, indices, 6);
+	// Set material
+	buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
+	buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
+	//buf->getMaterial().setTexture(0, NULL);
+	buf->getMaterial().setTexture
+			(0, driver->getTexture(getTexturePath("firefly.png").c_str()));
+	buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
+	buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
+	buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+	// Add to mesh
+	mesh->addMeshBuffer(buf);
+	buf->drop();
+	m_node = smgr->addMeshSceneNode(mesh, NULL);
+	mesh->drop();
+	// Set it to use the materials of the meshbuffers directly.
+	// This is needed for changing the texture in the future
+	m_node->setReadOnlyMaterials(true);
+	updateNodePos();
+}
+
+void FireflyCAO::removeFromScene()
+{
+	if(m_node == NULL)
+		return;
+
+	m_node->remove();
+	m_node = NULL;
+}
+
+void FireflyCAO::updateLight(u8 light_at_pos)
+{
+	if(m_node == NULL)
+		return;
+
+	u8 li = 255;
+	video::SColor color(255,li,li,li);
+
+	scene::IMesh *mesh = m_node->getMesh();
+	if(mesh == NULL)
+		return;
+	
+	u16 mc = mesh->getMeshBufferCount();
+	for(u16 j=0; j<mc; j++)
+	{
+		scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
+		video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
+		u16 vc = buf->getVertexCount();
+		for(u16 i=0; i<vc; i++)
+		{
+			vertices[i].Color = color;
+		}
+	}
+}
+
+v3s16 FireflyCAO::getLightPosition()
+{
+	return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
+}
+
+void FireflyCAO::updateNodePos()
+{
+	if(m_node == NULL)
+		return;
+
+	//m_node->setPosition(m_position);
+	m_node->setPosition(pos_translator.vect_show);
+
+	v3f rot = m_node->getRotation();
+	rot.Y = 180.0 - m_yaw;
+	m_node->setRotation(rot);
+}
+
+void FireflyCAO::step(float dtime, ClientEnvironment *env)
+{
+	pos_translator.translate(dtime);
+	updateNodePos();
+}
+
+void FireflyCAO::processMessage(const std::string &data)
+{
+	//dstream<<"FireflyCAO: Got message"<<std::endl;
+	std::istringstream is(data, std::ios::binary);
+	// command
+	u8 cmd = readU8(is);
+	if(cmd == 0)
+	{
+		// pos
+		m_position = readV3F1000(is);
+		pos_translator.update(m_position);
+		// yaw
+		m_yaw = readF1000(is);
+		updateNodePos();
+	}
+}
+
+void FireflyCAO::initialize(const std::string &data)
+{
+	//dstream<<"FireflyCAO: Got init data"<<std::endl;
+	
+	{
+		std::istringstream is(data, std::ios::binary);
+		// version
+		u8 version = readU8(is);
+		// check version
+		if(version != 0)
+			return;
+		// pos
+		m_position = readV3F1000(is);
+		pos_translator.init(m_position);
+	}
+	
+	updateNodePos();
+}
diff --git a/src/content_cao.h b/src/content_cao.h
index 146e23b0c985f0db3bd7f5fa0071866bbd88e543..b984be1361cb79d98fbf016659058cec538f65dd 100644
--- a/src/content_cao.h
+++ b/src/content_cao.h
@@ -243,6 +243,48 @@ class Oerkki1CAO : public ClientActiveObject
 	bool m_damage_texture_enabled;
 };
 
+/*
+	FireflyCAO
+*/
+
+class FireflyCAO : public ClientActiveObject
+{
+public:
+	FireflyCAO();
+	virtual ~FireflyCAO();
+	
+	u8 getType() const
+	{
+		return ACTIVEOBJECT_TYPE_FIREFLY;
+	}
+	
+	static ClientActiveObject* create();
+
+	void addToScene(scene::ISceneManager *smgr);
+	void removeFromScene();
+	void updateLight(u8 light_at_pos);
+	v3s16 getLightPosition();
+	void updateNodePos();
+
+	void step(float dtime, ClientEnvironment *env);
+
+	void processMessage(const std::string &data);
+
+	void initialize(const std::string &data);
+	
+	core::aabbox3d<f32>* getSelectionBox()
+		{return &m_selection_box;}
+	v3f getPosition()
+		{return m_position;}
+
+private:
+	core::aabbox3d<f32> m_selection_box;
+	scene::IMeshSceneNode *m_node;
+	v3f m_position;
+	float m_yaw;
+	SmoothTranslator pos_translator;
+};
+
 
 #endif
 
diff --git a/src/content_craft.cpp b/src/content_craft.cpp
index e4573a211b2c41e07ccd8dd2e357ab6ca581241a..b5a1dc7761795deeee13cabf022ef920f15859f9 100644
--- a/src/content_craft.cpp
+++ b/src/content_craft.cpp
@@ -262,6 +262,24 @@ InventoryItem *craft_get_result(InventoryItem **items)
 		}
 	}
 
+	// 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];
@@ -314,6 +332,87 @@ InventoryItem *craft_get_result(InventoryItem **items)
 		}
 	}
 
+	// 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;
 }
 
@@ -354,10 +453,17 @@ void craft_set_creative_inventory(Player *player)
 		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,
diff --git a/src/content_inventory.cpp b/src/content_inventory.cpp
index 78fa6f3d1f4bee835abff93076144d3c15f74da0..3222506067169eb5af7be5805162fdc34dde07f8 100644
--- a/src/content_inventory.cpp
+++ b/src/content_inventory.cpp
@@ -49,14 +49,24 @@ 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 if(subname == "firefly")
+		return "firefly.png";
 	else
 		return "cloud.png"; // just something
 }
@@ -69,13 +79,18 @@ ServerActiveObject* item_craft_create_object(const std::string &subname,
 		ServerActiveObject *obj = new RatSAO(env, id, pos);
 		return obj;
 	}
+	else if(subname == "firefly")
+	{
+		ServerActiveObject *obj = new FireflySAO(env, id, pos);
+		return obj;
+	}
 
 	return NULL;
 }
 
 s16 item_craft_get_drop_count(const std::string &subname)
 {
-	if(subname == "rat")
+	if(subname == "rat" || subname == "firefly")
 		return 1;
 
 	return -1;
@@ -83,7 +98,7 @@ s16 item_craft_get_drop_count(const std::string &subname)
 
 bool item_craft_is_cookable(const std::string &subname)
 {
-	if(subname == "lump_of_iron")
+	if(subname == "lump_of_iron" || subname == "lump_of_clay")
 		return true;
 		
 	return false;
@@ -93,6 +108,8 @@ 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;
 }
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp
index 38bc9620cd748c6b7209adaf35d884c8527326f3..4a9fa5e98cdfaf8c4c54f184e56854c9125b6244 100644
--- a/src/content_mapblock.cpp
+++ b/src/content_mapblock.cpp
@@ -188,6 +188,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 	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++)
@@ -895,14 +905,56 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 						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.getContent() == CONTENT_PAPYRUS)
+		{
+			u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
+			video::SColor c(255,l,l,l);
+
+			for(u32 j=0; j<4; j++)
+			{
+				video::S3DVertex vertices[4] =
+				{
+					video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
+						pa_papyrus.x0(), pa_papyrus.y1()),
+					video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
+						pa_papyrus.x1(), pa_papyrus.y1()),
+					video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
+						pa_papyrus.x1(), pa_papyrus.y0()),
+					video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
+						pa_papyrus.x0(), pa_papyrus.y0()),
+				};
+
+				if(j == 0)
 				{
 					for(u16 i=0; i<4; i++)
-						vertices[i].Pos.rotateYZBy(-90);
+						vertices[i].Pos.rotateXZBy(45);
 				}
-				else if(j == 5)
+				else if(j == 1)
 				{
 					for(u16 i=0; i<4; i++)
-						vertices[i].Pos.rotateYZBy(90);
+						vertices[i].Pos.rotateXZBy(-45);
+				}
+				else if(j == 2)
+				{
+					for(u16 i=0; i<4; i++)
+						vertices[i].Pos.rotateXZBy(135);
+				}
+				else if(j == 3)
+				{
+					for(u16 i=0; i<4; i++)
+						vertices[i].Pos.rotateXZBy(-135);
 				}
 
 				for(u16 i=0; i<4; i++)
@@ -912,11 +964,113 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 
 				u16 indices[] = {0,1,2,2,3,0};
 				// Add to mesh collector
-				collector.append(material_general, vertices, 4, indices, 6);
+				collector.append(material_papyrus, vertices, 4, indices, 6);
 			}
 		}
-#endif
+		else if(n.getContent() == CONTENT_RAIL)
+		{
+			u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+			video::SColor c(255,l,l,l);
+
+			bool is_rail_x [] = { false, false };  /* x-1, x+1 */
+			bool is_rail_z [] = { false, false };  /* z-1, z+1 */
+
+			MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
+			MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
+			MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
+			MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
+
+			if(n_minus_x.getContent() == CONTENT_RAIL)
+				is_rail_x[0] = true;
+			if(n_plus_x.getContent() == CONTENT_RAIL)
+				is_rail_x[1] = true;
+			if(n_minus_z.getContent() == CONTENT_RAIL)
+				is_rail_z[0] = true;
+			if(n_plus_z.getContent() == CONTENT_RAIL)
+				is_rail_z[1] = true;
+
+			float d = (float)BS/16;
+			video::S3DVertex vertices[4] =
+			{
+				video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
+					0, 1),
+				video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
+					1, 1),
+				video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
+					1, 0),
+				video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
+					0, 0),
+			};
+
+			video::SMaterial material_rail;
+			material_rail.setFlag(video::EMF_LIGHTING, false);
+			material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
+			material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
+			material_rail.setFlag(video::EMF_FOG_ENABLE, true);
+			material_rail.MaterialType
+					= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
+			int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
+
+			// Assign textures
+			if(adjacencies < 2)
+				material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
+			else if(adjacencies == 2)
+			{
+				if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
+					material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
+				else
+					material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png"));
+			}
+			else if(adjacencies == 3)
+				material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png"));
+			else if(adjacencies == 4)
+				material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png"));
 
+			// Rotate textures
+			int angle = 0;
+
+			if(adjacencies == 1)
+			{
+				if(is_rail_x[0] || is_rail_x[1])
+					angle = 90;
+			}
+			else if(adjacencies == 2)
+			{
+				if(is_rail_x[0] && is_rail_x[1])
+					angle = 90;
+				else if(is_rail_x[0] && is_rail_z[0])
+					angle = 270;
+				else if(is_rail_x[0] && is_rail_z[1])
+					angle = 180;
+				else if(is_rail_x[1] && is_rail_z[1])
+					angle = 90;
+			}
+			else if(adjacencies == 3)
+			{
+				if(!is_rail_x[0])
+					angle=0;
+				if(!is_rail_x[1])
+					angle=180;
+				if(!is_rail_z[0])
+					angle=90;
+				if(!is_rail_z[1])
+					angle=270;
+			}
+
+			if(angle != 0) {
+				for(u16 i=0; i<4; i++)
+					vertices[i].Pos.rotateXZBy(angle);
+			}
+
+			for(s32 i=0; i<4; i++)
+			{
+				vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+			}
+
+			u16 indices[] = {0,1,2,2,3,0};
+			collector.append(material_rail, vertices, 4, indices, 6);
+		}
 	}
 }
 #endif
diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp
index e2aed24581e208ca1bbe2772c8c3bf6465e10bc1..8701ab887ff84e56b65110cf69a7e23938bbca26 100644
--- a/src/content_mapnode.cpp
+++ b/src/content_mapnode.cpp
@@ -47,6 +47,12 @@ content_t trans_table_19[][2] = {
 	{CONTENT_GLASS, 20},
 	{CONTENT_MOSSYCOBBLE, 22},
 	{CONTENT_GRAVEL, 23},
+	{CONTENT_SANDSTONE, 24},
+	{CONTENT_CACTUS, 25},
+	{CONTENT_BRICK, 26},
+	{CONTENT_CLAY, 27},
+	{CONTENT_PAPYRUS, 28},
+	{CONTENT_BOOKSHELF, 29},
 };
 
 MapNode mapnode_translate_from_internal(MapNode n_from, u8 version)
@@ -152,6 +158,33 @@ void content_mapnode_init()
 	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");
@@ -180,6 +213,40 @@ void content_mapnode_init()
 	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;
@@ -201,6 +268,18 @@ void content_mapnode_init()
 	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);
@@ -255,6 +334,7 @@ void content_mapnode_init()
 	f->buildable_to = true;
 	f->liquid_type = LIQUID_FLOWING;
 	f->liquid_alternative_flowing = CONTENT_WATER;
+	f->liquid_alternative_source = CONTENT_WATERSOURCE;
 	
 	i = CONTENT_WATERSOURCE;
 	f = &content_features(i);
@@ -286,6 +366,7 @@ void content_mapnode_init()
 	f->liquid_type = LIQUID_SOURCE;
 	f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
 	f->liquid_alternative_flowing = CONTENT_WATER;
+	f->liquid_alternative_source = CONTENT_WATERSOURCE;
 	
 	i = CONTENT_TORCH;
 	f = &content_features(i);
diff --git a/src/content_mapnode.h b/src/content_mapnode.h
index b484cb2255be8be0ed694f83ac84ba2aa7753e5c..02c604c60d3279971a6dbdafe14275b553d00e40 100644
--- a/src/content_mapnode.h
+++ b/src/content_mapnode.h
@@ -34,7 +34,7 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
 
 // 0x000...0x07f (0...127): param2 is fully usable
 // 126 and 127 are reserved.
-// Use these sparingly, only when the extra space in param2 is needed.
+// Use these sparingly, only when the extra space in param2 might be needed.
 #define CONTENT_STONE 0
 #define CONTENT_WATER 2
 #define CONTENT_TORCH 3
@@ -42,10 +42,10 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
 #define CONTENT_SIGN_WALL 14
 #define CONTENT_CHEST 15
 #define CONTENT_FURNACE 16
-//#define CONTENT_WORKBENCH 17
 #define CONTENT_FENCE 21
+#define CONTENT_RAIL 30
 
-// 0x800...0xfff: param2 higher 4 bytes are not usable
+// 0x800...0xfff (2048...4095): higher 4 bytes of param2 are not usable
 #define CONTENT_GRASS 0x800 //1
 #define CONTENT_TREE 0x801 //4
 #define CONTENT_LEAVES 0x802 //5
@@ -62,6 +62,13 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
 #define CONTENT_GLASS 0x80c //20
 #define CONTENT_MOSSYCOBBLE 0x80d //22
 #define CONTENT_GRAVEL 0x80e //23
+#define CONTENT_SANDSTONE 0x80f //24
+#define CONTENT_CACTUS 0x810 //25
+#define CONTENT_BRICK 0x811 //26
+#define CONTENT_CLAY 0x812 //27
+#define CONTENT_PAPYRUS 0x813 //28
+#define CONTENT_BOOKSHELF 0x814 //29
+
 
 #endif
 
diff --git a/src/content_object.h b/src/content_object.h
index ecabd8a3877ab5eaf850857564df86a5b06a3ee0..47f93d7d4ad492828609f671d139613d00f2aa02 100644
--- a/src/content_object.h
+++ b/src/content_object.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define ACTIVEOBJECT_TYPE_ITEM 2
 #define ACTIVEOBJECT_TYPE_RAT 3
 #define ACTIVEOBJECT_TYPE_OERKKI1 4
+#define ACTIVEOBJECT_TYPE_FIREFLY 5
 
 #endif
 
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index c41f4ed784297e0aaf08efb5ac6dc1f0bba0a07c..0b81855c16aec55f0c9fffaa685e9ed411ba86ca 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -693,4 +693,179 @@ void Oerkki1SAO::doDamage(u16 d)
 	}
 }
 
+/*
+	FireflySAO
+*/
+
+// Prototype
+FireflySAO proto_FireflySAO(NULL, 0, v3f(0,0,0));
 
+FireflySAO::FireflySAO(ServerEnvironment *env, u16 id, v3f pos):
+	ServerActiveObject(env, id, pos),
+	m_is_active(false),
+	m_speed_f(0,0,0)
+{
+	ServerActiveObject::registerType(getType(), create);
+
+	m_oldpos = v3f(0,0,0);
+	m_last_sent_position = v3f(0,0,0);
+	m_yaw = 0;
+	m_counter1 = 0;
+	m_counter2 = 0;
+	m_age = 0;
+	m_touching_ground = false;
+}
+
+ServerActiveObject* FireflySAO::create(ServerEnvironment *env, u16 id, v3f pos,
+		const std::string &data)
+{
+	std::istringstream is(data, std::ios::binary);
+	char buf[1];
+	// read version
+	is.read(buf, 1);
+	u8 version = buf[0];
+	// check if version is supported
+	if(version != 0)
+		return NULL;
+	return new FireflySAO(env, id, pos);
+}
+
+void FireflySAO::step(float dtime, bool send_recommended)
+{
+	assert(m_env);
+
+	if(m_is_active == false)
+	{
+		if(m_inactive_interval.step(dtime, 0.5)==false)
+			return;
+	}
+
+	/*
+		The AI
+	*/
+
+	// Apply (less) gravity
+	m_speed_f.Y -= dtime*3*BS;
+
+	/*
+		Move around if some player is close
+	*/
+	bool player_is_close = false;
+	// Check connected players
+	core::list<Player*> players = m_env->getPlayers(true);
+	core::list<Player*>::Iterator i;
+	for(i = players.begin();
+			i != players.end(); i++)
+	{
+		Player *player = *i;
+		v3f playerpos = player->getPosition();
+		if(m_base_position.getDistanceFrom(playerpos) < BS*10.0)
+		{
+			player_is_close = true;
+			break;
+		}
+	}
+
+	m_is_active = player_is_close;
+	
+	if(player_is_close == false)
+	{
+		m_speed_f.X = 0;
+		m_speed_f.Z = 0;
+	}
+	else
+	{
+		// Move around
+		v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
+		f32 speed = BS/2;
+		m_speed_f.X = speed * dir.X;
+		m_speed_f.Z = speed * dir.Z;
+
+		if(m_touching_ground && (m_oldpos - m_base_position).getLength()
+				< dtime*speed/2)
+		{
+			m_counter1 -= dtime;
+			if(m_counter1 < 0.0)
+			{
+				m_counter1 += 1.0;
+				m_speed_f.Y = 5.0*BS;
+			}
+		}
+
+		{
+			m_counter2 -= dtime;
+			if(m_counter2 < 0.0)
+			{
+				m_counter2 += (float)(myrand()%100)/100*3.0;
+				m_yaw += ((float)(myrand()%200)-100)/100*180;
+				m_yaw = wrapDegrees(m_yaw);
+			}
+		}
+	}
+	
+	m_oldpos = m_base_position;
+
+	/*
+		Move it, with collision detection
+	*/
+
+	core::aabbox3d<f32> box(-BS/3.,-BS*2/3.0,-BS/3., BS/3.,BS*4./3.,BS/3.);
+	collisionMoveResult moveresult;
+	// Maximum movement without glitches
+	f32 pos_max_d = BS*0.25;
+	// Limit speed
+	if(m_speed_f.getLength()*dtime > pos_max_d)
+		m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
+	v3f pos_f = getBasePosition();
+	v3f pos_f_old = pos_f;
+	moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
+			box, dtime, pos_f, m_speed_f);
+	m_touching_ground = moveresult.touching_ground;
+	
+	setBasePosition(pos_f);
+
+	if(send_recommended == false)
+		return;
+
+	if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
+	{
+		m_last_sent_position = pos_f;
+
+		std::ostringstream os(std::ios::binary);
+		// command (0 = update position)
+		writeU8(os, 0);
+		// pos
+		writeV3F1000(os, m_base_position);
+		// yaw
+		writeF1000(os, m_yaw);
+		// create message and add to list
+		ActiveObjectMessage aom(getId(), false, os.str());
+		m_messages_out.push_back(aom);
+	}
+}
+
+std::string FireflySAO::getClientInitializationData()
+{
+	std::ostringstream os(std::ios::binary);
+	// version
+	writeU8(os, 0);
+	// pos
+	writeV3F1000(os, m_base_position);
+	return os.str();
+}
+
+std::string FireflySAO::getStaticData()
+{
+	//dstream<<__FUNCTION_NAME<<std::endl;
+	std::ostringstream os(std::ios::binary);
+	// version
+	writeU8(os, 0);
+	return os.str();
+}
+
+InventoryItem* FireflySAO::createPickedUpItem()
+{
+	std::istringstream is("CraftItem firefly 1", std::ios_base::binary);
+	InventoryItem *item = InventoryItem::deSerialize(is);
+	return item;
+}
diff --git a/src/content_sao.h b/src/content_sao.h
index 030232a9ee05317fc8e0173d1a58984968645041..e5b1223d466c68c54bf221d6ceff5c1beb2dde85 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -113,6 +113,30 @@ class Oerkki1SAO : public ServerActiveObject
 	float m_after_jump_timer;
 };
 
+class FireflySAO : public ServerActiveObject
+{
+public:
+	FireflySAO(ServerEnvironment *env, u16 id, v3f pos);
+	u8 getType() const
+		{return ACTIVEOBJECT_TYPE_FIREFLY;}
+	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+			const std::string &data);
+	void step(float dtime, bool send_recommended);
+	std::string getClientInitializationData();
+	std::string getStaticData();
+	InventoryItem* createPickedUpItem();
+private:
+	bool m_is_active;
+	IntervalLimiter m_inactive_interval;
+	v3f m_speed_f;
+	v3f m_oldpos;
+	v3f m_last_sent_position;
+	float m_yaw;
+	float m_counter1;
+	float m_counter2;
+	float m_age;
+	bool m_touching_ground;
+};
 
 #endif
 
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index cda9eb79a613723e2cf6796fa29349f10b995a57..cbc78ad3f51a70773a17658618fa0e32d5728812 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -70,6 +70,8 @@ void set_default_settings()
 	g_settings.setDefault("fast_move", "false");
 	g_settings.setDefault("invert_mouse", "false");
 	g_settings.setDefault("enable_farmesh", "false");
+	g_settings.setDefault("farmesh_trees", "true");
+	g_settings.setDefault("farmesh_distance", "40");
 	g_settings.setDefault("enable_clouds", "true");
 	g_settings.setDefault("invisible_stone", "false");
 	g_settings.setDefault("screenshot_path", ".");
diff --git a/src/environment.cpp b/src/environment.cpp
index b16ee59abda367791e603759c4bd9ada6779cb83..f5e4b071a08aec49653c622cbad0a4569b5c10d7 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -976,7 +976,8 @@ void ServerEnvironment::step(float dtime)
 		//TestSAO *obj = new TestSAO(this, 0, pos);
 		//ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
 		//ServerActiveObject *obj = new RatSAO(this, 0, pos);
-		ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
+		//ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
+		ServerActiveObject *obj = new FireflySAO(this, 0, pos);
 		addActiveObject(obj);
 	}
 #endif
diff --git a/src/farmesh.cpp b/src/farmesh.cpp
index 8f91e3a1ab4ddc427cf05a3032acb04e16549d1c..2cd92243425c7d3c8a11212ac4288d882d291127 100644
--- a/src/farmesh.cpp
+++ b/src/farmesh.cpp
@@ -70,6 +70,7 @@ FarMesh::FarMesh(
 	m_box = core::aabbox3d<f32>(-BS*1000000,-BS*31000,-BS*1000000,
 			BS*1000000,BS*31000,BS*1000000);
 
+    trees = g_settings.getBool("farmesh_trees");
 }
 
 FarMesh::~FarMesh()
@@ -313,12 +314,11 @@ void FarMesh::render()
 				}
 				else
 				{
-					/*// Trees if there are over 0.01 trees per MapNode
-					if(tree_amount_avg > 0.01)
+					// Trees if there are over 0.01 trees per MapNode
+					if(trees && tree_amount_avg > 0.01)
 						c = video::SColor(255,50,128,50);
 					else
-						c = video::SColor(255,107,134,51);*/
-					c = video::SColor(255,107,134,51);
+						c = video::SColor(255,107,134,51);
 					ground_is_mud = true;
 				}
 			}
@@ -351,7 +351,7 @@ void FarMesh::render()
 				video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
 
 		// Add some trees if appropriate
-		if(tree_amount_avg >= 0.0065 && steepness < 1.4
+		if(trees && tree_amount_avg >= 0.0065 && steepness < 1.4
 				&& ground_is_mud == true)
 		{
 			driver->setMaterial(m_materials[1]);
@@ -404,11 +404,11 @@ void FarMesh::step(float dtime)
 	m_time += dtime;
 }
 
-void FarMesh::update(v2f camera_p, float brightness, s16 render_range)
+void FarMesh::update(v2f camera_p, float brightness)
 {
 	m_camera_pos = camera_p;
 	m_brightness = brightness;
-	m_render_range = render_range;
+	m_render_range = g_settings.getS16("farmesh_distance")*10;
 }
 
 
diff --git a/src/farmesh.h b/src/farmesh.h
index 0a30a8aefe348a000186f6f2213333c65ab13eb3..577224e152bad3ba24a5b7043cca9d4d1e38e96a 100644
--- a/src/farmesh.h
+++ b/src/farmesh.h
@@ -67,7 +67,7 @@ class FarMesh : public scene::ISceneNode
 
 	void step(float dtime);
 
-	void update(v2f camera_p, float brightness, s16 render_range);
+	void update(v2f camera_p, float brightness);
 
 private:
 	video::SMaterial m_materials[FARMESH_MATERIAL_COUNT];
@@ -79,6 +79,7 @@ class FarMesh : public scene::ISceneNode
 	float m_time;
 	Client *m_client;
 	s16 m_render_range;
+    bool trees;
 };
 
 #endif
diff --git a/src/game.cpp b/src/game.cpp
index 7e2ee44fc25b39faac46205ebaa819fdfc7e3a3b..b26d489673e600cd4a9aec664db06b4551421477 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -538,6 +538,43 @@ void getPointedNode(Client *client, v3f player_position,
 				}
 			}
 		}
+		else if(n.getContent() == CONTENT_RAIL)
+		{
+			v3s16 dir = unpackDir(n.param0);
+			v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
+			dir_f *= BS/2 - BS/6 - BS/20;
+			v3f cpf = npf + dir_f;
+			f32 distance = (cpf - camera_position).getLength();
+
+			float d = (float)BS/16;
+			v3f vertices[4] =
+			{
+				v3f(BS/2, -BS/2+d, -BS/2),
+				v3f(-BS/2, -BS/2, BS/2),
+			};
+
+			for(s32 i=0; i<2; i++)
+			{
+				vertices[i] += npf;
+			}
+
+			core::aabbox3d<f32> box;
+
+			box = core::aabbox3d<f32>(vertices[0]);
+			box.addInternalPoint(vertices[1]);
+
+			if(distance < mindistance)
+			{
+				if(box.intersectsWithLine(shootline))
+				{
+					nodefound = true;
+					nodepos = np;
+					neighbourpos = np;
+					mindistance = distance;
+					nodehilightbox = box;
+				}
+			}
+		}
 		/*
 			Regular blocks
 		*/
@@ -1915,15 +1952,9 @@ void the_game(
 		*/
 		if(farmesh)
 		{
-			farmesh_range = draw_control.wanted_range * 10;
-			if(draw_control.range_all && farmesh_range < 500)
-				farmesh_range = 500;
-			if(farmesh_range > 1000)
-				farmesh_range = 1000;
-
 			farmesh->step(dtime);
 			farmesh->update(v2f(player_position.X, player_position.Z),
-					0.05+brightness*0.95, farmesh_range);
+					0.05+brightness*0.95);
 		}
 		
 		// Store brightness value
@@ -1990,7 +2021,7 @@ void the_game(
 			endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
 			
 			char temptext[300];
-			snprintf(temptext, 300, "Minetest-c55 %s ("
+			snprintf(temptext, 300, "Minetest-delta %s ("
 					"R: range_all=%i"
 					")"
 					" drawtime=%.0f, beginscenetime=%.0f"
diff --git a/src/gettext.h b/src/gettext.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ddb9534691bde14bf5b105f14ea437836985b7c
--- /dev/null
+++ b/src/gettext.h
@@ -0,0 +1,19 @@
+#ifdef USE_GETTEXT
+#include <libintl.h>
+#else
+#define gettext(String) String
+#define bindtextdomain(domain, dir) /* */
+#define textdomain(domain) /* */
+#endif
+
+#define _(String) gettext(String)
+#define gettext_noop(String) String
+#define N_(String) gettext_noop (String)
+
+inline wchar_t* chartowchar_t(const char *str)
+{
+	size_t l = strlen(str)+1;
+	wchar_t* nstr = new wchar_t[l];
+	mbstowcs(nstr, str, l);
+	return nstr;
+}
diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a11cf93c0ee3a16e28ffadfe6bf55b9c639185b
--- /dev/null
+++ b/src/guiKeyChangeMenu.cpp
@@ -0,0 +1,598 @@
+/*
+ Minetest-delta
+ Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
+ Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
+ Copyright (C) 2011 teddydestodes <derkomtur@schattengang.net>
+
+ 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 "guiKeyChangeMenu.h"
+#include "debug.h"
+#include "serialization.h"
+#include "keycode.h"
+#include "main.h"
+#include <string>
+
+GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
+		gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
+	GUIModalMenu(env, parent, id, menumgr)
+{
+	activeKey = -1;
+	init_keys();
+}
+
+GUIKeyChangeMenu::~GUIKeyChangeMenu()
+{
+	removeChildren();
+}
+
+void GUIKeyChangeMenu::removeChildren()
+{
+	const core::list<gui::IGUIElement*> &children = getChildren();
+	core::list<gui::IGUIElement*> children_copy;
+	for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
+			!= children.end(); i++)
+	{
+		children_copy.push_back(*i);
+	}
+	for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
+			!= children_copy.end(); i++)
+	{
+		(*i)->remove();
+	}
+}
+
+void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
+{
+	/*
+	 Remove stuff
+	 */
+	removeChildren();
+
+	/*
+	 Calculate new sizes and positions
+	 */
+
+	v2s32 size(620, 430);
+
+	core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
+			screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
+			screensize.Y / 2 + size.Y / 2);
+
+	DesiredRect = rect;
+	recalculateAbsolutePosition(false);
+
+	v2s32 topleft(0, 0);
+
+	{
+		core::rect < s32 > rect(0, 0, 125, 20);
+		rect += topleft + v2s32(25, 3);
+		const wchar_t *text = L"KEYBINDINGS";
+		//gui::IGUIStaticText *t =
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+	v2s32 offset(25, 40);
+	// buttons
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Forward";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->forward = Environment->addButton(rect, this,
+				GUI_ID_KEY_FORWARD_BUTTON,
+				narrow_to_wide(KeyNames[key_forward]).c_str());
+	}
+
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Backward";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->backward = Environment->addButton(rect, this,
+				GUI_ID_KEY_BACKWARD_BUTTON,
+				narrow_to_wide(KeyNames[key_backward]).c_str());
+	}
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Left";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->left = Environment->addButton(rect, this, GUI_ID_KEY_LEFT_BUTTON,
+				narrow_to_wide(KeyNames[key_left]).c_str());
+	}
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Right";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->right = Environment->addButton(rect, this,
+				GUI_ID_KEY_RIGHT_BUTTON,
+				narrow_to_wide(KeyNames[key_right]).c_str());
+	}
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Use";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->use = Environment->addButton(rect, this, GUI_ID_KEY_USE_BUTTON,
+				narrow_to_wide(KeyNames[key_use]).c_str());
+	}
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Sneak";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->sneak = Environment->addButton(rect, this,
+				GUI_ID_KEY_SNEAK_BUTTON,
+				narrow_to_wide(KeyNames[key_sneak]).c_str());
+	}
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Jump";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->jump = Environment->addButton(rect, this, GUI_ID_KEY_JUMP_BUTTON,
+				narrow_to_wide(KeyNames[key_jump]).c_str());
+	}
+
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Inventory";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->inventory = Environment->addButton(rect, this,
+				GUI_ID_KEY_INVENTORY_BUTTON,
+				narrow_to_wide(KeyNames[key_inventory]).c_str());
+	}
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Chat";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->chat = Environment->addButton(rect, this, GUI_ID_KEY_CHAT_BUTTON,
+				narrow_to_wide(KeyNames[key_chat]).c_str());
+	}
+
+	//next col
+	offset = v2s32(250, 40);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Toggle fly";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->fly = Environment->addButton(rect, this, GUI_ID_KEY_FLY_BUTTON,
+				narrow_to_wide(KeyNames[key_fly]).c_str());
+	}
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Toggle fast";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->fast = Environment->addButton(rect, this, GUI_ID_KEY_FAST_BUTTON,
+				narrow_to_wide(KeyNames[key_fast]).c_str());
+	}
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Range select";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->range = Environment->addButton(rect, this,
+				GUI_ID_KEY_RANGE_BUTTON,
+				narrow_to_wide(KeyNames[key_range]).c_str());
+	}
+
+	offset += v2s32(0, 25);
+	{
+		core::rect < s32 > rect(0, 0, 100, 20);
+		rect += topleft + v2s32(offset.X, offset.Y);
+		const wchar_t *text = L"Print stacks";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+	}
+
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+		this->dump = Environment->addButton(rect, this, GUI_ID_KEY_DUMP_BUTTON,
+				narrow_to_wide(KeyNames[key_dump]).c_str());
+	}
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40);
+		Environment->addButton(rect, this, GUI_ID_BACK_BUTTON, L"Save");
+	}
+	{
+		core::rect < s32 > rect(0, 0, 100, 30);
+		rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40);
+		Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON, L"Cancel");
+	}
+}
+
+void GUIKeyChangeMenu::drawMenu()
+{
+	gui::IGUISkin* skin = Environment->getSkin();
+	if (!skin)
+		return;
+	video::IVideoDriver* driver = Environment->getVideoDriver();
+
+	video::SColor bgcolor(140, 0, 0, 0);
+
+	{
+		core::rect < s32 > rect(0, 0, 620, 620);
+		rect += AbsoluteRect.UpperLeftCorner;
+		driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
+	}
+
+	gui::IGUIElement::draw();
+}
+
+bool GUIKeyChangeMenu::acceptInput()
+{
+	g_settings.set("keymap_forward", keycode_to_keyname(key_forward));
+	g_settings.set("keymap_backward", keycode_to_keyname(key_backward));
+	g_settings.set("keymap_left", keycode_to_keyname(key_left));
+	g_settings.set("keymap_right", keycode_to_keyname(key_right));
+	g_settings.set("keymap_jump", keycode_to_keyname(key_jump));
+	g_settings.set("keymap_sneak", keycode_to_keyname(key_sneak));
+	g_settings.set("keymap_inventory", keycode_to_keyname(key_inventory));
+	g_settings.set("keymap_chat", keycode_to_keyname(key_chat));
+	g_settings.set("keymap_rangeselect", keycode_to_keyname(key_range));
+	g_settings.set("keymap_freemove", keycode_to_keyname(key_fly));
+	g_settings.set("keymap_fastmove", keycode_to_keyname(key_fast));
+	g_settings.set("keymap_special1", keycode_to_keyname(key_use));
+	g_settings.set("keymap_print_debug_stacks", keycode_to_keyname(key_dump));
+	clearKeyCache();
+	return true;
+}
+void GUIKeyChangeMenu::init_keys()
+{
+	key_forward = getKeySetting("keymap_forward");
+	key_backward = getKeySetting("keymap_backward");
+	key_left = getKeySetting("keymap_left");
+	key_right = getKeySetting("keymap_right");
+	key_jump = getKeySetting("keymap_jump");
+	key_sneak = getKeySetting("keymap_sneak");
+	key_inventory = getKeySetting("keymap_inventory");
+	key_chat = getKeySetting("keymap_chat");
+	key_range = getKeySetting("keymap_rangeselect");
+	key_fly = getKeySetting("keymap_freemove");
+	key_fast = getKeySetting("keymap_fastmove");
+	key_use = getKeySetting("keymap_special1");
+	key_dump = getKeySetting("keymap_print_debug_stacks");
+}
+
+bool GUIKeyChangeMenu::resetMenu()
+{
+	if (activeKey >= 0)
+	{
+		switch (activeKey)
+		{
+		case GUI_ID_KEY_FORWARD_BUTTON:
+			this->forward->setText(
+					narrow_to_wide(KeyNames[key_forward]).c_str());
+			break;
+		case GUI_ID_KEY_BACKWARD_BUTTON:
+			this->backward->setText(
+					narrow_to_wide(KeyNames[key_backward]).c_str());
+			break;
+		case GUI_ID_KEY_LEFT_BUTTON:
+			this->left->setText(narrow_to_wide(KeyNames[key_left]).c_str());
+			break;
+		case GUI_ID_KEY_RIGHT_BUTTON:
+			this->right->setText(narrow_to_wide(KeyNames[key_right]).c_str());
+			break;
+		case GUI_ID_KEY_JUMP_BUTTON:
+			this->jump->setText(narrow_to_wide(KeyNames[key_jump]).c_str());
+			break;
+		case GUI_ID_KEY_SNEAK_BUTTON:
+			this->sneak->setText(narrow_to_wide(KeyNames[key_sneak]).c_str());
+			break;
+		case GUI_ID_KEY_INVENTORY_BUTTON:
+			this->inventory->setText(
+					narrow_to_wide(KeyNames[key_inventory]).c_str());
+			break;
+		case GUI_ID_KEY_CHAT_BUTTON:
+			this->chat->setText(narrow_to_wide(KeyNames[key_chat]).c_str());
+			break;
+		case GUI_ID_KEY_RANGE_BUTTON:
+			this->range->setText(narrow_to_wide(KeyNames[key_range]).c_str());
+			break;
+		case GUI_ID_KEY_FLY_BUTTON:
+			this->fly->setText(narrow_to_wide(KeyNames[key_fly]).c_str());
+			break;
+		case GUI_ID_KEY_FAST_BUTTON:
+			this->fast->setText(narrow_to_wide(KeyNames[key_fast]).c_str());
+			break;
+		case GUI_ID_KEY_USE_BUTTON:
+			this->use->setText(narrow_to_wide(KeyNames[key_use]).c_str());
+			break;
+		case GUI_ID_KEY_DUMP_BUTTON:
+			this->dump->setText(narrow_to_wide(KeyNames[key_dump]).c_str());
+			break;
+		}
+		activeKey = -1;
+		return false;
+	}
+	return true;
+}
+bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
+{
+	if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
+			&& event.KeyInput.PressedDown)
+	{
+		if (activeKey == GUI_ID_KEY_FORWARD_BUTTON)
+		{
+			this->forward->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_forward = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_BACKWARD_BUTTON)
+		{
+			this->backward->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_backward = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_LEFT_BUTTON)
+		{
+			this->left->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_left = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_RIGHT_BUTTON)
+		{
+			this->right->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_right = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_JUMP_BUTTON)
+		{
+			this->jump->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_jump = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_SNEAK_BUTTON)
+		{
+			this->sneak->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_sneak = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_INVENTORY_BUTTON)
+		{
+			this->inventory->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_inventory = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_CHAT_BUTTON)
+		{
+			this->chat->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_chat = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_RANGE_BUTTON)
+		{
+			this->range->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_range = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_FLY_BUTTON)
+		{
+			this->fly->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_fly = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_FAST_BUTTON)
+		{
+			this->fast->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_fast = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_USE_BUTTON)
+		{
+			this->use->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_use = event.KeyInput.Key;
+		}
+		else if (activeKey == GUI_ID_KEY_DUMP_BUTTON)
+		{
+			this->dump->setText(
+					narrow_to_wide(KeyNames[event.KeyInput.Key]).c_str());
+			this->key_dump = event.KeyInput.Key;
+		}
+
+		activeKey = -1;
+		return true;
+	}
+	if (event.EventType == EET_GUI_EVENT)
+	{
+		if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
+				&& isVisible())
+		{
+			if (!canTakeFocus(event.GUIEvent.Element))
+			{
+				dstream << "GUIMainMenu: Not allowing focus change."
+						<< std::endl;
+				// Returning true disables focus change
+				return true;
+			}
+		}
+		if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
+		{
+			switch (event.GUIEvent.Caller->getID())
+			{
+			case GUI_ID_BACK_BUTTON: //back
+				acceptInput();
+				quitMenu();
+				return true;
+			case GUI_ID_ABORT_BUTTON: //abort
+				quitMenu();
+				return true;
+			case GUI_ID_KEY_FORWARD_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->forward->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_BACKWARD_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->backward->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_LEFT_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->left->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_RIGHT_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->right->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_USE_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->use->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_FLY_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->fly->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_FAST_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->fast->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_JUMP_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->jump->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_CHAT_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->chat->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_SNEAK_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->sneak->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_INVENTORY_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->inventory->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_DUMP_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->dump->setText(L"press Key");
+				break;
+			case GUI_ID_KEY_RANGE_BUTTON:
+				resetMenu();
+				activeKey = event.GUIEvent.Caller->getID();
+				this->range->setText(L"press Key");
+				break;
+			}
+			//Buttons
+
+		}
+	}
+	return Parent ? Parent->OnEvent(event) : false;
+}
+
diff --git a/src/guiKeyChangeMenu.h b/src/guiKeyChangeMenu.h
new file mode 100644
index 0000000000000000000000000000000000000000..389ce7aeecc692c9cbe5587969f21501f42f0f09
--- /dev/null
+++ b/src/guiKeyChangeMenu.h
@@ -0,0 +1,133 @@
+/*
+ Minetest-delta
+ Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
+ Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
+ Copyright (C) 2011 teddydestodes <derkomtur@schattengang.net>
+
+ 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 GUIKEYCHANGEMENU_HEADER
+#define GUIKEYCHANGEMENU_HEADER
+
+#include "common_irrlicht.h"
+#include "utility.h"
+#include "modalMenu.h"
+#include "client.h"
+#include <string>
+
+static const char *KeyNames[] =
+	{ "-", "Left Button", "Right Button", "Cancel", "Middle Button", "X Button 1",
+			"X Button 2", "-", "Back", "Tab", "-", "-", "Clear", "Return", "-",
+			"-", "Shift", "Control", "Menu", "Pause", "Capital", "Kana", "-",
+			"Junja", "Final", "Kanji", "-", "Escape", "Convert", "Nonconvert",
+			"Accept", "Mode Change", "Space", "Priot", "Next", "End", "Home",
+			"Left", "Up", "Right", "Down", "Select", "Print", "Execute",
+			"Snapshot", "Insert", "Delete", "Help", "0", "1", "2", "3", "4", "5",
+			"6", "7", "8", "9", "-", "-", "-", "-", "-", "-", "-", "A", "B", "C",
+			"D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
+			"R", "S", "T", "U", "V", "W", "X", "Y", "Z", "Left Windows",
+			"Right Windows", "Apps", "-", "Sleep", "Numpad 0", "Numpad 1",
+			"Numpad 2", "Numpad 3", "Numpad 4", "Numpad 5", "Numpad 6", "Numpad 7",
+			"Numpad 8", "Numpad 9", "Numpad *", "Numpad +", "Numpad /", "Numpad -",
+			"Numpad .", "Numpad /", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8",
+			"F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18",
+			"F19", "F20", "F21", "F22", "F23", "F24", "-", "-", "-", "-", "-", "-",
+			"-", "-", "Num Lock", "Scroll Lock", "-", "-", "-", "-", "-", "-", "-",
+			"-", "-", "-", "-", "-", "-", "-", "Left Shift", "Right Shight",
+			"Left Control", "Right Control", "Left Menu", "Right Menu", "-", "-",
+			"-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+			"-", "-", "-", "-", "-", "Plus", "Comma", "Minus", "Period", "-", "-",
+			"-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+			"-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+			"-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
+			"-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "Attn", "CrSel",
+			"ExSel", "Erase OEF", "Play", "Zoom", "PA1", "OEM Clear", "-" };
+	enum
+	{
+		GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
+		//buttons
+		GUI_ID_KEY_FORWARD_BUTTON,
+		GUI_ID_KEY_BACKWARD_BUTTON,
+		GUI_ID_KEY_LEFT_BUTTON,
+		GUI_ID_KEY_RIGHT_BUTTON,
+		GUI_ID_KEY_USE_BUTTON,
+		GUI_ID_KEY_FLY_BUTTON,
+		GUI_ID_KEY_FAST_BUTTON,
+		GUI_ID_KEY_JUMP_BUTTON,
+		GUI_ID_KEY_CHAT_BUTTON,
+		GUI_ID_KEY_SNEAK_BUTTON,
+		GUI_ID_KEY_INVENTORY_BUTTON,
+		GUI_ID_KEY_DUMP_BUTTON,
+		GUI_ID_KEY_RANGE_BUTTON
+	};
+
+class GUIKeyChangeMenu: public GUIModalMenu
+{
+public:
+	GUIKeyChangeMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
+			s32 id, IMenuManager *menumgr);
+	~GUIKeyChangeMenu();
+
+	void removeChildren();
+	/*
+	 Remove and re-add (or reposition) stuff
+	 */
+	void regenerateGui(v2u32 screensize);
+
+	void drawMenu();
+
+	bool acceptInput();
+
+	bool OnEvent(const SEvent& event);
+
+private:
+
+	void init_keys();
+
+	bool resetMenu();
+
+	gui::IGUIButton *forward;
+	gui::IGUIButton *backward;
+	gui::IGUIButton *left;
+	gui::IGUIButton *right;
+	gui::IGUIButton *use;
+	gui::IGUIButton *sneak;
+	gui::IGUIButton *jump;
+	gui::IGUIButton *inventory;
+	gui::IGUIButton *fly;
+	gui::IGUIButton *fast;
+	gui::IGUIButton *range;
+	gui::IGUIButton *dump;
+	gui::IGUIButton *chat;
+
+	u32 activeKey;
+	u32 key_forward;
+	u32 key_backward;
+	u32 key_left;
+	u32 key_right;
+	u32 key_use;
+	u32 key_sneak;
+	u32 key_jump;
+	u32 key_inventory;
+	u32 key_fly;
+	u32 key_fast;
+	u32 key_range;
+	u32 key_chat;
+	u32 key_dump;
+};
+
+#endif
+
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
index ef0a013f14e850403e5be8b65332d0426e01943a..1d7f224f614fa3bf3af6be185414f09fbd7f85f5 100644
--- a/src/guiMainMenu.cpp
+++ b/src/guiMainMenu.cpp
@@ -18,10 +18,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "guiMainMenu.h"
+#include "guiKeyChangeMenu.h"
 #include "debug.h"
 #include "serialization.h"
 #include <string>
 
+
+
+#include "gettext.h"
+
 GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
 		gui::IGUIElement* parent, s32 id,
 		IMenuManager *menumgr,
@@ -34,6 +39,10 @@ GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
 	m_gamecallback(gamecallback)
 {
 	assert(m_data);
+	this->env = env;
+	this->parent = parent;
+	this->id = id;
+	this->menumgr = menumgr;
 }
 
 GUIMainMenu::~GUIMainMenu()
@@ -70,35 +79,35 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	
 	// Client options
 	{
-		gui::IGUIElement *e = getElementFromId(258);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
 		if(e != NULL)
 			text_name = e->getText();
 		else
 			text_name = m_data->name;
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(256);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_ADDRESS_INPUT);
 		if(e != NULL)
 			text_address = e->getText();
 		else
 			text_address = m_data->address;
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(257);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_PORT_INPUT);
 		if(e != NULL)
 			text_port = e->getText();
 		else
 			text_port = m_data->port;
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(263);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_FANCYTREE_CB);
 		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
 			fancy_trees = ((gui::IGUICheckBox*)e)->isChecked();
 		else
 			fancy_trees = m_data->fancy_trees;
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(262);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_SMOOTH_LIGHTING_CB);
 		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
 			smooth_lighting = ((gui::IGUICheckBox*)e)->isChecked();
 		else
@@ -107,14 +116,14 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	
 	// Server options
 	{
-		gui::IGUIElement *e = getElementFromId(259);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_CREATIVE_CB);
 		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
 			creative_mode = ((gui::IGUICheckBox*)e)->isChecked();
 		else
 			creative_mode = m_data->creative_mode;
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(261);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_DAMAGE_CB);
 		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
 			enable_damage = ((gui::IGUICheckBox*)e)->isChecked();
 		else
@@ -168,14 +177,14 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 110, 20);
 		rect += topleft_client + v2s32(35, 50+6);
-		const wchar_t *text = L"Name/Password";
-		Environment->addStaticText(text, rect, false, true, this, -1);
+		Environment->addStaticText(chartowchar_t(gettext("Name/Password")), 
+			rect, false, true, this, -1);
 	}
 	{
 		core::rect<s32> rect(0, 0, 230, 30);
 		rect += topleft_client + v2s32(160, 50);
 		gui::IGUIElement *e = 
-		Environment->addEditBox(text_name.c_str(), rect, true, this, 258);
+		Environment->addEditBox(text_name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
 		if(text_name == L"")
 			Environment->setFocus(e);
 	}
@@ -191,14 +200,14 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 110, 20);
 		rect += topleft_client + v2s32(35, 100+6);
-		const wchar_t *text = L"Address/Port";
-		Environment->addStaticText(text, rect, false, true, this, -1);
+		Environment->addStaticText(chartowchar_t(gettext("Address/Port")),
+			rect, false, true, this, -1);
 	}
 	{
 		core::rect<s32> rect(0, 0, 230, 30);
 		rect += topleft_client + v2s32(160, 100);
 		gui::IGUIElement *e = 
-		Environment->addEditBox(text_address.c_str(), rect, true, this, 256);
+		Environment->addEditBox(text_address.c_str(), rect, true, this, GUI_ID_ADDRESS_INPUT);
 		if(text_name != L"")
 			Environment->setFocus(e);
 	}
@@ -206,34 +215,43 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 		core::rect<s32> rect(0, 0, 120, 30);
 		//rect += topleft_client + v2s32(160+250+20, 125);
 		rect += topleft_client + v2s32(size_client.X-60-100, 100);
-		Environment->addEditBox(text_port.c_str(), rect, true, this, 257);
+		Environment->addEditBox(text_port.c_str(), rect, true, this, GUI_ID_PORT_INPUT);
 	}
 	{
 		core::rect<s32> rect(0, 0, 400, 20);
 		rect += topleft_client + v2s32(160, 100+35);
-		const wchar_t *text = L"Leave address blank to start a local server.";
-		Environment->addStaticText(text, rect, false, true, this, -1);
+		Environment->addStaticText(chartowchar_t(gettext("Leave address blank to start a local server.")),
+			rect, false, true, this, -1);
 	}
 	{
 		core::rect<s32> rect(0, 0, 250, 30);
 		rect += topleft_client + v2s32(35, 150);
-		Environment->addCheckBox(fancy_trees, rect, this, 263,
-				L"Fancy trees");
+		Environment->addCheckBox(fancy_trees, rect, this, GUI_ID_FANCYTREE_CB,
+			chartowchar_t(gettext("Fancy trees"))); 
 	}
 	{
 		core::rect<s32> rect(0, 0, 250, 30);
 		rect += topleft_client + v2s32(35, 150+30);
-		Environment->addCheckBox(smooth_lighting, rect, this, 262,
-				L"Smooth Lighting");
+		Environment->addCheckBox(smooth_lighting, rect, this, GUI_ID_SMOOTH_LIGHTING_CB,
+				chartowchar_t(gettext("Smooth Lighting")));
 	}
 	// Start game button
 	{
 		core::rect<s32> rect(0, 0, 180, 30);
 		//rect += topleft_client + v2s32(size_client.X/2-180/2, 225-30/2);
 		rect += topleft_client + v2s32(size_client.X-180-40, 150+25);
-		Environment->addButton(rect, this, 257, L"Start Game / Connect");
+		Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
+			chartowchar_t(gettext("Start Game / Connect")));
 	}
 
+	// Key change button
+	{
+		core::rect<s32> rect(0, 0, 100, 30);
+		//rect += topleft_client + v2s32(size_client.X/2-180/2, 225-30/2);
+		rect += topleft_client + v2s32(size_client.X-180-40-100-20, 150+25);
+		Environment->addButton(rect, this, GUI_ID_CHANGE_KEYS_BUTTON,
+			chartowchar_t(gettext("Change keys")));
+	}
 	/*
 		Server section
 	*/
@@ -254,19 +272,22 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 250, 30);
 		rect += topleft_server + v2s32(35, 30);
-		Environment->addCheckBox(creative_mode, rect, this, 259, L"Creative Mode");
+		Environment->addCheckBox(creative_mode, rect, this, GUI_ID_CREATIVE_CB,
+			chartowchar_t(gettext("Creative Mode")));
 	}
 	{
 		core::rect<s32> rect(0, 0, 250, 30);
 		rect += topleft_server + v2s32(35, 60);
-		Environment->addCheckBox(enable_damage, rect, this, 261, L"Enable Damage");
+		Environment->addCheckBox(enable_damage, rect, this, GUI_ID_DAMAGE_CB,
+			chartowchar_t(gettext("Enable Damage")));
 	}
 	// Map delete button
 	{
 		core::rect<s32> rect(0, 0, 130, 30);
 		//rect += topleft_server + v2s32(size_server.X-40-130, 100+25);
 		rect += topleft_server + v2s32(40, 100+25);
-		Environment->addButton(rect, this, 260, L"Delete world");
+		Environment->addButton(rect, this, GUI_ID_DELETE_MAP_BUTTON,
+			  chartowchar_t(gettext("Delete map")));
 	}
 }
 
@@ -300,7 +321,7 @@ void GUIMainMenu::drawMenu()
 void GUIMainMenu::acceptInput()
 {
 	{
-		gui::IGUIElement *e = getElementFromId(258);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
 		if(e != NULL)
 			m_data->name = e->getText();
 	}
@@ -310,32 +331,32 @@ void GUIMainMenu::acceptInput()
 			m_data->password = e->getText();
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(256);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_ADDRESS_INPUT);
 		if(e != NULL)
 			m_data->address = e->getText();
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(257);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_PORT_INPUT);
 		if(e != NULL)
 			m_data->port = e->getText();
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(259);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_CREATIVE_CB);
 		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
 			m_data->creative_mode = ((gui::IGUICheckBox*)e)->isChecked();
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(261);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_DAMAGE_CB);
 		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
 			m_data->enable_damage = ((gui::IGUICheckBox*)e)->isChecked();
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(262);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_SMOOTH_LIGHTING_CB);
 		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
 			m_data->smooth_lighting = ((gui::IGUICheckBox*)e)->isChecked();
 	}
 	{
-		gui::IGUIElement *e = getElementFromId(263);
+		gui::IGUIElement *e = getElementFromId(GUI_ID_FANCYTREE_CB);
 		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
 			m_data->fancy_trees = ((gui::IGUICheckBox*)e)->isChecked();
 	}
@@ -377,11 +398,16 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
 		{
 			switch(event.GUIEvent.Caller->getID())
 			{
-			case 257: // Start game
+			case GUI_ID_JOIN_GAME_BUTTON: // Start game
 				acceptInput();
 				quitMenu();
 				return true;
-			case 260: // Delete map
+			case GUI_ID_CHANGE_KEYS_BUTTON: {
+				GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(env, parent, -1,menumgr);
+				kmenu->drop();
+				return true;
+			}
+			case GUI_ID_DELETE_MAP_BUTTON: // Delete map
 				// Don't accept input data, just set deletion request
 				m_data->delete_map = true;
 				m_accepted = true;
@@ -393,7 +419,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
 		{
 			switch(event.GUIEvent.Caller->getID())
 			{
-				case 256: case 257: case 258: case 264:
+				case GUI_ID_ADDRESS_INPUT: case GUI_ID_PORT_INPUT: case GUI_ID_NAME_INPUT: case 264:
 				acceptInput();
 				quitMenu();
 				return true;
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
index edd519024fb6a56879538d6cf144183f92f4c18b..87561f7974fd075dd4bea9c6fc440a5470fffcfb 100644
--- a/src/guiMainMenu.h
+++ b/src/guiMainMenu.h
@@ -27,6 +27,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 // 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():
@@ -87,6 +102,11 @@ class GUIMainMenu : public GUIModalMenu
 	MainMenuData *m_data;
 	bool m_accepted;
 	IGameCallback *m_gamecallback;
+
+	gui::IGUIEnvironment* env;
+	gui::IGUIElement* parent;
+	s32 id;
+	IMenuManager *menumgr;
 };
 
 #endif
diff --git a/src/guiMessageMenu.cpp b/src/guiMessageMenu.cpp
index 192911355151a7afdf7fe4a672798fa712d10571..155be6029c8b0f08174a121e8bb50c4d44617853 100644
--- a/src/guiMessageMenu.cpp
+++ b/src/guiMessageMenu.cpp
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serialization.h"
 #include <string>
 
+#include "gettext.h"
+
 GUIMessageMenu::GUIMessageMenu(gui::IGUIEnvironment* env,
 		gui::IGUIElement* parent, s32 id,
 		IMenuManager *menumgr,
@@ -87,7 +89,8 @@ void GUIMessageMenu::regenerateGui(v2u32 screensize)
 		core::rect<s32> rect(0, 0, 140, 30);
 		rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2+25);
 		gui::IGUIElement *e = 
-		Environment->addButton(rect, this, 257, L"Proceed");
+		Environment->addButton(rect, this, 257,
+			chartowchar_t(gettext("Proceed")));
 		Environment->setFocus(e);
 	}
 }
diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp
index ec1cd029ab321165df02086198bc0b71240316b1..fabe7511013d8ccd56394c1626859793b4a19d3e 100644
--- a/src/guiPasswordChange.cpp
+++ b/src/guiPasswordChange.cpp
@@ -21,6 +21,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "serialization.h"
 #include <string>
 
+#include "gettext.h"
+
 const int ID_oldPassword = 256;
 const int ID_newPassword1 = 257;
 const int ID_newPassword2 = 258;
@@ -97,8 +99,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 110, 20);
 		rect += topleft_client + v2s32(35, ypos+6);
-		const wchar_t *text = L"Old Password";
-		Environment->addStaticText(text, rect, false, true, this, -1);
+		Environment->addStaticText(chartowchar_t(gettext("Old Password")),
+			rect, false, true, this, -1);
 	}
 	{
 		core::rect<s32> rect(0, 0, 230, 30);
@@ -112,8 +114,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 110, 20);
 		rect += topleft_client + v2s32(35, ypos+6);
-		const wchar_t *text = L"New Password";
-		Environment->addStaticText(text, rect, false, true, this, -1);
+		Environment->addStaticText(chartowchar_t(gettext("New Password")),
+			rect, false, true, this, -1);
 	}
 	{
 		core::rect<s32> rect(0, 0, 230, 30);
@@ -126,8 +128,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 110, 20);
 		rect += topleft_client + v2s32(35, ypos+6);
-		const wchar_t *text = L"Confirm Password";
-		Environment->addStaticText(text, rect, false, true, this, -1);
+		Environment->addStaticText(chartowchar_t(gettext("Confirm Password")),
+			rect, false, true, this, -1);
 	}
 	{
 		core::rect<s32> rect(0, 0, 230, 30);
@@ -141,16 +143,17 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 140, 30);
 		rect = rect + v2s32(size.X/2-140/2, ypos);
-		Environment->addButton(rect, this, ID_change, L"Change");
+		Environment->addButton(rect, this, ID_change, chartowchar_t(gettext("Change")));
 	}
 
 	ypos += 50;
 	{
 		core::rect<s32> rect(0, 0, 300, 20);
 		rect += topleft_client + v2s32(35, ypos);
-		const wchar_t *text = L"Passwords do not match!";
 		IGUIElement *e = 
-		Environment->addStaticText(text, rect, false, true, this, ID_message);
+		Environment->addStaticText(
+			chartowchar_t(gettext("Passwords do not match!")),
+			rect, false, true, this, ID_message);
 		e->setVisible(false);
 	}
 
diff --git a/src/guiPauseMenu.cpp b/src/guiPauseMenu.cpp
index d32d1a10b375024d48fd9de457c695ef3956b110..61485518c0d3dcdadbb94be03c854c53b6bdbe4d 100644
--- a/src/guiPauseMenu.cpp
+++ b/src/guiPauseMenu.cpp
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "config.h"
 #include "main.h"
 
+#include "gettext.h"
+
 GUIPauseMenu::GUIPauseMenu(gui::IGUIEnvironment* env,
 		gui::IGUIElement* parent, s32 id,
 		IGameCallback *gamecallback,
@@ -104,43 +106,47 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 140, btn_height);
 		rect = rect + v2s32(size.X/2-140/2, btn_y);
-		Environment->addButton(rect, this, 256, L"Continue");
+		Environment->addButton(rect, this, 256,
+			chartowchar_t(gettext("Continue")));
 	}
 	btn_y += btn_height + btn_gap;
 	{
 		core::rect<s32> rect(0, 0, 140, btn_height);
 		rect = rect + v2s32(size.X/2-140/2, btn_y);
-		Environment->addButton(rect, this, 261, L"Change Password");
+		Environment->addButton(rect, this, 261,
+			chartowchar_t(gettext("Change Password")));
 	}
 	btn_y += btn_height + btn_gap;
 	{
 		core::rect<s32> rect(0, 0, 140, btn_height);
 		rect = rect + v2s32(size.X/2-140/2, btn_y);
-		Environment->addButton(rect, this, 260, L"Disconnect");
+		Environment->addButton(rect, this, 260,
+			chartowchar_t(gettext("Disconnect")));
 	}
 	btn_y += btn_height + btn_gap;
 	{
 		core::rect<s32> rect(0, 0, 140, btn_height);
 		rect = rect + v2s32(size.X/2-140/2, btn_y);
-		Environment->addButton(rect, this, 257, L"Exit to OS");
+		Environment->addButton(rect, this, 257,
+			chartowchar_t(gettext("Exit to OS")));
 	}
 
 	{
 		core::rect<s32> rect(0, 0, 180, 240);
 		rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2);
-		const wchar_t *text =
-		L"Keys:\n"
-		L"- WASD: Walk\n"
-		L"- Mouse left: dig blocks\n"
-		L"- Mouse right: place blocks\n"
-		L"- Mouse wheel: select item\n"
-		L"- 0...9: select item\n"
-		L"- Shift: sneak\n"
-		L"- R: Toggle viewing all loaded chunks\n"
-		L"- I: Inventory menu\n"
-		L"- ESC: This menu\n"
-		L"- T: Chat\n";
-		Environment->addStaticText(text, rect, false, true, this, 258);
+		Environment->addStaticText(chartowchar_t(gettext(
+		"Keys:\n"
+		"- WASD: Walk\n"
+		"- Mouse left: dig blocks\n"
+		"- Mouse right: place blocks\n"
+		"- Mouse wheel: select item\n"
+		"- 0...9: select item\n"
+		"- Shift: sneak\n"
+		"- R: Toggle viewing all loaded chunks\n"
+		"- I: Inventory menu\n"
+		"- ESC: This menu\n"
+		"- T: Chat\n"
+		)), rect, false, true, this, 258);
 	}
 	{
 		core::rect<s32> rect(0, 0, 180, 220);
@@ -166,8 +172,8 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
 		);*/
 
 		std::ostringstream os;
-		os<<"Minetest-c55\n";
-		os<<"by Perttu Ahola\n";
+		os<<"Minetest-delta\n";
+		os<<"by Perttu Ahola and contributors\n";
 		os<<"celeron55@gmail.com\n";
 		os<<BUILD_INFO<<"\n";
 		os<<"ud_path = "<<wrap_rows(porting::path_userdata, 20)<<"\n";
diff --git a/src/guiTextInputMenu.cpp b/src/guiTextInputMenu.cpp
index 2cb8cae6295b3e5417989408d650d959986e10b3..252e452f5d30f690c86c24abd2340b51c506c613 100644
--- a/src/guiTextInputMenu.cpp
+++ b/src/guiTextInputMenu.cpp
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serialization.h"
 #include <string>
 
+#include "gettext.h"
+
 GUITextInputMenu::GUITextInputMenu(gui::IGUIEnvironment* env,
 		gui::IGUIElement* parent, s32 id,
 		IMenuManager *menumgr,
@@ -105,7 +107,8 @@ void GUITextInputMenu::regenerateGui(v2u32 screensize)
 	{
 		core::rect<s32> rect(0, 0, 140, 30);
 		rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2+25);
-		Environment->addButton(rect, this, 257, L"Proceed");
+		Environment->addButton(rect, this, 257,
+			chartowchar_t(gettext("Proceed")));
 	}
 }
 
diff --git a/src/keycode.cpp b/src/keycode.cpp
index ad3c0b4018bf074dabf120eb5bb18f479fe50fe8..f014914d02a6f27e3070a60aa4693e9affc36a36 100644
--- a/src/keycode.cpp
+++ b/src/keycode.cpp
@@ -171,6 +171,46 @@ irr::EKEY_CODE keyname_to_keycode(const char *name)
 	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
 */
@@ -189,4 +229,7 @@ irr::EKEY_CODE getKeySetting(const char *settingname)
 	return c;
 }
 
-
+void clearKeyCache()
+{
+	g_key_setting_cache.clear();
+}
diff --git a/src/keycode.h b/src/keycode.h
index f19fe344282c77c8f999b2ddffe7cbb286a9d38b..300682b121d7fded978b9638bdfebde227b46bfe 100644
--- a/src/keycode.h
+++ b/src/keycode.h
@@ -21,11 +21,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define KEYCODE_HEADER
 
 #include "common_irrlicht.h"
+#include <string>
 
 irr::EKEY_CODE keyname_to_keycode(const char *name);
+std::string keycode_to_keyname(s32 keycode);
 
 // Key configuration getter
 irr::EKEY_CODE getKeySetting(const char *settingname);
 
+// Clear fast lookup cache
+void clearKeyCache();
+
 #endif
 
diff --git a/src/main.cpp b/src/main.cpp
index 5aff62bf29b776153ee77d75d1d7640515816670..783faa4e24a83cd68a82607fcc46573729f74db5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -357,6 +357,12 @@ TODO: Merge bahamada's audio stuff (clean patch available)
 
 TODO: Move content_features to mapnode_content_features.{h,cpp} or so
 
+TODO: Add some kind of content range validation to mapnode serialization
+
+TODO: Make sure menu text position is fixed
+
+TODO: Fix sector over limits error
+
 Making it more portable:
 ------------------------
  
@@ -419,6 +425,8 @@ Doing currently:
 #include "keycode.h"
 #include "tile.h"
 
+#include "gettext.h"
+
 // This makes textures
 ITextureSource *g_texturesource = NULL;
 
@@ -1148,6 +1156,10 @@ int main(int argc, char *argv[])
 
 	// Create user data directory
 	fs::CreateDir(porting::path_userdata);
+
+	setlocale(LC_MESSAGES, "");
+	bindtextdomain("minetest", (porting::path_userdata+"/locale").c_str());
+	textdomain("minetest");
 	
 	// Initialize debug streams
 #ifdef RUN_IN_PLACE
@@ -1363,6 +1375,9 @@ int main(int argc, char *argv[])
 	
 	// Set device in game parameters
 	device = device;
+
+	// Set the window caption
+	device->setWindowCaption(L"Minetest [Main Menu]");
 	
 	// Create time getter
 	g_timegetter = new IrrlichtTimeGetter(device);
diff --git a/src/map.cpp b/src/map.cpp
index dd32e55ba83dd9c4937f6910719460502f5ef2ca..1c63943c4c775f696a8bebef3b428b5b8f6bb85b 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -1240,17 +1240,19 @@ void Map::removeNodeAndUpdate(v3s16 p,
 	}
 
 	/*
-		Add neighboring liquid nodes to transform queue.
+		Add neighboring liquid nodes and this node to transform queue.
+		(it's vital for the node itself to get updated last.)
 	*/
-	v3s16 dirs[6] = {
+	v3s16 dirs[7] = {
 		v3s16(0,0,1), // back
 		v3s16(0,1,0), // top
 		v3s16(1,0,0), // right
 		v3s16(0,0,-1), // front
 		v3s16(0,-1,0), // bottom
 		v3s16(-1,0,0), // left
+		v3s16(0,0,0), // self
 	};
-	for(u16 i=0; i<6; i++)
+	for(u16 i=0; i<7; i++)
 	{
 		try
 		{
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index d4143b6d6a93199594591f4d86b5693269c5f915..bc4f822808273ff7aace824cbc3a9dde4ca66622 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -85,7 +85,7 @@ static void make_tree(VoxelManipulator &vmanip, v3s16 p0)
 	MapNode treenode(CONTENT_TREE);
 	MapNode leavesnode(CONTENT_LEAVES);
 
-	s16 trunk_h = myrand_range(3, 6);
+	s16 trunk_h = myrand_range(4, 5);
 	v3s16 p1 = p0;
 	for(s16 ii=0; ii<trunk_h; ii++)
 	{
@@ -97,7 +97,7 @@ static void make_tree(VoxelManipulator &vmanip, v3s16 p0)
 	// p1 is now the last piece of the trunk
 	p1.Y -= 1;
 
-	VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
+	VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
 	//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
 	Buffer<u8> leaves_d(leaves_a.getVolume());
 	for(s32 i=0; i<leaves_a.getVolume(); i++)
@@ -152,6 +152,34 @@ static void make_tree(VoxelManipulator &vmanip, v3s16 p0)
 	}
 }
 
+void make_papyrus(VoxelManipulator &vmanip, v3s16 p0)
+{
+	MapNode papyrusnode(CONTENT_PAPYRUS);
+
+	s16 trunk_h = myrand_range(2, 3);
+	v3s16 p1 = p0;
+	for(s16 ii=0; ii<trunk_h; ii++)
+	{
+		if(vmanip.m_area.contains(p1))
+			vmanip.m_data[vmanip.m_area.index(p1)] = papyrusnode;
+		p1.Y++;
+	}
+}
+
+void make_cactus(VoxelManipulator &vmanip, v3s16 p0)
+{
+	MapNode cactusnode(CONTENT_CACTUS);
+
+	s16 trunk_h = 3;
+	v3s16 p1 = p0;
+	for(s16 ii=0; ii<trunk_h; ii++)
+	{
+		if(vmanip.m_area.contains(p1))
+			vmanip.m_data[vmanip.m_area.index(p1)] = cactusnode;
+		p1.Y++;
+	}
+}
+
 #if 0
 static void make_randomstone(VoxelManipulator &vmanip, v3s16 p0)
 {
@@ -909,8 +937,8 @@ NoiseParams get_cave_noise2_params(u64 seed)
 
 NoiseParams get_ground_noise1_params(u64 seed)
 {
-	return NoiseParams(NOISE_PERLIN, seed+983240, 5,
-			0.60, 100.0, 30.0);
+	return NoiseParams(NOISE_PERLIN, seed+983240, 4,
+			0.55, 80.0, 40.0);
 }
 
 NoiseParams get_ground_crumbleness_params(u64 seed)
@@ -945,7 +973,7 @@ bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed)
 
 	double f = 0.55 + noise2d_perlin(
 			0.5+(float)p.X/250, 0.5+(float)p.Z/250,
-			seed+920381, 3, 0.5);
+			seed+920381, 3, 0.45);
 	if(f < 0.01)
 		f = 0.01;
 	else if(f >= 1.0)
@@ -1345,7 +1373,8 @@ void make_block(BlockMakeData *data)
 			data->seed, v2s16(blockpos.X, blockpos.Z), 1);
 	// Maximum amount of ground above the bottom of the central block
 	s16 maximum_ground_depth = maximum_groundlevel - node_min.Y;
-	
+
+	#if 0
 	/*
 		Special case for high air or water: Just fill with air and water.
 	*/
@@ -1379,6 +1408,7 @@ void make_block(BlockMakeData *data)
 		// We're done
 		return;
 	}
+	#endif
 
 	/*
 		If block is deep underground, this is set to true and ground
@@ -1808,6 +1838,8 @@ void make_block(BlockMakeData *data)
 				u32 current_depth = 0;
 				bool air_detected = false;
 				bool water_detected = false;
+				bool have_clay = false;
+
 				// Use fast index incrementing
 				s16 start_y = node_max.Y+2;
 				v3s16 em = vmanip.m_area.getExtent();
@@ -1834,7 +1866,19 @@ void make_block(BlockMakeData *data)
 						{
 							if(have_sand)
 							{
-								vmanip.m_data[i] = MapNode(CONTENT_SAND);
+								// Determine whether to have clay in the sand here
+								double claynoise = noise2d_perlin(
+										0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
+										data->seed+4321, 6, 0.95) + 0.5;
+				
+								have_clay = (y <= WATER_LEVEL) && (y >= WATER_LEVEL-2) && (
+									((claynoise > 0) && (claynoise < 0.04) && (current_depth == 0)) ||
+									((claynoise > 0) && (claynoise < 0.12) && (current_depth == 1))
+									);
+								if (have_clay)
+									vmanip.m_data[i] = MapNode(CONTENT_CLAY);
+								else
+									vmanip.m_data[i] = MapNode(CONTENT_SAND);
 							}
 							#if 1
 							else if(current_depth==0 && !water_detected
@@ -1894,7 +1938,7 @@ void make_block(BlockMakeData *data)
 			{
 				u32 i = data->vmanip->m_area.index(p);
 				MapNode *n = &data->vmanip->m_data[i];
-				if(n->getContent() != CONTENT_AIR && n->getContent() != CONTENT_IGNORE)
+				if(n->getContent() != CONTENT_AIR && n->getContent() != CONTENT_WATERSOURCE && n->getContent() != CONTENT_IGNORE)
 				{
 					found = true;
 					break;
@@ -1903,19 +1947,33 @@ void make_block(BlockMakeData *data)
 			// If not found, handle next one
 			if(found == false)
 				continue;
-			/*
-				Trees grow only on mud and grass
-			*/
+
 			{
 				u32 i = data->vmanip->m_area.index(p);
 				MapNode *n = &data->vmanip->m_data[i];
-				if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS)
-					continue;
+
+				if(n->getContent() != CONTENT_MUD && n->getContent() != CONTENT_GRASS && n->getContent() != CONTENT_SAND)
+						continue;
+
+				// Papyrus grows only on mud and in water
+				if(n->getContent() == CONTENT_MUD && y <= WATER_LEVEL)
+				{
+					p.Y++;
+					make_papyrus(vmanip, p);
+				}
+				// Trees grow only on mud and grass, on land
+				else if((n->getContent() == CONTENT_MUD || n->getContent() == CONTENT_GRASS) && y > WATER_LEVEL + 2)
+				{
+					p.Y++;
+					make_tree(vmanip, p);
+				}
+				// Cactii grow only on sand, on land
+				else if(n->getContent() == CONTENT_SAND && y > WATER_LEVEL + 2)
+				{
+					p.Y++;
+					make_cactus(vmanip, p);
+				}
 			}
-			// Tree will be placed one higher
-			p.Y++;
-			// Make a tree
-			make_tree(vmanip, p);
 		}
 
 #if 0
diff --git a/src/mapnode.h b/src/mapnode.h
index 1b10a546b4d0470ac442eaba2496ce2698e8473c..3b7ef58787899aa0780f3a0d8fd5e227c9c57cc0 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -148,6 +148,8 @@ struct ContentFeatures
 	// If the content is liquid, this is the flowing version of the liquid.
 	// If content is liquid, this is the same content.
 	content_t liquid_alternative_flowing;
+	// If the content is liquid, this is the source version of the liquid.
+	content_t liquid_alternative_source;
 	
 	// Amount of light the node emits
 	u8 light_source;
diff --git a/src/porting.cpp b/src/porting.cpp
index 7de042ab571143eb1e69b8d6fd958c60c094cd83..ff8cb3862276f3c1158b8336839dc7e9643221b7 100644
--- a/src/porting.cpp
+++ b/src/porting.cpp
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include "config.h"
 #include "debug.h"
+#include "filesys.h"
 
 #ifdef __APPLE__
 	#include "CoreFoundation/CoreFoundation.h"
@@ -210,6 +211,11 @@ void initializePaths()
 
 	path_data = std::string(buf) + "/../share/" + APPNAME;
 	//path_data = std::string(INSTALL_PREFIX) + "/share/" + APPNAME;
+	if (!fs::PathExists(path_data)) {
+		dstream<<"WARNING: data path " << path_data << " not found!";
+		path_data = std::string(buf) + "/../data";
+		dstream<<" Trying " << path_data << std::endl;
+	}
 	
 	path_userdata = std::string(getenv("HOME")) + "/." + APPNAME;